text: /*
   * 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);
  
  #if defined(CONFIG_STACK_SENTINEL) && !defined(CONFIG_ARCH_POSIX)
  #define OVERFLOW_STACKSIZE (STACKSIZE / 2)
  static k_thread_stack_t *overflow_stack =
  		alt_stack + (STACKSIZE - OVERFLOW_STACKSIZE);
  #else
  #if defined(CONFIG_USERSPACE) && defined(CONFIG_ARC)
  /* for ARC, privilege stack is merged into defined stack */
  #define OVERFLOW_STACKSIZE (STACKSIZE + CONFIG_PRIVILEGED_STACK_SIZE)
  #else
  #define OVERFLOW_STACKSIZE STACKSIZE
  #endif
  #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)
  {
  	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");
  #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");
  
  	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");
  
  #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);
  
  #ifdef CONFIG_ARM
  	/* FIXME: See #7706 */
  	zassert_true(crash_reason == _NANO_ERR_STACK_CHK_FAIL ||
  		     crash_reason == _NANO_ERR_HW_EXCEPTION, NULL);
  #else
  	zassert_equal(crash_reason, _NANO_ERR_STACK_CHK_FAIL,
  		      "bad reason code got %d expected %d\n",
  		      crash_reason, _NANO_ERR_STACK_CHK_FAIL);
  #endif
  	zassert_not_equal(rv, TC_FAIL, "thread was not aborted");
  
  	/* 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);
  #ifdef CONFIG_NXP_MPU
  	/* FIXME: See #7706 */
  	zassert_true(crash_reason == _NANO_ERR_STACK_CHK_FAIL ||
  		     crash_reason == _NANO_ERR_HW_EXCEPTION, NULL);
  #else
  	zassert_equal(crash_reason, _NANO_ERR_STACK_CHK_FAIL,
  		      "bad reason code got %d expected %d\n",
  		      crash_reason, _NANO_ERR_STACK_CHK_FAIL);
  #endif
  	zassert_not_equal(rv, TC_FAIL, "thread was not aborted");
  #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);
  }
  