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