blob: 620daf33786bf2903ed7ae4274c817fca0bf7ef1 [file] [log] [blame]
AlexeiFedorov2f30f102023-03-13 19:37:46 +00001/*
AlexeiFedorovc398c8f2025-01-16 14:35:48 +00002 * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
AlexeiFedorov2f30f102023-03-13 19:37:46 +00003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
AlexeiFedorovc398c8f2025-01-16 14:35:48 +00007#include <stdlib.h>
8
AlexeiFedorov2f30f102023-03-13 19:37:46 +00009#include <arch_helpers.h>
10#include <arm_arch_svc.h>
11#include <debug.h>
12#include <drivers/arm/gic_v3.h>
13
14#include <host_realm_pmu.h>
15#include <realm_rsi.h>
16
17/* PMUv3 events */
18#define PMU_EVT_SW_INCR 0x0
19#define PMU_EVT_INST_RETIRED 0x8
20#define PMU_EVT_CPU_CYCLES 0x11
21#define PMU_EVT_MEM_ACCESS 0x13
22
23#define NOP_REPETITIONS 50
AlexeiFedorov2f30f102023-03-13 19:37:46 +000024
25#define PRE_OVERFLOW ~(0xF)
26
AlexeiFedorovc398c8f2025-01-16 14:35:48 +000027#define DELAY_MS 3000UL
AlexeiFedorov2f30f102023-03-13 19:37:46 +000028
AlexeiFedorovc398c8f2025-01-16 14:35:48 +000029static inline void read_all_counters(u_register_t *array, unsigned int num_cnts)
AlexeiFedorov2f30f102023-03-13 19:37:46 +000030{
31 array[0] = read_pmccntr_el0();
AlexeiFedorovc398c8f2025-01-16 14:35:48 +000032 for (unsigned int i = 0U; i < num_cnts; i++) {
AlexeiFedorov2f30f102023-03-13 19:37:46 +000033 array[i + 1] = read_pmevcntrn_el0(i);
34 }
35}
36
AlexeiFedorovc398c8f2025-01-16 14:35:48 +000037static inline void read_all_counter_configs(u_register_t *array, unsigned int num_cnts)
AlexeiFedorov2f30f102023-03-13 19:37:46 +000038{
39 array[0] = read_pmccfiltr_el0();
AlexeiFedorovc398c8f2025-01-16 14:35:48 +000040 for (unsigned int i = 0U; i < num_cnts; i++) {
AlexeiFedorov2f30f102023-03-13 19:37:46 +000041 array[i + 1] = read_pmevtypern_el0(i);
42 }
43}
44
45static inline void read_all_pmu_configs(u_register_t *array)
46{
47 array[0] = read_pmcntenset_el0();
48 array[1] = read_pmcr_el0();
49 array[2] = read_pmselr_el0();
50}
51
52static inline void enable_counting(void)
53{
54 write_pmcr_el0(read_pmcr_el0() | PMCR_EL0_E_BIT);
55 /* This function means we are about to use the PMU, synchronize */
56 isb();
57}
58
59static inline void disable_counting(void)
60{
61 write_pmcr_el0(read_pmcr_el0() & ~PMCR_EL0_E_BIT);
62 /* We also rely that disabling really did work */
63 isb();
64}
65
66static inline void clear_counters(void)
67{
68 write_pmcr_el0(read_pmcr_el0() | PMCR_EL0_C_BIT | PMCR_EL0_P_BIT);
69 isb();
70}
71
72static void pmu_reset(void)
73{
74 /* Reset all counters */
75 write_pmcr_el0(read_pmcr_el0() |
76 PMCR_EL0_DP_BIT | PMCR_EL0_C_BIT | PMCR_EL0_P_BIT);
77
78 /* Disable all counters */
79 write_pmcntenclr_el0(PMU_CLEAR_ALL);
80
81 /* Clear overflow status */
82 write_pmovsclr_el0(PMU_CLEAR_ALL);
83
84 /* Disable overflow interrupts on all counters */
85 write_pmintenclr_el1(PMU_CLEAR_ALL);
86 isb();
87}
88
89/*
90 * This test runs in Realm EL1, don't bother enabling counting at lower ELs
91 * and secure world. TF-A has other controls for them and counting there
92 * doesn't impact us.
93 */
94static inline void enable_cycle_counter(void)
95{
96 /*
97 * Set PMCCFILTR_EL0.U != PMCCFILTR_EL0.RLU
98 * to disable counting in Realm EL0.
99 * Set PMCCFILTR_EL0.P = PMCCFILTR_EL0.RLK
100 * to enable counting in Realm EL1.
101 * Set PMCCFILTR_EL0.NSH = PMCCFILTR_EL0_EL0.RLH
102 * to disable event counting in Realm EL2.
103 */
104 write_pmccfiltr_el0(PMCCFILTR_EL0_U_BIT |
105 PMCCFILTR_EL0_P_BIT | PMCCFILTR_EL0_RLK_BIT |
106 PMCCFILTR_EL0_NSH_BIT | PMCCFILTR_EL0_RLH_BIT);
107 write_pmcntenset_el0(read_pmcntenset_el0() | PMCNTENSET_EL0_C_BIT);
108 isb();
109}
110
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000111static inline void enable_event_counter(unsigned int ctr_num)
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000112{
113 /*
114 * Set PMEVTYPER_EL0.U != PMEVTYPER_EL0.RLU
115 * to disable event counting in Realm EL0.
116 * Set PMEVTYPER_EL0.P = PMEVTYPER_EL0.RLK
117 * to enable counting in Realm EL1.
118 * Set PMEVTYPER_EL0.NSH = PMEVTYPER_EL0.RLH
119 * to disable event counting in Realm EL2.
120 */
121 write_pmevtypern_el0(ctr_num,
122 PMEVTYPER_EL0_U_BIT |
123 PMEVTYPER_EL0_P_BIT | PMEVTYPER_EL0_RLK_BIT |
124 PMEVTYPER_EL0_NSH_BIT | PMEVTYPER_EL0_RLH_BIT |
125 (PMU_EVT_INST_RETIRED & PMEVTYPER_EL0_EVTCOUNT_BITS));
126 write_pmcntenset_el0(read_pmcntenset_el0() |
127 PMCNTENSET_EL0_P_BIT(ctr_num));
128 isb();
129}
130
131/* Doesn't really matter what happens, as long as it happens a lot */
132static inline void execute_nops(void)
133{
134 for (unsigned int i = 0U; i < NOP_REPETITIONS; i++) {
135 __asm__ ("orr x0, x0, x0\n");
136 }
137}
138
139/*
140 * Try the cycle counter with some NOPs to see if it works
141 */
142bool test_pmuv3_cycle_works_realm(void)
143{
144 u_register_t ccounter_start;
145 u_register_t ccounter_end;
146
147 pmu_reset();
148
149 enable_cycle_counter();
150 enable_counting();
151
152 ccounter_start = read_pmccntr_el0();
153 execute_nops();
154 ccounter_end = read_pmccntr_el0();
155 disable_counting();
156 clear_counters();
157
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000158 realm_printf("cycle counter counted from %lu to %lu\n",
159 ccounter_start, ccounter_end);
160 return (ccounter_start != ccounter_end);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000161}
162
Shruti Guptab1b37922024-01-13 21:49:04 +0000163/* Test if max counter available is same as that programmed by host */
164bool test_pmuv3_counter(void)
165{
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000166 unsigned int num_cnts, num_cnts_host;
Shruti Guptab1b37922024-01-13 21:49:04 +0000167
168 num_cnts_host = realm_shared_data_get_my_host_val(HOST_ARG1_INDEX);
169 num_cnts = GET_PMU_CNT;
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000170 realm_printf("CPU=%u num_cnts=%u num_cnts_host=%u\n", read_mpidr_el1() & MPID_MASK,
Shruti Guptab1b37922024-01-13 21:49:04 +0000171 num_cnts, num_cnts_host);
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000172 return (num_cnts == num_cnts_host);
Shruti Guptab1b37922024-01-13 21:49:04 +0000173}
174
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000175/*
176 * Try an event counter with some NOPs to see if it works.
177 */
178bool test_pmuv3_event_works_realm(void)
179{
180 u_register_t evcounter_start;
181 u_register_t evcounter_end;
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000182 unsigned int num_cnts = GET_PMU_CNT;
183 unsigned int ctr_num;
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000184
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000185 /* Seed the random number generator */
186 srand((unsigned int)read_cntpct_el0());
187
188 /* Select a random number of event counter */
189 ctr_num = (unsigned int)rand() % num_cnts;
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000190
191 pmu_reset();
192
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000193 enable_event_counter(ctr_num);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000194 enable_counting();
195
196 /*
197 * If any is enabled it will be in the first range.
198 */
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000199 evcounter_start = read_pmevcntrn_el0(ctr_num);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000200 execute_nops();
201 disable_counting();
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000202 evcounter_end = read_pmevcntrn_el0(ctr_num);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000203 clear_counters();
204
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000205 realm_printf("event counter #%u counted from %lu to %lu\n",
206 ctr_num, evcounter_start, evcounter_end);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000207 if (evcounter_start != evcounter_end) {
208 return true;
209 }
210 return false;
211}
212
213/*
214 * Check if entering/exiting RMM (with a NOP) preserves all PMU registers.
215 */
216bool test_pmuv3_rmm_preserves(void)
217{
218 u_register_t ctr_start[MAX_COUNTERS] = {0};
219 u_register_t ctr_cfg_start[MAX_COUNTERS] = {0};
220 u_register_t pmu_cfg_start[3];
221 u_register_t ctr_end[MAX_COUNTERS] = {0};
222 u_register_t ctr_cfg_end[MAX_COUNTERS] = {0};
223 u_register_t pmu_cfg_end[3];
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000224 unsigned int num_cnts = GET_PMU_CNT;
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000225
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000226 if (num_cnts == 0U) {
227 realm_printf("testing cycle counter\n");
228 } else {
229 realm_printf("testing %u event counters\n", num_cnts);
230 }
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000231
232 pmu_reset();
233
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000234 /* Pretend all counters have just been used */
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000235 enable_cycle_counter();
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000236
237 for (unsigned int i = 0U; i < num_cnts; i++) {
238 enable_event_counter(i);
239 }
240
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000241 enable_counting();
242 execute_nops();
243 disable_counting();
244
245 /* Get before reading */
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000246 read_all_counters(ctr_start, num_cnts);
247 read_all_counter_configs(ctr_cfg_start, num_cnts);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000248 read_all_pmu_configs(pmu_cfg_start);
249
250 /* Give RMM a chance to scramble everything */
Shruti Gupta40de8ec2023-10-12 21:45:12 +0100251 (void)rsi_get_version(RSI_ABI_VERSION_VAL);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000252
253 /* Get after reading */
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000254 read_all_counters(ctr_end, num_cnts);
255 read_all_counter_configs(ctr_cfg_end, num_cnts);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000256 read_all_pmu_configs(pmu_cfg_end);
257
258 if (memcmp(ctr_start, ctr_end, sizeof(ctr_start)) != 0) {
Shruti Guptaa276b202023-12-18 10:07:43 +0000259 realm_printf("SMC call did not preserve %s\n",
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000260 "counters");
261 return false;
262 }
263
264 if (memcmp(ctr_cfg_start, ctr_cfg_end, sizeof(ctr_cfg_start)) != 0) {
Shruti Guptaa276b202023-12-18 10:07:43 +0000265 realm_printf("SMC call did not preserve %s\n",
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000266 "counter config");
267 return false;
268 }
269
270 if (memcmp(pmu_cfg_start, pmu_cfg_end, sizeof(pmu_cfg_start)) != 0) {
Shruti Guptaa276b202023-12-18 10:07:43 +0000271 realm_printf("SMC call did not preserve %s\n",
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000272 "PMU registers");
273 return false;
274 }
275
276 return true;
277}
278
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000279bool test_pmuv3_overflow_interrupt(bool cycle_cnt)
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000280{
281 unsigned long priority_bits, priority;
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000282 unsigned long delay_time = DELAY_MS;
283 unsigned int num_cnts, ctr_num;
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000284
285 pmu_reset();
286
287 /* Get the number of priority bits implemented */
288 priority_bits = ((read_icv_ctrl_el1() >> ICV_CTLR_EL1_PRIbits_SHIFT) &
289 ICV_CTLR_EL1_PRIbits_MASK) + 1UL;
290
291 /* Unimplemented bits are RES0 and start from LSB */
292 priority = (0xFFUL << (8UL - priority_bits)) & 0xFFUL;
293
294 /* Set the priority mask register to allow all interrupts */
295 write_icv_pmr_el1(priority);
296
297 /* Enable Virtual Group 1 interrupts */
298 write_icv_igrpen1_el1(ICV_IGRPEN1_EL1_Enable);
299
300 /* Enable IRQ */
301 enable_irq();
302
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000303 if (cycle_cnt) {
304 write_pmccntr_el0(PRE_OVERFLOW);
305 enable_cycle_counter();
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000306
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000307 /* Enable interrupt on cycle counter */
308 write_pmintenset_el1(PMINTENSET_EL1_C_BIT);
309 realm_printf("waiting for PMU cycle counter vIRQ...\n");
310 } else {
311 num_cnts = GET_PMU_CNT;
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000312
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000313 /* Seed the random number generator */
314 srand((unsigned int)read_cntpct_el0());
315
316 /* Select a random number of event counter */
317 ctr_num = (unsigned int)rand() % num_cnts;
318
319 write_pmevcntrn_el0(ctr_num, PRE_OVERFLOW);
320 enable_event_counter(ctr_num);
321
322 /* Enable interrupt on event counter */
323 write_pmintenset_el1(PMINTENSET_EL1_P_BIT(ctr_num));
324 realm_printf("waiting for PMU event counter #%u vIRQ...\n",
325 ctr_num);
326 }
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000327
328 enable_counting();
329 execute_nops();
330
331 /*
332 * Interrupt handler will clear
333 * Performance Monitors Interrupt Enable Set register
334 * as part of handling the overflow interrupt.
335 */
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000336 while ((read_pmintenset_el1() != 0UL) && (delay_time != 0UL)) {
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000337 --delay_time;
338 }
339
340 /* Disable IRQ */
341 disable_irq();
342
343 pmu_reset();
344
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000345 if (delay_time == 0UL) {
346 realm_printf("PMU vIRQ %sreceived in %lums\n", "not ",
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000347 DELAY_MS);
348 return false;
349 }
350
AlexeiFedorovc398c8f2025-01-16 14:35:48 +0000351 realm_printf("PMU vIRQ %sreceived in %lums\n", "",
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000352 DELAY_MS - delay_time);
AlexeiFedorov2f30f102023-03-13 19:37:46 +0000353 return true;
354}