tests: arch: arm: add test-suite to verify syscall internals
This commit contributes a simple test-suite which verifies the internal (ARCH) implementation for user mode syscalls, as well as the stack limit checking mechanism for ARMv8-M MCUS. Signed-off-by: Ioannis Glaropoulos <Ioannis.Glaropoulos@nordicsemi.no>
This commit is contained in:
parent
bd8a3bba54
commit
83b278ec01
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
241
tests/arch/arm/arm_thread_swap/src/arm_syscalls.c
Normal file
241
tests/arch/arm/arm_thread_swap/src/arm_syscalls.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ztest.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <arch/arm/aarch32/cortex_m/cmsis.h>
|
||||
#include <kernel_structs.h>
|
||||
#include <offsets_short_arch.h>
|
||||
#include <ksched.h>
|
||||
|
||||
#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 <syscall_handler.h>
|
||||
#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 <syscalls/test_arm_user_syscall_mrsh.c>
|
||||
|
||||
|
||||
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 */
|
||||
/**
|
||||
* @}
|
||||
*/
|
|
@ -7,10 +7,16 @@
|
|||
#include <ztest.h>
|
||||
|
||||
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
|
||||
}
|
||||
|
|
15
tests/arch/arm/arm_thread_swap/src/test_syscalls.h
Normal file
15
tests/arch/arm/arm_thread_swap/src/test_syscalls.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _TEST_SYSCALLS_H_
|
||||
#define _TEST_SYSCALLS_H_
|
||||
#include <zephyr.h>
|
||||
|
||||
__syscall void test_arm_user_syscall(void);
|
||||
|
||||
#include <syscalls/test_syscalls.h>
|
||||
|
||||
#endif /* _TEST_SYSCALLS_H_ */
|
Loading…
Reference in a new issue