blob: ac3b33857899ff4f16f5b868c94e03f0abcb5733 [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
Boyan Karatotev794b0ac2025-06-20 13:13:29 +01002 * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch_helpers.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02008#include <debug.h>
Antonio Nino Diaz09a00ef2019-01-11 13:12:58 +00009#include <drivers/arm/arm_gic.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020010#include <events.h>
11#include <irq.h>
12#include <plat_topology.h>
13#include <platform.h>
14#include <platform_def.h>
15#include <power_management.h>
16#include <psci.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020017#include <stdlib.h>
18#include <tftf_lib.h>
19
20static event_t cpu_ready[PLATFORM_CORE_COUNT];
21static volatile unsigned int sgi_received[PLATFORM_CORE_COUNT];
22
23static test_result_t (*psci_validate_test_function)(void);
24
25/*
26 * Sets sgi_received flag for indicating SGI is processed so that
27 * test can exit in a clean state
28 */
29static int validate_pstate_sgi_handler(void *data)
30{
31 unsigned int core_pos;
32
33 core_pos = platform_get_core_pos(read_mpidr_el1());
34 sgi_received[core_pos] = 1;
35 return 0;
36}
37
38/*
39 * Gets the next possible composite state ID's combination and creates
40 * a composite ID from 0 to max power levels in the system. It then calculates
41 * whether the calculated composite ID is valid or invalid and validates EL3
42 * firmware's return value.
43 *
44 * Returns:
45 * TEST_RESULT_SUCCESS : If PSCI return value is as expected
46 * TEST_RESULT_FAIL : If PSCI return value is not as expected
47 */
48static test_result_t validate_el3_pstate_parsing(void)
49{
50 unsigned int j;
51 unsigned int test_suspend_type;
52 unsigned int suspend_state_id;
53 unsigned int power_level;
54 int psci_ret;
55 int expected_return_val;
56 unsigned int power_state;
57 unsigned int pstate_id_idx[PLAT_MAX_PWR_LEVEL + 1];
58
59 INIT_PWR_LEVEL_INDEX(pstate_id_idx);
60
61 for (j = 0; j <= PLAT_MAX_PWR_LEVEL; j++) {
62 do {
63 tftf_set_next_state_id_idx(j, pstate_id_idx);
64
65 if (pstate_id_idx[0] == PWR_STATE_INIT_INDEX)
66 break;
67
68 expected_return_val = tftf_get_pstate_vars(&power_level,
69 &test_suspend_type,
70 &suspend_state_id,
71 pstate_id_idx);
72 power_state = tftf_make_psci_pstate(power_level,
73 test_suspend_type,
74 suspend_state_id);
75
76 psci_ret = tftf_cpu_suspend(power_state);
77
78 if (expected_return_val != psci_ret) {
79 tftf_testcase_printf("Failed with values: "
80 " psci_ret:%d"
81 " expected_return_val:%d"
82 " power_state:0x%x\n",
83 psci_ret,
84 expected_return_val,
85 power_state);
86 return TEST_RESULT_FAIL;
87 }
88 } while (1);
89 }
90
91 return TEST_RESULT_SUCCESS;
92}
93
94/*
95 * Creates a composite state ID of a single valid local level above level
96 * zero and tests the EL3 firmware's return value matches
97 * PSCI_E_INVALID_PARAMS.
98 *
99 * For level 0, both local and composite power state are same. Hence, it's
100 * skipped.
101 *
102 * Returns:
103 * TEST_RESULT_SUCCESS : If PSCI return value is as expected
104 * TEST_RESULT_FAIL : If PSCI return value is not as expected
105 * TEST_RESULT_SKIPPED : If PLAT_MAX_PWR_LEVEL is < 1
106 */
107static test_result_t valid_only_local_stateid(void)
108{
109 unsigned int power_state;
110 int psci_ret;
111 unsigned int pstate_id_idx[PLAT_MAX_PWR_LEVEL + 1];
112 const plat_state_prop_t *local_level_state;
113 unsigned int i;
114
115 /* If only single power level is possible, SKIP the test */
116 if (!PLAT_MAX_PWR_LEVEL) {
117 tftf_testcase_printf("Platform has only a single valid local level\n");
118 return TEST_RESULT_SKIPPED;
119 }
120
121 INIT_PWR_LEVEL_INDEX(pstate_id_idx);
122
123 /*
124 * Start from power level 1, as local state for power level zero will
125 * be a valid composite id
126 */
127 for (i = 1; i <= PLAT_MAX_PWR_LEVEL; i++) {
128 do {
129
130 INFO("Getting next local state:\n");
131 tftf_set_next_local_state_id_idx(i, pstate_id_idx);
132
133 if (pstate_id_idx[i] == PWR_STATE_INIT_INDEX)
134 break;
135 local_level_state = plat_get_state_prop(i) + pstate_id_idx[i];
136 power_state = tftf_make_psci_pstate(i,
137 local_level_state->is_pwrdown,
138 local_level_state->state_ID << PLAT_LOCAL_PSTATE_WIDTH);
139
140 psci_ret = tftf_cpu_suspend(power_state);
141
142 if (psci_ret != PSCI_E_INVALID_PARAMS) {
143 tftf_testcase_printf("Expected invalid params but got :"
144 " psci_ret: %d"
145 " power_state:0x%x\n",
146 psci_ret,
147 power_state);
148
149 return TEST_RESULT_FAIL;
150 }
151 } while (pstate_id_idx[i] != PWR_STATE_INIT_INDEX);
152 }
153
154 return TEST_RESULT_SUCCESS;
155}
156
157/*
158 * Create a composite state ID of invalid state ID's at all levels and
159 * tests the EL3 firmware's return value matches PSCI_E_INVALID_PARAMS
160 *
161 * Returns:
162 * TEST_RESULT_SUCCESS : If PSCI return value is as expected
163 * TEST_RESULT_FAIL : If PSCI return value is not as expected
164 */
165static test_result_t completely_invalid_stateid(void)
166{
167 unsigned int state_id;
168 int i;
169 unsigned int power_state;
170 int psci_ret;
171
172 state_id = 0;
173
174 /* Make stateID with all invalid ID's for all power levels */
175 for (i = 0; i < PLAT_MAX_PWR_LEVEL; i++)
176 state_id = state_id |
177 ((PLAT_PSCI_DUMMY_STATE_ID & ((1 << PLAT_LOCAL_PSTATE_WIDTH) - 1))
178 << (PLAT_LOCAL_PSTATE_WIDTH * i));
179
180 power_state = tftf_make_psci_pstate(PLAT_MAX_PWR_LEVEL, PSTATE_TYPE_POWERDOWN, state_id);
181 psci_ret = tftf_cpu_suspend(power_state);
182
183 if (psci_ret != PSCI_E_INVALID_PARAMS) {
184 tftf_testcase_printf("Expected invalid params but got : %d"
185 " power_state:0x%x\n",
186 psci_ret,
187 power_state);
188
189 return TEST_RESULT_FAIL;
190 }
191
192 return TEST_RESULT_SUCCESS;
193}
194
195/*
196 * Creates a composite power state with invalid state type and tests
197 * the EL3 firmware's return value matches PSCI_E_INVALID_PARAMS
198 *
199 * Returns:
200 * TEST_RESULT_SUCCESS : If PSCI return value is as expected
201 * TEST_RESULT_FAIL : If PSCI return value is not as expected
202 */
203static test_result_t invalid_state_type(void)
204{
205 unsigned int test_suspend_type;
206 unsigned int suspend_state_id;
207 unsigned int power_level;
208 int psci_ret;
209 int expected_return_val;
210 unsigned int power_state;
211 unsigned int pstate_id_idx[PLAT_MAX_PWR_LEVEL + 1];
212
213 INIT_PWR_LEVEL_INDEX(pstate_id_idx);
214
215 do {
216 tftf_set_next_state_id_idx(PLAT_MAX_PWR_LEVEL, pstate_id_idx);
217
218 if (pstate_id_idx[0] == PWR_STATE_INIT_INDEX)
219 break;
220
221 expected_return_val = tftf_get_pstate_vars(&power_level,
222 &test_suspend_type,
223 &suspend_state_id,
224 pstate_id_idx);
225
226 if (expected_return_val != PSCI_E_SUCCESS)
227 continue;
228
229 /* Reverse the suspend type */
230 power_state = tftf_make_psci_pstate(power_level, !test_suspend_type, suspend_state_id);
231
232 psci_ret = tftf_cpu_suspend(power_state);
233
234 if (PSCI_E_INVALID_PARAMS != psci_ret) {
235 tftf_testcase_printf("Failed with values:"
236 " psci_ret:%d"
237 " power_state:0x%x\n",
238 psci_ret,
239 power_state);
240 return TEST_RESULT_FAIL;
241 }
242 } while (1);
243
244 return TEST_RESULT_SUCCESS;
245}
246
247/*
248 * Creates a composite power state with valid local state but invalid
249 * power level and tests the EL3 firmware's return value matches
250 * PSCI_E_INVALID_PARAMS
251 *
252 * Returns:
253 * TEST_RESULT_SUCCESS : If PSCI return value is as expected
254 * TEST_RESULT_FAIL : If PSCI return value is not as expected
255 * TEST_RESULT_SKIPPED : If EL3 firmware supports extended state ID
256 */
257static test_result_t invalid_power_level(void)
258{
259 unsigned int test_suspend_type;
260 unsigned int suspend_state_id;
261 unsigned int power_level;
262 int psci_ret;
263 int expected_return_val;
264 unsigned int power_state;
265 unsigned int pstate_id_idx[PLAT_MAX_PWR_LEVEL + 1];
266
267 /* Skip the test if EL3 firmware code supports extended state ID */
268 if (!tftf_is_psci_pstate_format_original())
269 return TEST_RESULT_SKIPPED;
270
271 INIT_PWR_LEVEL_INDEX(pstate_id_idx);
272
273 do {
274 tftf_set_next_state_id_idx(PLAT_MAX_PWR_LEVEL, pstate_id_idx);
275
276 if (pstate_id_idx[0] == PWR_STATE_INIT_INDEX)
277 break;
278
279 expected_return_val = tftf_get_pstate_vars(&power_level,
280 &test_suspend_type,
281 &suspend_state_id,
282 pstate_id_idx);
283
284 if (expected_return_val != PSCI_E_SUCCESS)
285 continue;
286
287 /* Make a power state with invalid power level */
288 power_state = tftf_make_psci_pstate(power_level + 1,
289 test_suspend_type,
290 suspend_state_id);
291
292 psci_ret = tftf_cpu_suspend(power_state);
293
294 if (PSCI_E_INVALID_PARAMS != psci_ret) {
295 tftf_testcase_printf("Failed with values:"
296 " psci_ret:%d"
297 " power_state:0x%x\n",
298 psci_ret,
299 power_state);
300 return TEST_RESULT_FAIL;
301 }
302 } while (1);
303
304 return TEST_RESULT_SUCCESS;
305}
306
307/*
308 * Creates a composite state ID of valid local state at some levels
309 * and invalid state ID at others and tests the EL3 firmware's return
310 * value matches PSCI_E_INVALID_PARAMS
311 *
312 * Returns:
313 * TEST_RESULT_SUCCESS : If PSCI return value is as expected
314 * TEST_RESULT_FAIL : If PSCI return value is not as expected
315 * TEST_RESULT_SKIPPED : If PLAT_MAX_PWR_LEVEL is < 1
316 */
317static test_result_t mixed_state_id(void)
318{
319 unsigned int test_suspend_type;
320 unsigned int suspend_state_id;
321 unsigned int power_level;
322 int psci_ret;
323 unsigned int power_state;
324 unsigned int j;
325 unsigned int pstate_id_idx[PLAT_MAX_PWR_LEVEL + 1];
326 unsigned int invalid_id_set;
327
328 /*
329 * Platform contains only one power level and hence we cann't have
330 * both valid and invalid local state
331 */
332 if (!PLAT_MAX_PWR_LEVEL)
333 return TEST_RESULT_SKIPPED;
334
335 INIT_PWR_LEVEL_INDEX(pstate_id_idx);
336
337 do {
338 tftf_set_next_state_id_idx(PLAT_MAX_PWR_LEVEL, pstate_id_idx);
339
340 if (pstate_id_idx[0] == PWR_STATE_INIT_INDEX)
341 break;
342
343 if (tftf_get_pstate_vars(&power_level,
344 &test_suspend_type,
345 &suspend_state_id,
346 pstate_id_idx) != PSCI_E_SUCCESS)
347 continue;
348
349 invalid_id_set = 0;
350
351 /*
352 * Generate a state ID with valid and invalid local state ID's at
353 * different levels
354 */
355 for (j = 0; j <= power_level; j++) {
356 /* Set index to invalid level for even power levels */
357 if (rand() % 2) {
358 suspend_state_id = suspend_state_id |
359 ((PLAT_PSCI_DUMMY_STATE_ID &
360 ((1 << PLAT_LOCAL_PSTATE_WIDTH) - 1))
361 << (PLAT_LOCAL_PSTATE_WIDTH * j));
362 invalid_id_set = 1;
363 }
364 }
365
366 /*
367 * Overwrite state ID for a random level if none of the
368 * levels are invalid
369 */
370 if (!invalid_id_set) {
371 j = rand() % (power_level + 1);
372 suspend_state_id = suspend_state_id |
373 ((PLAT_PSCI_DUMMY_STATE_ID &
374 ((1 << PLAT_LOCAL_PSTATE_WIDTH) - 1))
375 << (PLAT_LOCAL_PSTATE_WIDTH * j));
376 }
377
378 power_state = tftf_make_psci_pstate(power_level, test_suspend_type, suspend_state_id);
379 psci_ret = tftf_cpu_suspend(power_state);
380
381 if (psci_ret != PSCI_E_INVALID_PARAMS) {
382 tftf_testcase_printf("Failed with values: power_level: %d"
383 " test_suspend_type: %d"
384 " suspend_state_id:%d"
385 " psci_ret:%d"
386 " power_state:0x%x\n",
387 power_level,
388 test_suspend_type,
389 suspend_state_id,
390 psci_ret,
391 power_state);
392 return TEST_RESULT_FAIL;
393 }
394 } while (1);
395
396 return TEST_RESULT_SUCCESS;
397}
398
399
400/*
401 * This function contains common code for all test cases and runs the testcase
402 * specific code.
403 *
404 * Returns the return value of test case specific code
405 */
406static test_result_t test_execute_test_function(void)
407{
408 test_result_t ret;
409 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
410
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100411 tftf_irq_register_handler_sgi(IRQ_NS_SGI_0, validate_pstate_sgi_handler);
412 tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200413
414 /*
415 * Mask IRQ to prevent the interrupt handler being invoked
416 * and clearing the interrupt. A pending interrupt will cause this
417 * CPU to wake-up from suspend.
418 */
419 disable_irq();
420
421 /* Configure an SGI to wake-up from suspend */
422 tftf_send_sgi(IRQ_NS_SGI_0, core_pos);
423
424 ret = (*psci_validate_test_function)();
425
426 enable_irq();
427
428 while (!sgi_received[core_pos])
429 ;
430
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100431 tftf_irq_disable_sgi(IRQ_NS_SGI_0);
432 tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200433
434 return ret;
435}
436
437/*
438 * Non-lead CPU entry point function for all PSCI PSTATE validation functions.
439 *
440 * Returns the return value of test case specific code
441 */
442static test_result_t test_non_lead_cpu_validate_ep(void)
443{
444 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
445
446 /*
447 * Tell the lead CPU that the calling CPU is ready to validate
448 * extended power state parsing
449 */
450 tftf_send_event(&cpu_ready[core_pos]);
451
452 return test_execute_test_function();
453}
454
455/*
456 * Lead CPU entry point function for all PSCI PSTATE validation functions. It
457 * powers on all secondaries and executes the test cases specific code.
458 *
459 * Returns the return value of test case specific code or SKIPPED in case
460 * if it is unable to power on a core or EL3 firmware only supports NULL
461 * stateID.
462 */
463static test_result_t test_lead_cpu_validate_ep(void)
464{
465 test_result_t ret;
466 unsigned int core_pos;
467 unsigned long lead_mpid;
468 unsigned long target_mpid;
469 unsigned long cpu_node;
470 int i;
471
472 if (tftf_is_psci_state_id_null()) {
473 tftf_testcase_printf("EL3 firmware supports only NULL stateID\n");
474 return TEST_RESULT_SKIPPED;
475 }
476
477 /* Initialise cpu_ready event variable */
478 for (i = 0; i < PLATFORM_CORE_COUNT; i++)
479 tftf_init_event(&cpu_ready[i]);
480
481 lead_mpid = read_mpidr_el1() & MPID_MASK;
482
483 /*
484 * Preparation step: Power on all cores.
485 */
486 for_each_cpu(cpu_node) {
487 target_mpid = tftf_get_mpidr_from_node(cpu_node);
488 /* Skip lead CPU as it is already on */
489 if (target_mpid == lead_mpid)
490 continue;
491
492 ret = tftf_cpu_on(target_mpid,
493 (uintptr_t) test_non_lead_cpu_validate_ep,
494 0);
495 if (ret != PSCI_E_SUCCESS) {
496 tftf_testcase_printf(
497 "Failed to power on CPU 0x%x (%d)\n",
498 (unsigned int)target_mpid, ret);
499
500 return TEST_RESULT_SKIPPED;
501 }
502 }
503
504 /* Wait for all non-lead CPUs to be ready */
505 for_each_cpu(cpu_node) {
506 target_mpid = tftf_get_mpidr_from_node(cpu_node);
507 /* Skip lead CPU */
508 if (target_mpid == lead_mpid)
509 continue;
510
511 core_pos = platform_get_core_pos(target_mpid);
512 tftf_wait_for_event(&cpu_ready[core_pos]);
513 }
514
515 /* Call this to execute the test case specific code */
516 return test_execute_test_function();
517}
518
519/*
520 * Creates all possible valid local state ID's at all levels and tests
521 * the EL3 firmware's return value matches the expected one.
522 */
523test_result_t test_psci_validate_pstate(void)
524{
525 psci_validate_test_function = &validate_el3_pstate_parsing;
526 return test_lead_cpu_validate_ep();
527}
528
529/*
530 * Creates a composite state ID of a single valid local level and
531 * tests the EL3 firmware's return value matches the expected one.
532 */
533test_result_t test_psci_valid_local_pstate(void)
534{
535 psci_validate_test_function = &valid_only_local_stateid;
536 return test_lead_cpu_validate_ep();
537}
538
539/*
540 * Create a composite state ID of invalid state ID's at all levels
541 * and tests the EL3 firmware's return value matches the expected
542 * one.
543 */
544test_result_t test_psci_invalid_stateID(void)
545{
546 psci_validate_test_function = &completely_invalid_stateid;
547 return test_lead_cpu_validate_ep();
548}
549
550/*
551 * Creates a composite state ID of invalid state type and tests the
552 * EL3 firmware's return value matches the expected one.
553 */
554test_result_t test_psci_invalid_state_type(void)
555{
556 psci_validate_test_function = &invalid_state_type;
557 return test_lead_cpu_validate_ep();
558}
559
560/*
561 * Creates a composite state ID of invalid power level in original
562 * state format and tests the EL3 firmware's return value matches the
563 * expected value.
564 */
565test_result_t test_psci_invalid_power_level(void)
566{
567 psci_validate_test_function = &invalid_power_level;
568 return test_lead_cpu_validate_ep();
569}
570
571/*
572 * Creates a composite state ID of valid local state at some levels
573 * and invalid state ID at others and tests the EL3 firmware's return
574 * value matches the expected value
575 */
576test_result_t test_psci_mixed_state_id(void)
577{
578 psci_validate_test_function = &mixed_state_id;
579 return test_lead_cpu_validate_ep();
580}