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