1 /* Copyright (c) 2021 Intel Corporation.
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 
5 #include <zephyr/kernel.h>
6 #include <zephyr/kernel/smp.h>
7 #include <zephyr/ztest.h>
8 
9 /* Experimentally 10ms is enough time to get the second CPU to run on
10  * all known platforms.
11  */
12 #define CPU_START_DELAY 10000
13 
14 /* IPIs happen  much faster than CPU startup */
15 #define CPU_IPI_DELAY 1000
16 
17 BUILD_ASSERT(CONFIG_SMP);
18 BUILD_ASSERT(CONFIG_SMP_BOOT_DELAY);
19 BUILD_ASSERT(CONFIG_MP_MAX_NUM_CPUS > 1);
20 
21 #define STACKSZ 2048
22 char stack[STACKSZ];
23 
24 volatile bool mp_flag;
25 
26 struct k_thread cpu_thr;
27 K_THREAD_STACK_DEFINE(thr_stack, STACKSZ);
28 
thread_fn(void * a,void * b,void * c)29 static void thread_fn(void *a, void *b, void *c)
30 {
31 	mp_flag = true;
32 }
33 
ZTEST(smp_boot_delay,test_smp_boot_delay)34 ZTEST(smp_boot_delay, test_smp_boot_delay)
35 {
36 	/* Create a thread of lower priority.  This could run on
37 	 * another CPU if it was available, but will not preempt us
38 	 * unless we block (which we do not).
39 	 */
40 	k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack),
41 			thread_fn, NULL, NULL, NULL,
42 			1, 0, K_NO_WAIT);
43 
44 	/* Make sure that thread has not run (because the cpu is halted) */
45 	k_busy_wait(CPU_START_DELAY);
46 	zassert_false(mp_flag, "CPU1 must not be running yet");
47 
48 	/* Start the second CPU */
49 	k_smp_cpu_start(1, NULL, NULL);
50 
51 	/* Verify the thread ran */
52 	k_busy_wait(CPU_START_DELAY);
53 	zassert_true(mp_flag, "CPU1 did not start");
54 
55 	k_thread_abort(&cpu_thr);
56 	k_thread_join(&cpu_thr, K_FOREVER);
57 
58 	/* Spawn the same thread to do the same thing, but this time
59 	 * expect that the thread is going to run synchronously on the
60 	 * other CPU as soon as its created.  Intended to test whether
61 	 * IPIs were correctly set up on the runtime-launched CPU.
62 	 */
63 	mp_flag = false;
64 	k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack),
65 			thread_fn, NULL, NULL, NULL,
66 			1, 0, K_NO_WAIT);
67 
68 	k_busy_wait(CPU_IPI_DELAY);
69 
70 	k_thread_abort(&cpu_thr);
71 	k_thread_join(&cpu_thr, K_FOREVER);
72 
73 	zassert_true(mp_flag, "CPU1 did not start thread via IPI");
74 }
75 
76 volatile bool custom_init_flag;
77 
custom_init_fn(void * arg)78 void custom_init_fn(void *arg)
79 {
80 	volatile bool *flag = (void *)arg;
81 
82 	*flag = true;
83 }
84 
ZTEST(smp_boot_delay,test_smp_custom_start)85 ZTEST(smp_boot_delay, test_smp_custom_start)
86 {
87 	k_tid_t thr;
88 
89 	if (CONFIG_MP_MAX_NUM_CPUS <= 2) {
90 		/* CPU#1 has been started in test_smp_boot_delay
91 		 * so we need another CPU for this test.
92 		 */
93 		ztest_test_skip();
94 	}
95 
96 	mp_flag = false;
97 	custom_init_flag = false;
98 
99 	/* Create a thread pinned on CPU#2 so that it will not
100 	 * run on other CPUs.
101 	 */
102 	thr = k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack),
103 			      thread_fn, NULL, NULL, NULL,
104 			      1, 0, K_FOREVER);
105 	(void)k_thread_cpu_pin(thr, 2);
106 	k_thread_start(thr);
107 
108 	/* Make sure that thread has not run (because the cpu is halted) */
109 	k_busy_wait(CPU_START_DELAY);
110 	zassert_false(mp_flag, "CPU2 must not be running yet");
111 
112 	/* Start the third CPU */
113 	k_smp_cpu_start(2, custom_init_fn, (void *)&custom_init_flag);
114 
115 	/* Verify the thread ran */
116 	k_busy_wait(CPU_START_DELAY);
117 	zassert_true(mp_flag, "CPU2 did not start");
118 
119 	/* Verify that the custom init function has been called. */
120 	zassert_true(custom_init_flag, "Custom init function has not been called.");
121 
122 	k_thread_abort(&cpu_thr);
123 	k_thread_join(&cpu_thr, K_FOREVER);
124 }
125 
126 
127 ZTEST_SUITE(smp_boot_delay, NULL, NULL, NULL, NULL, NULL);
128