blob: 77f6a0e80c6045735ee1d2e6c6746f4c2083a8af [file] [log] [blame]
/*
* Copyright (c) 2014 Wind River Systems, Inc.
* Copyright (c) 2020 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Common fault handler for ARM Cortex-M
*
* Common fault handler for ARM Cortex-M processors.
*/
#include <zephyr/kernel.h>
#include <kernel_internal.h>
#include <inttypes.h>
#include <zephyr/exc_handle.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
#if defined(CONFIG_PRINTK) || defined(CONFIG_LOG)
#define PR_EXC(...) LOG_ERR(__VA_ARGS__)
#define STORE_xFAR(reg_var, reg) uint32_t reg_var = (uint32_t)reg
#else
#define PR_EXC(...)
#define STORE_xFAR(reg_var, reg)
#endif /* CONFIG_PRINTK || CONFIG_LOG */
#if (CONFIG_FAULT_DUMP == 2)
#define PR_FAULT_INFO(...) PR_EXC(__VA_ARGS__)
#else
#define PR_FAULT_INFO(...)
#endif
#if defined(CONFIG_ARM_MPU) && defined(CONFIG_CPU_HAS_NXP_MPU)
#define EMN(edr) (((edr) & SYSMPU_EDR_EMN_MASK) >> SYSMPU_EDR_EMN_SHIFT)
#define EACD(edr) (((edr) & SYSMPU_EDR_EACD_MASK) >> SYSMPU_EDR_EACD_SHIFT)
#endif
/* 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.
*/
/* Prefix. Indicates that this is an EXC_RETURN value.
* This field reads as 0b11111111.
*/
#define EXC_RETURN_INDICATOR_PREFIX (0xFF << 24)
/* bit[0]: Exception Secure. The security domain the exception was taken to. */
#define EXC_RETURN_EXCEPTION_SECURE_Pos 0
#define EXC_RETURN_EXCEPTION_SECURE_Msk \
BIT(EXC_RETURN_EXCEPTION_SECURE_Pos)
#define EXC_RETURN_EXCEPTION_SECURE_Non_Secure 0
#define EXC_RETURN_EXCEPTION_SECURE_Secure EXC_RETURN_EXCEPTION_SECURE_Msk
/* bit[2]: Stack Pointer selection. */
#define EXC_RETURN_SPSEL_Pos 2
#define EXC_RETURN_SPSEL_Msk BIT(EXC_RETURN_SPSEL_Pos)
#define EXC_RETURN_SPSEL_MAIN 0
#define EXC_RETURN_SPSEL_PROCESS EXC_RETURN_SPSEL_Msk
/* bit[3]: Mode. Indicates the Mode that was stacked from. */
#define EXC_RETURN_MODE_Pos 3
#define EXC_RETURN_MODE_Msk BIT(EXC_RETURN_MODE_Pos)
#define EXC_RETURN_MODE_HANDLER 0
#define EXC_RETURN_MODE_THREAD EXC_RETURN_MODE_Msk
/* bit[4]: Stack frame type. Indicates whether the stack frame is a standard
* integer only stack frame or an extended floating-point stack frame.
*/
#define EXC_RETURN_STACK_FRAME_TYPE_Pos 4
#define EXC_RETURN_STACK_FRAME_TYPE_Msk BIT(EXC_RETURN_STACK_FRAME_TYPE_Pos)
#define EXC_RETURN_STACK_FRAME_TYPE_EXTENDED 0
#define EXC_RETURN_STACK_FRAME_TYPE_STANDARD EXC_RETURN_STACK_FRAME_TYPE_Msk
/* bit[5]: Default callee register stacking. Indicates whether the default
* stacking rules apply, or whether the callee registers are already on the
* stack.
*/
#define EXC_RETURN_CALLEE_STACK_Pos 5
#define EXC_RETURN_CALLEE_STACK_Msk BIT(EXC_RETURN_CALLEE_STACK_Pos)
#define EXC_RETURN_CALLEE_STACK_SKIPPED 0
#define EXC_RETURN_CALLEE_STACK_DEFAULT EXC_RETURN_CALLEE_STACK_Msk
/* bit[6]: Secure or Non-secure stack. Indicates whether a Secure or
* Non-secure stack is used to restore stack frame on exception return.
*/
#define EXC_RETURN_RETURN_STACK_Pos 6
#define EXC_RETURN_RETURN_STACK_Msk BIT(EXC_RETURN_RETURN_STACK_Pos)
#define EXC_RETURN_RETURN_STACK_Non_Secure 0
#define EXC_RETURN_RETURN_STACK_Secure EXC_RETURN_RETURN_STACK_Msk
/* Integrity signature for an ARMv8-M implementation */
#if defined(CONFIG_ARMV7_M_ARMV8_M_FP)
#define INTEGRITY_SIGNATURE_STD 0xFEFA125BUL
#define INTEGRITY_SIGNATURE_EXT 0xFEFA125AUL
#else
#define INTEGRITY_SIGNATURE 0xFEFA125BUL
#endif /* CONFIG_ARMV7_M_ARMV8_M_FP */
/* Size (in words) of the additional state context that is pushed
* to the Secure stack during a Non-Secure exception entry.
*/
#define ADDITIONAL_STATE_CONTEXT_WORDS 10
#if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
/* helpers to access memory/bus/usage faults */
#define SCB_CFSR_MEMFAULTSTR \
((SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk) >> SCB_CFSR_MEMFAULTSR_Pos)
#define SCB_CFSR_BUSFAULTSTR \
((SCB->CFSR & SCB_CFSR_BUSFAULTSR_Msk) >> SCB_CFSR_BUSFAULTSR_Pos)
#define SCB_CFSR_USGFAULTSTR \
((SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk) >> SCB_CFSR_USGFAULTSR_Pos)
#endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */
/**
*
* Dump information regarding fault (FAULT_DUMP == 1)
*
* Dump information regarding the fault when CONFIG_FAULT_DUMP is set to 1
* (short form).
*
* eg. (precise bus error escalated to hard fault):
*
* Fault! EXC #3
* HARD FAULT: Escalation (see below)!
* MMFSR: 0x00000000, BFSR: 0x00000082, UFSR: 0x00000000
* BFAR: 0xff001234
*
*
*
* Dump information regarding fault (FAULT_DUMP == 2)
*
* Dump information regarding the fault when CONFIG_FAULT_DUMP is set to 2
* (long form), and return the error code for the kernel to identify the fatal
* error reason.
*
* eg. (precise bus error escalated to hard fault):
*
* ***** HARD FAULT *****
* Fault escalation (see below)
* ***** BUS FAULT *****
* Precise data bus error
* Address: 0xff001234
*
*/
#if (CONFIG_FAULT_DUMP == 1)
static void fault_show(const z_arch_esf_t *esf, int fault)
{
PR_EXC("Fault! EXC #%d", fault);
#if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
PR_EXC("MMFSR: 0x%x, BFSR: 0x%x, UFSR: 0x%x", SCB_CFSR_MEMFAULTSR,
SCB_CFSR_BUSFAULTSR, SCB_CFSR_USGFAULTSR);
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
PR_EXC("SFSR: 0x%x", SAU->SFSR);
#endif /* CONFIG_ARM_SECURE_FIRMWARE */
#endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */
}
#else
/* For Dump level 2, detailed information is generated by the
* fault handling functions for individual fault conditions, so this
* function is left empty.
*
* For Dump level 0, no information needs to be generated.
*/
static void fault_show(const z_arch_esf_t *esf, int fault)
{
(void)esf;
(void)fault;
}
#endif /* FAULT_DUMP == 1 */
#ifdef CONFIG_USERSPACE
Z_EXC_DECLARE(z_arm_user_string_nlen);
static const struct z_exc_handle exceptions[] = {
Z_EXC_HANDLE(z_arm_user_string_nlen)
};
#endif
/* Perform an assessment whether an MPU fault shall be
* treated as recoverable.
*
* @return true if error is recoverable, otherwise return false.
*/
static bool memory_fault_recoverable(z_arch_esf_t *esf, bool synchronous)
{
#ifdef CONFIG_USERSPACE
for (int i = 0; i < ARRAY_SIZE(exceptions); i++) {
/* Mask out instruction mode */
uint32_t start = (uint32_t)exceptions[i].start & ~0x1U;
uint32_t end = (uint32_t)exceptions[i].end & ~0x1U;
#if defined(CONFIG_NULL_POINTER_EXCEPTION_DETECTION_DWT)
/* Non-synchronous exceptions (e.g. DebugMonitor) may have
* allowed PC to continue to the next instruction.
*/
end += (synchronous) ? 0x0 : 0x4;
#else
ARG_UNUSED(synchronous);
#endif
if (esf->basic.pc >= start && esf->basic.pc < end) {
esf->basic.pc = (uint32_t)(exceptions[i].fixup);
return true;
}
}
#endif
return false;
}
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
/* HardFault is used for all fault conditions on ARMv6-M. */
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
uint32_t z_check_thread_stack_fail(const uint32_t fault_addr,
const uint32_t psp);
#endif /* CONFIG_MPU_STACK_GUARD || defined(CONFIG_USERSPACE) */
/**
*
* @brief Dump MemManage fault information
*
* See z_arm_fault_dump() for example.
*
* @return error code to identify the fatal error reason
*/
static uint32_t mem_manage_fault(z_arch_esf_t *esf, int from_hard_fault,
bool *recoverable)
{
uint32_t reason = K_ERR_CPU_EXCEPTION;
uint32_t mmfar = -EINVAL;
PR_FAULT_INFO("***** MPU FAULT *****");
if ((SCB->CFSR & SCB_CFSR_MSTKERR_Msk) != 0) {
PR_FAULT_INFO(" Stacking error (context area might be"
" not valid)");
}
if ((SCB->CFSR & SCB_CFSR_MUNSTKERR_Msk) != 0) {
PR_FAULT_INFO(" Unstacking error");
}
if ((SCB->CFSR & SCB_CFSR_DACCVIOL_Msk) != 0) {
PR_FAULT_INFO(" Data Access Violation");
/* In a fault handler, to determine the true faulting address:
* 1. Read and save the MMFAR value.
* 2. Read the MMARVALID bit in the MMFSR.
* The MMFAR address is valid only if this bit is 1.
*
* Software must follow this sequence because another higher
* priority exception might change the MMFAR value.
*/
uint32_t temp = SCB->MMFAR;
if ((SCB->CFSR & SCB_CFSR_MMARVALID_Msk) != 0) {
mmfar = temp;
PR_EXC(" MMFAR Address: 0x%x", mmfar);
if (from_hard_fault != 0) {
/* clear SCB_MMAR[VALID] to reset */
SCB->CFSR &= ~SCB_CFSR_MMARVALID_Msk;
}
}
}
if ((SCB->CFSR & SCB_CFSR_IACCVIOL_Msk) != 0) {
PR_FAULT_INFO(" Instruction Access Violation");
}
#if defined(CONFIG_ARMV7_M_ARMV8_M_FP)
if ((SCB->CFSR & SCB_CFSR_MLSPERR_Msk) != 0) {
PR_FAULT_INFO(
" Floating-point lazy state preservation error");
}
#endif /* CONFIG_ARMV7_M_ARMV8_M_FP */
/* When stack protection is enabled, we need to assess
* if the memory violation error is a stack corruption.
*
* By design, being a Stacking MemManage fault is a necessary
* and sufficient condition for a thread stack corruption.
* [Cortex-M process stack pointer is always descending and
* is never modified by code (except for the context-switch
* routine), therefore, a stacking error implies the PSP has
* crossed into an area beyond the thread stack.]
*
* Data Access Violation errors may or may not be caused by
* thread stack overflows.
*/
if ((SCB->CFSR & SCB_CFSR_MSTKERR_Msk) ||
(SCB->CFSR & SCB_CFSR_DACCVIOL_Msk)) {
#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
/* MemManage Faults are always banked between security
* states. Therefore, we can safely assume the fault
* originated from the same security state.
*
* As we only assess thread stack corruption, we only
* process the error further if the stack frame is on
* PSP. For always-banked MemManage Fault, this is
* equivalent to inspecting the RETTOBASE flag.
*
* Note:
* It is possible that MMFAR address is not written by the
* Cortex-M core; this occurs when the stacking error is
* not accompanied by a data access violation error (i.e.
* when stack overflows due to the exception entry frame
* stacking): z_check_thread_stack_fail() shall be able to
* handle the case of 'mmfar' holding the -EINVAL value.
*/
if (SCB->ICSR & SCB_ICSR_RETTOBASE_Msk) {
uint32_t min_stack_ptr = z_check_thread_stack_fail(mmfar,
((uint32_t) &esf[0]));
if (min_stack_ptr) {
/* When MemManage Stacking Error has occurred,
* the stack context frame might be corrupted
* but the stack pointer may have actually
* descent below the allowed (thread) stack
* area. We may face a problem with un-stacking
* the frame, upon the exception return, if we
* do not have sufficient access permissions to
* read the corrupted stack frame. Therefore,
* we manually force the stack pointer to the
* lowest allowed position, inside the thread's
* stack.
*
* Note:
* The PSP will normally be adjusted in a tail-
* chained exception performing context switch,
* after aborting the corrupted thread. The
* adjustment, here, is required as tail-chain
* cannot always be guaranteed.
*
* The manual adjustment of PSP is safe, as we
* will not be re-scheduling this thread again
* for execution; thread stack corruption is a
* fatal error and a thread that corrupted its
* stack needs to be aborted.
*/
__set_PSP(min_stack_ptr);
reason = K_ERR_STACK_CHK_FAIL;
} else {
__ASSERT(!(SCB->CFSR & SCB_CFSR_MSTKERR_Msk),
"Stacking error not a stack fail\n");
}
}
#else
(void)mmfar;
__ASSERT(!(SCB->CFSR & SCB_CFSR_MSTKERR_Msk),
"Stacking or Data Access Violation error "
"without stack guard, user-mode or null-pointer detection\n");
#endif /* CONFIG_MPU_STACK_GUARD || CONFIG_USERSPACE */
}
/* When we were handling this fault, we may have triggered a fp
* lazy stacking Memory Manage fault. At the time of writing, this
* can happen when printing. If that's true, we should clear the
* pending flag in addition to the clearing the reason for the fault
*/
#if defined(CONFIG_ARMV7_M_ARMV8_M_FP)
if ((SCB->CFSR & SCB_CFSR_MLSPERR_Msk) != 0) {
SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTPENDED_Msk;
}
#endif /* CONFIG_ARMV7_M_ARMV8_M_FP */
/* clear MMFSR sticky bits */
SCB->CFSR |= SCB_CFSR_MEMFAULTSR_Msk;
/* Assess whether system shall ignore/recover from this MPU fault. */
*recoverable = memory_fault_recoverable(esf, true);
return reason;
}
/**
*
* @brief Dump BusFault information
*
* See z_arm_fault_dump() for example.
*
* @return error code to identify the fatal error reason.
*
*/
static int bus_fault(z_arch_esf_t *esf, int from_hard_fault, bool *recoverable)
{
uint32_t reason = K_ERR_CPU_EXCEPTION;
PR_FAULT_INFO("***** BUS FAULT *****");
if (SCB->CFSR & SCB_CFSR_STKERR_Msk) {
PR_FAULT_INFO(" Stacking error");
}
if (SCB->CFSR & SCB_CFSR_UNSTKERR_Msk) {
PR_FAULT_INFO(" Unstacking error");
}
if (SCB->CFSR & SCB_CFSR_PRECISERR_Msk) {
PR_FAULT_INFO(" Precise data bus error");
/* In a fault handler, to determine the true faulting address:
* 1. Read and save the BFAR value.
* 2. Read the BFARVALID bit in the BFSR.
* The BFAR address is valid only if this bit is 1.
*
* Software must follow this sequence because another
* higher priority exception might change the BFAR value.
*/
STORE_xFAR(bfar, SCB->BFAR);
if ((SCB->CFSR & SCB_CFSR_BFARVALID_Msk) != 0) {
PR_EXC(" BFAR Address: 0x%x", bfar);
if (from_hard_fault != 0) {
/* clear SCB_CFSR_BFAR[VALID] to reset */
SCB->CFSR &= ~SCB_CFSR_BFARVALID_Msk;
}
}
}
if (SCB->CFSR & SCB_CFSR_IMPRECISERR_Msk) {
PR_FAULT_INFO(" Imprecise data bus error");
}
if ((SCB->CFSR & SCB_CFSR_IBUSERR_Msk) != 0) {
PR_FAULT_INFO(" Instruction bus error");
#if !defined(CONFIG_ARMV7_M_ARMV8_M_FP)
}
#else
} else if (SCB->CFSR & SCB_CFSR_LSPERR_Msk) {
PR_FAULT_INFO(" Floating-point lazy state preservation error");
} else {
;
}
#endif /* !defined(CONFIG_ARMV7_M_ARMV8_M_FP) */
#if defined(CONFIG_ARM_MPU) && defined(CONFIG_CPU_HAS_NXP_MPU)
uint32_t sperr = SYSMPU->CESR & SYSMPU_CESR_SPERR_MASK;
uint32_t mask = BIT(31);
int i;
uint32_t ear = -EINVAL;
if (sperr) {
for (i = 0; i < SYSMPU_EAR_COUNT; i++, mask >>= 1) {
if ((sperr & mask) == 0U) {
continue;
}
STORE_xFAR(edr, SYSMPU->SP[i].EDR);
ear = SYSMPU->SP[i].EAR;
PR_FAULT_INFO(" NXP MPU error, port %d", i);
PR_FAULT_INFO(" Mode: %s, %s Address: 0x%x",
edr & BIT(2) ? "Supervisor" : "User",
edr & BIT(1) ? "Data" : "Instruction",
ear);
PR_FAULT_INFO(
" Type: %s, Master: %d, Regions: 0x%x",
edr & BIT(0) ? "Write" : "Read",
EMN(edr), EACD(edr));
/* When stack protection is enabled, we need to assess
* if the memory violation error is a stack corruption.
*
* By design, being a Stacking Bus fault is a necessary
* and sufficient condition for a stack corruption.
*/
if (SCB->CFSR & SCB_CFSR_STKERR_Msk) {
#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
/* Note: we can assume the fault originated
* from the same security state for ARM
* platforms implementing the NXP MPU
* (CONFIG_CPU_HAS_NXP_MPU=y).
*
* As we only assess thread stack corruption,
* we only process the error further, if the
* stack frame is on PSP. For NXP MPU-related
* Bus Faults (banked), this is equivalent to
* inspecting the RETTOBASE flag.
*/
if (SCB->ICSR & SCB_ICSR_RETTOBASE_Msk) {
uint32_t min_stack_ptr =
z_check_thread_stack_fail(ear,
((uint32_t) &esf[0]));
if (min_stack_ptr) {
/* When BusFault Stacking Error
* has occurred, the stack
* context frame might be
* corrupted but the stack
* pointer may have actually
* moved. We may face problems
* with un-stacking the frame,
* upon exception return, if we
* do not have sufficient
* permissions to read the
* corrupted stack frame.
* Therefore, we manually force
* the stack pointer to the
* lowest allowed position.
*
* Note:
* The PSP will normally be
* adjusted in a tail-chained
* exception performing context
* switch, after aborting the
* corrupted thread. Here, the
* adjustment is required as
* tail-chain cannot always be
* guaranteed.
*/
__set_PSP(min_stack_ptr);
reason =
K_ERR_STACK_CHK_FAIL;
break;
}
}
#else
(void)ear;
__ASSERT(0,
"Stacking error without stack guard"
"or User-mode support");
#endif /* CONFIG_MPU_STACK_GUARD || CONFIG_USERSPACE */
}
}
SYSMPU->CESR &= ~sperr;
}
#endif /* defined(CONFIG_ARM_MPU) && defined(CONFIG_CPU_HAS_NXP_MPU) */
/* clear BFSR sticky bits */
SCB->CFSR |= SCB_CFSR_BUSFAULTSR_Msk;
*recoverable = memory_fault_recoverable(esf, true);
return reason;
}
/**
*
* @brief Dump UsageFault information
*
* See z_arm_fault_dump() for example.
*
* @return error code to identify the fatal error reason
*/
static uint32_t usage_fault(const z_arch_esf_t *esf)
{
uint32_t reason = K_ERR_CPU_EXCEPTION;
PR_FAULT_INFO("***** USAGE FAULT *****");
/* bits are sticky: they stack and must be reset */
if ((SCB->CFSR & SCB_CFSR_DIVBYZERO_Msk) != 0) {
PR_FAULT_INFO(" Division by zero");
}
if ((SCB->CFSR & SCB_CFSR_UNALIGNED_Msk) != 0) {
PR_FAULT_INFO(" Unaligned memory access");
}
#if defined(CONFIG_ARMV8_M_MAINLINE)
if ((SCB->CFSR & SCB_CFSR_STKOF_Msk) != 0) {
PR_FAULT_INFO(" Stack overflow (context area not valid)");
#if defined(CONFIG_BUILTIN_STACK_GUARD)
/* Stack Overflows are always reported as stack corruption
* errors. Note that the built-in stack overflow mechanism
* prevents the context area to be loaded on the stack upon
* UsageFault exception entry. As a result, we cannot rely
* on the reported faulty instruction address, to determine
* the instruction that triggered the stack overflow.
*/
reason = K_ERR_STACK_CHK_FAIL;
#endif /* CONFIG_BUILTIN_STACK_GUARD */
}
#endif /* CONFIG_ARMV8_M_MAINLINE */
if ((SCB->CFSR & SCB_CFSR_NOCP_Msk) != 0) {
PR_FAULT_INFO(" No coprocessor instructions");
}
if ((SCB->CFSR & SCB_CFSR_INVPC_Msk) != 0) {
PR_FAULT_INFO(" Illegal load of EXC_RETURN into PC");
}
if ((SCB->CFSR & SCB_CFSR_INVSTATE_Msk) != 0) {
PR_FAULT_INFO(" Illegal use of the EPSR");
}
if ((SCB->CFSR & SCB_CFSR_UNDEFINSTR_Msk) != 0) {
PR_FAULT_INFO(" Attempt to execute undefined instruction");
}
/* clear UFSR sticky bits */
SCB->CFSR |= SCB_CFSR_USGFAULTSR_Msk;
return reason;
}
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
/**
*
* @brief Dump SecureFault information
*
* See z_arm_fault_dump() for example.
*
*/
static void secure_fault(const z_arch_esf_t *esf)
{
PR_FAULT_INFO("***** SECURE FAULT *****");
STORE_xFAR(sfar, SAU->SFAR);
if ((SAU->SFSR & SAU_SFSR_SFARVALID_Msk) != 0) {
PR_EXC(" Address: 0x%x", sfar);
}
/* bits are sticky: they stack and must be reset */
if ((SAU->SFSR & SAU_SFSR_INVEP_Msk) != 0) {
PR_FAULT_INFO(" Invalid entry point");
} else if ((SAU->SFSR & SAU_SFSR_INVIS_Msk) != 0) {
PR_FAULT_INFO(" Invalid integrity signature");
} else if ((SAU->SFSR & SAU_SFSR_INVER_Msk) != 0) {
PR_FAULT_INFO(" Invalid exception return");
} else if ((SAU->SFSR & SAU_SFSR_AUVIOL_Msk) != 0) {
PR_FAULT_INFO(" Attribution unit violation");
} else if ((SAU->SFSR & SAU_SFSR_INVTRAN_Msk) != 0) {
PR_FAULT_INFO(" Invalid transition");
} else if ((SAU->SFSR & SAU_SFSR_LSPERR_Msk) != 0) {
PR_FAULT_INFO(" Lazy state preservation");
} else if ((SAU->SFSR & SAU_SFSR_LSERR_Msk) != 0) {
PR_FAULT_INFO(" Lazy state error");
}
/* clear SFSR sticky bits */
SAU->SFSR |= 0xFF;
}
#endif /* defined(CONFIG_ARM_SECURE_FIRMWARE) */
/**
*
* @brief Dump debug monitor exception information
*
* See z_arm_fault_dump() for example.
*
*/
static void debug_monitor(z_arch_esf_t *esf, bool *recoverable)
{
*recoverable = false;
PR_FAULT_INFO(
"***** Debug monitor exception *****");
#if defined(CONFIG_NULL_POINTER_EXCEPTION_DETECTION_DWT)
if (!z_arm_debug_monitor_event_error_check()) {
/* By default, all debug monitor exceptions that are not
* treated as errors by z_arm_debug_event_error_check(),
* they are considered as recoverable errors.
*/
*recoverable = true;
} else {
*recoverable = memory_fault_recoverable(esf, false);
}
#endif
}
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
static inline bool z_arm_is_synchronous_svc(z_arch_esf_t *esf)
{
uint16_t *ret_addr = (uint16_t *)esf->basic.pc;
/* SVC is a 16-bit instruction. On a synchronous SVC
* escalated to Hard Fault, the return address is the
* next instruction, i.e. after the SVC.
*/
#define _SVC_OPCODE 0xDF00
/* We are about to de-reference the program counter at the
* time of fault to determine if it was a SVC
* instruction. However, we don't know if the pc itself is
* valid -- we could have faulted due to trying to execute a
* corrupted function pointer.
*
* We will temporarily ignore BusFault's so a bad program
* counter does not trigger ARM lockup condition.
*/
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) && !defined(CONFIG_ARMV8_M_BASELINE)
/* Note: ARMv6-M does not support CCR.BFHFNMIGN so this access
* could generate a fault if the pc was invalid.
*/
uint16_t fault_insn = *(ret_addr - 1);
#else
SCB->CCR |= SCB_CCR_BFHFNMIGN_Msk;
__DSB();
__ISB();
uint16_t fault_insn = *(ret_addr - 1);
SCB->CCR &= ~SCB_CCR_BFHFNMIGN_Msk;
__DSB();
__ISB();
#endif /* ARMV6_M_ARMV8_M_BASELINE && !ARMV8_M_BASELINE */
if (((fault_insn & 0xff00) == _SVC_OPCODE) &&
((fault_insn & 0x00ff) == _SVC_CALL_RUNTIME_EXCEPT)) {
return true;
}
#undef _SVC_OPCODE
return false;
}
/**
*
* @brief Dump hard fault information
*
* See z_arm_fault_dump() for example.
*
* @return error code to identify the fatal error reason
*/
static uint32_t hard_fault(z_arch_esf_t *esf, bool *recoverable)
{
uint32_t reason = K_ERR_CPU_EXCEPTION;
PR_FAULT_INFO("***** HARD FAULT *****");
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
/* Workaround for #18712:
* HardFault may be due to escalation, as a result of
* an SVC instruction that could not be executed; this
* can occur if ARCH_EXCEPT() is called by an ISR,
* which executes at priority equal to the SVC handler
* priority. We handle the case of Kernel OOPS and Stack
* Fail here.
*/
if (z_arm_is_synchronous_svc(esf)) {
PR_EXC("ARCH_EXCEPT with reason %x\n", esf->basic.r0);
reason = esf->basic.r0;
}
*recoverable = memory_fault_recoverable(esf, true);
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
*recoverable = false;
if ((SCB->HFSR & SCB_HFSR_VECTTBL_Msk) != 0) {
PR_EXC(" Bus fault on vector table read");
} else if ((SCB->HFSR & SCB_HFSR_DEBUGEVT_Msk) != 0) {
PR_EXC(" Debug event");
} else if ((SCB->HFSR & SCB_HFSR_FORCED_Msk) != 0) {
PR_EXC(" Fault escalation (see below)");
if (z_arm_is_synchronous_svc(esf)) {
PR_EXC("ARCH_EXCEPT with reason %x\n", esf->basic.r0);
reason = esf->basic.r0;
} else if ((SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk) != 0) {
reason = mem_manage_fault(esf, 1, recoverable);
} else if ((SCB->CFSR & SCB_CFSR_BUSFAULTSR_Msk) != 0) {
reason = bus_fault(esf, 1, recoverable);
} else if ((SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk) != 0) {
reason = usage_fault(esf);
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
} else if (SAU->SFSR != 0) {
secure_fault(esf);
#endif /* CONFIG_ARM_SECURE_FIRMWARE */
} else {
__ASSERT(0,
"Fault escalation without FSR info");
}
} else {
__ASSERT(0,
"HardFault without HFSR info"
" Shall never occur");
}
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
return reason;
}
/**
*
* @brief Dump reserved exception information
*
* See z_arm_fault_dump() for example.
*
*/
static void reserved_exception(const z_arch_esf_t *esf, int fault)
{
ARG_UNUSED(esf);
PR_FAULT_INFO("***** %s %d) *****",
fault < 16 ? "Reserved Exception (" : "Spurious interrupt (IRQ ",
fault - 16);
}
/* Handler function for ARM fault conditions. */
static uint32_t fault_handle(z_arch_esf_t *esf, int fault, bool *recoverable)
{
uint32_t reason = K_ERR_CPU_EXCEPTION;
*recoverable = false;
switch (fault) {
case 3:
reason = hard_fault(esf, recoverable);
break;
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
/* HardFault is raised for all fault conditions on ARMv6-M. */
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
case 4:
reason = mem_manage_fault(esf, 0, recoverable);
break;
case 5:
reason = bus_fault(esf, 0, recoverable);
break;
case 6:
reason = usage_fault(esf);
break;
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
case 7:
secure_fault(esf);
break;
#endif /* CONFIG_ARM_SECURE_FIRMWARE */
case 12:
debug_monitor(esf, recoverable);
break;
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
default:
reserved_exception(esf, fault);
break;
}
if ((*recoverable) == false) {
/* Dump generic information about the fault. */
fault_show(esf, fault);
}
return reason;
}
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
#if (CONFIG_FAULT_DUMP == 2)
/**
* @brief Dump the Secure Stack information for an exception that
* has occurred in Non-Secure state.
*
* @param secure_esf Pointer to the secure stack frame.
*/
static void secure_stack_dump(const z_arch_esf_t *secure_esf)
{
/*
* In case a Non-Secure exception interrupted the Secure
* execution, the Secure state has stacked the additional
* state context and the top of the stack contains the
* integrity signature.
*
* In case of a Non-Secure function call the top of the
* stack contains the return address to Secure state.
*/
uint32_t *top_of_sec_stack = (uint32_t *)secure_esf;
uint32_t sec_ret_addr;
#if defined(CONFIG_ARMV7_M_ARMV8_M_FP)
if ((*top_of_sec_stack == INTEGRITY_SIGNATURE_STD) ||
(*top_of_sec_stack == INTEGRITY_SIGNATURE_EXT)) {
#else
if (*top_of_sec_stack == INTEGRITY_SIGNATURE) {
#endif /* CONFIG_ARMV7_M_ARMV8_M_FP */
/* Secure state interrupted by a Non-Secure exception.
* The return address after the additional state
* context, stacked by the Secure code upon
* Non-Secure exception entry.
*/
top_of_sec_stack += ADDITIONAL_STATE_CONTEXT_WORDS;
secure_esf = (const z_arch_esf_t *)top_of_sec_stack;
sec_ret_addr = secure_esf->basic.pc;
} else {
/* Exception during Non-Secure function call.
* The return address is located on top of stack.
*/
sec_ret_addr = *top_of_sec_stack;
}
PR_FAULT_INFO(" S instruction address: 0x%x", sec_ret_addr);
}
#define SECURE_STACK_DUMP(esf) secure_stack_dump(esf)
#else
/* We do not dump the Secure stack information for lower dump levels. */
#define SECURE_STACK_DUMP(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(uint32_t msp, uint32_t psp, uint32_t exc_return,
bool *nested_exc)
{
bool alternative_state_exc = false;
z_arch_esf_t *ptr_esf = NULL;
*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
*
* 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.
* @param callee_regs Callee-saved registers (R4-R11, PSP)
*
*/
void z_arm_fault(uint32_t msp, uint32_t psp, uint32_t exc_return,
_callee_saved_t *callee_regs)
{
uint32_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 */
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.");
#ifdef CONFIG_DEBUG_COREDUMP
z_arm_coredump_fault_sp = POINTER_TO_UINT(esf);
#endif
reason = fault_handle(esf, fault, &recoverable);
if (recoverable) {
return;
}
/* Copy ESF */
#if !defined(CONFIG_EXTRA_EXCEPTION_INFO)
memcpy(&esf_copy, esf, sizeof(z_arch_esf_t));
ARG_UNUSED(callee_regs);
#else
/* the extra exception info is not present in the original esf
* so we only copy the fields before those.
*/
memcpy(&esf_copy, esf, offsetof(z_arch_esf_t, extra_info));
esf_copy.extra_info = (struct __extra_esf_info) {
.callee = callee_regs,
.exc_return = exc_return,
.msp = msp
};
#endif /* CONFIG_EXTRA_EXCEPTION_INFO */
/* 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);
}
/**
*
* @brief Initialization of fault handling
*
* Turns on the desired hardware faults.
*
*/
void z_arm_fault_init(void)
{
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
#if defined(CONFIG_BUILTIN_STACK_GUARD)
/* If Stack guarding via SP limit checking is enabled, disable
* SP limit checking inside HardFault and NMI. This is done
* in order to allow for the desired fault logging to execute
* properly in all cases.
*
* Note that this could allow a Secure Firmware Main Stack
* to descend into non-secure region during HardFault and
* NMI exception entry. To prevent from this, non-secure
* memory regions must be located higher than secure memory
* regions.
*
* For Non-Secure Firmware this could allow the Non-Secure Main
* Stack to attempt to descend into secure region, in which case a
* Secure Hard Fault will occur and we can track the fault from there.
*/
SCB->CCR |= SCB_CCR_STKOFHFNMIGN_Msk;
#endif /* CONFIG_BUILTIN_STACK_GUARD */
#ifdef CONFIG_TRAP_UNALIGNED_ACCESS
SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
#endif /* CONFIG_TRAP_UNALIGNED_ACCESS */
}