feat(rme): add PMU Realm tests
This patch adds Realm PMU payload tests with
PMU interrupt handling.
Signed-off-by: AlexeiFedorov <Alexei.Fedorov@arm.com>
Change-Id: I86ef96252e04c57db385e129227cc0d7dcd1fec2
diff --git a/realm/aarch64/realm_exceptions.S b/realm/aarch64/realm_exceptions.S
index 6ce8810..99b601d 100644
--- a/realm/aarch64/realm_exceptions.S
+++ b/realm/aarch64/realm_exceptions.S
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -14,7 +14,7 @@
*/
.macro unhandled_exception name
vector_entry \name
- b crash_dump
+ b crash_dump
end_vector_entry \name
.endm
@@ -106,11 +106,11 @@
mov x19, sp
bl tftf_sync_exception_handler
cbnz x0, 0f
- mov x0, x19
+ mov x0, x19
/* Save original stack pointer value on the stack */
add x1, x0, #0x100
str x1, [x0, #0xf8]
- b print_exception
+ b print_exception
0: restore_gp_regs
add sp, sp, #0x100
eret
diff --git a/realm/platform.h b/realm/include/platform.h
similarity index 65%
rename from realm/platform.h
rename to realm/include/platform.h
index 2c6ad27..de91c16 100644
--- a/realm/platform.h
+++ b/realm/include/platform.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
-#ifndef REALM_PLATFORM_H
-#define REALM_PLATFORM_H
+#ifndef PLATFORM_H
+#define PLATFORM_H
/*
* Helper that returns a linear core ID from a MPID
@@ -17,4 +17,4 @@
return 0U;
}
-#endif /* REALM_PLATFORM_H */
+#endif /* PLATFORM_H */
diff --git a/realm/realm_rsi.h b/realm/include/realm_rsi.h
similarity index 95%
rename from realm/realm_rsi.h
rename to realm/include/realm_rsi.h
index 34721cd..c7ea5a5 100644
--- a/realm/realm_rsi.h
+++ b/realm/include/realm_rsi.h
@@ -9,6 +9,7 @@
#define REALM_RSI_H
#include <stdint.h>
+#include <host_shared_data.h>
#include <tftf_lib.h>
#define SMC_RSI_CALL_BASE 0xC4000190
@@ -63,12 +64,6 @@
RSI_ERROR_COUNT
} rsi_status_t;
-enum host_call_cmd {
- HOST_CALL_GET_SHARED_BUFF_CMD = 1U,
- HOST_CALL_EXIT_SUCCESS_CMD,
- HOST_CALL_EXIT_FAILED_CMD
-};
-
struct rsi_realm_config {
/* IPA width in bits */
SET_MEMBER(unsigned long ipa_width, 0, 0x1000); /* Offset 0 */
@@ -100,6 +95,7 @@
#define RSI_ABI_VERSION SMC_RSI_FID(0U)
+
/*
* arg0 == struct rsi_realm_config address
*/
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
new file mode 100644
index 0000000..0e653ba
--- /dev/null
+++ b/realm/include/realm_tests.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef REALM_TESTS_H
+#define REALM_TESTS_H
+
+bool test_pmuv3_cycle_works_realm(void);
+bool test_pmuv3_event_works_realm(void);
+bool test_pmuv3_rmm_preserves(void);
+bool test_pmuv3_overflow_interrupt(void);
+
+#endif /* REALM_TESTS_H */
+
diff --git a/realm/realm.mk b/realm/realm.mk
index 638f02e..b033627 100644
--- a/realm/realm.mk
+++ b/realm/realm.mk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2022, Arm Limited. All rights reserved.
+# Copyright (c) 2022-2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -18,15 +18,17 @@
-Iinclude/runtime_services \
-Iinclude/runtime_services/host_realm_managment \
-Irealm \
- -Irealm/aarch64
+ -Irealm/aarch64 \
+ -Irealm/include
REALM_SOURCES:= \
$(addprefix realm/, \
aarch64/realm_entrypoint.S \
aarch64/realm_exceptions.S \
realm_debug.c \
- realm_payload_main.c \
realm_interrupt.c \
+ realm_payload_main.c \
+ realm_pmuv3.c \
realm_rsi.c \
realm_shared_data.c \
)
diff --git a/realm/realm_interrupt.c b/realm/realm_interrupt.c
index 02ab55c..7f8dc15 100644
--- a/realm/realm_interrupt.c
+++ b/realm/realm_interrupt.c
@@ -1,13 +1,27 @@
/*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
+#include <arch_helpers.h>
#include <debug.h>
+#include <host_realm_pmu.h>
-/* dummy interrupt handler as for now*/
+/* Realm interrupt handler */
void realm_interrupt_handler(void)
{
- INFO("%s\n", __func__);
+ /* Read INTID and acknowledge interrupt */
+ unsigned long iar1_el1 = read_icv_iar1_el1();
+
+ /* Deactivate interrupt */
+ write_icv_eoir1_el1(iar1_el1);
+
+ /* Clear PMU interrupt */
+ if (iar1_el1 == PMU_VIRQ) {
+ write_pmintenclr_el1(read_pmintenset_el1());
+ isb();
+ } else {
+ panic();
+ }
}
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index bd4dec7..414c329 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -12,17 +12,18 @@
#include <host_shared_data.h>
#include "realm_def.h"
#include <realm_rsi.h>
+#include <realm_tests.h>
#include <tftf_lib.h>
/*
- * This function reads sleep time in ms from shared buffer and spins PE in a loop
- * for that time period.
+ * This function reads sleep time in ms from shared buffer and spins PE
+ * in a loop for that time period.
*/
static void realm_sleep_cmd(void)
{
uint64_t sleep = realm_shared_data_get_host_val(HOST_SLEEP_INDEX);
- INFO("REALM_PAYLOAD: Realm payload going to sleep for %llums\n", sleep);
+ realm_printf("Realm: going to sleep for %llums\n", sleep);
waitms(sleep);
}
@@ -35,11 +36,11 @@
version = rsi_get_version();
if (version == (u_register_t)SMC_UNKNOWN) {
- ERROR("SMC_RSI_ABI_VERSION failed (%ld)", (long)version);
+ realm_printf("SMC_RSI_ABI_VERSION failed (%ld)", (long)version);
return;
}
- INFO("RSI ABI version %u.%u (expected: %u.%u)",
+ realm_printf("RSI ABI version %u.%u (expected: %u.%u)",
RSI_ABI_VERSION_GET_MAJOR(version),
RSI_ABI_VERSION_GET_MINOR(version),
RSI_ABI_VERSION_GET_MAJOR(RSI_ABI_VERSION),
@@ -56,12 +57,13 @@
*/
void realm_payload_main(void)
{
- uint8_t cmd = 0U;
bool test_succeed = false;
realm_set_shared_structure((host_shared_data_t *)rsi_get_ns_buffer());
+
if (realm_get_shared_structure() != NULL) {
- cmd = realm_shared_data_get_realm_cmd();
+ uint8_t cmd = realm_shared_data_get_realm_cmd();
+
switch (cmd) {
case REALM_SLEEP_CMD:
realm_sleep_cmd();
@@ -71,8 +73,20 @@
realm_get_rsi_version();
test_succeed = true;
break;
+ case REALM_PMU_CYCLE:
+ test_succeed = test_pmuv3_cycle_works_realm();
+ break;
+ case REALM_PMU_EVENT:
+ test_succeed = test_pmuv3_event_works_realm();
+ break;
+ case REALM_PMU_PRESERVE:
+ test_succeed = test_pmuv3_rmm_preserves();
+ break;
+ case REALM_PMU_INTERRUPT:
+ test_succeed = test_pmuv3_overflow_interrupt();
+ break;
default:
- INFO("REALM_PAYLOAD: %s invalid cmd=%hhu", __func__, cmd);
+ realm_printf("%s() invalid cmd %u\n", __func__, cmd);
break;
}
}
diff --git a/realm/realm_pmuv3.c b/realm/realm_pmuv3.c
new file mode 100644
index 0000000..862e93e
--- /dev/null
+++ b/realm/realm_pmuv3.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <arm_arch_svc.h>
+#include <debug.h>
+#include <drivers/arm/gic_v3.h>
+
+#include <host_realm_pmu.h>
+#include <realm_rsi.h>
+
+/* PMUv3 events */
+#define PMU_EVT_SW_INCR 0x0
+#define PMU_EVT_INST_RETIRED 0x8
+#define PMU_EVT_CPU_CYCLES 0x11
+#define PMU_EVT_MEM_ACCESS 0x13
+
+#define NOP_REPETITIONS 50
+#define MAX_COUNTERS 32
+
+#define PRE_OVERFLOW ~(0xF)
+
+#define DELAY_MS 3000ULL
+
+static inline void read_all_counters(u_register_t *array, int impl_ev_ctrs)
+{
+ array[0] = read_pmccntr_el0();
+ for (unsigned int i = 0U; i < impl_ev_ctrs; i++) {
+ array[i + 1] = read_pmevcntrn_el0(i);
+ }
+}
+
+static inline void read_all_counter_configs(u_register_t *array, int impl_ev_ctrs)
+{
+ array[0] = read_pmccfiltr_el0();
+ for (unsigned int i = 0U; i < impl_ev_ctrs; i++) {
+ array[i + 1] = read_pmevtypern_el0(i);
+ }
+}
+
+static inline void read_all_pmu_configs(u_register_t *array)
+{
+ array[0] = read_pmcntenset_el0();
+ array[1] = read_pmcr_el0();
+ array[2] = read_pmselr_el0();
+}
+
+static inline void enable_counting(void)
+{
+ write_pmcr_el0(read_pmcr_el0() | PMCR_EL0_E_BIT);
+ /* This function means we are about to use the PMU, synchronize */
+ isb();
+}
+
+static inline void disable_counting(void)
+{
+ write_pmcr_el0(read_pmcr_el0() & ~PMCR_EL0_E_BIT);
+ /* We also rely that disabling really did work */
+ isb();
+}
+
+static inline void clear_counters(void)
+{
+ write_pmcr_el0(read_pmcr_el0() | PMCR_EL0_C_BIT | PMCR_EL0_P_BIT);
+ isb();
+}
+
+static void pmu_reset(void)
+{
+ /* Reset all counters */
+ write_pmcr_el0(read_pmcr_el0() |
+ PMCR_EL0_DP_BIT | PMCR_EL0_C_BIT | PMCR_EL0_P_BIT);
+
+ /* Disable all counters */
+ write_pmcntenclr_el0(PMU_CLEAR_ALL);
+
+ /* Clear overflow status */
+ write_pmovsclr_el0(PMU_CLEAR_ALL);
+
+ /* Disable overflow interrupts on all counters */
+ write_pmintenclr_el1(PMU_CLEAR_ALL);
+ isb();
+}
+
+/*
+ * This test runs in Realm EL1, don't bother enabling counting at lower ELs
+ * and secure world. TF-A has other controls for them and counting there
+ * doesn't impact us.
+ */
+static inline void enable_cycle_counter(void)
+{
+ /*
+ * Set PMCCFILTR_EL0.U != PMCCFILTR_EL0.RLU
+ * to disable counting in Realm EL0.
+ * Set PMCCFILTR_EL0.P = PMCCFILTR_EL0.RLK
+ * to enable counting in Realm EL1.
+ * Set PMCCFILTR_EL0.NSH = PMCCFILTR_EL0_EL0.RLH
+ * to disable event counting in Realm EL2.
+ */
+ write_pmccfiltr_el0(PMCCFILTR_EL0_U_BIT |
+ PMCCFILTR_EL0_P_BIT | PMCCFILTR_EL0_RLK_BIT |
+ PMCCFILTR_EL0_NSH_BIT | PMCCFILTR_EL0_RLH_BIT);
+ write_pmcntenset_el0(read_pmcntenset_el0() | PMCNTENSET_EL0_C_BIT);
+ isb();
+}
+
+static inline void enable_event_counter(int ctr_num)
+{
+ /*
+ * Set PMEVTYPER_EL0.U != PMEVTYPER_EL0.RLU
+ * to disable event counting in Realm EL0.
+ * Set PMEVTYPER_EL0.P = PMEVTYPER_EL0.RLK
+ * to enable counting in Realm EL1.
+ * Set PMEVTYPER_EL0.NSH = PMEVTYPER_EL0.RLH
+ * to disable event counting in Realm EL2.
+ */
+ write_pmevtypern_el0(ctr_num,
+ PMEVTYPER_EL0_U_BIT |
+ PMEVTYPER_EL0_P_BIT | PMEVTYPER_EL0_RLK_BIT |
+ PMEVTYPER_EL0_NSH_BIT | PMEVTYPER_EL0_RLH_BIT |
+ (PMU_EVT_INST_RETIRED & PMEVTYPER_EL0_EVTCOUNT_BITS));
+ write_pmcntenset_el0(read_pmcntenset_el0() |
+ PMCNTENSET_EL0_P_BIT(ctr_num));
+ isb();
+}
+
+/* Doesn't really matter what happens, as long as it happens a lot */
+static inline void execute_nops(void)
+{
+ for (unsigned int i = 0U; i < NOP_REPETITIONS; i++) {
+ __asm__ ("orr x0, x0, x0\n");
+ }
+}
+
+/*
+ * Try the cycle counter with some NOPs to see if it works
+ */
+bool test_pmuv3_cycle_works_realm(void)
+{
+ u_register_t ccounter_start;
+ u_register_t ccounter_end;
+
+ pmu_reset();
+
+ enable_cycle_counter();
+ enable_counting();
+
+ ccounter_start = read_pmccntr_el0();
+ execute_nops();
+ ccounter_end = read_pmccntr_el0();
+ disable_counting();
+ clear_counters();
+
+ realm_printf("Realm: counted from %lu to %lu\n",
+ ccounter_start, ccounter_end);
+ if (ccounter_start != ccounter_end) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Try an event counter with some NOPs to see if it works.
+ */
+bool test_pmuv3_event_works_realm(void)
+{
+ u_register_t evcounter_start;
+ u_register_t evcounter_end;
+
+ if (GET_CNT_NUM == 0) {
+ realm_printf("Realm: no event counters implemented\n");
+ return false;
+ }
+
+ pmu_reset();
+
+ enable_event_counter(0);
+ enable_counting();
+
+ /*
+ * If any is enabled it will be in the first range.
+ */
+ evcounter_start = read_pmevcntrn_el0(0);
+ execute_nops();
+ disable_counting();
+ evcounter_end = read_pmevcntrn_el0(0);
+ clear_counters();
+
+ realm_printf("Realm: counted from %lu to %lu\n",
+ evcounter_start, evcounter_end);
+ if (evcounter_start != evcounter_end) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Check if entering/exiting RMM (with a NOP) preserves all PMU registers.
+ */
+bool test_pmuv3_rmm_preserves(void)
+{
+ u_register_t ctr_start[MAX_COUNTERS] = {0};
+ u_register_t ctr_cfg_start[MAX_COUNTERS] = {0};
+ u_register_t pmu_cfg_start[3];
+ u_register_t ctr_end[MAX_COUNTERS] = {0};
+ u_register_t ctr_cfg_end[MAX_COUNTERS] = {0};
+ u_register_t pmu_cfg_end[3];
+ unsigned int impl_ev_ctrs = GET_CNT_NUM;
+
+ realm_printf("Realm: testing %u event counters\n", impl_ev_ctrs);
+
+ pmu_reset();
+
+ /* Pretend counters have just been used */
+ enable_cycle_counter();
+ enable_event_counter(0);
+ enable_counting();
+ execute_nops();
+ disable_counting();
+
+ /* Get before reading */
+ read_all_counters(ctr_start, impl_ev_ctrs);
+ read_all_counter_configs(ctr_cfg_start, impl_ev_ctrs);
+ read_all_pmu_configs(pmu_cfg_start);
+
+ /* Give RMM a chance to scramble everything */
+ (void)rsi_get_version();
+
+ /* Get after reading */
+ read_all_counters(ctr_end, impl_ev_ctrs);
+ read_all_counter_configs(ctr_cfg_end, impl_ev_ctrs);
+ read_all_pmu_configs(pmu_cfg_end);
+
+ if (memcmp(ctr_start, ctr_end, sizeof(ctr_start)) != 0) {
+ realm_printf("Realm: SMC call did not preserve %s\n",
+ "counters");
+ return false;
+ }
+
+ if (memcmp(ctr_cfg_start, ctr_cfg_end, sizeof(ctr_cfg_start)) != 0) {
+ realm_printf("Realm: SMC call did not preserve %s\n",
+ "counter config");
+ return false;
+ }
+
+ if (memcmp(pmu_cfg_start, pmu_cfg_end, sizeof(pmu_cfg_start)) != 0) {
+ realm_printf("Realm: SMC call did not preserve %s\n",
+ "PMU registers");
+ return false;
+ }
+
+ return true;
+}
+
+bool test_pmuv3_overflow_interrupt(void)
+{
+ unsigned long priority_bits, priority;
+ uint64_t delay_time = DELAY_MS;
+
+ pmu_reset();
+
+ /* Get the number of priority bits implemented */
+ priority_bits = ((read_icv_ctrl_el1() >> ICV_CTLR_EL1_PRIbits_SHIFT) &
+ ICV_CTLR_EL1_PRIbits_MASK) + 1UL;
+
+ /* Unimplemented bits are RES0 and start from LSB */
+ priority = (0xFFUL << (8UL - priority_bits)) & 0xFFUL;
+
+ /* Set the priority mask register to allow all interrupts */
+ write_icv_pmr_el1(priority);
+
+ /* Enable Virtual Group 1 interrupts */
+ write_icv_igrpen1_el1(ICV_IGRPEN1_EL1_Enable);
+
+ /* Enable IRQ */
+ enable_irq();
+
+ write_pmevcntrn_el0(0, PRE_OVERFLOW);
+ enable_event_counter(0);
+
+ /* Enable interrupt on event counter #0 */
+ write_pmintenset_el1((1UL << 0));
+
+ realm_printf("Realm: waiting for PMU vIRQ...\n");
+
+ enable_counting();
+ execute_nops();
+
+ /*
+ * Interrupt handler will clear
+ * Performance Monitors Interrupt Enable Set register
+ * as part of handling the overflow interrupt.
+ */
+ while ((read_pmintenset_el1() != 0UL) && (delay_time != 0ULL)) {
+ --delay_time;
+ }
+
+ /* Disable IRQ */
+ disable_irq();
+
+ pmu_reset();
+
+ if (delay_time == 0ULL) {
+ realm_printf("Realm: PMU vIRQ %sreceived in %llums\n", "not ",
+ DELAY_MS);
+ return false;
+ }
+
+ realm_printf("Realm: PMU vIRQ %sreceived in %llums\n", "",
+ DELAY_MS - delay_time);
+
+ return true;
+}