arch: arm: major cleanup and refactoring for fault function
This commit refactors and cleans up __fault, so the function - reduces to supplying MSP, PSP, and EXC_RETURN to the C function for fault handling - simplifies itself, removing conditional implementation, i.e. based on ARM Secure firmware, The reason for that is simple: it is much better to write the fault handling in C instead of assembly, so we really do only what is strictly required, in assembly. Therefore, the commit refactors the z_arm_fault() function as well, organizing better the different functional blocks, that is: - unlocking interrupts - retriving ESF - asserting for HW errors - printing additional error logs The refactoring unifies the way the ESF is retrieved for the different Cortex-M variants and security execution states. Signed-off-by: Ioannis Glaropoulos <Ioannis.Glaropoulos@nordicsemi.no>
This commit is contained in:
parent
26e4d43916
commit
4aa3f71337
2 changed files with 182 additions and 158 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 <toolchain.h>
|
||||
#include <linker/sections.h>
|
||||
#include <arch/cpu.h>
|
||||
|
||||
_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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue