diff --git a/arch/arm/core/cortex_m/fault.c b/arch/arm/core/cortex_m/fault.c index 62c2e036d33..00b95bc5769 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,6 +773,128 @@ static void secure_stack_dump(const z_arch_esf_t *secure_esf) #endif /* CONFIG_FAULT_DUMP== 2 */ #endif /* CONFIG_ARM_SECURE_FIRMWARE */ +/* + * This internal function does the following: + * + * - Retrieves the exception stack frame + * - Evaluates whether to report being in a nested exception + * + * If the ESF is not successfully retrieved, the function signals + * an error by returning NULL. + * + * @return ESF pointer on success, otherwise return NULL + */ +static inline z_arch_esf_t *get_esf(u32_t msp, u32_t psp, u32_t exc_return, + bool *nested_exc) +{ + bool alternative_state_exc = false; + z_arch_esf_t *ptr_esf; + + *nested_exc = false; + + if ((exc_return & EXC_RETURN_INDICATOR_PREFIX) != + EXC_RETURN_INDICATOR_PREFIX) { + /* 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. + */ + 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, + * 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. */ + 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) { + ptr_esf = (z_arch_esf_t *)__TZ_get_PSP_NS(); + } else { + ptr_esf = (z_arch_esf_t *)__TZ_get_MSP_NS(); + } + } +#elif defined(CONFIG_ARM_NONSECURE_FIRMWARE) + if (exc_return & EXC_RETURN_EXCEPTION_SECURE_Secure) { + /* Non-Secure Firmware shall only handle Non-Secure Exceptions. + * This is a fatal error. + */ + return NULL; + } + + if (exc_return & EXC_RETURN_RETURN_STACK_Secure) { + /* Exception entry occurred in Secure stack. + * + * Note that Non-Secure firmware cannot inspect the Secure + * stack to determine the root cause of the fault. Fault + * 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 + /* 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 @@ -791,106 +909,66 @@ static void secure_stack_dump(const z_arch_esf_t *secure_esf) * 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 + * 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. * - * @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. + * 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. * - * Note: exc_return argument shall only be used by the Fault handler if we are - * running a Secure Firmware. */ -void z_arm_fault(z_arch_esf_t *esf, u32_t exc_return) +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; + bool recoverable, nested_exc; + z_arch_esf_t *esf; -#if defined(CONFIG_ARM_SECURE_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) == 0U) { - /* Secure Firmware shall only handle Secure Exceptions. - * This is a fatal error. - */ - goto _exit_fatal; - } + /* Create a stack-ed copy of the ESF to be used during + * the fault handling process. + */ + z_arch_esf_t esf_copy; - 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 stack frame is located in the Non-Secure stack. - */ + /* Force unlock interrupts */ + z_arch_irq_unlock(0); - /* Dump the Secure stack before handling the actual fault. */ - SECURE_STACK_DUMP(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; - } - } 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; - } - } - } -#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; - } - - if (exc_return & EXC_RETURN_RETURN_STACK_Secure) { - /* Exception entry occurred in Secure stack. - * - * Note that Non-Secure firmware cannot inspect the Secure - * stack to determine the root cause of the fault. Fault - * inspection will indicate the Non-Secure instruction - * that performed the branch to the Secure domain. - */ - PR_FAULT_INFO("Exception occurred in Secure State"); - } -#else - (void) exc_return; -#endif /* CONFIG_ARM_SECURE_FIRMWARE */ + /* 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/fault_s.S b/arch/arm/core/fault_s.S index 50f5cee6697..4bdd766f67a 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: + mrs r1, PSP + mov r2, lr /* EXC_RETURN */ -#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 - - 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)