fix(realm): fix realm PMU tests
- FEATURE_PMU_NUM_CTRS field in feature_flag was used
to pass number of PMU event counters in realm creation.
The width of this field was set to 4, which was not
enough to pass numbers > 15 and was causing PMU tests
failures in FVP configuration with more than 15 event
counters implemented.
- This patch removes all FEATURE_XXX macros for setting
feature_flag and replaces them with the corresponding
RMI_FEATURE_REGISTER_0_XXX to match feature register 0.
- In host_set_pmu_state() function was setting PMSELR_EL0
to incorrect value 0 instead of 31 to select PMU cycle
counter for configurations with no event counters implemented.
- Test host_realm_pmuv3_mul_rec() was running incorrectly
with number of event counters set to 0 or 31.
- Reads and writes of PMXEVCNTR_EL0 and PMXEVTYPER_EL0
can be constrained unpredictable depending on the
value of PMSELR_EL0.SEL and number of accessible event
counters. See corresponding TF-RMM patch
https://review.trustedfirmware.org/c/TF-RMM/tf-rmm/+/34573
This patch fixes host_set_pmu_state() and
host_check_pmu_state() functions to avoid unpredictable access
to these registers.
This patch makes Realm PMU tests pass for all possible FVP
configurations clusterN.pmu-num_counters=[0...31].
Change-Id: I07cc0c14d5705338cb946ddbeddf4c2bad93abe8
Signed-off-by: AlexeiFedorov <Alexei.Fedorov@arm.com>
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
index 2c1d3a1..9abbb4a 100644
--- a/realm/include/realm_tests.h
+++ b/realm/include/realm_tests.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, Arm Limited. All rights reserved.
+ * Copyright (c) 2023-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -12,7 +12,7 @@
bool test_pmuv3_counter(void);
bool test_pmuv3_event_works_realm(void);
bool test_pmuv3_rmm_preserves(void);
-bool test_pmuv3_overflow_interrupt(void);
+bool test_pmuv3_overflow_interrupt(bool cycle_cnt);
bool test_realm_pauth_set_cmd(void);
bool test_realm_pauth_check_cmd(void);
bool test_realm_pauth_fault(void);
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index aeadb9e..5bc1ef4 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -433,8 +433,11 @@
case REALM_PMU_PRESERVE:
test_succeed = test_pmuv3_rmm_preserves();
break;
- case REALM_PMU_INTERRUPT:
- test_succeed = test_pmuv3_overflow_interrupt();
+ case REALM_PMU_CYCLE_INTERRUPT:
+ test_succeed = test_pmuv3_overflow_interrupt(true);
+ break;
+ case REALM_PMU_EVENT_INTERRUPT:
+ test_succeed = test_pmuv3_overflow_interrupt(false);
break;
case REALM_REQ_FPU_FILL_CMD:
fpu_state_write_rand(&rl_fpu_state_write);
diff --git a/realm/realm_pmuv3.c b/realm/realm_pmuv3.c
index 214f6df..620daf3 100644
--- a/realm/realm_pmuv3.c
+++ b/realm/realm_pmuv3.c
@@ -1,9 +1,11 @@
/*
- * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
+#include <stdlib.h>
+
#include <arch_helpers.h>
#include <arm_arch_svc.h>
#include <debug.h>
@@ -22,20 +24,20 @@
#define PRE_OVERFLOW ~(0xF)
-#define DELAY_MS 3000ULL
+#define DELAY_MS 3000UL
-static inline void read_all_counters(u_register_t *array, int impl_ev_ctrs)
+static inline void read_all_counters(u_register_t *array, unsigned int num_cnts)
{
array[0] = read_pmccntr_el0();
- for (unsigned int i = 0U; i < impl_ev_ctrs; i++) {
+ for (unsigned int i = 0U; i < num_cnts; i++) {
array[i + 1] = read_pmevcntrn_el0(i);
}
}
-static inline void read_all_counter_configs(u_register_t *array, int impl_ev_ctrs)
+static inline void read_all_counter_configs(u_register_t *array, unsigned int num_cnts)
{
array[0] = read_pmccfiltr_el0();
- for (unsigned int i = 0U; i < impl_ev_ctrs; i++) {
+ for (unsigned int i = 0U; i < num_cnts; i++) {
array[i + 1] = read_pmevtypern_el0(i);
}
}
@@ -106,7 +108,7 @@
isb();
}
-static inline void enable_event_counter(int ctr_num)
+static inline void enable_event_counter(unsigned int ctr_num)
{
/*
* Set PMEVTYPER_EL0.U != PMEVTYPER_EL0.RLU
@@ -153,27 +155,21 @@
disable_counting();
clear_counters();
- realm_printf("counted from %lu to %lu\n",
- ccounter_start, ccounter_end);
- if (ccounter_start != ccounter_end) {
- return true;
- }
- return false;
+ realm_printf("cycle counter counted from %lu to %lu\n",
+ ccounter_start, ccounter_end);
+ return (ccounter_start != ccounter_end);
}
/* Test if max counter available is same as that programmed by host */
bool test_pmuv3_counter(void)
{
- uint64_t num_cnts, num_cnts_host;
+ unsigned int num_cnts, num_cnts_host;
num_cnts_host = realm_shared_data_get_my_host_val(HOST_ARG1_INDEX);
num_cnts = GET_PMU_CNT;
- realm_printf("CPU=%u num_cnts=%lu num_cnts_host=%lu\n", read_mpidr_el1() & MPID_MASK,
+ realm_printf("CPU=%u num_cnts=%u num_cnts_host=%u\n", read_mpidr_el1() & MPID_MASK,
num_cnts, num_cnts_host);
- if (num_cnts == num_cnts_host) {
- return true;
- }
- return false;
+ return (num_cnts == num_cnts_host);
}
/*
@@ -183,28 +179,31 @@
{
u_register_t evcounter_start;
u_register_t evcounter_end;
+ unsigned int num_cnts = GET_PMU_CNT;
+ unsigned int ctr_num;
- if (GET_PMU_CNT == 0) {
- realm_printf("no event counters implemented\n");
- return false;
- }
+ /* Seed the random number generator */
+ srand((unsigned int)read_cntpct_el0());
+
+ /* Select a random number of event counter */
+ ctr_num = (unsigned int)rand() % num_cnts;
pmu_reset();
- enable_event_counter(0);
+ enable_event_counter(ctr_num);
enable_counting();
/*
* If any is enabled it will be in the first range.
*/
- evcounter_start = read_pmevcntrn_el0(0);
+ evcounter_start = read_pmevcntrn_el0(ctr_num);
execute_nops();
disable_counting();
- evcounter_end = read_pmevcntrn_el0(0);
+ evcounter_end = read_pmevcntrn_el0(ctr_num);
clear_counters();
- realm_printf("counted from %lu to %lu\n",
- evcounter_start, evcounter_end);
+ realm_printf("event counter #%u counted from %lu to %lu\n",
+ ctr_num, evcounter_start, evcounter_end);
if (evcounter_start != evcounter_end) {
return true;
}
@@ -222,30 +221,38 @@
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_PMU_CNT;
+ unsigned int num_cnts = GET_PMU_CNT;
- realm_printf("testing %u event counters\n", impl_ev_ctrs);
+ if (num_cnts == 0U) {
+ realm_printf("testing cycle counter\n");
+ } else {
+ realm_printf("testing %u event counters\n", num_cnts);
+ }
pmu_reset();
- /* Pretend counters have just been used */
+ /* Pretend all counters have just been used */
enable_cycle_counter();
- enable_event_counter(0);
+
+ for (unsigned int i = 0U; i < num_cnts; i++) {
+ enable_event_counter(i);
+ }
+
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_counters(ctr_start, num_cnts);
+ read_all_counter_configs(ctr_cfg_start, num_cnts);
read_all_pmu_configs(pmu_cfg_start);
/* Give RMM a chance to scramble everything */
(void)rsi_get_version(RSI_ABI_VERSION_VAL);
/* Get after reading */
- read_all_counters(ctr_end, impl_ev_ctrs);
- read_all_counter_configs(ctr_cfg_end, impl_ev_ctrs);
+ read_all_counters(ctr_end, num_cnts);
+ read_all_counter_configs(ctr_cfg_end, num_cnts);
read_all_pmu_configs(pmu_cfg_end);
if (memcmp(ctr_start, ctr_end, sizeof(ctr_start)) != 0) {
@@ -269,10 +276,11 @@
return true;
}
-bool test_pmuv3_overflow_interrupt(void)
+bool test_pmuv3_overflow_interrupt(bool cycle_cnt)
{
unsigned long priority_bits, priority;
- uint64_t delay_time = DELAY_MS;
+ unsigned long delay_time = DELAY_MS;
+ unsigned int num_cnts, ctr_num;
pmu_reset();
@@ -292,13 +300,30 @@
/* Enable IRQ */
enable_irq();
- write_pmevcntrn_el0(0, PRE_OVERFLOW);
- enable_event_counter(0);
+ if (cycle_cnt) {
+ write_pmccntr_el0(PRE_OVERFLOW);
+ enable_cycle_counter();
- /* Enable interrupt on event counter #0 */
- write_pmintenset_el1((1UL << 0));
+ /* Enable interrupt on cycle counter */
+ write_pmintenset_el1(PMINTENSET_EL1_C_BIT);
+ realm_printf("waiting for PMU cycle counter vIRQ...\n");
+ } else {
+ num_cnts = GET_PMU_CNT;
- realm_printf("waiting for PMU vIRQ...\n");
+ /* Seed the random number generator */
+ srand((unsigned int)read_cntpct_el0());
+
+ /* Select a random number of event counter */
+ ctr_num = (unsigned int)rand() % num_cnts;
+
+ write_pmevcntrn_el0(ctr_num, PRE_OVERFLOW);
+ enable_event_counter(ctr_num);
+
+ /* Enable interrupt on event counter */
+ write_pmintenset_el1(PMINTENSET_EL1_P_BIT(ctr_num));
+ realm_printf("waiting for PMU event counter #%u vIRQ...\n",
+ ctr_num);
+ }
enable_counting();
execute_nops();
@@ -308,7 +333,7 @@
* Performance Monitors Interrupt Enable Set register
* as part of handling the overflow interrupt.
*/
- while ((read_pmintenset_el1() != 0UL) && (delay_time != 0ULL)) {
+ while ((read_pmintenset_el1() != 0UL) && (delay_time != 0UL)) {
--delay_time;
}
@@ -317,14 +342,13 @@
pmu_reset();
- if (delay_time == 0ULL) {
- realm_printf("PMU vIRQ %sreceived in %llums\n", "not ",
+ if (delay_time == 0UL) {
+ realm_printf("PMU vIRQ %sreceived in %lums\n", "not ",
DELAY_MS);
return false;
}
- realm_printf("PMU vIRQ %sreceived in %llums\n", "",
+ realm_printf("PMU vIRQ %sreceived in %lums\n", "",
DELAY_MS - delay_time);
-
return true;
}