Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2014 Wind River Systems, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * @brief Common fault handler for ARM Cortex-M
 *
 * Common fault handler for ARM Cortex-M processors.
 */

#include <toolchain.h>
#include <sections.h>

#include <nanokernel.h>
#include <nano_private.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)
{
	int escalation = 0;

	PR_EXC("Fault! EXC #%d, Thread: %x, instr @ %x\n",
	       fault,
	       sys_thread_self_get(),
	       esf->pc);

	if (3 == fault) { /* hard fault */
		escalation = _ScbHardFaultIsForced();
		PR_EXC("HARD FAULT: %s\n",
		       escalation ? "Escalation (see below)!"
				  : "Bus fault on vector table read\n");
	}

	PR_EXC("MMFSR: %x, BFSR: %x, UFSR: %x\n",
	       __scs.scb.cfsr.byte.mmfsr.val,
	       __scs.scb.cfsr.byte.bfsr.val,
	       __scs.scb.cfsr.byte.ufsr.val);

	if (_ScbMemFaultIsMmfarValid()) {
		PR_EXC("MMFAR: %x\n", _ScbMemFaultAddrGet());
		if (escalation) {
			_ScbMemFaultMmfarReset();
		}
	}
	if (_ScbBusFaultIsBfarValid()) {
		PR_EXC("BFAR: %x\n", _ScbBusFaultAddrGet());
		if (escalation) {
			_ScbBusFaultBfarReset();
		}
	}

	/* clear USFR sticky bits */
	_ScbUsageFaultAllFaultsReset();
}
#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): 0x%x\n"
	       "  Faulting instruction address:  0x%x\n",
	       sys_thread_self_get(),
	       esf->pc);
}

/**
 *
 * @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 (_ScbMemFaultIsStacking()) {
		PR_EXC("  Stacking error\n");
	} else if (_ScbMemFaultIsUnstacking()) {
		PR_EXC("  Unstacking error\n");
	} else if (_ScbMemFaultIsDataAccessViolation()) {
		PR_EXC("  Data Access Violation\n");
		if (_ScbMemFaultIsMmfarValid()) {
			PR_EXC("  Address: 0x%x\n", _ScbMemFaultAddrGet());
			if (fromHardFault) {
				_ScbMemFaultMmfarReset();
			}
		}
	} else if (_ScbMemFaultIsInstrAccessViolation()) {
		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 (_ScbBusFaultIsStacking()) {
		PR_EXC("  Stacking error\n");
	} else if (_ScbBusFaultIsUnstacking()) {
		PR_EXC("  Unstacking error\n");
	} else if (_ScbBusFaultIsPrecise()) {
		PR_EXC("  Precise data bus error\n");
		if (_ScbBusFaultIsBfarValid()) {
			PR_EXC("  Address: 0x%x\n", _ScbBusFaultAddrGet());
			if (fromHardFault) {
				_ScbBusFaultBfarReset();
			}
		}
		/* it's possible to have both a precise and imprecise fault */
		if (_ScbBusFaultIsImprecise()) {
			PR_EXC("  Imprecise data bus error\n");
		}
	} else if (_ScbBusFaultIsImprecise()) {
		PR_EXC("  Imprecise data bus error\n");
	} else if (_ScbBusFaultIsInstrBusErr()) {
		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 (_ScbUsageFaultIsDivByZero()) {
		PR_EXC("  Division by zero\n");
	}
	if (_ScbUsageFaultIsUnaligned()) {
		PR_EXC("  Unaligned memory access\n");
	}
	if (_ScbUsageFaultIsNoCp()) {
		PR_EXC("  No coprocessor instructions\n");
	}
	if (_ScbUsageFaultIsInvalidPcLoad()) {
		PR_EXC("  Illegal load of EXC_RETURN into PC\n");
	}
	if (_ScbUsageFaultIsInvalidState()) {
		PR_EXC("  Illegal use of the EPSR\n");
	}
	if (_ScbUsageFaultIsUndefinedInstr()) {
		PR_EXC("  Attempt to execute undefined instruction\n");
	}

	_ScbUsageFaultAllFaultsReset();
}

/**
 *
 * @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 (_ScbHardFaultIsBusErrOnVectorRead()) {
		PR_EXC("  Bus fault on vector table read\n");
	} else if (_ScbHardFaultIsForced()) {
		PR_EXC("  Fault escalation (see below)\n");
		if (_ScbIsMemFault()) {
			_MpuFault(esf, 1);
		} else if (_ScbIsBusFault()) {
			_BusFault(esf, 1);
		} else if (_ScbIsUsageFault()) {
			_UsageFault(esf);
		}
	}
}

/**
 *
 * @brief Dump debug monitor exception information
 *
 * See _FaultDump() for example.
 *
 * @return N/A
 */
static void _DebugMonitor(const NANO_ESF *esf)
{
	PR_EXC("***** Debug monitor exception (not implemented) *****\n");
}

/**
 *
 * @brief Dump reserved exception information
 *
 * See _FaultDump() for example.
 *
 * @return N/A
 */
static void _ReservedException(const NANO_ESF *esf, int fault)
{
	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;
	case 4:
		_MpuFault(esf, 0);
		break;
	case 5:
		_BusFault(esf, 0);
		break;
	case 6:
		_UsageFault(esf);
		break;
	case 12:
		_DebugMonitor(esf);
		break;
	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.
 *
 * @return This function does not return.
 */
void _Fault(const NANO_ESF *esf)
{
	int fault = _ScbActiveVectorGet();

	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)
{
	_ScbDivByZeroFaultEnable();
}