Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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 */
/**
 * @}
 */