diff --git a/tests/arch/arm/arm_thread_swap/README.txt b/tests/arch/arm/arm_thread_swap/README.txt index 863fa4dcc8..4f6a06bbb2 100644 --- a/tests/arch/arm/arm_thread_swap/README.txt +++ b/tests/arch/arm/arm_thread_swap/README.txt @@ -1,7 +1,10 @@ -Title: Test to verify the thread-swap (context-switch) mechanism (ARM Only) +Title: Test suite to verify the thread-swap (context-switch) and system-calls +mechanisms (ARM Only) Description: +Thread-swap test: + This test verifies that the ARM thread context-switch mechanism behaves as expected. In particular, the test verifies that: - the callee-saved registers are saved and restored, properly, @@ -25,6 +28,18 @@ Notes: The test is currently supported in ARM Cortex-M Baseline and Mainline targets. +Syscalls test: + +This test verifies that the ARM mechanism for user system calls +behaves as expected. In particular, the test verifies that: +- the mode variable with respect to the user mode flag always indicates + the mode in which a user thread is currently executing. +- threads in system calls are using the privileged thread stack +- stack pointer limit checking mechanism behaves as expected for + user threads in PRIV mode and supervisor threads + +The test is currently supported in ARM Cortex-M Baseline and Mainline +targets with support for user space. --------------------------------------------------------------------------- @@ -59,5 +74,13 @@ starting test - test_arm_thread_swap PASS - test_arm_thread_swap =================================================================== Test suite arm_thread_swap succeeded +Running test suite arm_syscalls +=================================================================== +starting test - test_arm_syscalls +Available IRQ line: 68 +USR Thread: IRQ Line: 68 +PASS - test_arm_syscalls +=================================================================== +Test suite arm_syscalls succeeded =================================================================== PROJECT EXECUTION SUCCESSFUL diff --git a/tests/arch/arm/arm_thread_swap/prj.conf b/tests/arch/arm/arm_thread_swap/prj.conf index f3c13ff7d1..3cabc1bb2e 100644 --- a/tests/arch/arm/arm_thread_swap/prj.conf +++ b/tests/arch/arm/arm_thread_swap/prj.conf @@ -1,3 +1,5 @@ CONFIG_ZTEST=y +CONFIG_DYNAMIC_INTERRUPTS=y CONFIG_TEST_USERSPACE=y +CONFIG_APPLICATION_DEFINED_SYSCALL=y CONFIG_MAIN_STACK_SIZE=1024 diff --git a/tests/arch/arm/arm_thread_swap/src/arm_syscalls.c b/tests/arch/arm/arm_thread_swap/src/arm_syscalls.c new file mode 100644 index 0000000000..608bbf8194 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap/src/arm_syscalls.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#if !defined(__GNUC__) +#error __FILE__ goes only with Cortex-M GCC +#endif + +#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 + +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((_current->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() >= _current->arch.priv_stack_start) && + (__get_PSP() < (_current->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() == _current->arch.priv_stack_start, + "PSPLIM not guarding the thread's privileged stack\n"); + zassert_true(__get_MSPLIM() == (u32_t)_interrupt_stack, + "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(void *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((_current->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() >= _current->stack_info.start) && + (__get_PSP() < (_current->stack_info.start + + _current->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(_current); + 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() == (u32_t)_interrupt_stack, + "MSPLIM not guarding the interrupt stack\n"); +#endif + } +} + +static void user_thread_entry(u32_t irq_line) +{ + /* 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", (u32_t)irq_line); + + NVIC->STIR = irq_line; + __DSB(); + __ISB(); + + /* 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; + __DSB(); + __ISB(); +#endif +} + +void test_arm_syscalls(void) +{ + 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((_current->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() >= _current->stack_info.start) && + (__get_PSP() < (_current->stack_info.start + + _current->stack_info.size))), + "Process SP outside thread stack limits\n"); + +#if defined(CONFIG_BUILTIN_STACK_GUARD) + zassert_true(__get_PSPLIM() == _current->stack_info.start, + "PSPLIM not guarding the default stack\n"); + zassert_true(__get_MSPLIM() == (u32_t)_interrupt_stack, + "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, + NULL, + 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), + (k_thread_entry_t)user_thread_entry, + (u32_t *)i, NULL, NULL, + K_PRIO_COOP(PRIORITY), K_USER, + K_NO_WAIT); +} +#endif /* CONFIG_USERSPACE */ +/** + * @} + */ diff --git a/tests/arch/arm/arm_thread_swap/src/main.c b/tests/arch/arm/arm_thread_swap/src/main.c index 30a50d281b..78e4f3b77c 100644 --- a/tests/arch/arm/arm_thread_swap/src/main.c +++ b/tests/arch/arm/arm_thread_swap/src/main.c @@ -7,10 +7,16 @@ #include extern void test_arm_thread_swap(void); +extern void test_arm_syscalls(void); void test_main(void) { ztest_test_suite(arm_thread_swap, ztest_unit_test(test_arm_thread_swap)); ztest_run_test_suite(arm_thread_swap); +#if defined(CONFIG_USERSPACE) + ztest_test_suite(arm_syscalls, + ztest_unit_test(test_arm_syscalls)); + ztest_run_test_suite(arm_syscalls); +#endif } diff --git a/tests/arch/arm/arm_thread_swap/src/test_syscalls.h b/tests/arch/arm/arm_thread_swap/src/test_syscalls.h new file mode 100644 index 0000000000..66f8ef294a --- /dev/null +++ b/tests/arch/arm/arm_thread_swap/src/test_syscalls.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TEST_SYSCALLS_H_ +#define _TEST_SYSCALLS_H_ +#include + +__syscall void test_arm_user_syscall(void); + +#include + +#endif /* _TEST_SYSCALLS_H_ */