/* * Copyright (c) 2016 Cadence Design Systems, Inc. * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include const NANO_ESF _default_esf = { {0xdeaddead}, /* sp */ 0xdeaddead, /* pc */ }; /* Need to do this as a macro since regnum must be an immediate value */ #define get_sreg(regnum_p) ({ \ unsigned int retval; \ __asm__ volatile( \ "rsr %[retval], %[regnum]\n\t" \ : [retval] "=r" (retval) \ : [regnum] "i" (regnum_p)); \ retval; \ }) /** * * @brief Fatal error handler * * This routine is called when fatal error conditions are detected by software * 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. * * 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 use a pointer to the global default ESF <_default_esf>. * * @param reason the reason that the handler was called * @param pEsf pointer to the exception stack frame * * @return This function does not return. */ FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf) { switch (reason) { case _NANO_ERR_HW_EXCEPTION: case _NANO_ERR_RESERVED_IRQ: 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", k_current_get(), pEsf->pc); /* * Now that the error has been reported, call the user implemented * policy * to respond to the error. The decisions as to what responses are * appropriate to the various errors are something the customer must * decide. */ _SysFatalErrorHandler(reason, pEsf); } #ifdef CONFIG_PRINTK static char *cause_str(unsigned int cause_code) { switch (cause_code) { case 0: return "illegal instruction"; case 1: return "syscall"; case 2: return "instr fetch error"; case 3: return "load/store error"; case 4: return "level-1 interrupt"; case 5: return "alloca"; case 6: return "divide by zero"; case 8: return "privileged"; case 9: return "load/store alignment"; case 12: return "instr PIF data error"; case 13: return "load/store PIF data error"; case 14: return "instr PIF addr error"; case 15: return "load/store PIF addr error"; case 16: return "instr TLB miss"; case 17: return "instr TLB multi hit"; case 18: return "instr fetch privilege"; case 20: return "inst fetch prohibited"; case 24: return "load/store TLB miss"; case 25: return "load/store TLB multi hit"; case 26: return "load/store privilege"; case 28: return "load prohibited"; case 29: return "store prohibited"; case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: return "coprocessor disabled"; default: return "unknown/reserved"; } } #endif static inline unsigned int get_bits(int offset, int num_bits, unsigned int val) { int mask; mask = (1 << num_bits) - 1; val = val >> offset; return val & mask; } static void dump_exc_state(void) { #ifdef CONFIG_PRINTK unsigned int cause, ps; cause = get_sreg(EXCCAUSE); ps = get_sreg(PS); printk("Exception cause %d (%s):\n" " EPC1 : 0x%08x EXCSAVE1 : 0x%08x EXCVADDR : 0x%08x\n", cause, cause_str(cause), get_sreg(EPC_1), get_sreg(EXCSAVE_1), get_sreg(EXCVADDR)); printk("Program state (PS):\n" " INTLEVEL : %02d EXCM : %d UM : %d RING : %d WOE : %d\n", get_bits(0, 4, ps), get_bits(4, 1, ps), get_bits(5, 1, ps), get_bits(6, 2, ps), get_bits(18, 1, ps)); #ifndef __XTENSA_CALL0_ABI__ printk(" OWB : %02d CALLINC : %d\n", get_bits(8, 4, ps), get_bits(16, 2, ps)); #endif #endif /* CONFIG_PRINTK */ } FUNC_NORETURN void FatalErrorHandler(void) { printk("*** Unhandled exception ****\n"); dump_exc_state(); _NanoFatalErrorHandler(_NANO_ERR_HW_EXCEPTION, &_default_esf); } FUNC_NORETURN void ReservedInterruptHandler(unsigned int intNo) { printk("*** Reserved Interrupt ***\n"); dump_exc_state(); printk("INTENABLE = 0x%x\n" "INTERRUPT = 0x%x (%d)\n", get_sreg(INTENABLE), (1 << intNo), intNo); _NanoFatalErrorHandler(_NANO_ERR_RESERVED_IRQ, &_default_esf); } void exit(int return_code) { #ifdef XT_SIMULATOR __asm__ ( "mov a3, %[code]\n\t" "movi a2, %[call]\n\t" "simcall\n\t" : : [code] "r" (return_code), [call] "i" (SYS_exit) : "a3", "a2"); #else printk("exit(%d)\n", return_code); k_panic(); #endif } /** * * @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 the fatal error reason * @param pEsf pointer to exception stack frame * * @return N/A */ FUNC_NORETURN __weak void _SysFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf) { ARG_UNUSED(pEsf); #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 #ifdef XT_SIMULATOR exit(255 - reason); #else for (;;) { k_cpu_idle(); } #endif CODE_UNREACHABLE; }