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