zephyr/arch/arm/core/fault.c

385 lines
8.3 KiB
C

/*
* Copyright (c) 2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Common fault handler for ARM Cortex-M
*
* Common fault handler for ARM Cortex-M processors.
*/
#include <toolchain.h>
#include <linker/sections.h>
#include <kernel.h>
#include <kernel_structs.h>
#include <inttypes.h>
#ifdef CONFIG_PRINTK
#include <misc/printk.h>
#define PR_EXC(...) printk(__VA_ARGS__)
#else
#define PR_EXC(...)
#endif /* CONFIG_PRINTK */
#if (CONFIG_FAULT_DUMP > 0)
#define FAULT_DUMP(esf, fault) _FaultDump(esf, fault)
#else
#define FAULT_DUMP(esf, fault) \
do { \
(void) esf; \
(void) fault; \
} while ((0))
#endif
#if (CONFIG_FAULT_DUMP == 1)
/**
*
* @brief 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, Thread: 0x200000dc, instr: 0x000011d3
* HARD FAULT: Escalation (see below)!
* MMFSR: 0x00000000, BFSR: 0x00000082, UFSR: 0x00000000
* BFAR: 0xff001234
*
* @return N/A
*/
void _FaultDump(const NANO_ESF *esf, int fault)
{
PR_EXC("Fault! EXC #%d, Thread: %p, instr @ 0x%x\n",
fault,
k_current_get(),
esf->pc);
#if defined(CONFIG_ARMV6_M)
#elif defined(CONFIG_ARMV7_M)
int escalation = 0;
if (3 == fault) { /* hard fault */
escalation = SCB->HFSR & SCB_HFSR_FORCED_Msk;
PR_EXC("HARD FAULT: %s\n",
escalation ? "Escalation (see below)!"
: "Bus fault on vector table read\n");
}
PR_EXC("MMFSR: 0x%x, BFSR: 0x%x, UFSR: 0x%x\n",
SCB_MMFSR, SCB_BFSR, SCB_MMFSR);
if (SCB->CFSR & CFSR_MMARVALID_Msk) {
PR_EXC("MMFAR: 0x%x\n", SCB->MMFAR);
if (escalation) {
/* clear MMAR[VALID] to reset */
SCB->CFSR &= ~CFSR_MMARVALID_Msk;
}
}
if (SCB->CFSR & CFSR_BFARVALID_Msk) {
PR_EXC("BFAR: 0x%x\n", SCB->BFAR);
if (escalation) {
/* clear CFSR_BFAR[VALID] to reset */
SCB->CFSR &= ~CFSR_BFARVALID_Msk;
}
}
/* clear USFR sticky bits */
SCB->CFSR |= SCB_CFSR_USGFAULTSR_Msk;
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M */
}
#endif
#if (CONFIG_FAULT_DUMP == 2)
/**
*
* @brief Dump thread information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _FaultThreadShow(const NANO_ESF *esf)
{
PR_EXC(" Executing thread ID (thread): %p\n"
" Faulting instruction address: 0x%x\n",
k_current_get(), esf->pc);
}
#if defined(CONFIG_ARMV6_M)
#elif defined(CONFIG_ARMV7_M)
/**
*
* @brief Dump MPU fault information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _MpuFault(const NANO_ESF *esf, int fromHardFault)
{
PR_EXC("***** MPU FAULT *****\n");
_FaultThreadShow(esf);
if (SCB->CFSR & CFSR_MSTKERR_Msk) {
PR_EXC(" Stacking error\n");
} else if (SCB->CFSR & CFSR_MUNSTKERR_Msk) {
PR_EXC(" Unstacking error\n");
} else if (SCB->CFSR & CFSR_DACCVIOL_Msk) {
PR_EXC(" Data Access Violation\n");
if (SCB->CFSR & CFSR_MMARVALID_Msk) {
PR_EXC(" Address: 0x%x\n", (u32_t)SCB->MMFAR);
if (fromHardFault) {
/* clear MMAR[VALID] to reset */
SCB->CFSR &= ~CFSR_MMARVALID_Msk;
}
}
} else if (SCB->CFSR & CFSR_IACCVIOL_Msk) {
PR_EXC(" Instruction Access Violation\n");
}
}
/**
*
* @brief Dump bus fault information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _BusFault(const NANO_ESF *esf, int fromHardFault)
{
PR_EXC("***** BUS FAULT *****\n");
_FaultThreadShow(esf);
if (SCB->CFSR & CFSR_STKERR_Msk) {
PR_EXC(" Stacking error\n");
} else if (SCB->CFSR & CFSR_UNSTKERR_Msk) {
PR_EXC(" Unstacking error\n");
} else if (SCB->CFSR & CFSR_PRECISERR_Msk) {
PR_EXC(" Precise data bus error\n");
if (SCB->CFSR & CFSR_BFARVALID_Msk) {
PR_EXC(" Address: 0x%x\n", (u32_t)SCB->BFAR);
if (fromHardFault) {
/* clear CFSR_BFAR[VALID] to reset */
SCB->CFSR &= ~CFSR_BFARVALID_Msk;
}
}
/* it's possible to have both a precise and imprecise fault */
if (SCB->CFSR & CFSR_IMPRECISERR_Msk) {
PR_EXC(" Imprecise data bus error\n");
}
} else if (SCB->CFSR & CFSR_IMPRECISERR_Msk) {
PR_EXC(" Imprecise data bus error\n");
} else if (SCB->CFSR & CFSR_IBUSERR_Msk) {
PR_EXC(" Instruction bus error\n");
}
}
/**
*
* @brief Dump usage fault information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _UsageFault(const NANO_ESF *esf)
{
PR_EXC("***** USAGE FAULT *****\n");
_FaultThreadShow(esf);
/* bits are sticky: they stack and must be reset */
if (SCB->CFSR & CFSR_DIVBYZERO_Msk) {
PR_EXC(" Division by zero\n");
}
if (SCB->CFSR & CFSR_UNALIGNED_Msk) {
PR_EXC(" Unaligned memory access\n");
}
if (SCB->CFSR & CFSR_NOCP_Msk) {
PR_EXC(" No coprocessor instructions\n");
}
if (SCB->CFSR & CFSR_INVPC_Msk) {
PR_EXC(" Illegal load of EXC_RETURN into PC\n");
}
if (SCB->CFSR & CFSR_INVSTATE_Msk) {
PR_EXC(" Illegal use of the EPSR\n");
}
if (SCB->CFSR & CFSR_UNDEFINSTR_Msk) {
PR_EXC(" Attempt to execute undefined instruction\n");
}
/* clear USFR sticky bits */
SCB->CFSR |= SCB_CFSR_USGFAULTSR_Msk;
}
/**
*
* @brief Dump debug monitor exception information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _DebugMonitor(const NANO_ESF *esf)
{
ARG_UNUSED(esf);
PR_EXC("***** Debug monitor exception (not implemented) *****\n");
}
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M */
/**
*
* @brief Dump hard fault information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _HardFault(const NANO_ESF *esf)
{
PR_EXC("***** HARD FAULT *****\n");
#if defined(CONFIG_ARMV6_M)
_FaultThreadShow(esf);
#elif defined(CONFIG_ARMV7_M)
if (SCB->HFSR & SCB_HFSR_VECTTBL_Msk) {
PR_EXC(" Bus fault on vector table read\n");
} else if (SCB->HFSR & SCB_HFSR_FORCED_Msk) {
PR_EXC(" Fault escalation (see below)\n");
if (SCB_MMFSR) {
_MpuFault(esf, 1);
} else if (SCB_BFSR) {
_BusFault(esf, 1);
} else if (SCB_UFSR) {
_UsageFault(esf);
}
}
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M */
}
/**
*
* @brief Dump reserved exception information
*
* See _FaultDump() for example.
*
* @return N/A
*/
static void _ReservedException(const NANO_ESF *esf, int fault)
{
ARG_UNUSED(esf);
PR_EXC("***** %s %d) *****\n",
fault < 16 ? "Reserved Exception (" : "Spurious interrupt (IRQ ",
fault - 16);
}
/**
*
* @brief Dump information regarding fault (FAULT_DUMP == 2)
*
* Dump information regarding the fault when CONFIG_FAULT_DUMP is set to 2
* (long form).
*
* eg. (precise bus error escalated to hard fault):
*
* Executing thread ID (thread): 0x200000dc
* Faulting instruction address: 0x000011d3
* ***** HARD FAULT *****
* Fault escalation (see below)
* ***** BUS FAULT *****
* Precise data bus error
* Address: 0xff001234
*
* @return N/A
*/
static void _FaultDump(const NANO_ESF *esf, int fault)
{
switch (fault) {
case 3:
_HardFault(esf);
break;
#if defined(CONFIG_ARMV6_M)
#elif defined(CONFIG_ARMV7_M)
case 4:
_MpuFault(esf, 0);
break;
case 5:
_BusFault(esf, 0);
break;
case 6:
_UsageFault(esf);
break;
case 12:
_DebugMonitor(esf);
break;
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M */
default:
_ReservedException(esf, fault);
break;
}
}
#endif /* FAULT_DUMP == 2 */
/**
*
* @brief Fault handler
*
* This routine is called when fatal error conditions are detected by hardware
* and is responsible only for reporting the error. Once reported, it then
* invokes the user provided routine _SysFatalErrorHandler() which is
* responsible for implementing the error handling policy.
*
* Since the ESF can be either on the MSP or PSP depending if an exception or
* interrupt was already being handled, it is passed a pointer to both and has
* to find out on which the ESP is present.
*
* @param esf ESF on the stack, either MSP or PSP depending at what processor
* state the exception was taken.
*/
void _Fault(const NANO_ESF *esf)
{
int fault = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk;
FAULT_DUMP(esf, fault);
_SysFatalErrorHandler(_NANO_ERR_HW_EXCEPTION, esf);
}
/**
*
* @brief Initialization of fault handling
*
* Turns on the desired hardware faults.
*
* @return N/A
*/
void _FaultInit(void)
{
#if defined(CONFIG_ARMV6_M)
#elif defined(CONFIG_ARMV7_M)
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M */
}