Boot Linux faster!

Check our new training course

Boot Linux faster!

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

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2017 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <ztest.h>
#include <tc_util.h>
#include <kernel_structs.h>
#include <irq_offload.h>
#include <kswap.h>

#if defined(CONFIG_X86) && defined(CONFIG_X86_MMU)
#define STACKSIZE (8192)
#else
#define  STACKSIZE (2048)
#endif
#define MAIN_PRIORITY 7
#define PRIORITY 5

static K_THREAD_STACK_DEFINE(alt_stack, STACKSIZE);

#ifdef CONFIG_STACK_SENTINEL
#define OVERFLOW_STACKSIZE (STACKSIZE / 2)
static k_thread_stack_t *overflow_stack =
		alt_stack + (STACKSIZE - OVERFLOW_STACKSIZE);
#else
#define OVERFLOW_STACKSIZE STACKSIZE
#endif

static struct k_thread alt_thread;
volatile int rv;

static volatile int crash_reason;

/* On some architectures, k_thread_abort(_current) will return instead
 * of _Swap'ing away.
 *
 * On ARM the PendSV exception is queued and immediately fires upon
 * completing the exception path; the faulting thread is never run
 * again.
 *
 * On Xtensa/asm2 the handler is running in interrupt context and on
 * the interrupt stack and needs to return through the interrupt exit
 * code.
 *
 * In both cases the thread is guaranteed never to run again once we
 * return from the _SysFatalErrorHandler().
 */
#if !(defined(CONFIG_ARM) || defined(CONFIG_XTENSA_ASM2) || defined(CONFIG_ARC))
#define ERR_IS_NORETURN 1
#endif

#ifdef ERR_IS_NORETURN
FUNC_NORETURN
#endif
void _SysFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf)
{
	TC_PRINT("Caught system error -- reason %d\n", reason);
	crash_reason = reason;

	k_thread_abort(_current);
#ifdef ERR_IS_NORETURN
	CODE_UNREACHABLE;
#endif
}

void alt_thread1(void)
{
#if defined(CONFIG_X86)
	__asm__ volatile ("ud2");
#elif defined(CONFIG_NIOS2)
	__asm__ volatile ("trap");
#elif defined(CONFIG_ARC)
	__asm__ volatile ("swi");
#else
	/* Triggers usage fault on ARM, illegal instruction on RISCV32
	 * and xtensa
	 */
	{
		int illegal = 0;
		((void(*)(void))&illegal)();
	}
#endif
	rv = TC_FAIL;
}


void alt_thread2(void)
{
	int key;

	key = irq_lock();
	k_oops();
	TC_ERROR("SHOULD NEVER SEE THIS\n");
	rv = TC_FAIL;
	irq_unlock(key);
}

void alt_thread3(void)
{
	int key;

	key = irq_lock();
	k_panic();
	TC_ERROR("SHOULD NEVER SEE THIS\n");
	rv = TC_FAIL;
	irq_unlock(key);
}

void blow_up_stack(void)
{
	char buf[OVERFLOW_STACKSIZE];

	TC_PRINT("posting %zu bytes of junk to stack...\n", sizeof(buf));
	memset(buf, 0xbb, sizeof(buf));
}

void stack_thread1(void)
{
	/* Test that stack overflow check due to timer interrupt works */
	blow_up_stack();
	TC_PRINT("busy waiting...\n");
	k_busy_wait(1024 * 1024);
	TC_ERROR("should never see this\n");
	rv = TC_FAIL;
}


void stack_thread2(void)
{
	int key = irq_lock();

	/* Test that stack overflow check due to swap works */
	blow_up_stack();
	TC_PRINT("swapping...\n");
	_Swap(irq_lock());
	TC_ERROR("should never see this\n");
	rv = TC_FAIL;
	irq_unlock(key);
}


void test_fatal(void)
{
	int expected_reason;

#if defined(CONFIG_ARCH_POSIX)
	ARG_UNUSED(expected_reason);
	ARG_UNUSED(overflow_stack);
#endif

	rv = TC_PASS;

	/*
	 * Main thread(test_main) priority was 10 but ztest thread runs at
	 * priority -1. To run the test smoothly make both main and ztest
	 * threads run at same priority level.
	 */
	k_thread_priority_set(_current, K_PRIO_PREEMPT(MAIN_PRIORITY));

#ifndef CONFIG_ARCH_POSIX
	TC_PRINT("test alt thread 1: generic CPU exception\n");
	k_thread_create(&alt_thread, alt_stack,
			K_THREAD_STACK_SIZEOF(alt_stack),
			(k_thread_entry_t)alt_thread1,
			NULL, NULL, NULL, K_PRIO_COOP(PRIORITY), 0,
			K_NO_WAIT);
	zassert_not_equal(rv, TC_FAIL, "thread was not aborted\n");
#else
	/*
	 * We want the native OS to handle segfaults so we can debug it
	 * with the normal linux tools
	 */
	TC_PRINT("test alt thread 1: skipped for POSIX ARCH\n");
#endif

	TC_PRINT("test alt thread 2: initiate kernel oops\n");
	k_thread_create(&alt_thread, alt_stack,
			K_THREAD_STACK_SIZEOF(alt_stack),
			(k_thread_entry_t)alt_thread2,
			NULL, NULL, NULL, K_PRIO_COOP(PRIORITY), 0,
			K_NO_WAIT);
	k_thread_abort(&alt_thread);
	zassert_equal(crash_reason, _NANO_ERR_KERNEL_OOPS,
		      "bad reason code got %d expected %d\n",
		      crash_reason, _NANO_ERR_KERNEL_OOPS);
	zassert_not_equal(rv, TC_FAIL, "thread was not aborted\n");

	TC_PRINT("test alt thread 3: initiate kernel panic\n");
	k_thread_create(&alt_thread, alt_stack,
			K_THREAD_STACK_SIZEOF(alt_stack),
			(k_thread_entry_t)alt_thread3,
			NULL, NULL, NULL, K_PRIO_COOP(PRIORITY), 0,
			K_NO_WAIT);
	k_thread_abort(&alt_thread);
	zassert_equal(crash_reason, _NANO_ERR_KERNEL_PANIC,
		      "bad reason code got %d expected %d\n",
		      crash_reason, _NANO_ERR_KERNEL_PANIC);
	zassert_not_equal(rv, TC_FAIL, "thread was not aborted\n");

#ifndef CONFIG_ARCH_POSIX
	TC_PRINT("test stack overflow - timer irq\n");
#ifdef CONFIG_STACK_SENTINEL
	/* When testing stack sentinel feature, the overflow stack is a
	 * smaller section of alt_stack near the end.
	 * In this way when it gets overflowed by blow_up_stack() we don't
	 * corrupt anything else and prevent the test case from completing.
	 */
	k_thread_create(&alt_thread, overflow_stack, OVERFLOW_STACKSIZE,
#else
	k_thread_create(&alt_thread, alt_stack,
			K_THREAD_STACK_SIZEOF(alt_stack),
#endif
			(k_thread_entry_t)stack_thread1,
			NULL, NULL, NULL, K_PRIO_PREEMPT(PRIORITY), 0,
			K_NO_WAIT);

	expected_reason = _NANO_ERR_STACK_CHK_FAIL;

	zassert_equal(crash_reason, expected_reason,
		      "bad reason code got %d expected %d\n",
		      crash_reason, expected_reason);
	zassert_not_equal(rv, TC_FAIL, "thread was not aborted\n");

	/* Stack sentinel has to be invoked, make sure it happens during
	 * a context switch. Also ensure HW-based solutions can run more
	 * than once.
	 */
	TC_PRINT("test stack overflow - swap\n");
#ifdef CONFIG_STACK_SENTINEL
	k_thread_create(&alt_thread, overflow_stack, OVERFLOW_STACKSIZE,
#else
	k_thread_create(&alt_thread, alt_stack,
			K_THREAD_STACK_SIZEOF(alt_stack),
#endif
			(k_thread_entry_t)stack_thread2,
			NULL, NULL, NULL, K_PRIO_PREEMPT(PRIORITY), 0,
			K_NO_WAIT);
	zassert_equal(crash_reason, _NANO_ERR_STACK_CHK_FAIL,
		      "bad reason code got %d expected %d\n",
		      crash_reason, _NANO_ERR_STACK_CHK_FAIL);

	zassert_not_equal(rv, TC_FAIL, "thread was not aborted\n");
#else
	TC_PRINT("test stack overflow - skipped for POSIX arch\n");
	/*
	 * We do not have a stack check for the posix ARCH
	 * again we relay on the native OS
	 */
#endif
}

/*test case main entry*/
void test_main(void)
{
	ztest_test_suite(fatal,
			ztest_unit_test(test_fatal));
	ztest_run_test_suite(fatal);
}