1 /*
2  * Copyright (c) 2019 Nordic Semiconductor ASA.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/arch/cpu.h>
9 #include <cmsis_core.h>
10 #include <zephyr/kernel_structs.h>
11 #include <zephyr/sys/barrier.h>
12 #include <offsets_short_arch.h>
13 #include <ksched.h>
14 
15 #if !defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) && !defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
16 #error "Unsupported architecture"
17 #endif
18 
19 #define PRIORITY           0
20 #define BASEPRI_MODIFIED_1 0x20
21 #define BASEPRI_MODIFIED_2 0x40
22 #define SWAP_RETVAL        0x1234
23 
24 #ifndef EXC_RETURN_FTYPE
25 /* bit [4] allocate stack for floating-point context: 0=done 1=skipped  */
26 #define EXC_RETURN_FTYPE (0x00000010UL)
27 #endif
28 
29 #if defined(CONFIG_ARMV8_1_M_MAINLINE)
30 /*
31  * For ARMv8.1-M, the FPSCR[18:16] LTPSIZE field may always read 0b010 if MVE
32  * is not implemented, so mask it when validating the value of the FPSCR.
33  */
34 #define FPSCR_MASK (~FPU_FPDSCR_LTPSIZE_Msk)
35 #else
36 #define FPSCR_MASK (0xffffffffU)
37 #endif
38 
39 extern void z_move_thread_to_end_of_prio_q(struct k_thread *thread);
40 
41 static struct k_thread alt_thread;
42 static K_THREAD_STACK_DEFINE(alt_thread_stack, 1024);
43 
44 /* Status variable to indicate that context-switch has occurred. */
45 bool volatile switch_flag;
46 
47 struct k_thread *p_ztest_thread;
48 
49 _callee_saved_t ztest_thread_callee_saved_regs_container;
50 int ztest_swap_return_val;
51 
52 /* Arbitrary values for the callee-saved registers,
53  * enforced in the beginning of the test.
54  */
55 const _callee_saved_t ztest_thread_callee_saved_regs_init = {.v1 = 0x12345678,
56 							     .v2 = 0x23456789,
57 							     .v3 = 0x3456789a,
58 							     .v4 = 0x456789ab,
59 							     .v5 = 0x56789abc,
60 							     .v6 = 0x6789abcd,
61 							     .v7 = 0x789abcde,
62 							     .v8 = 0x89abcdef};
63 
load_callee_saved_regs(const _callee_saved_t * regs)64 static void load_callee_saved_regs(const _callee_saved_t *regs)
65 {
66 	/* Load the callee-saved registers with given values */
67 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
68 	__asm__ volatile("mov r1, r7;\n\t"
69 			 "mov r0, %0;\n\t"
70 			 "add r0, #16;\n\t"
71 			 "ldmia r0!, {r4-r7};\n\t"
72 			 "mov r8, r4;\n\t"
73 			 "mov r9, r5;\n\t"
74 			 "mov r10, r6;\n\t"
75 			 "mov r11, r7;\n\t"
76 			 "sub r0, #32;\n\t"
77 			 "ldmia r0!, {r4-r7};\n\t"
78 			 "mov r7, r1;\n\t"
79 			 : /* no output */
80 			 : "r"(regs)
81 			 : "memory", "r1", "r0");
82 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
83 	__asm__ volatile("mov r1, r7;\n\t"
84 			 "ldmia %0, {v1-v8};\n\t"
85 			 "mov r7, r1;\n\t"
86 			 : /* no output */
87 			 : "r"(regs)
88 			 : "memory", "r1");
89 #endif
90 	barrier_dsync_fence_full();
91 }
92 
verify_callee_saved(const _callee_saved_t * src,const _callee_saved_t * dst)93 static void verify_callee_saved(const _callee_saved_t *src, const _callee_saved_t *dst)
94 {
95 	/* Verify callee-saved registers are as expected */
96 	zassert_true((src->v1 == dst->v1) && (src->v2 == dst->v2) && (src->v3 == dst->v3) &&
97 			     (src->v4 == dst->v4) && (src->v5 == dst->v5) && (src->v6 == dst->v6) &&
98 			     (src->v7 == dst->v7) && (src->v8 == dst->v8),
99 		     " got: 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x\n"
100 		     " expected:  0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x\n",
101 		     src->v1, src->v2, src->v3, src->v4, src->v5, src->v6, src->v7, src->v8,
102 		     dst->v1, dst->v2, dst->v3, dst->v4, dst->v5, dst->v6, dst->v7, dst->v8);
103 }
104 
105 #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
106 
107 /* Create the alternative thread with K_FP_REGS options */
108 #define ALT_THREAD_OPTIONS K_FP_REGS
109 
110 /* Arbitrary values for the floating-point callee-saved registers */
111 struct _preempt_float ztest_thread_fp_callee_saved_regs = {
112 	.s16 = 0x11111111,
113 	.s17 = 0x22222222,
114 	.s18 = 0x33333333,
115 	.s19 = 0x44444444,
116 	.s20 = 0x55555555,
117 	.s21 = 0x66666666,
118 	.s22 = 0x77777777,
119 	.s23 = 0x88888888,
120 	.s24 = 0x99999999,
121 	.s25 = 0xaaaaaaaa,
122 	.s26 = 0xbbbbbbbb,
123 	.s27 = 0xcccccccc,
124 	.s28 = 0xdddddddd,
125 	.s29 = 0xeeeeeeee,
126 	.s30 = 0xffffffff,
127 	.s31 = 0x00000000,
128 };
129 
load_fp_callee_saved_regs(const volatile struct _preempt_float * regs)130 static void load_fp_callee_saved_regs(const volatile struct _preempt_float *regs)
131 {
132 	__asm__ volatile("vldmia %0, {s16-s31};\n\t" : : "r"(regs) : "memory");
133 	barrier_dsync_fence_full();
134 }
135 
verify_fp_callee_saved(const struct _preempt_float * src,const struct _preempt_float * dst)136 static void verify_fp_callee_saved(const struct _preempt_float *src,
137 				   const struct _preempt_float *dst)
138 {
139 	/* Verify FP callee-saved registers are as expected */
140 	/* clang-format off */
141 	zassert_true((src->s16 == dst->s16) &&
142 		     (src->s17 == dst->s17) && (src->s18 == dst->s18) &&
143 		     (src->s19 == dst->s19) && (src->s20 == dst->s20) &&
144 		     (src->s21 == dst->s21) && (src->s22 == dst->s22) &&
145 		     (src->s23 == dst->s23) && (src->s24 == dst->s24) &&
146 		     (src->s25 == dst->s25) && (src->s26 == dst->s26) &&
147 		     (src->s27 == dst->s27) && (src->s28 == dst->s28) &&
148 		     (src->s29 == dst->s29) && (src->s30 == dst->s30) &&
149 		     (src->s31 == dst->s31),
150 		     " got: %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n"
151 		     " expected:  %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n",
152 		     (double)src->s16, (double)src->s17, (double)src->s18, (double)src->s19,
153 		     (double)src->s20, (double)src->s21, (double)src->s22, (double)src->s23,
154 		     (double)src->s24, (double)src->s25, (double)src->s26, (double)src->s27,
155 		     (double)src->s28, (double)src->s29, (double)src->s30, (double)src->s31,
156 		     (double)dst->s16, (double)dst->s17, (double)dst->s18, (double)dst->s19,
157 		     (double)dst->s20, (double)dst->s21, (double)dst->s22, (double)dst->s23,
158 		     (double)dst->s24, (double)dst->s25, (double)dst->s26, (double)dst->s27,
159 		     (double)dst->s28, (double)dst->s29, (double)dst->s30, (double)dst->s31);
160 	/* clang-format on */
161 }
162 
163 #else
164 /* No options passed */
165 #define ALT_THREAD_OPTIONS 0
166 #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */
167 
alt_thread_entry(void * p1,void * p2,void * p3)168 static void alt_thread_entry(void *p1, void *p2, void *p3)
169 {
170 	ARG_UNUSED(p1);
171 	ARG_UNUSED(p2);
172 	ARG_UNUSED(p3);
173 
174 	int init_flag, post_flag;
175 
176 	/* Lock interrupts to make sure we get preempted only when
177 	 * it is required by the test
178 	 */
179 	(void)irq_lock();
180 
181 	init_flag = switch_flag;
182 	zassert_true(init_flag == false,
183 		     "Alternative thread: switch flag not false on thread entry\n");
184 
185 	/* Set switch flag */
186 	switch_flag = true;
187 
188 #if defined(CONFIG_NO_OPTIMIZATIONS)
189 	zassert_true(p_ztest_thread->arch.basepri == 0,
190 		     "ztest thread basepri not preserved in swap-out\n");
191 #else
192 	/* Verify that the main test thread has the correct value
193 	 * for state variable thread.arch.basepri (set before swap).
194 	 */
195 	zassert_true(p_ztest_thread->arch.basepri == BASEPRI_MODIFIED_1,
196 		     "ztest thread basepri not preserved in swap-out\n");
197 
198 	/* Verify original swap return value (set by arch_swap() */
199 	zassert_true(p_ztest_thread->arch.swap_return_value == -EAGAIN,
200 		     "ztest thread swap-return-value not preserved in swap-out\n");
201 #endif
202 
203 	/* Verify that the main test thread (ztest) has stored the callee-saved
204 	 * registers properly in its corresponding callee-saved container.
205 	 */
206 	verify_callee_saved((const _callee_saved_t *)&p_ztest_thread->callee_saved,
207 			    &ztest_thread_callee_saved_regs_container);
208 
209 	/* Zero the container of the callee-saved registers, to validate,
210 	 * later, that it is populated properly.
211 	 */
212 	memset(&ztest_thread_callee_saved_regs_container, 0, sizeof(_callee_saved_t));
213 
214 #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
215 
216 	/*  Verify that the _current_ (alt) thread is initialized with FPCA cleared. */
217 	zassert_true((__get_CONTROL() & CONTROL_FPCA_Msk) == 0,
218 		     "CONTROL.FPCA is not cleared at initialization: 0x%x\n", __get_CONTROL());
219 
220 	/* Verify that the _current_ (alt) thread is
221 	 * initialized with EXC_RETURN.Ftype set
222 	 */
223 	zassert_true((_current->arch.mode_exc_return & EXC_RETURN_FTYPE) != 0,
224 		     "Alt thread FPCA flag not clear at initialization\n");
225 #if defined(CONFIG_MPU_STACK_GUARD)
226 	/* Alt thread is created with K_FP_REGS set, so we
227 	 * expect lazy stacking and long guard to be enabled.
228 	 */
229 	zassert_true((_current->arch.mode & Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) != 0,
230 		     "Alt thread MPU GUAR DFLOAT flag not set at initialization\n");
231 	zassert_true((_current->base.user_options & K_FP_REGS) != 0,
232 		     "Alt thread K_FP_REGS not set at initialization\n");
233 	zassert_true((FPU->FPCCR & FPU_FPCCR_LSPEN_Msk) != 0,
234 		     "Lazy FP Stacking not set at initialization\n");
235 #endif
236 
237 	/* Verify that the _current_ (alt) thread is initialized with FPSCR cleared. */
238 	zassert_true((__get_FPSCR() & FPSCR_MASK) == 0,
239 		     "(Alt thread) FPSCR is not cleared at initialization: 0x%x\n", __get_FPSCR());
240 
241 	zassert_true((p_ztest_thread->arch.mode_exc_return & EXC_RETURN_FTYPE) == 0,
242 		     "ztest thread mode Ftype flag not updated at swap-out: 0x%0x\n",
243 		     p_ztest_thread->arch.mode);
244 
245 	/* Verify that the main test thread (ztest) has stored the FP
246 	 *  callee-saved registers properly in its corresponding FP
247 	 *  callee-saved container.
248 	 */
249 	verify_fp_callee_saved((const struct _preempt_float *)&p_ztest_thread->arch.preempt_float,
250 			       &ztest_thread_fp_callee_saved_regs);
251 
252 	/* Zero the container of the FP callee-saved registers, to validate,
253 	 * later, that it is populated properly.
254 	 */
255 	memset(&ztest_thread_fp_callee_saved_regs, 0, sizeof(ztest_thread_fp_callee_saved_regs));
256 
257 #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */
258 
259 	/* Modify the arch.basepri flag of the main test thread, to verify,
260 	 * later, that this is passed properly to the BASEPRI.
261 	 */
262 	p_ztest_thread->arch.basepri = BASEPRI_MODIFIED_2;
263 
264 #if !defined(CONFIG_NO_OPTIMIZATIONS)
265 	/* Modify the arch.swap_return_value flag of the main test thread,
266 	 * to verify later, that this value is properly returned by swap.
267 	 */
268 	p_ztest_thread->arch.swap_return_value = SWAP_RETVAL;
269 #endif
270 
271 	z_move_thread_to_end_of_prio_q(_current);
272 
273 	/* Modify the callee-saved registers by zero-ing them.
274 	 * The main test thread will, later, assert that they
275 	 * are restored to their original values upon context
276 	 * switch.
277 	 *
278 	 * Note: preserve r7 register (frame pointer).
279 	 */
280 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
281 	__asm__ volatile(
282 		/* Stash r4-r11 in stack, they will be restored much later in
283 		 * another inline asm -- that should be reworked since stack
284 		 * must be balanced when we leave any inline asm. We could
285 		 * use simply an alternative stack for storing them instead
286 		 * of the function's stack.
287 		 */
288 		"push {r4-r7};\n\t"
289 		"mov r2, r8;\n\t"
290 		"mov r3, r9;\n\t"
291 		"push {r2, r3};\n\t"
292 		"mov r2, r10;\n\t"
293 		"mov r3, r11;\n\t"
294 		"push {r2, r3};\n\t"
295 
296 		/* Save r0 and r7 since we want to preserve them but they
297 		 * are used below: r0 is used as a copy of struct pointer
298 		 * we don't want to mess and r7 is the frame pointer which
299 		 * we must not clobber it.
300 		 */
301 		"push {r0, r7};\n\t"
302 
303 		/* Load struct into r4-r11 */
304 		"mov r0, %0;\n\t"
305 		"add r0, #16;\n\t"
306 		"ldmia r0!, {r4-r7};\n\t"
307 		"mov r8, r4;\n\t"
308 		"mov r9, r5;\n\t"
309 		"mov r10, r6;\n\t"
310 		"mov r11, r7;\n\t"
311 		"sub r0, #32;\n\t"
312 		"ldmia r0!, {r4-r7};\n\t"
313 
314 		/* Restore r0 and r7 */
315 		"pop {r0, r7};\n\t"
316 
317 		: /* no output */
318 		: "r"(&ztest_thread_callee_saved_regs_container)
319 		: "memory", "r0", "r2", "r3");
320 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
321 	__asm__ volatile("push {v1-v8};\n\t"
322 			 "push {r0, r1};\n\t"
323 			 "mov r0, r7;\n\t"
324 			 "ldmia %0, {v1-v8};\n\t"
325 			 "mov r7, r0;\n\t"
326 			 "pop {r0, r1};\n\t"
327 			 : /* no output */
328 			 : "r"(&ztest_thread_callee_saved_regs_container)
329 			 : "memory", "r0");
330 #endif
331 
332 	/* Manually trigger a context-switch, to swap-out
333 	 * the alternative test thread.
334 	 */
335 	barrier_dmem_fence_full();
336 	SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
337 	irq_unlock(0);
338 
339 	/* Restore stacked callee-saved registers */
340 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
341 	__asm__ volatile("pop {r4, r5, r6, r7};\n\t"
342 			 "mov r8, r4;\n\t"
343 			 "mov r9, r5;\n\t"
344 			 "mov r10, r6;\n\t"
345 			 "mov r11, r7;\n\t"
346 			 "pop {r4, r5, r6, r7};\n\t"
347 			 :
348 			 :
349 			 :);
350 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
351 	__asm__ volatile("pop {v1-v8};\n\t" : : :);
352 #endif
353 
354 	/* Verify that the main test thread has managed to resume, before
355 	 * we return to the alternative thread (we verify this by checking
356 	 * the status of the switch flag; the main test thread will clear
357 	 * it when it is swapped-back in.
358 	 */
359 	post_flag = switch_flag;
360 	zassert_true(post_flag == false,
361 		     "Alternative thread: switch flag not false on thread exit\n");
362 }
363 
364 #if !defined(CONFIG_NO_OPTIMIZATIONS)
arch_swap_wrapper(void)365 static int __noinline arch_swap_wrapper(void)
366 {
367 	return arch_swap(BASEPRI_MODIFIED_1);
368 }
369 #endif
370 
371 /**
372  * @brief Test the ARM thread swap mechanism
373  * @ingroup kernel_arch_sched_tests
374  */
ZTEST(arm_thread_swap,test_arm_thread_swap)375 ZTEST(arm_thread_swap, test_arm_thread_swap)
376 {
377 	int test_flag;
378 
379 	/* Main test thread (ztest)
380 	 *
381 	 * Simulating initial conditions:
382 	 * - set arbitrary values at the callee-saved registers
383 	 * - set arbitrary values at the FP callee-saved registers,
384 	 *   if building with CONFIG_FPU/CONFIG_FPU_SHARING
385 	 * - zero the thread's callee-saved data structure
386 	 * - set thread's priority same as the alternative test thread
387 	 */
388 
389 	/* Load the callee-saved registers with initial arbitrary values
390 	 * (from ztest_thread_callee_saved_regs_init)
391 	 */
392 	load_callee_saved_regs(&ztest_thread_callee_saved_regs_init);
393 
394 	k_thread_priority_set(_current, K_PRIO_COOP(PRIORITY));
395 
396 	/* Export current thread's callee-saved registers pointer
397 	 * and arch.basepri variable pointer, into global pointer
398 	 * variables, so they can be easily accessible by other
399 	 * (alternative) test thread.
400 	 */
401 	p_ztest_thread = _current;
402 
403 	/* Confirm initial conditions before starting the test. */
404 	test_flag = switch_flag;
405 	zassert_true(test_flag == false, "Switch flag not initialized properly\n");
406 	zassert_true(_current->arch.basepri == 0,
407 		     "Thread BASEPRI flag not clear at thread start\n");
408 	/* Verify, also, that the interrupts are unlocked. */
409 #if defined(CONFIG_CPU_CORTEX_M_HAS_BASEPRI)
410 	zassert_true(__get_BASEPRI() == 0, "initial BASEPRI not zero\n");
411 #else
412 	/* For Cortex-M Baseline architecture, we verify that
413 	 * the interrupt lock is disabled.
414 	 */
415 	zassert_true(__get_PRIMASK() == 0, "initial PRIMASK not zero\n");
416 #endif /* CONFIG_CPU_CORTEX_M_HAS_BASEPRI */
417 
418 #if defined(CONFIG_USERSPACE)
419 	/* The main test thread is set to run in privilege mode */
420 	zassert_false((arch_is_user_context()),
421 		      "Main test thread does not start in privilege mode\n");
422 
423 	/* Assert that the mode status variable indicates privilege mode */
424 	zassert_true((_current->arch.mode & CONTROL_nPRIV_Msk) == 0,
425 		     "Thread nPRIV flag not clear for supervisor thread: 0x%0x\n",
426 		     _current->arch.mode);
427 #endif /* CONFIG_USERSPACE */
428 
429 #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
430 	/* The main test thread is not (yet) actively using the FP registers */
431 	zassert_true((_current->arch.mode_exc_return & EXC_RETURN_FTYPE) != 0,
432 		     "Thread Ftype flag not set at initialization 0x%0x\n", _current->arch.mode);
433 
434 	/* Verify that the main test thread is initialized with FPCA cleared. */
435 	zassert_true((__get_CONTROL() & CONTROL_FPCA_Msk) == 0,
436 		     "CONTROL.FPCA is not cleared at initialization: 0x%x\n", __get_CONTROL());
437 	/* Verify that the main test thread is initialized with FPSCR cleared. */
438 	zassert_true((__get_FPSCR() & FPSCR_MASK) == 0,
439 		     "FPSCR is not cleared at initialization: 0x%x\n", __get_FPSCR());
440 
441 	/* Clear the thread's floating-point callee-saved registers' container.
442 	 * The container will, later, be populated by the swap mechanism.
443 	 */
444 	memset(&_current->arch.preempt_float, 0, sizeof(struct _preempt_float));
445 
446 	/* Randomize the FP callee-saved registers at test initialization */
447 	load_fp_callee_saved_regs(&ztest_thread_fp_callee_saved_regs);
448 
449 	/* Modify bit-0 of the FPSCR - will be checked again upon swap-in. */
450 	zassert_true((__get_FPSCR() & 0x1) == 0, "FPSCR bit-0 has been set before testing it\n");
451 	__set_FPSCR(__get_FPSCR() | 0x1);
452 
453 	/* The main test thread is using the FP registers, but the .mode
454 	 * flag is not updated until the next context switch.
455 	 */
456 	zassert_true((_current->arch.mode_exc_return & EXC_RETURN_FTYPE) != 0,
457 		     "Thread Ftype flag not set at initialization\n");
458 #if defined(CONFIG_MPU_STACK_GUARD)
459 	zassert_true((_current->arch.mode & Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) == 0,
460 		     "Thread MPU GUAR DFLOAT flag not clear at initialization\n");
461 	zassert_true((_current->base.user_options & K_FP_REGS) == 0,
462 		     "Thread K_FP_REGS not clear at initialization\n");
463 	zassert_true((FPU->FPCCR & FPU_FPCCR_LSPEN_Msk) == 0,
464 		     "Lazy FP Stacking not clear at initialization\n");
465 #endif
466 #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */
467 
468 	/* Create an alternative (supervisor) testing thread */
469 	k_thread_create(&alt_thread, alt_thread_stack, K_THREAD_STACK_SIZEOF(alt_thread_stack),
470 			alt_thread_entry, NULL, NULL, NULL, K_PRIO_COOP(PRIORITY),
471 			ALT_THREAD_OPTIONS, K_NO_WAIT);
472 
473 	/* Verify context-switch has not occurred. */
474 	test_flag = switch_flag;
475 	zassert_true(test_flag == false, "Switch flag incremented when it should not have\n");
476 
477 	/* Prepare to force a context switch to the alternative thread,
478 	 * by manually adding the current thread to the end of the queue,
479 	 * so it will be context switched-out.
480 	 *
481 	 * Lock interrupts to make sure we get preempted only when it is
482 	 * explicitly required by the test.
483 	 */
484 	(void)irq_lock();
485 	z_move_thread_to_end_of_prio_q(_current);
486 
487 	/* Clear the thread's callee-saved registers' container.
488 	 * The container will, later, be populated by the swap
489 	 * mechanism.
490 	 */
491 	memset(&_current->callee_saved, 0, sizeof(_callee_saved_t));
492 
493 	/* Verify context-switch has not occurred yet. */
494 	test_flag = switch_flag;
495 	zassert_true(test_flag == false, "Switch flag incremented by unexpected context-switch.\n");
496 
497 	/* Store the callee-saved registers to some global memory
498 	 * accessible to the alternative testing thread. That
499 	 * thread is going to verify that the callee-saved regs
500 	 * are successfully loaded into the thread's callee-saved
501 	 * registers' container.
502 	 */
503 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
504 	__asm__ volatile("push {r0, r1, r2, r3};\n\t"
505 			 "mov r1, %0;\n\t"
506 			 "stmia r1!, {r4-r7};\n\t"
507 			 "mov r2, r8;\n\t"
508 			 "mov r3, r9;\n\t"
509 			 "stmia r1!, {r2-r3};\n\t"
510 			 "mov r2, r10;\n\t"
511 			 "mov r3, r11;\n\t"
512 			 "stmia r1!, {r2-r3};\n\t"
513 			 "pop {r0, r1, r2, r3};\n\t"
514 			 :
515 			 : "r"(&ztest_thread_callee_saved_regs_container)
516 			 : "memory");
517 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
518 	__asm__ volatile("stmia %0, {v1-v8};\n\t"
519 			 :
520 			 : "r"(&ztest_thread_callee_saved_regs_container)
521 			 : "memory");
522 #endif
523 
524 	/* Manually trigger a context-switch to swap-out the current thread.
525 	 * Request a return to a different interrupt lock state.
526 	 */
527 	barrier_dmem_fence_full();
528 
529 #if defined(CONFIG_NO_OPTIMIZATIONS)
530 	SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
531 	irq_unlock(0);
532 	/* The thread is now swapped-back in. */
533 
534 #else /* CONFIG_NO_OPTIMIZATIONS */
535 
536 	/* Fake a different irq_unlock key when performing swap.
537 	 * This will be verified by the alternative test thread.
538 	 *
539 	 * Force an indirect call to arch_swap() to prevent the compiler from
540 	 * changing the saved callee registers as arch_swap() is inlined.
541 	 */
542 	register int swap_return_val __asm__("r0") = arch_swap_wrapper();
543 
544 #endif /* CONFIG_NO_OPTIMIZATIONS */
545 
546 	/* Dump callee-saved registers to memory. */
547 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
548 	__asm__ volatile("push {r0, r1, r2, r3};\n\t"
549 			 "mov r1, %0;\n\t"
550 			 "stmia r1!, {r4-r7};\n\t"
551 			 "mov r2, r8;\n\t"
552 			 "mov r3, r9;\n\t"
553 			 "stmia r1!, {r2-r3};\n\t"
554 			 "mov r2, r10;\n\t"
555 			 "mov r3, r11;\n\t"
556 			 "stmia r1!, {r2-r3};\n\t"
557 			 "pop {r0, r1, r2, r3};\n\t"
558 			 :
559 			 : "r"(&ztest_thread_callee_saved_regs_container)
560 			 : "memory");
561 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
562 	__asm__ volatile("stmia %0, {v1-v8};\n\t"
563 			 :
564 			 : "r"(&ztest_thread_callee_saved_regs_container)
565 			 : "memory");
566 #endif
567 
568 #if !defined(CONFIG_NO_OPTIMIZATIONS)
569 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
570 	/* Note: ARMv6-M will always write back the base register,
571 	 * so we make sure we preserve the state of the register
572 	 * used, as base register in the Store Multiple instruction.
573 	 * We also enforce write-back to suppress assembler warning.
574 	 */
575 	__asm__ volatile("push {r0, r1, r2, r3, r4, r5, r6, r7};\n\t"
576 			 "stm %0!, {%1};\n\t"
577 			 "pop {r0, r1, r2, r3, r4, r5, r6, r7};\n\t"
578 			 :
579 			 : "r"(&ztest_swap_return_val), "r"(swap_return_val)
580 			 : "memory");
581 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
582 	__asm__ volatile("stm %0, {%1};\n\t"
583 			 :
584 			 : "r"(&ztest_swap_return_val), "r"(swap_return_val)
585 			 : "memory");
586 #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
587 #endif
588 
589 	/* After swap-back, verify that the callee-saved registers loaded,
590 	 * look exactly as what is located in the respective callee-saved
591 	 * container of the thread.
592 	 */
593 	verify_callee_saved(&ztest_thread_callee_saved_regs_container, &_current->callee_saved);
594 
595 	/* Verify context-switch did occur. */
596 	test_flag = switch_flag;
597 	zassert_true(test_flag == true, "Switch flag not incremented as expected %u\n",
598 		     switch_flag);
599 	/* Clear the switch flag to signal that the main test thread
600 	 * has been successfully swapped-in, as expected by the test.
601 	 */
602 	switch_flag = false;
603 
604 	/* Verify that the arch.basepri flag is cleared, after
605 	 * the alternative thread modified it, since the thread
606 	 * is now switched back in.
607 	 */
608 	zassert_true(_current->arch.basepri == 0,
609 		     "arch.basepri value not in accordance with the update\n");
610 
611 #if defined(CONFIG_CPU_CORTEX_M_HAS_BASEPRI)
612 	/* Verify that the BASEPRI register is updated during the last
613 	 * swap-in of the thread.
614 	 */
615 	zassert_true(__get_BASEPRI() == BASEPRI_MODIFIED_2,
616 		     "BASEPRI not in accordance with the update: 0x%0x\n", __get_BASEPRI());
617 #else
618 	/* For Cortex-M Baseline architecture, we verify that
619 	 * the interrupt lock is enabled.
620 	 */
621 	zassert_true(__get_PRIMASK() != 0, "PRIMASK not in accordance with the update: 0x%0x\n",
622 		     __get_PRIMASK());
623 #endif /* CONFIG_CPU_CORTEX_M_HAS_BASEPRI */
624 
625 #if !defined(CONFIG_NO_OPTIMIZATIONS)
626 	/* The thread is now swapped-back in. */
627 	zassert_equal(_current->arch.swap_return_value, SWAP_RETVAL,
628 		      "Swap value not set as expected: 0x%x (0x%x)\n",
629 		      _current->arch.swap_return_value, SWAP_RETVAL);
630 	zassert_equal(_current->arch.swap_return_value, ztest_swap_return_val,
631 		      "Swap value not returned as expected 0x%x (0x%x)\n",
632 		      _current->arch.swap_return_value, ztest_swap_return_val);
633 #endif
634 
635 #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
636 	/* Dump callee-saved registers to memory. */
637 	__asm__ volatile("vstmia %0, {s16-s31};\n\t"
638 			 :
639 			 : "r"(&ztest_thread_fp_callee_saved_regs)
640 			 : "memory");
641 
642 	/* After swap-back, verify that the FP callee-saved registers loaded,
643 	 * look exactly as what is located in the respective FP callee-saved
644 	 * container of the thread.
645 	 */
646 	verify_fp_callee_saved(&ztest_thread_fp_callee_saved_regs, &_current->arch.preempt_float);
647 
648 	/* Verify that the main test thread restored the FPSCR bit-0. */
649 	zassert_true((__get_FPSCR() & 0x1) == 0x1, "FPSCR bit-0 not restored at swap: 0x%x\n",
650 		     __get_FPSCR());
651 
652 	/* The main test thread is using the FP registers, and the .mode
653 	 * flag and MPU GUARD flag are now updated.
654 	 */
655 	zassert_true((_current->arch.mode_exc_return & EXC_RETURN_FTYPE) == 0,
656 		     "Thread Ftype flag not cleared after main returned back\n");
657 #if defined(CONFIG_MPU_STACK_GUARD)
658 	zassert_true((_current->arch.mode & Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) != 0,
659 		     "Thread MPU GUARD FLOAT flag not set\n");
660 	zassert_true((_current->base.user_options & K_FP_REGS) != 0,
661 		     "Thread K_FPREGS not set after main returned back\n");
662 	zassert_true((FPU->FPCCR & FPU_FPCCR_LSPEN_Msk) != 0,
663 		     "Lazy FP Stacking not set after main returned back\n");
664 #endif
665 #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */
666 }
667