blob: 9543704b9c88726cd69be7984d5141185c48b263 [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>
Antonio Nino Diaz9c9f92c2019-03-13 13:57:39 +00008#include <arch_features.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02009#include <assert.h>
10#include <debug.h>
Antonio Nino Diaz09a00ef2019-01-11 13:12:58 +000011#include <drivers/arm/arm_gic.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020012#include <irq.h>
13#include <mmio.h>
14#include <nvm.h>
Alexei Fedorov719714f2019-10-03 10:57:53 +010015#include <pauth.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020016#include <plat_topology.h>
17#include <platform.h>
18#include <platform_def.h>
19#include <power_management.h>
20#include <psci.h>
Ambroise Vincent602b7f52019-02-11 14:13:43 +000021#include <stdint.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020022#include <string.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020023#include <tftf.h>
24#include <tftf_lib.h>
25#include <timer.h>
26
Jayanth Dodderi Chidanand5cf04632022-05-04 15:34:20 +010027#define MIN_RETRY_TO_POWER_ON_LEAD_CPU 10
28
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020029/* version information for TFTF */
30extern const char version_string[];
31
32unsigned int lead_cpu_mpid;
33
34/* Defined in hotplug.c */
35extern volatile test_function_t test_entrypoint[PLATFORM_CORE_COUNT];
36
37/* Per-CPU results for the current test */
38static test_result_t test_results[PLATFORM_CORE_COUNT];
39
40/* Context ID passed to tftf_psci_cpu_on() */
41static u_register_t cpu_on_ctx_id_arr[PLATFORM_CORE_COUNT];
42
43static unsigned int test_is_rebooting;
44
Alexei Fedorov09ed7102020-01-30 14:06:28 +000045/* Parameters arg0 and arg1 passed from BL31 */
Harrison Mutai6e011642023-09-22 17:17:35 +010046#if TRANSFER_LIST
47u_register_t ns_tl;
48u_register_t tl_signature;
49#else
Alexei Fedorov09ed7102020-01-30 14:06:28 +000050u_register_t fw_config_base;
Harrison Mutai6e011642023-09-22 17:17:35 +010051#endif
Alexei Fedorov09ed7102020-01-30 14:06:28 +000052u_register_t hw_config_base;
53
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020054static inline const test_suite_t *current_testsuite(void)
55{
56 test_ref_t test_to_run;
57 tftf_get_test_to_run(&test_to_run);
58 return &testsuites[test_to_run.testsuite_idx];
59}
60
61static inline const test_case_t *current_testcase(void)
62{
63 test_ref_t test_to_run;
64 tftf_get_test_to_run(&test_to_run);
65 return &testsuites[test_to_run.testsuite_idx].
66 testcases[test_to_run.testcase_idx];
67}
68
69/*
70 * Identify the next test in the tests list and update the NVM data to point to
71 * that test.
72 * If there is no more tests to execute, return NULL.
73 * Otherwise, return the test case.
74 */
75static const test_case_t *advance_to_next_test(void)
76{
77 test_ref_t test_to_run;
78 const test_case_t *testcase;
79 unsigned int testcase_idx;
80 unsigned int testsuite_idx;
81
82#if DEBUG
83 test_progress_t progress;
84 tftf_get_test_progress(&progress);
85 assert(progress == TEST_COMPLETE);
86#endif
87
88 tftf_get_test_to_run(&test_to_run);
89 testcase_idx = test_to_run.testcase_idx;
90 testsuite_idx = test_to_run.testsuite_idx;
91
92 /* Move to the next test case in the current test suite */
93 ++testcase_idx;
94 testcase = &testsuites[testsuite_idx].testcases[testcase_idx];
95
96 if (testcase->name == NULL) {
97 /*
98 * There's no more test cases in the current test suite so move
99 * to the first test case of the next test suite.
100 */
101 const test_suite_t *testsuite;
102 testcase_idx = 0;
103 ++testsuite_idx;
104 testsuite = &testsuites[testsuite_idx];
105 testcase = &testsuite->testcases[0];
106
107 if (testsuite->name == NULL) {
108 /*
109 * This was the last test suite so there's no more tests
110 * at all.
111 */
112 return NULL;
113 }
114 }
115
116 VERBOSE("Moving to test (%u,%u)\n", testsuite_idx, testcase_idx);
117 test_to_run.testsuite_idx = testsuite_idx;
118 test_to_run.testcase_idx = testcase_idx;
119 tftf_set_test_to_run(test_to_run);
120 tftf_set_test_progress(TEST_READY);
121
122 return testcase;
123}
124
125/*
126 * This function is executed only by the lead CPU.
127 * It prepares the environment for the next test to run.
128 */
129static void prepare_next_test(void)
130{
131 unsigned int mpid;
132 unsigned int core_pos;
133 unsigned int cpu_node;
134
135 /* This function should be called by the lead CPU only */
136 assert((read_mpidr_el1() & MPID_MASK) == lead_cpu_mpid);
137
138 /*
139 * Only the lead CPU should be powered on at this stage. All other CPUs
140 * should be powered off or powering off. If some CPUs are not powered
141 * off yet, wait for them to power off.
142 */
143 for_each_cpu(cpu_node) {
144 mpid = tftf_get_mpidr_from_node(cpu_node);
145 if (mpid == lead_cpu_mpid)
146 assert(tftf_is_cpu_online(mpid));
147 else
148 while (tftf_psci_affinity_info(mpid, MPIDR_AFFLVL0)
149 == PSCI_STATE_ON)
150 ;
151 }
152
153 /* No CPU should have entered the test yet */
154 assert(tftf_get_ref_cnt() == 0);
155
156 /* Populate the test entrypoint for the lead CPU */
157 core_pos = platform_get_core_pos(lead_cpu_mpid);
158 test_entrypoint[core_pos] = (test_function_t) current_testcase()->test;
159
160 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i)
161 test_results[i] = TEST_RESULT_NA;
162
Sandrine Bailleux125d58c2018-11-07 17:11:59 +0100163 /* If we're starting a new testsuite, announce it. */
164 test_ref_t test_to_run;
165 tftf_get_test_to_run(&test_to_run);
166 if (test_to_run.testcase_idx == 0) {
167 print_testsuite_start(current_testsuite());
168 }
169
170 print_test_start(current_testcase());
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200171
172 /* Program the watchdog */
173 tftf_platform_watchdog_set();
174
175 /* TODO: Take a 1st timestamp to be able to measure test duration */
176
177 tftf_set_test_progress(TEST_IN_PROGRESS);
178}
179
180/*
181 * Go through individual CPUs' test results and determine the overall
182 * test result from that.
183 */
184static test_result_t get_overall_test_result(void)
185{
186 test_result_t result = TEST_RESULT_NA;
187 unsigned int cpu_mpid;
188 unsigned int cpu_node;
189 unsigned int core_pos;
190
191 for_each_cpu(cpu_node) {
192 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
193 core_pos = platform_get_core_pos(cpu_mpid);
194
195 switch (test_results[core_pos]) {
196 case TEST_RESULT_NA:
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200197 /* Ignoring */
198 break;
199
200 case TEST_RESULT_SKIPPED:
201 /*
202 * If at least one CPU skipped the test, consider the
203 * whole test as skipped as well.
204 */
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200205 return TEST_RESULT_SKIPPED;
206
207 case TEST_RESULT_SUCCESS:
208 result = TEST_RESULT_SUCCESS;
209 break;
210
211 case TEST_RESULT_FAIL:
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200212 return TEST_RESULT_FAIL;
213
214 case TEST_RESULT_CRASHED:
215 /*
216 * Means the CPU never returned from the test whereas it
217 * was supposed to. Either there is a bug in the test's
218 * implementation or some sort of unexpected crash
219 * happened.
220 * If at least one CPU crashed, consider the whole test
221 * as crashed as well.
222 */
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200223 return TEST_RESULT_CRASHED;
224
225 default:
226 ERROR("Unknown test result value: %u\n",
227 test_results[core_pos]);
228 panic();
229 }
230 }
231
232 /*
233 * At least one CPU (i.e. the lead CPU) should have participated in the
234 * test.
235 */
236 assert(result != TEST_RESULT_NA);
237 return result;
238}
239
240/*
241 * This function is executed by the last CPU to exit the test only.
242 * It does the necessary bookkeeping and reports the overall test result.
243 * If it was the last test, it will also generate the final test report.
244 * Otherwise, it will reset the platform, provided that the platform
245 * supports reset from non-trusted world. This ensures that the next test
246 * runs in a clean environment
247 *
248 * Return 1 if this was the last test, 0 otherwise.
249 */
250static unsigned int close_test(void)
251{
252 const test_case_t *next_test;
253
254#if DEBUG
255 /*
256 * Check that the test didn't pretend resetting the platform, when in
257 * fact it returned into the framework.
258 *
259 * If that happens, the test implementation should be fixed.
260 * However, it is not a fatal error so just flag the problem in debug
261 * builds.
262 */
263 test_progress_t progress;
264 tftf_get_test_progress(&progress);
265 assert(progress != TEST_REBOOTING);
266#endif /* DEBUG */
267
268 tftf_set_test_progress(TEST_COMPLETE);
269 test_is_rebooting = 0;
270
271 /* TODO: Take a 2nd timestamp and compute test duration */
272
273 /* Reset watchdog */
274 tftf_platform_watchdog_reset();
275
276 /* Ensure no CPU is still executing the test */
277 assert(tftf_get_ref_cnt() == 0);
278
279 /* Save test result in NVM */
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200280 tftf_testcase_set_result(current_testcase(),
Sandrine Bailleux125d58c2018-11-07 17:11:59 +0100281 get_overall_test_result(),
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200282 0);
283
Sandrine Bailleux125d58c2018-11-07 17:11:59 +0100284 print_test_end(current_testcase());
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200285
286 /* The test is finished, let's move to the next one (if any) */
287 next_test = advance_to_next_test();
288
289 /* If this was the last test then report all results */
290 if (!next_test) {
Sandrine Bailleux125d58c2018-11-07 17:11:59 +0100291 print_tests_summary();
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200292 tftf_clean_nvm();
293 return 1;
294 } else {
295#if (PLAT_SUPPORTS_NS_RESET && !NEW_TEST_SESSION && USE_NVM)
296 /*
297 * Reset the platform so that the next test runs in a clean
298 * environment.
299 */
300 INFO("Reset platform before executing next test:%p\n",
301 (void *) &(next_test->test));
302 tftf_plat_reset();
303 bug_unreachable();
304#endif
305 }
306
307 return 0;
308}
309
310/*
311 * Hand over to lead CPU, i.e.:
312 * 1) Power on lead CPU
313 * 2) Power down calling CPU
314 */
315static void __dead2 hand_over_to_lead_cpu(void)
316{
317 int ret;
Jayanth Dodderi Chidanand5cf04632022-05-04 15:34:20 +0100318 unsigned int tftf_cpu_pwr_on_ctr = 0U;
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200319 unsigned int mpid = read_mpidr_el1() & MPID_MASK;
320 unsigned int core_pos = platform_get_core_pos(mpid);
321
322 VERBOSE("CPU%u: Hand over to lead CPU%u\n", core_pos,
323 platform_get_core_pos(lead_cpu_mpid));
324
325 /*
326 * Power on lead CPU.
327 * The entry point address passed as the 2nd argument of tftf_cpu_on()
328 * doesn't matter because it will be overwritten by prepare_next_test().
329 * Pass a NULL pointer to easily catch the problem in case something
330 * goes wrong.
Jayanth Dodderi Chidanand5cf04632022-05-04 15:34:20 +0100331 *
332 * In CI with four world system (Normal, Secure, Root and Realm), on few
333 * instances, while the framework tries to turn on the CPU for next-test
334 * it fails to do so and receives error code (-4 : ALREADY_ON).
335 * This is due to the fact that the lead-cpu is still powering down as
336 * per EL-3 but invisible to EL-2. Hence retrying it in a loop with a
337 * small delay in bewteen for certain iterations will resolve it.
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200338 */
Jayanth Dodderi Chidanand5cf04632022-05-04 15:34:20 +0100339 while (tftf_cpu_pwr_on_ctr < MIN_RETRY_TO_POWER_ON_LEAD_CPU) {
340 ret = tftf_cpu_on(lead_cpu_mpid, 0, 0);
341 if (ret == PSCI_E_SUCCESS) {
342 break;
343 } else {
344 tftf_cpu_pwr_on_ctr += 1;
345 waitms(1);
346 }
347 }
348
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200349 if (ret != PSCI_E_SUCCESS) {
350 ERROR("CPU%u: Failed to power on lead CPU%u (%d)\n",
351 core_pos, platform_get_core_pos(lead_cpu_mpid), ret);
352 panic();
353 }
354
355 /* Wait for lead CPU to be actually powered on */
356 while (!tftf_is_cpu_online(lead_cpu_mpid))
357 ;
358
359 /*
360 * Lead CPU has successfully booted, let's now power down the calling
361 * core.
362 */
363 tftf_cpu_off();
364 panic();
365}
366
367void __dead2 run_tests(void)
368{
369 unsigned int mpid = read_mpidr_el1() & MPID_MASK;
370 unsigned int core_pos = platform_get_core_pos(mpid);
371 unsigned int test_session_finished;
372 unsigned int cpus_cnt;
373
374 while (1) {
375 if (mpid == lead_cpu_mpid && (tftf_get_ref_cnt() == 0))
376 prepare_next_test();
377
378 /*
379 * Increment the reference count to indicate that the CPU is
380 * participating in the test.
381 */
382 tftf_inc_ref_cnt();
383
384 /*
385 * Mark the CPU's test result as "crashed". This is meant to be
386 * overwritten by the actual test result when the CPU returns
387 * from the test function into the framework. In case the CPU
388 * crashes in the test (and thus, never returns from it), this
389 * variable will hold the right value.
390 */
391 test_results[core_pos] = TEST_RESULT_CRASHED;
392
393 /*
394 * Jump to the test entrypoint for this core.
395 * - For the lead CPU, it has been populated by
396 * prepare_next_test()
397 * - For other CPUs, it has been populated by tftf_cpu_on() or
398 * tftf_try_cpu_on()
399 */
400 while (test_entrypoint[core_pos] == 0)
401 ;
402
403 test_results[core_pos] = test_entrypoint[core_pos]();
404 test_entrypoint[core_pos] = 0;
405
406 /*
407 * Decrement the reference count to indicate that the CPU is not
408 * participating in the test any longer.
409 */
410 cpus_cnt = tftf_dec_ref_cnt();
411
412 /*
413 * Last CPU to exit the test gets to do the necessary
414 * bookkeeping and to report the overall test result.
415 * Other CPUs shut down.
416 */
417 if (cpus_cnt == 0) {
418 test_session_finished = close_test();
419 if (test_session_finished)
420 break;
421
422 if (mpid != lead_cpu_mpid) {
423 hand_over_to_lead_cpu();
424 bug_unreachable();
425 }
426 } else {
427 tftf_cpu_off();
428 panic();
429 }
430 }
431
432 tftf_exit();
433
434 /* Should never reach this point */
435 bug_unreachable();
436}
437
438u_register_t tftf_get_cpu_on_ctx_id(unsigned int core_pos)
439{
440 assert(core_pos < PLATFORM_CORE_COUNT);
441
442 return cpu_on_ctx_id_arr[core_pos];
443}
444
445void tftf_set_cpu_on_ctx_id(unsigned int core_pos, u_register_t context_id)
446{
447 assert(core_pos < PLATFORM_CORE_COUNT);
448
449 cpu_on_ctx_id_arr[core_pos] = context_id;
450}
451
452unsigned int tftf_is_rebooted(void)
453{
454 return test_is_rebooting;
455}
456
457/*
458 * Return 0 if the test session can be resumed
459 * -1 otherwise.
460 */
461static int resume_test_session(void)
462{
463 test_ref_t test_to_run;
464 test_progress_t test_progress;
465 const test_case_t *next_test;
466
467 /* Get back on our feet. Where did we stop? */
468 tftf_get_test_to_run(&test_to_run);
469 tftf_get_test_progress(&test_progress);
470 assert(TEST_PROGRESS_IS_VALID(test_progress));
471
472 switch (test_progress) {
473 case TEST_READY:
474 /*
475 * The TFTF has reset in the framework code, before the test
476 * actually started.
477 * Nothing to update, just start the test from scratch.
478 */
479 break;
480
481 case TEST_IN_PROGRESS:
482 /*
483 * The test crashed, i.e. it couldn't complete.
484 * Update the test result in NVM then move to the next test.
485 */
486 INFO("Test has crashed, moving to the next one\n");
487 tftf_testcase_set_result(current_testcase(),
488 TEST_RESULT_CRASHED,
489 0);
490 next_test = advance_to_next_test();
491 if (!next_test) {
492 INFO("No more tests\n");
493 return -1;
494 }
495 break;
496
497 case TEST_COMPLETE:
498 /*
499 * The TFTF has reset in the framework code, after the test had
500 * completed but before we finished the framework maintenance
501 * required to move to the next test.
502 *
503 * In this case, we don't know the exact state of the data:
504 * maybe we had the time to update the test result,
505 * maybe we had the time to move to the next test.
506 * We can't be sure so let's stay on the safe side and just
507 * restart the test session from the beginning...
508 */
509 NOTICE("The test framework has been interrupted in the middle "
510 "of critical maintenance operations.\n");
511 NOTICE("Can't recover execution.\n");
512 return -1;
513
514 case TEST_REBOOTING:
515 /*
516 * Nothing to update about the test session, as we want to
517 * re-enter the same test. Just remember that the test is
518 * rebooting in case it queries this information.
519 */
520 test_is_rebooting = 1;
521 break;
522
523 default:
524 bug_unreachable();
525 }
526
527 return 0;
528}
529
530/*
531 * C entry point in the TFTF.
532 * This function is executed by the primary CPU only.
533 */
534void __dead2 tftf_cold_boot_main(void)
535{
536 STATUS status;
537 int rc;
538
539 NOTICE("%s\n", TFTF_WELCOME_STR);
540 NOTICE("%s\n", build_message);
541 NOTICE("%s\n\n", version_string);
542
Deepika Bhavnanic249d5e2020-02-06 16:29:45 -0600543#ifdef __aarch64__
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200544 NOTICE("Running at NS-EL%u\n", IS_IN_EL(1) ? 1 : 2);
545#else
546 NOTICE("Running in AArch32 HYP mode\n");
547#endif
548
549 tftf_arch_setup();
Antonio Nino Diaz9c9f92c2019-03-13 13:57:39 +0000550
551 /*
552 * Enable pointer authentication. tftf_cold_boot_main() never returns,
553 * so it is safe to do it here. If this function was to return, the
554 * authentication would fail then.
555 */
556#if ENABLE_PAUTH
Juan Pablo Condeebd1b692022-06-30 17:47:35 -0400557 assert(is_armv8_3_pauth_apa_api_apa3_present());
Antonio Nino Diaz9c9f92c2019-03-13 13:57:39 +0000558
Alexei Fedorov719714f2019-10-03 10:57:53 +0100559 /*
560 * Program APIAKey_EL1 key and enable ARMv8.3-PAuth here as this
561 * function doesn't return, and RETAA instuction won't be executed,
562 * what would cause translation fault otherwise.
563 */
564 pauth_init_enable();
Antonio Nino Diaz9c9f92c2019-03-13 13:57:39 +0000565#endif /* ENABLE_PAUTH */
566
Boyan Karatoteve5629bd2025-06-16 11:45:34 +0100567 arm_gic_probe();
568
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200569 tftf_platform_setup();
570 tftf_init_topology();
571
572 tftf_irq_setup();
573
574 rc = tftf_initialise_timer();
575 if (rc != 0) {
576 ERROR("Failed to initialize the timer subsystem (%d).\n", rc);
577 tftf_exit();
578 }
579
580 /* Enable the SGI used by the timer management framework */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100581 tftf_irq_enable_sgi(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200582 enable_irq();
583
584 if (new_test_session()) {
585 NOTICE("Starting a new test session\n");
586 status = tftf_init_nvm();
587 if (status != STATUS_SUCCESS) {
588 /*
589 * TFTF will have an undetermined behavior if its data
590 * structures have not been initialised. There's no
591 * point in continuing execution.
592 */
593 ERROR("FATAL: Failed to initialise internal data structures in NVM.\n");
594 tftf_clean_nvm();
595 tftf_exit();
596 }
597 } else {
598 NOTICE("Resuming interrupted test session\n");
599 rc = resume_test_session();
600 if (rc < 0) {
Sandrine Bailleux125d58c2018-11-07 17:11:59 +0100601 print_tests_summary();
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200602 tftf_clean_nvm();
603 tftf_exit();
604 }
605 }
606
607 /* Initialise the CPUs status map */
608 tftf_init_cpus_status_map();
609
610 /*
611 * Detect power state format and get power state information for
612 * a platform.
613 */
614 tftf_init_pstate_framework();
615
616 /* The lead CPU is always the primary core. */
617 lead_cpu_mpid = read_mpidr_el1() & MPID_MASK;
618
619 /*
620 * Hand over to lead CPU if required.
621 * If the primary CPU is not the lead CPU for the first test then:
622 * 1) Power on the lead CPU
623 * 2) Power down the primary CPU
624 */
625 if ((read_mpidr_el1() & MPID_MASK) != lead_cpu_mpid) {
626 hand_over_to_lead_cpu();
627 bug_unreachable();
628 }
629
630 /* Enter the test session */
631 run_tests();
632
633 /* Should never reach this point */
634 bug_unreachable();
635}
636
637void __dead2 tftf_exit(void)
638{
639 NOTICE("Exiting tests.\n");
640
641 /* Let the platform code clean up if required */
642 tftf_platform_end();
643
644 while (1)
645 wfi();
646}