diff --git a/arch/Kconfig b/arch/Kconfig index a6cf9d8d24ea..34b7d6e1718c 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -354,6 +354,9 @@ config ARCH_HAS_NOCACHE_MEMORY_SUPPORT config ARCH_HAS_RAMFUNC_SUPPORT bool +config ARCH_HAS_NESTED_EXCEPTION_DETECTION + bool + # # Other architecture related options # diff --git a/arch/arm/core/Kconfig b/arch/arm/core/Kconfig index a6a529b33b55..8229d908a917 100644 --- a/arch/arm/core/Kconfig +++ b/arch/arm/core/Kconfig @@ -23,6 +23,7 @@ config CPU_CORTEX_M select ARCH_HAS_USERSPACE if ARM_MPU select ARCH_HAS_NOCACHE_MEMORY_SUPPORT if ARM_MPU && CPU_HAS_ARM_MPU && CPU_CORTEX_M7 select ARCH_HAS_RAMFUNC_SUPPORT + select ARCH_HAS_NESTED_EXCEPTION_DETECTION select SWAP_NONATOMIC help This option signifies the use of a CPU of the Cortex-M family. diff --git a/arch/arm/core/cortex_m/fault.c b/arch/arm/core/cortex_m/fault.c index 6bfad45cd69c..0fc7602f66a8 100644 --- a/arch/arm/core/cortex_m/fault.c +++ b/arch/arm/core/cortex_m/fault.c @@ -40,9 +40,6 @@ LOG_MODULE_DECLARE(os); #define EACD(edr) (((edr) & SYSMPU_EDR_EACD_MASK) >> SYSMPU_EDR_EACD_SHIFT) #endif -#if defined(CONFIG_ARM_SECURE_FIRMWARE) || \ - defined(CONFIG_ARM_NONSECURE_FIRMWARE) - /* Exception Return (EXC_RETURN) is provided in LR upon exception entry. * It is used to perform an exception return and to detect possible state * transition upon exception. @@ -102,7 +99,6 @@ LOG_MODULE_DECLARE(os); * to the Secure stack during a Non-Secure exception entry. */ #define ADDITIONAL_STATE_CONTEXT_WORDS 10 -#endif /* CONFIG_ARM_SECURE_FIRMWARE || CONFIG_ARM_NONSECURE_FIRMWARE */ /** * @@ -777,94 +773,78 @@ static void secure_stack_dump(const z_arch_esf_t *secure_esf) #endif /* CONFIG_FAULT_DUMP== 2 */ #endif /* CONFIG_ARM_SECURE_FIRMWARE */ -/** - * - * @brief ARM Fault handler - * - * This routine is called when fatal error conditions are detected by hardware - * and is responsible for: - * - resetting the processor fault status registers (for the case when the - * error handling policy allows the system to recover from the error), - * - reporting the error information, - * - determining the error reason to be provided as input to the user- - * provided routine, k_sys_fatal_error_handler(). - * The k_sys_fatal_error_handler() is invoked once the above operations are - * completed, and is responsible for implementing the error handling policy. - * - * The provided ESF pointer points to the exception stack frame of the current - * security state. Note that the current security state might not be the actual - * state in which the processor was executing, when the exception occurred. - * The actual state may need to be determined by inspecting the EXC_RETURN - * value, which is provided as argument to the Fault handler. +/* + * This internal function does the following: * - * @param esf Pointer to the exception stack frame of the current security - * state. The stack frame may be either on the Main stack (MSP) or Process - * stack (PSP) depending at what execution state the exception was taken. + * - Retrieves the exception stack frame + * - Evaluates whether to report being in a nested exception * - * @param exc_return EXC_RETURN value present in LR after exception entry. + * If the ESF is not successfully retrieved, the function signals + * an error by returning NULL. * - * Note: exc_return argument shall only be used by the Fault handler if we are - * running a Secure Firmware. + * @return ESF pointer on success, otherwise return NULL */ -void z_arm_fault(z_arch_esf_t *esf, u32_t exc_return) +static inline z_arch_esf_t *get_esf(u32_t msp, u32_t psp, u32_t exc_return, + bool *nested_exc) { - u32_t reason = K_ERR_CPU_EXCEPTION; - int fault = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; - bool recoverable; + bool alternative_state_exc = false; + z_arch_esf_t *ptr_esf; + + *nested_exc = false; -#if defined(CONFIG_ARM_SECURE_FIRMWARE) if ((exc_return & EXC_RETURN_INDICATOR_PREFIX) != EXC_RETURN_INDICATOR_PREFIX) { - /* Invalid EXC_RETURN value */ - goto _exit_fatal; + /* Invalid EXC_RETURN value. This is a fatal error. */ + return NULL; } + +#if defined(CONFIG_ARM_SECURE_FIRMWARE) if ((exc_return & EXC_RETURN_EXCEPTION_SECURE_Secure) == 0U) { /* Secure Firmware shall only handle Secure Exceptions. * This is a fatal error. */ - goto _exit_fatal; + return NULL; } if (exc_return & EXC_RETURN_RETURN_STACK_Secure) { /* Exception entry occurred in Secure stack. */ } else { - /* Exception entry occurred in Non-Secure stack. Therefore, 'esf' - * holds the Secure stack information, however, the actual + /* Exception entry occurred in Non-Secure stack. Therefore, + * msp/psp point to the Secure stack, however, the actual * exception stack frame is located in the Non-Secure stack. */ + alternative_state_exc = true; /* Dump the Secure stack before handling the actual fault. */ - SECURE_STACK_DUMP(esf); + z_arch_esf_t *secure_esf; + + if (exc_return & EXC_RETURN_SPSEL_PROCESS) { + /* Secure stack pointed by PSP */ + secure_esf = (z_arch_esf_t *)psp; + } else { + /* Secure stack pointed by MSP */ + secure_esf = (z_arch_esf_t *)msp; + *nested_exc = true; + } + + SECURE_STACK_DUMP(secure_esf); /* Handle the actual fault. * Extract the correct stack frame from the Non-Secure state * and supply it to the fault handing function. */ if (exc_return & EXC_RETURN_MODE_THREAD) { - esf = (z_arch_esf_t *)__TZ_get_PSP_NS(); - if ((SCB->ICSR & SCB_ICSR_RETTOBASE_Msk) == 0) { - PR_EXC("RETTOBASE does not match EXC_RETURN"); - goto _exit_fatal; - } + ptr_esf = (z_arch_esf_t *)__TZ_get_PSP_NS(); } else { - esf = (z_arch_esf_t *)__TZ_get_MSP_NS(); - if ((SCB->ICSR & SCB_ICSR_RETTOBASE_Msk) != 0) { - PR_EXC("RETTOBASE does not match EXC_RETURN"); - goto _exit_fatal; - } + ptr_esf = (z_arch_esf_t *)__TZ_get_MSP_NS(); } } #elif defined(CONFIG_ARM_NONSECURE_FIRMWARE) - if ((exc_return & EXC_RETURN_INDICATOR_PREFIX) != - EXC_RETURN_INDICATOR_PREFIX) { - /* Invalid EXC_RETURN value */ - goto _exit_fatal; - } if (exc_return & EXC_RETURN_EXCEPTION_SECURE_Secure) { /* Non-Secure Firmware shall only handle Non-Secure Exceptions. * This is a fatal error. */ - goto _exit_fatal; + return NULL; } if (exc_return & EXC_RETURN_RETURN_STACK_Secure) { @@ -875,22 +855,120 @@ void z_arm_fault(z_arch_esf_t *esf, u32_t exc_return) * inspection will indicate the Non-Secure instruction * that performed the branch to the Secure domain. */ + alternative_state_exc = true; + PR_FAULT_INFO("Exception occurred in Secure State"); + + if (exc_return & EXC_RETURN_SPSEL_PROCESS) { + /* Non-Secure stack frame on PSP */ + ptr_esf = (z_arch_esf_t *)psp; + } else { + /* Non-Secure stack frame on MSP */ + ptr_esf = (z_arch_esf_t *)msp; + } + } else { + /* Exception entry occurred in Non-Secure stack. */ } #else - (void) exc_return; + /* The processor has a single execution state. + * We verify that the Thread mode is using PSP. + */ + if ((exc_return & EXC_RETURN_MODE_THREAD) && + (!(exc_return & EXC_RETURN_SPSEL_PROCESS))) { + PR_EXC("SPSEL in thread mode does not indicate PSP"); + return NULL; + } #endif /* CONFIG_ARM_SECURE_FIRMWARE */ + if (!alternative_state_exc) { + if (exc_return & EXC_RETURN_MODE_THREAD) { + /* Returning to thread mode */ + ptr_esf = (z_arch_esf_t *)psp; + + } else { + /* Returning to handler mode */ + ptr_esf = (z_arch_esf_t *)msp; + *nested_exc = true; + } + } + + return ptr_esf; +} + +/** + * + * @brief ARM Fault handler + * + * This routine is called when fatal error conditions are detected by hardware + * and is responsible for: + * - resetting the processor fault status registers (for the case when the + * error handling policy allows the system to recover from the error), + * - reporting the error information, + * - determining the error reason to be provided as input to the user- + * provided routine, k_sys_fatal_error_handler(). + * The k_sys_fatal_error_handler() is invoked once the above operations are + * completed, and is responsible for implementing the error handling policy. + * + * The function needs, first, to determine the exception stack frame. + * Note that the current security state might not be the actual + * state in which the processor was executing, when the exception occurred. + * The actual state may need to be determined by inspecting the EXC_RETURN + * value, which is provided as argument to the Fault handler. + * + * If the exception occurred in the same security state, the stack frame + * will be pointed to by either MSP or PSP depending on the processor + * execution state when the exception occurred. MSP and PSP values are + * provided as arguments to the Fault handler. + * + * @param msp MSP value immediately after the exception occurred + * @param psp PSP value immediately after the exception occurred + * @param exc_return EXC_RETURN value present in LR after exception entry. + * + */ +void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return) +{ + u32_t reason = K_ERR_CPU_EXCEPTION; + int fault = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; + bool recoverable, nested_exc; + z_arch_esf_t *esf; + + /* Create a stack-ed copy of the ESF to be used during + * the fault handling process. + */ + z_arch_esf_t esf_copy; + + /* Force unlock interrupts */ + z_arch_irq_unlock(0); + + /* Retrieve the Exception Stack Frame (ESF) to be supplied + * as argument to the remainder of the fault handling process. + */ + esf = get_esf(msp, psp, exc_return, &nested_exc); + __ASSERT(esf != NULL, + "ESF could not be retrieved successfully. Shall never occur."); + reason = fault_handle(esf, fault, &recoverable); if (recoverable) { return; } -#if defined(CONFIG_ARM_SECURE_FIRMWARE) || \ - defined(CONFIG_ARM_NONSECURE_FIRMWARE) -_exit_fatal: -#endif - z_arm_fatal_error(reason, esf); + /* Copy ESF */ + memcpy(&esf_copy, esf, sizeof(z_arch_esf_t)); + + /* Overwrite stacked IPSR to mark a nested exception, + * or a return to Thread mode. Note that this may be + * required, if the retrieved ESF contents are invalid + * due to, for instance, a stacking error. + */ + if (nested_exc) { + if ((esf_copy.basic.xpsr & IPSR_ISR_Msk) == 0) { + esf_copy.basic.xpsr |= IPSR_ISR_Msk; + } + } else { + esf_copy.basic.xpsr &= ~(IPSR_ISR_Msk); + } + + z_arm_fatal_error(reason, &esf_copy); } /** diff --git a/arch/arm/core/fatal.c b/arch/arm/core/fatal.c index 86a19c8493d2..ccbd3a359fb2 100644 --- a/arch/arm/core/fatal.c +++ b/arch/arm/core/fatal.c @@ -52,6 +52,21 @@ void z_arm_fatal_error(unsigned int reason, const z_arch_esf_t *esf) z_fatal_error(reason, esf); } +/** + * @brief Handle a software-generated fatal exception + * (e.g. kernel oops, panic, etc.). + * + * Notes: + * - the function is invoked in SVC Handler + * - if triggered from nPRIV mode, only oops and stack fail error reasons + * may be propagated to the fault handling process. + * - We expect the supplied exception stack frame to always be a valid + * frame. That is because, if the ESF cannot be stacked during an SVC, + * a processor fault (e.g. stacking error) will be generated, and the + * fault handler will executed insted of the SVC. + * + * @param esf exception frame + */ void z_do_kernel_oops(const z_arch_esf_t *esf) { /* Stacked R0 holds the exception reason. */ diff --git a/arch/arm/core/fault_s.S b/arch/arm/core/fault_s.S index 50f5cee66977..4bdd766f67ac 100644 --- a/arch/arm/core/fault_s.S +++ b/arch/arm/core/fault_s.S @@ -1,5 +1,6 @@ /* * Copyright (c) 2013-2014 Wind River Systems, Inc. + * Copyright (c) 2017-2019 Nordic Semiconductor ASA. * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,7 +14,6 @@ #include #include -#include _ASM_FILE_PROLOGUE @@ -43,14 +43,18 @@ GTEXT(z_arm_reserved) * * @brief Fault handler installed in the fault and reserved vectors * - * Entry point for the hard fault, MPU fault, bus fault, usage fault, debug - * monitor and reserved exceptions. + * Entry point for the HardFault, MemManageFault, BusFault, UsageFault, + * SecureFault, Debug Monitor, and reserved exceptions. * - * Save the values of the MSP and PSP in r0 and r1 respectively, so the first - * and second parameters to the z_arm_fault() C function that will handle the - * rest. This has to be done because at this point we do not know if the fault - * happened while handling an exception or not, and thus the ESF could be on - * either stack. z_arm_fault() will find out where the ESF resides. + * For Cortex-M: the function supplies the values of + * - the MSP + * - the PSP + * - the EXC_RETURN value + * as parameters to the z_arm_fault() C function that will perform the + * rest of the fault handling (i.e. z_arm_fault(MSP, PSP, EXC_RETURN)). + * + * For Cortex-R: the function simply invokes z_arm_fault() with currently + * unused arguments. * * Provides these symbols: * @@ -83,82 +87,24 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,z_arm_data_abort) #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ SECTION_SUBSEC_FUNC(TEXT,__fault,z_arm_reserved) -#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) - /* force unlock interrupts */ - cpsie i - - /* Use EXC_RETURN state to find out if stack frame is on the - * MSP or PSP - */ - ldr r0, =0x4 - mov r1, lr - tst r1, r0 - beq _stack_frame_msp - mrs r0, PSP - bne _stack_frame_endif -_stack_frame_msp: +#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) || \ + defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) mrs r0, MSP -_stack_frame_endif: - -#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) - /* force unlock interrupts */ - eors.n r0, r0 - msr BASEPRI, r0 - -#if !defined(CONFIG_ARM_SECURE_FIRMWARE) && \ - !defined(CONFIG_ARM_NONSECURE_FIRMWARE) - /* this checks to see if we are in a nested exception */ - ldr ip, =_SCS_ICSR - ldr ip, [ip] - ands.w ip, #_SCS_ICSR_RETTOBASE + mrs r1, PSP + mov r2, lr /* EXC_RETURN */ - ite eq /* is the RETTOBASE bit zero ? */ - mrseq r0, MSP /* if so, we're not returning to thread mode, - * thus this is a nested exception: the stack - * frame is on the MSP */ - mrsne r0, PSP /* if not, we are returning to thread mode, thus - * this is not a nested exception: the stack - * frame is on the PSP */ -#else - /* RETTOBASE flag is not banked between security states. - * Therefore, we cannot rely on this flag, to obtain the SP - * of the current security state. - * Instead, we use the EXC_RETURN.SPSEL flag. - */ - ldr r0, =0x4 - mov r1, lr - tst r1, r0 - beq _s_stack_frame_msp - mrs r0, PSP - bne _s_stack_frame_endif -_s_stack_frame_msp: - mrs r0, MSP -_s_stack_frame_endif: -#endif /* CONFIG_ARM_SECURE_FIRMWARE || CONFIG_ARM_NONSECURE_FIRMWARE */ + push {r0, lr} #elif defined(CONFIG_ARMV7_R) /* - * Pass null for the esf to z_arm_fault for now. A future PR will add - * better exception debug for Cortex-R that subsumes what esf + * Pass null for the esf to z_arm_fault for now. A future PR will add + * better exception debug for Cortex-R that subsumes what esf * provides. */ mov r0, #0 #else #error Unknown ARM architecture -#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ +#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE || CONFIG_ARMv7_M_ARMV8_M_MAINLINE */ -#if defined(CONFIG_ARM_SECURE_FIRMWARE) || \ - defined(CONFIG_ARM_NONSECURE_FIRMWARE) - /* The stack pointer that is retrieved above, points to the stack, - * where the exception is taken. However, the exeption may have - * occurred in the alternative security state. - * - * To determine this we need to inspect the EXC_RETURN value - * located in the LR. Therefore, we supply the LR value as an - * argument to the fault handler. - */ - mov r1, lr -#endif /* CONFIG_ARM_SECURE_FIRMWARE || CONFIG_ARM_NONSECURE_FIRMWARE */ - push {r0, lr} bl z_arm_fault #if defined(CONFIG_CPU_CORTEX_M) diff --git a/arch/arm/core/swap_helper.S b/arch/arm/core/swap_helper.S index 2762f26fc726..b4847b2daed7 100644 --- a/arch/arm/core/swap_helper.S +++ b/arch/arm/core/swap_helper.S @@ -468,6 +468,7 @@ SECTION_FUNC(TEXT, z_arm_svc) _oops: push {r0, lr} bl z_do_kernel_oops + /* return from SVC exception is done here */ pop {r0, pc} #if defined(CONFIG_USERSPACE) diff --git a/arch/arm/include/cortex_m/exc.h b/arch/arm/include/cortex_m/exc.h index ac188899d25a..e4f57aab6462 100644 --- a/arch/arm/include/cortex_m/exc.h +++ b/arch/arm/include/cortex_m/exc.h @@ -38,35 +38,40 @@ extern volatile irq_offload_routine_t offload_routine; * to the Vector Key field, otherwise the writes are ignored. */ #define AIRCR_VECT_KEY_PERMIT_WRITE 0x05FAUL -/* The current executing vector is found in the IPSR register. We consider the - * IRQs (exception 16 and up), and the PendSV and SYSTICK exceptions to be - * interrupts. Taking a fault within an exception is also considered in - * interrupt context. + +/* + * The current executing vector is found in the IPSR register. All + * IRQs and system exceptions are considered as interrupt context. */ static ALWAYS_INLINE bool z_arch_is_in_isr(void) { - u32_t vector = __get_IPSR(); + return (__get_IPSR()) ? (true) : (false); +} - /* IRQs + PendSV (14) + SYSTICK (15) are interrupts. */ - return (vector > 13) -#ifdef CONFIG_IRQ_OFFLOAD - /* Only non-NULL if currently running an offloaded function */ - || offload_routine != NULL -#endif -#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) - /* On ARMv6-M there is no nested execution bit, so we check - * exception 3, hard fault, to a detect a nested exception. - */ - || (vector == 3U) -#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) - /* If not in thread mode, and if RETTOBASE bit in ICSR is 0, - * then there are preempted active exceptions to execute. - */ - || (vector && !(SCB->ICSR & SCB_ICSR_RETTOBASE_Msk)) -#else -#error Unknown ARM architecture -#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ - ; +/** + * @brief Find out if we were in ISR context + * before the current exception occurred. + * + * A function that determines, based on inspecting the current + * ESF, whether the processor was in handler mode before entering + * the current exception state (i.e. nested exception) or not. + * + * Notes: + * - The function shall only be called from ISR context. + * - We do not use ARM processor state flags to determine + * whether we are in a nested exception; we rely on the + * RETPSR value stacked on the ESF. Hence, the function + * assumes that the ESF stack frame has a valid RETPSR + * value. + * + * @param esf the exception stack frame (cannot be NULL) + * @return true if execution state was in handler mode, before + * the current exception occurred, otherwise false. + */ +static ALWAYS_INLINE bool z_arch_is_in_nested_exception( + const z_arch_esf_t *esf) +{ + return (esf->basic.xpsr & IPSR_ISR_Msk) ? (true) : (false); } /** diff --git a/include/arch/arm/error.h b/include/arch/arm/error.h index f57597b36367..71c8f7d575c7 100644 --- a/include/arch/arm/error.h +++ b/include/arch/arm/error.h @@ -40,7 +40,6 @@ do { \ : \ : "r" (r0), [id] "i" (_SVC_CALL_RUNTIME_EXCEPT) \ : "memory"); \ - CODE_UNREACHABLE; \ } while (false) #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) #define Z_ARCH_EXCEPT(reason_p) do { \ @@ -52,7 +51,6 @@ do { \ : \ : [reason] "i" (reason_p), [id] "i" (_SVC_CALL_RUNTIME_EXCEPT) \ : "memory"); \ - CODE_UNREACHABLE; \ } while (false) #elif defined(CONFIG_ARMV7_R) /* Pick up the default definition in kernel.h for now */ diff --git a/include/kernel.h b/include/kernel.h index 53707516c070..309cf7e1dac1 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -4795,7 +4795,7 @@ static inline void k_cpu_atomic_idle(unsigned int key) extern void z_sys_power_save_idle_exit(s32_t ticks); #ifdef Z_ARCH_EXCEPT -/* This archtecture has direct support for triggering a CPU exception */ +/* This architecture has direct support for triggering a CPU exception */ #define z_except_reason(reason) Z_ARCH_EXCEPT(reason) #else diff --git a/kernel/fatal.c b/kernel/fatal.c index 4830eab3a283..65daa38d93c6 100644 --- a/kernel/fatal.c +++ b/kernel/fatal.c @@ -5,7 +5,9 @@ */ #include + #include +#include #include #include #include @@ -93,11 +95,12 @@ void z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) * an IRQ or exception was being handled, or thread context. * * See #17656 - * - * if (k_is_in_isr()) { - * LOG_ERR("Fault during interrupt handling\n"); - * } */ +#if defined(CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION) + if (z_arch_is_in_nested_exception(esf)) { + LOG_ERR("Fault during interrupt handling\n"); + } +#endif LOG_ERR("Current thread: %p (%s)", thread, log_strdup(thread_name_get(thread))); @@ -107,6 +110,13 @@ void z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) /* If the system fatal error handler returns, then kill the faulting * thread; a policy decision was made not to hang the system. * + * Policy for fatal errors in ISRs: unconditionally panic. + * + * There is one exception to this policy: a stack sentinel + * check may be performed (on behalf of the current thread) + * during ISR exit, but in this case the thread should be + * aborted. + * * Note that k_thread_abort() returns on some architectures but * not others; e.g. on ARC, x86_64, Xtensa with ASM2, ARM */ @@ -114,8 +124,31 @@ void z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) __ASSERT(reason != K_ERR_KERNEL_PANIC, "Attempted to recover from a kernel panic condition"); /* FIXME: #17656 */ - __ASSERT(!k_is_in_isr(), - "Attempted to recover from a fatal error in ISR"); +#if defined(CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION) + if (z_arch_is_in_nested_exception(esf)) { +#if defined(CONFIG_STACK_SENTINEL) + if (reason != K_ERR_STACK_CHK_FAIL) { + __ASSERT(0, + "Attempted to recover from a fatal error in ISR"); + } +#endif /* CONFIG_STACK_SENTINEL */ + } +#endif /* CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION */ + } else { + /* Test mode */ +#if defined(CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION) + if (z_arch_is_in_nested_exception(esf)) { + /* Abort the thread only on STACK Sentinel check fail. */ +#if defined(CONFIG_STACK_SENTINEL) + if (reason != K_ERR_STACK_CHK_FAIL) { + return; + } +#else + return; +#endif /* CONFIG_STACK_SENTINEL */ + } +#endif /*CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION */ } + k_thread_abort(thread); } diff --git a/tests/arch/arm/arm_interrupt/CMakeLists.txt b/tests/arch/arm/arm_interrupt/CMakeLists.txt new file mode 100644 index 000000000000..c78728ec6a84 --- /dev/null +++ b/tests/arch/arm/arm_interrupt/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(arm_interrupt) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/arch/arm/arm_interrupt/README.txt b/tests/arch/arm/arm_interrupt/README.txt new file mode 100644 index 000000000000..aec6307d4f7a --- /dev/null +++ b/tests/arch/arm/arm_interrupt/README.txt @@ -0,0 +1,67 @@ +Title: Test to verify code fault handling in ISR execution context (ARM Only) + +Description: + +This test verifies that we can handle system fault conditions +while running in handler mode (i.e. in an ISR). Only for ARM +Cortex-M targets. + +--------------------------------------------------------------------------- + +Building and Running Project: + +This project outputs to the console. It can be built and executed on QEMU as +follows: + + ninja/make run + +--------------------------------------------------------------------------- + +Troubleshooting: + +Problems caused by out-dated project information can be addressed by +issuing one of the following commands then rebuilding the project: + + ninja/make clean # discard results of previous builds + # but keep existing configuration info +or + ninja/make pristine # discard results of previous builds + # and restore pre-defined configuration info + +--------------------------------------------------------------------------- + +Sample Output: +***** Booting Zephyr OS build zephyr-v2.0.0-1066-ga087055d4e3d ***** + Running test suite arm_interrupt + =================================================================== + starting test - test_arm_interrupt + Available IRQ line: 25 + E: ***** HARD FAULT ***** + E: Z_ARCH_EXCEPT with reason 3 + + E: r0/a1: 0x00000003 r1/a2: 0x20001240 r2/a3: 0x00000003 + E: r3/a4: 0x20001098 r12/ip: 0x00000000 r14/lr: 0x000012c9 + E: xpsr: 0x01000029 + E: Faulting instruction address (r15/pc): 0x000003de + E: >>> ZEPHYR FATAL ERROR 3: Kernel oops + E: Current thread: 0x20000058 (unknown) + Caught system error -- reason 3 + E: Fault during interrupt handling + + E: ***** HARD FAULT ***** + E: Z_ARCH_EXCEPT with reason 4 + + E: r0/a1: 0x00000004 r1/a2: 0x20001240 r2/a3: 0x00000004 + E: r3/a4: 0x20001098 r12/ip: 0x00000000 r14/lr: 0x000012c9 + E: xpsr: 0x01000029 + E: Faulting instruction address (r15/pc): 0x000003e8 + E: >>> ZEPHYR FATAL ERROR 4: Kernel panic + E: Current thread: 0x20000058 (unknown) + Caught system error -- reason 4 + E: Fault during interrupt handling + + PASS - test_arm_interrupt + =================================================================== + Test suite arm_interrupt succeeded + =================================================================== + PROJECT EXECUTION SUCCESSFUL diff --git a/tests/arch/arm/arm_interrupt/prj.conf b/tests/arch/arm/arm_interrupt/prj.conf new file mode 100644 index 000000000000..a3f0a41c8d24 --- /dev/null +++ b/tests/arch/arm/arm_interrupt/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_DYNAMIC_INTERRUPTS=y diff --git a/tests/arch/arm/arm_interrupt/src/arm_interrupt.c b/tests/arch/arm/arm_interrupt/src/arm_interrupt.c new file mode 100644 index 000000000000..6c3f3934994b --- /dev/null +++ b/tests/arch/arm/arm_interrupt/src/arm_interrupt.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static volatile int test_flag; +static volatile int expected_reason = -1; + +void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *pEsf) +{ + TC_PRINT("Caught system error -- reason %d\n", reason); + + if (expected_reason == -1) { + printk("Was not expecting a crash\n"); + k_fatal_halt(reason); + } + + if (reason != expected_reason) { + printk("Wrong crash type got %d expected %d\n", reason, + expected_reason); + k_fatal_halt(reason); + } + + expected_reason = -1; +} + +void arm_isr_handler(void *args) +{ + ARG_UNUSED(args); + + test_flag++; + + if (test_flag == 1) { + /* Intentional Kernel oops */ + expected_reason = K_ERR_KERNEL_OOPS; + k_oops(); + } else if (test_flag == 2) { + /* Intentional Kernel panic */ + expected_reason = K_ERR_KERNEL_PANIC; + k_panic(); + } else if (test_flag == 3) { + /* Intentional ASSERT */ + expected_reason = K_ERR_KERNEL_PANIC; + __ASSERT(0, "Intentional assert\n"); + } +} + +void test_arm_interrupt(void) +{ + /* Determine an NVIC IRQ line that is not currently in use. */ + int i; + int init_flag, post_flag; + + init_flag = test_flag; + + zassert_false(init_flag, "Test flag not initialized to zero\n"); + + 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); + + z_arch_irq_connect_dynamic(i, 0 /* highest priority */, + arm_isr_handler, + NULL, + 0); + + NVIC_ClearPendingIRQ(i); + NVIC_EnableIRQ(i); + + for (int j = 1; j <= 3; j++) { + + /* Set the dynamic IRQ to pending state. */ + NVIC_SetPendingIRQ(i); + + /* + * Instruction barriers to make sure the NVIC IRQ is + * set to pending state before 'test_flag' is checked. + */ + __DSB(); + __ISB(); + + /* Returning here implies the thread was not aborted. */ + + /* Confirm test flag is set by the ISR handler. */ + post_flag = test_flag; + zassert_true(post_flag == j, "Test flag not set by ISR\n"); + } +} +/** + * @} + */ diff --git a/tests/arch/arm/arm_interrupt/src/main.c b/tests/arch/arm/arm_interrupt/src/main.c new file mode 100644 index 000000000000..132d7195f25b --- /dev/null +++ b/tests/arch/arm/arm_interrupt/src/main.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +extern void test_arm_interrupt(void); + +void test_main(void) +{ + ztest_test_suite(arm_interrupt, + ztest_unit_test(test_arm_interrupt)); + ztest_run_test_suite(arm_interrupt); +} diff --git a/tests/arch/arm/arm_interrupt/testcase.yaml b/tests/arch/arm/arm_interrupt/testcase.yaml new file mode 100644 index 000000000000..710e07b51fac --- /dev/null +++ b/tests/arch/arm/arm_interrupt/testcase.yaml @@ -0,0 +1,12 @@ +tests: + arch.interrupt: + filter: CONFIG_ARMV6_M_ARMV8_M_BASELINE or CONFIG_ARMV7_M_ARMV8_M_MAINLINE + tags: arm interrupt ignore_faults + arch_whitelist: arm + arch.interrupt.no_optimizations: + filter: CONFIG_ARMV6_M_ARMV8_M_BASELINE or CONFIG_ARMV7_M_ARMV8_M_MAINLINE + tags: arm interrupt ignore_faults + arch_whitelist: arm + extra_configs: + - CONFIG_NO_OPTIMIZATIONS=y + - CONFIG_IDLE_STACK_SIZE=512