diff options
Diffstat (limited to 'core/cortex-m/panic.c')
-rw-r--r-- | core/cortex-m/panic.c | 475 |
1 files changed, 0 insertions, 475 deletions
diff --git a/core/cortex-m/panic.c b/core/cortex-m/panic.c deleted file mode 100644 index da6900b1b9..0000000000 --- a/core/cortex-m/panic.c +++ /dev/null @@ -1,475 +0,0 @@ -/* Copyright 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "common.h" -#include "console.h" -#include "cpu.h" -#include "host_command.h" -#include "panic.h" -#include "panic-internal.h" -#include "printf.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "uart.h" -#include "util.h" -#include "watchdog.h" - -/* Whether bus fault is ignored */ -static int bus_fault_ignored; - - -/* Panic data goes at the end of RAM. */ -static struct panic_data * const pdata_ptr = PANIC_DATA_PTR; - -/* Preceded by stack, rounded down to nearest 64-bit-aligned boundary */ -static const uint32_t pstack_addr = (CONFIG_RAM_BASE + CONFIG_RAM_SIZE - - sizeof(struct panic_data)) & ~7; - -/** - * Print the name and value of a register - * - * This is a convenient helper function for displaying a register value. - * It shows the register name in a 3 character field, followed by a colon. - * The register value is regs[index], and this is shown in hex. If regs is - * NULL, then we display spaces instead. - * - * After displaying the value, either a space or \n is displayed depending - * on the register number, so that (assuming the caller passes all 16 - * registers in sequence) we put 4 values per line like this - * - * r0 :0000000b r1 :00000047 r2 :60000000 r3 :200012b5 - * r4 :00000000 r5 :08004e64 r6 :08004e1c r7 :200012a8 - * r8 :08004e64 r9 :00000002 r10:00000000 r11:00000000 - * r12:0000003f sp :200009a0 lr :0800270d pc :0800351a - * - * @param regnum Register number to display (0-15) - * @param regs Pointer to array holding the registers, or NULL - * @param index Index into array where the register value is present - */ -static void print_reg(int regnum, const uint32_t *regs, int index) -{ - static const char regname[] = "r10r11r12sp lr pc "; - static char rname[3] = "r "; - const char *name; - - rname[1] = '0' + regnum; - name = regnum < 10 ? rname : ®name[(regnum - 10) * 3]; - panic_printf("%c%c%c:", name[0], name[1], name[2]); - if (regs) - panic_printf("%08x", regs[index]); - else - panic_puts(" "); - panic_puts((regnum & 3) == 3 ? "\n" : " "); -} - -/* - * Returns non-zero if the exception frame was created on the main stack, or - * zero if it's on the process stack. - * - * See B1.5.8 "Exception return behavior" of ARM DDI 0403D for details. - */ -static int32_t is_frame_in_handler_stack(const uint32_t exc_return) -{ - return (exc_return & 0xf) == 1 || (exc_return & 0xf) == 9; -} - -#ifdef CONFIG_DEBUG_EXCEPTIONS -/* Names for each of the bits in the cfs register, starting at bit 0 */ -static const char * const cfsr_name[32] = { - /* MMFSR */ - [0] = "Instruction access violation", - [1] = "Data access violation", - [3] = "Unstack from exception violation", - [4] = "Stack from exception violation", - - /* BFSR */ - [8] = "Instruction bus error", - [9] = "Precise data bus error", - [10] = "Imprecise data bus error", - [11] = "Unstack from exception bus fault", - [12] = "Stack from exception bus fault", - - /* UFSR */ - [16] = "Undefined instructions", - [17] = "Invalid state", - [18] = "Invalid PC", - [19] = "No coprocessor", - [24] = "Unaligned", - [25] = "Divide by 0", -}; - -/* Names for the first 5 bits in the DFSR */ -static const char * const dfsr_name[] = { - "Halt request", - "Breakpoint", - "Data watchpoint/trace", - "Vector catch", - "External debug request", -}; - -/** - * Helper function to display a separator after the previous item - * - * If items have been displayed already, we display a comma separator. - * In any case, the count of items displayed is incremeneted. - * - * @param count Number of items displayed so far (0 for none) - */ -static void do_separate(int *count) -{ - if (*count) - panic_puts(", "); - (*count)++; -} - -/** - * Show a textual representaton of the fault registers - * - * A list of detected faults is shown, with no trailing newline. - * - * @param cfsr Value of Configurable Fault Status - * @param hfsr Value of Hard Fault Status - * @param dfsr Value of Debug Fault Status - */ -static void show_fault(uint32_t cfsr, uint32_t hfsr, uint32_t dfsr) -{ - unsigned int upto; - int count = 0; - - for (upto = 0; upto < 32; upto++) { - if ((cfsr & BIT(upto)) && cfsr_name[upto]) { - do_separate(&count); - panic_puts(cfsr_name[upto]); - } - } - - if (hfsr & CPU_NVIC_HFSR_DEBUGEVT) { - do_separate(&count); - panic_puts("Debug event"); - } - if (hfsr & CPU_NVIC_HFSR_FORCED) { - do_separate(&count); - panic_puts("Forced hard fault"); - } - if (hfsr & CPU_NVIC_HFSR_VECTTBL) { - do_separate(&count); - panic_puts("Vector table bus fault"); - } - - for (upto = 0; upto < 5; upto++) { - if ((dfsr & BIT(upto))) { - do_separate(&count); - panic_puts(dfsr_name[upto]); - } - } -} - -/* - * Returns the size of the exception frame. - * - * See B1.5.7 "Stack alignment on exception entry" of ARM DDI 0403D for details. - * In short, the exception frame size can be either 0x20, 0x24, 0x68, or 0x6c - * depending on FPU context and padding for 8-byte alignment. - */ -static uint32_t get_exception_frame_size(const struct panic_data *pdata) -{ - uint32_t frame_size = 0; - - /* base exception frame */ - frame_size += 8 * sizeof(uint32_t); - - /* CPU uses xPSR[9] to indicate whether it padded the stack for - * alignment or not. */ - if (pdata->cm.frame[7] & BIT(9)) - frame_size += sizeof(uint32_t); - -#ifdef CONFIG_FPU - /* CPU uses EXC_RETURN[4] to indicate whether it stored extended - * frame for FPU or not. */ - if (!(pdata->cm.regs[11] & BIT(4))) - frame_size += 18 * sizeof(uint32_t); -#endif - - return frame_size; -} - -/* - * Returns the position of the process stack before the exception frame. - * It computes the size of the exception frame and adds it to psp. - * If the exception happened in the exception context, it returns psp as is. - */ -static uint32_t get_process_stack_position(const struct panic_data *pdata) -{ - uint32_t psp = pdata->cm.regs[0]; - - if (!is_frame_in_handler_stack(pdata->cm.regs[11])) - psp += get_exception_frame_size(pdata); - - return psp; -} - -/* - * Show extra information that might be useful to understand a panic() - * - * We show fault register information, including the fault address registers - * if valid. - */ -static void panic_show_extra(const struct panic_data *pdata) -{ - show_fault(pdata->cm.cfsr, pdata->cm.hfsr, pdata->cm.dfsr); - if (pdata->cm.cfsr & CPU_NVIC_CFSR_BFARVALID) - panic_printf(", bfar = %x", pdata->cm.bfar); - if (pdata->cm.cfsr & CPU_NVIC_CFSR_MFARVALID) - panic_printf(", mfar = %x", pdata->cm.mfar); - panic_printf("\ncfsr = %x, ", pdata->cm.cfsr); - panic_printf("shcsr = %x, ", pdata->cm.shcsr); - panic_printf("hfsr = %x, ", pdata->cm.hfsr); - panic_printf("dfsr = %x\n", pdata->cm.dfsr); -} - -/* - * Prints process stack contents stored above the exception frame. - */ -static void panic_show_process_stack(const struct panic_data *pdata) -{ - panic_printf("\n=========== Process Stack Contents ==========="); - if (pdata->flags & PANIC_DATA_FLAG_FRAME_VALID) { - uint32_t psp = get_process_stack_position(pdata); - int i; - for (i = 0; i < 16; i++) { - if (psp + sizeof(uint32_t) > - CONFIG_RAM_BASE + CONFIG_RAM_SIZE) - break; - if (i % 4 == 0) - panic_printf("\n%08x:", psp); - panic_printf(" %08x", *(uint32_t *)psp); - psp += sizeof(uint32_t); - } - } else { - panic_printf("\nBad psp"); - } -} -#endif /* CONFIG_DEBUG_EXCEPTIONS */ - -/* - * Print panic data - */ -void panic_data_print(const struct panic_data *pdata) -{ - const uint32_t *lregs = pdata->cm.regs; - const uint32_t *sregs = NULL; - const int32_t in_handler = - is_frame_in_handler_stack(pdata->cm.regs[11]); - int i; - - if (pdata->flags & PANIC_DATA_FLAG_FRAME_VALID) - sregs = pdata->cm.frame; - - panic_printf("\n=== %s EXCEPTION: %02x ====== xPSR: %08x ===\n", - in_handler ? "HANDLER" : "PROCESS", - lregs[1] & 0xff, sregs ? sregs[7] : -1); - for (i = 0; i < 4; i++) - print_reg(i, sregs, i); - for (i = 4; i < 10; i++) - print_reg(i, lregs, i - 1); - print_reg(10, lregs, 9); - print_reg(11, lregs, 10); - print_reg(12, sregs, 4); - print_reg(13, lregs, in_handler ? 2 : 0); - print_reg(14, sregs, 5); - print_reg(15, sregs, 6); - -#ifdef CONFIG_DEBUG_EXCEPTIONS - panic_show_extra(pdata); -#endif -} - -void __keep report_panic(void) -{ - /* - * Don't need to get pointer via get_panic_data_write() - * because memory below pdata_ptr is stack now (see exception_panic()) - */ - struct panic_data *pdata = pdata_ptr; - uint32_t sp; - - pdata->magic = PANIC_DATA_MAGIC; - pdata->struct_size = sizeof(*pdata); - pdata->struct_version = 2; - pdata->arch = PANIC_ARCH_CORTEX_M; - pdata->flags = 0; - pdata->reserved = 0; - - /* Choose the right sp (psp or msp) based on EXC_RETURN value */ - sp = is_frame_in_handler_stack(pdata->cm.regs[11]) - ? pdata->cm.regs[2] : pdata->cm.regs[0]; - /* If stack is valid, copy exception frame to pdata */ - if ((sp & 3) == 0 && - sp >= CONFIG_RAM_BASE && - sp <= CONFIG_RAM_BASE + CONFIG_RAM_SIZE - 8 * sizeof(uint32_t)) { - const uint32_t *sregs = (const uint32_t *)sp; - int i; - - /* Skip r0-r3 and r12 registers if necessary */ - for (i = CORTEX_PANIC_FRAME_REGISTER_R0; - i <= CORTEX_PANIC_FRAME_REGISTER_R12; i++) - if (IS_ENABLED(CONFIG_PANIC_STRIP_GPR)) - pdata->cm.frame[i] = 0; - else - pdata->cm.frame[i] = sregs[i]; - - for (i = CORTEX_PANIC_FRAME_REGISTER_LR; - i < NUM_CORTEX_PANIC_FRAME_REGISTERS; i++) - pdata->cm.frame[i] = sregs[i]; - - pdata->flags |= PANIC_DATA_FLAG_FRAME_VALID; - } - - /* Save extra information */ - pdata->cm.cfsr = CPU_NVIC_CFSR; - pdata->cm.bfar = CPU_NVIC_BFAR; - pdata->cm.mfar = CPU_NVIC_MFAR; - pdata->cm.shcsr = CPU_NVIC_SHCSR; - pdata->cm.hfsr = CPU_NVIC_HFSR; - pdata->cm.dfsr = CPU_NVIC_DFSR; - -#ifdef CONFIG_UART_PAD_SWITCH - uart_reset_default_pad_panic(); -#endif - panic_data_print(pdata); -#ifdef CONFIG_DEBUG_EXCEPTIONS - panic_show_process_stack(pdata); - /* - * TODO(crosbug.com/p/23760): Dump main stack contents as well if the - * exception happened in a handler's context. - */ -#endif - - /* Make sure that all changes are saved into RAM */ - if (IS_ENABLED(CONFIG_ARMV7M_CACHE)) - cpu_clean_invalidate_dcache(); - - panic_reboot(); -} - -/** - * Default exception handler, which reports a panic. - * - * Declare this as a naked call so we can extract raw LR and IPSR values. - */ -void exception_panic(void) -{ - /* Save registers and branch directly to panic handler */ - asm volatile( - "mov r0, %[pregs]\n" - "mrs r1, psp\n" - "mrs r2, ipsr\n" - "mov r3, sp\n" -#ifdef CONFIG_PANIC_STRIP_GPR - /* - * Check if we are in exception. This is similar to - * in_interrupt_context(). Exception bits are 9 LSB, so - * we can perform left shift for 23 bits and check if result - * is 0 (lsls instruction is setting appropriate flags). - */ - "lsls r6, r2, #23\n" - /* - * If this is software panic (shift result == 0) then register - * r4 and r5 contain additional info about panic. - * Clear r6-r11 always and r4, r5 only if this is exception - * panic. To clear r4 and r5, 'movne' conditional instruction - * is used. It works only when flags contain information that - * result was != 0. Itt is pseudo instruction which is used - * to make sure we are using correct conditional instructions. - */ - "itt ne\n" - "movne r4, #0\n" - "movne r5, #0\n" - "mov r6, #0\n" - "mov r7, #0\n" - "mov r8, #0\n" - "mov r9, #0\n" - "mov r10, #0\n" - "mov r11, #0\n" -#endif - "stmia r0, {r1-r11, lr}\n" - "mov sp, %[pstack]\n" - "bl report_panic\n" : : - [pregs] "r" (pdata_ptr->cm.regs), - [pstack] "r" (pstack_addr) : - /* Constraints protecting these from being clobbered. - * Gcc should be using r0 & r12 for pregs and pstack. */ - "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", - "r10", "r11", "cc", "memory" - ); -} - -#ifdef CONFIG_SOFTWARE_PANIC -void software_panic(uint32_t reason, uint32_t info) -{ - __asm__("mov " STRINGIFY(SOFTWARE_PANIC_INFO_REG) ", %0\n" - "mov " STRINGIFY(SOFTWARE_PANIC_REASON_REG) ", %1\n" - "bl exception_panic\n" - : : "r"(info), "r"(reason)); - __builtin_unreachable(); -} - -void panic_set_reason(uint32_t reason, uint32_t info, uint8_t exception) -{ - struct panic_data * const pdata = get_panic_data_write(); - uint32_t *lregs; - - lregs = pdata->cm.regs; - - /* Setup panic data structure */ - memset(pdata, 0, CONFIG_PANIC_DATA_SIZE); - pdata->magic = PANIC_DATA_MAGIC; - pdata->struct_size = CONFIG_PANIC_DATA_SIZE; - pdata->struct_version = 2; - pdata->arch = PANIC_ARCH_CORTEX_M; - - /* Log panic cause */ - lregs[1] = exception; - lregs[3] = reason; - lregs[4] = info; -} - -void panic_get_reason(uint32_t *reason, uint32_t *info, uint8_t *exception) -{ - struct panic_data * const pdata = panic_get_data(); - uint32_t *lregs; - - if (pdata && pdata->struct_version == 2) { - lregs = pdata->cm.regs; - *exception = lregs[1]; - *reason = lregs[3]; - *info = lregs[4]; - } else { - *exception = *reason = *info = 0; - } -} -#endif - -void bus_fault_handler(void) -{ - if (!bus_fault_ignored) - exception_panic(); -} - -void ignore_bus_fault(int ignored) -{ - if (IS_ENABLED(CHIP_FAMILY_STM32H7)) { - if (ignored == 0) - asm volatile("dsb; isb"); - } - - /* - * Flash code might call this before cpu_init(), - * ensure that the bus faults really go through our handler. - */ - CPU_NVIC_SHCSR |= CPU_NVIC_SHCSR_BUSFAULTENA; - bus_fault_ignored = ignored; -} |