zephyr/arch/riscv32/core/fatal.c
Marti Bolivar f4c3163d3b arch: riscv32: provide a general mechanism for saving SoC context
RISC-V permits myriad extensions to the ISA, any of which may imply
additional context that must be saved and restored on ISR entry and
exit. The current in-tree example is the Pulpino core, which has extra
registers used by ISA extensions for running loops that shouldn't get
clobbered by an ISR.

This is currently supported by including pulpino-specific definitions
in the generic architecture code. This works, but it's a bit inelegant
and is something of a layering violation. A more generic mechanism is
required to support other RISC-V SoCs with similar requirements
without cluttering the arch code too much.

Provide that by extending the semantics of the existing
CONFIG_RISCV_SOC_CONTEXT_SAVE option to allow other SoCs to allocate
space for saving and restoring their own state, promoting the
currently pulpino-specific __soc_save_context / __soc_restore_context
routines to a RISC-V arch API.

The cost of making this generic is two more instructions in each ISR
to pass the SoC specific context to these routines in a0 rather than
just assuming the stack points to the right place. This is minimal,
and should have been done anyway to keep with the ABI.

As a first (and currently only in-tree) customer, convert the Pulpino
SoC code to this new mechanism.

Signed-off-by: Marti Bolivar <marti@foundries.io>
2018-12-04 22:54:23 -05:00

205 lines
4.5 KiB
C

/*
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <kernel_structs.h>
#include <inttypes.h>
#include <misc/printk.h>
#include <logging/log_ctrl.h>
const NANO_ESF _default_esf = {
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
#if defined(CONFIG_RISCV_SOC_CONTEXT_SAVE)
{
SOC_ESF_INIT,
},
#endif
};
/**
*
* @brief Fatal error handler
*
* This routine is called when a fatal error condition is detected by either
* hardware or software.
*
* The caller is expected to always provide a usable ESF. In the event that the
* fatal error does not have a hardware generated ESF, the caller should either
* create its own or call _Fault instead.
*
* @param reason the reason that the handler was called
* @param esf pointer to the exception stack frame
*
* @return This function does not return.
*/
FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason,
const NANO_ESF *esf)
{
LOG_PANIC();
switch (reason) {
case _NANO_ERR_CPU_EXCEPTION:
case _NANO_ERR_SPURIOUS_INT:
break;
#if defined(CONFIG_STACK_CANARIES) || defined(CONFIG_STACK_SENTINEL)
case _NANO_ERR_STACK_CHK_FAIL:
printk("***** Stack Check Fail! *****\n");
break;
#endif /* CONFIG_STACK_CANARIES */
case _NANO_ERR_ALLOCATION_FAIL:
printk("**** Kernel Allocation Failure! ****\n");
break;
case _NANO_ERR_KERNEL_OOPS:
printk("***** Kernel OOPS! *****\n");
break;
case _NANO_ERR_KERNEL_PANIC:
printk("***** Kernel Panic! *****\n");
break;
default:
printk("**** Unknown Fatal Error %d! ****\n", reason);
break;
}
printk("Current thread ID = %p\n"
"Faulting instruction address = 0x%x\n"
" ra: 0x%x gp: 0x%x tp: 0x%x t0: 0x%x\n"
" t1: 0x%x t2: 0x%x t3: 0x%x t4: 0x%x\n"
" t5: 0x%x t6: 0x%x a0: 0x%x a1: 0x%x\n"
" a2: 0x%x a3: 0x%x a4: 0x%x a5: 0x%x\n"
" a6: 0x%x a7: 0x%x\n",
k_current_get(),
(esf->mepc == 0xdeadbaad) ? 0xdeadbaad : esf->mepc,
esf->ra, esf->gp, esf->tp, esf->t0,
esf->t1, esf->t2, esf->t3, esf->t4,
esf->t5, esf->t6, esf->a0, esf->a1,
esf->a2, esf->a3, esf->a4, esf->a5,
esf->a6, esf->a7);
_SysFatalErrorHandler(reason, esf);
/* spin forever */
for (;;)
__asm__ volatile("nop");
}
/**
*
* @brief Fatal error handler
*
* This routine implements the corrective action to be taken when the system
* detects a fatal error.
*
* This sample implementation attempts to abort the current thread and allow
* the system to continue executing, which may permit the system to continue
* functioning with degraded capabilities.
*
* System designers may wish to enhance or substitute this sample
* implementation to take other actions, such as logging error (or debug)
* information to a persistent repository and/or rebooting the system.
*
* @param reason fatal error reason
* @param esf pointer to exception stack frame
*
* @return N/A
*/
FUNC_NORETURN __weak void _SysFatalErrorHandler(unsigned int reason,
const NANO_ESF *esf)
{
ARG_UNUSED(esf);
LOG_PANIC();
#if !defined(CONFIG_SIMPLE_FATAL_ERROR_HANDLER)
#ifdef CONFIG_STACK_SENTINEL
if (reason == _NANO_ERR_STACK_CHK_FAIL) {
goto hang_system;
}
#endif
if (reason == _NANO_ERR_KERNEL_PANIC) {
goto hang_system;
}
if (k_is_in_isr() || _is_thread_essential()) {
printk("Fatal fault in %s! Spinning...\n",
k_is_in_isr() ? "ISR" : "essential thread");
goto hang_system;
}
printk("Fatal fault in thread %p! Aborting.\n", _current);
k_thread_abort(_current);
hang_system:
#else
ARG_UNUSED(reason);
#endif
for (;;) {
k_cpu_idle();
}
CODE_UNREACHABLE;
}
#ifdef CONFIG_PRINTK
static char *cause_str(u32_t cause)
{
switch (cause) {
case 0:
return "Instruction address misaligned";
case 1:
return "Instruction Access fault";
case 2:
return "Illegal instruction";
case 3:
return "Breakpoint";
case 4:
return "Load address misaligned";
case 5:
return "Load access fault";
default:
return "unknown";
}
}
#endif
FUNC_NORETURN void _Fault(const NANO_ESF *esf)
{
u32_t mcause;
__asm__ volatile("csrr %0, mcause" : "=r" (mcause));
mcause &= SOC_MCAUSE_EXP_MASK;
#ifdef CONFIG_PRINTK
printk("Exception cause %s (%d)\n", cause_str(mcause), (int)mcause);
#endif
_NanoFatalErrorHandler(_NANO_ERR_CPU_EXCEPTION, esf);
}