/* * Copyright (c) 2022-2023, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include /* 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; }