/* * Copyright (c) 2020 Nordic Semiconductor ASA. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #if !defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) && \ !defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) #error "Unsupported architecture" #endif #if defined(CONFIG_USERSPACE) #define PRIORITY 0 #define DB_VAL 0xDEADBEEF static struct k_thread user_thread; static K_THREAD_STACK_DEFINE(user_thread_stack, 1024); #include #include "test_syscalls.h" void z_impl_test_arm_user_syscall(void) { /* User thread system call * * Verify the following * - mode variable indicates PRIV mode * - the PSP is inside the thread's privileged stack * - PSPLIM register guards the privileged stack * - MSPLIM register still guards the interrupt stack */ zassert_true((arch_current_thread()->arch.mode & CONTROL_nPRIV_Msk) == 0, "mode variable not set to PRIV mode in system call\n"); zassert_false(arch_is_user_context(), "arch_is_user_context() indicates nPRIV\n"); zassert_true( ((__get_PSP() >= arch_current_thread()->arch.priv_stack_start) && (__get_PSP() < (arch_current_thread()->arch.priv_stack_start + CONFIG_PRIVILEGED_STACK_SIZE))), "Process SP outside thread privileged stack limits\n"); #if defined(CONFIG_BUILTIN_STACK_GUARD) zassert_true(__get_PSPLIM() == arch_current_thread()->arch.priv_stack_start, "PSPLIM not guarding the thread's privileged stack\n"); zassert_true(__get_MSPLIM() == (uint32_t)z_interrupt_stacks, "MSPLIM not guarding the interrupt stack\n"); #endif } static inline void z_vrfy_test_arm_user_syscall(void) { z_impl_test_arm_user_syscall(); } #include void arm_isr_handler(const void *args) { ARG_UNUSED(args); /* Interrupt triggered while running a user thread * * Verify the following * - mode variable indicates nPRIV mode * - the PSP is inside the thread's default (user) stack * - PSPLIM register is not set (applies on the second ISR call) * - MSPLIM register still guards the interrupt stack */ zassert_true((arch_current_thread()->arch.mode & CONTROL_nPRIV_Msk) != 0, "mode variable not set to nPRIV mode for user thread\n"); zassert_false(arch_is_user_context(), "arch_is_user_context() indicates nPRIV in ISR\n"); zassert_true( ((__get_PSP() >= arch_current_thread()->stack_info.start) && (__get_PSP() < (arch_current_thread()->stack_info.start + arch_current_thread()->stack_info.size))), "Process SP outside thread stack limits\n"); static int first_call = 1; if (first_call == 1) { first_call = 0; /* Trigger thread yield() manually */ (void)irq_lock(); z_move_thread_to_end_of_prio_q(arch_current_thread()); SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; irq_unlock(0); } else if (first_call == 0) { #if defined(CONFIG_BUILTIN_STACK_GUARD) /* Second ISR run occurs after thread context-switch. * We expect PSPLIM to be clear at this point. */ zassert_true(__get_PSPLIM() == 0, "PSPLIM not clear\n"); zassert_true(__get_MSPLIM() == (uint32_t)z_interrupt_stacks, "MSPLIM not guarding the interrupt stack\n"); #endif NVIC_DisableIRQ((uint32_t)args); } } static void user_thread_entry(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); ARG_UNUSED(p3); uint32_t irq_line = POINTER_TO_INT(p1); /* User Thread */ #if !defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) ARG_UNUSED(irq_line); #endif /* Trigger a system call to switch to supervisor thread * mode and verify the thread state during system calls. */ test_arm_user_syscall(); #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) /* * Trigger an ISR to switch to handler mode, to inspect * the kernel structs and verify the thread state. */ TC_PRINT("USR Thread: IRQ Line: %u\n", (uint32_t)irq_line); NVIC->STIR = irq_line; barrier_dsync_fence_full(); barrier_isync_fence_full(); /* ISR is set to cause thread to context-switch -out and -in again. * We inspect for a second time, to verlfy the status, after * the user thread is switch back in. */ NVIC->STIR = irq_line; barrier_dsync_fence_full(); barrier_isync_fence_full(); #endif } ZTEST(arm_thread_swap, test_arm_syscalls) { int i = 0; /* Supervisor Thread (ztest thread) * * Verify the following: * - the "mode" variable indicates PRIV mode * - arch_is_user_context() is negative * - the PSP is inside the default thread stack * - PSPLIM register guards the default stack * - MSPLIM register guards the interrupt stack */ zassert_true((arch_current_thread()->arch.mode & CONTROL_nPRIV_Msk) == 0, "mode variable not set to PRIV mode for supervisor thread\n"); zassert_false(arch_is_user_context(), "arch_is_user_context() indicates nPRIV\n"); zassert_true( ((__get_PSP() >= arch_current_thread()->stack_info.start) && (__get_PSP() < (arch_current_thread()->stack_info.start + arch_current_thread()->stack_info.size))), "Process SP outside thread stack limits\n"); #if defined(CONFIG_BUILTIN_STACK_GUARD) zassert_true(__get_PSPLIM() == arch_current_thread()->stack_info.start, "PSPLIM not guarding the default stack\n"); zassert_true(__get_MSPLIM() == (uint32_t)z_interrupt_stacks, "MSPLIM not guarding the interrupt stack\n"); #endif #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) for (i = CONFIG_NUM_IRQS - 1; i >= 0; i--) { if (NVIC_GetEnableIRQ(i) == 0) { /* * Interrupts configured statically with IRQ_CONNECT(.) * are automatically enabled. NVIC_GetEnableIRQ() * returning false, here, implies that the IRQ line is * either not implemented or it is not enabled, thus, * currently not in use by Zephyr. */ /* Set the NVIC line to pending. */ NVIC_SetPendingIRQ(i); if (NVIC_GetPendingIRQ(i)) { /* If the NVIC line is pending, it is * guaranteed that it is implemented. */ break; } } } zassert_true(i >= 0, "No available IRQ line to use in the test\n"); TC_PRINT("Available IRQ line: %u\n", i); arch_irq_connect_dynamic(i, 0 /* highest priority */, arm_isr_handler, (uint32_t *)i, 0); NVIC_ClearPendingIRQ(i); NVIC_EnableIRQ(i); /* Allow the user thread to trigger an interrupt; * this is *ONLY* done for testing purposes, here, * i.e. to allow the inspection of the thread state * while running in user mode. */ SCB->CCR |= SCB_CCR_USERSETMPEND_Msk; #endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE*/ /* Create and switch to a user thread, passing * as argument the IRQ line to used in the test. */ k_thread_create(&user_thread, user_thread_stack, K_THREAD_STACK_SIZEOF(user_thread_stack), user_thread_entry, (uint32_t *)i, NULL, NULL, K_PRIO_COOP(PRIORITY), K_USER, K_NO_WAIT); } void z_impl_test_arm_cpu_write_reg(void) { /* User thread CPU write registers system call for testing * * Verify the following * - Write 0xDEADBEEF values during system call into registers * - In main test we will read that registers to verify * that all of them were scrubbed and do not contain any sensitive data */ /* Part below is made to test that kernel scrubs CPU registers * after returning from the system call */ TC_PRINT("Writing 0xDEADBEEF values into registers\n"); __asm__ volatile ( "ldr r0, =0xDEADBEEF;\n\t" "ldr r1, =0xDEADBEEF;\n\t" "ldr r2, =0xDEADBEEF;\n\t" "ldr r3, =0xDEADBEEF;\n\t" ); TC_PRINT("Exit from system call\n"); } static inline void z_vrfy_test_arm_cpu_write_reg(void) { z_impl_test_arm_cpu_write_reg(); } #include /** * @brief Test CPU scrubs registers after system call * * @details - Call from user mode a syscall test_arm_cpu_write_reg(), * the system call function writes into registers 0xDEADBEEF value * - Then in main test function below check registers values, * if no 0xDEADBEEF value detected, that means CPU scrubbed registers * before exit from the system call. * * @ingroup kernel_memprotect_tests */ ZTEST_USER(arm_thread_swap, test_syscall_cpu_scrubs_regs) { uint32_t arm_reg_val[4]; test_arm_cpu_write_reg(); __asm__ volatile ("mov %0, r0" : "=r"(arm_reg_val[0])); __asm__ volatile ("mov %0, r1" : "=r"(arm_reg_val[1])); __asm__ volatile ("mov %0, r2" : "=r"(arm_reg_val[2])); __asm__ volatile ("mov %0, r3" : "=r"(arm_reg_val[3])); for (int i = 0; i < 4; i++) { zassert_not_equal(arm_reg_val[i], DB_VAL, "register value is 0xDEADBEEF, " "not scrubbed after system call."); } } #else ZTEST_USER(arm_thread_swap, test_syscall_cpu_scrubs_regs) { ztest_test_skip(); } ZTEST(arm_thread_swap, test_arm_syscalls) { ztest_test_skip(); } #endif /* CONFIG_USERSPACE */ /** * @} */