.fill (1024), 4, 0x0
irqstack_end:
+fault_dump:
+ .fill 18, 4, 0x0 @ On fault, we save the registers + CPSR + handler address
+
.popsection
+fault:
+ cpsid aif @ Disable interrupts
+
+ ldr r13, =fault_dump
+ stmia r13, {r0-r12} @ Dump the non-banked registers directly (well, unless from FIQ mode)
+ str r14, [r13, #15 << 2] @ Our r14 is the faulting r15
+ mov r0, r13
+
+ @ Save the caller's CPSR (our SPSR) too.
+ mrs r1, SPSR
+ str r1, [r13, #16 << 2]
+
+ @ Switch to the mode we came from to get r13 and r14.
+ @ If coming from user mode, use System mode instead so we're still
+ @ privileged.
+ and r1, r1, #0x1f @ r1 = SPSR mode
+ cmp r1, #0x10 @ If from User mode
+ moveq r1, #0x1f @ Then use System mode instead
+
+ mrs r3, CPSR @ r3 = our CPSR
+ bic r2, r3, #0x1f
+ orr r2, r2, r1
+ msr CPSR, r2 @ Change to mode r1
+
+ @ Save old mode's r13, r14
+ str r13, [r0, #13 << 2]
+ str r14, [r0, #14 << 2]
+
+ msr CPSR, r3 @ Back to fault mode
+
+ ldr r1, [r0, #17 << 2]
+ sub r1, r1, #12 @ Fix to point at start of handler
+ str r1, [r0, #17 << 2]
+
+ @ Call C code to format the register dump.
+ @ Clobbers the stack, but we're not going to return anyway.
+ ldr sp, =_boot_stack_end
+ bl dump_registers
+ b do_exit
+
+@ We want to store a unique value to identify this handler, without corrupting
+@ any of the registers. So, we store r15 (which will point just after the branch).
+@ Later, we subtract 12 so the user gets pointed at the start of the exception
+@ handler.
+#define FAULT(name) \
+.globl fault_##name; \
+fault_##name: \
+ ldr r13, =fault_dump; \
+ str r15, [r13, #17 << 2]; \
+ b fault
+
+FAULT(reset)
+FAULT(undefined_instruction)
+FAULT(svc)
+FAULT(prefetch_call)
+FAULT(prefetch_abort)
+FAULT(data_abort)
+
@ exception base address
.align 5
.globl exception_vector_table
@ instruction to clear an existing tag is required on context switches."
@ -- ARM Cortex-A Series Programmer’s Guide (Version: 4.0)
exception_vector_table:
- b . @ reset
- b . @ undefined instruction
- b . @ supervisor call
- b . @ prefetch call
- b . @ prefetch abort
- b . @ data abort
- b irq_handler @ irq
+ b fault_reset
+ b fault_undefined_instruction
+ b fault_svc
+ b fault_prefetch_call
+ b fault_prefetch_abort
+ b fault_data_abort
+ b irq_handler @ IRQ
.word 0xe7f000f0 @ abort on FIQ
@ Call fault_undefined_instruction in "Undefined mode"
--- /dev/null
+/******************************************************************************
+ * panic.c
+ *
+ * Displays a register dump and stack trace for debugging.
+ *
+ * Copyright (c) 2014, Thomas Leonard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <mini-os/os.h>
+#include <mini-os/console.h>
+#include <arch_mm.h>
+
+extern int irqstack[];
+extern int irqstack_end[];
+
+typedef void handler(void);
+
+extern handler fault_reset;
+extern handler fault_undefined_instruction;
+extern handler fault_svc;
+extern handler fault_prefetch_call;
+extern handler fault_prefetch_abort;
+extern handler fault_data_abort;
+
+void dump_registers(int *saved_registers) {
+ static int in_dump = 0;
+ int *sp, *stack_top, *x;
+ char *fault_name;
+ void *fault_handler;
+ int i;
+
+ if (in_dump)
+ {
+ printk("Crash while in dump_registers! Not generating a second report.\n");
+ return;
+ }
+
+ in_dump = 1;
+
+ fault_handler = (handler *) saved_registers[17];
+ if (fault_handler == fault_reset)
+ fault_name = "reset";
+ else if (fault_handler == fault_undefined_instruction)
+ fault_name = "undefined_instruction";
+ else if (fault_handler == fault_svc)
+ fault_name = "svc";
+ else if (fault_handler == fault_prefetch_call)
+ fault_name = "prefetch_call";
+ else if (fault_handler == fault_prefetch_abort)
+ fault_name = "prefetch_abort";
+ else if (fault_handler == fault_data_abort)
+ fault_name = "data_abort";
+ else
+ fault_name = "unknown fault type!";
+
+ printk("Fault handler at %p called (%s)\n", fault_handler, fault_name);
+
+ for (i = 0; i < 16; i++) {
+ printk("r%d = %x\n", i, saved_registers[i]);
+ }
+ printk("CPSR = %x\n", saved_registers[16]);
+
+ printk("Stack dump (innermost last)\n");
+ sp = (int *) saved_registers[13];
+
+ if (sp >= _boot_stack && sp <= _boot_stack_end)
+ stack_top = _boot_stack_end; /* The boot stack */
+ else if (sp >= irqstack && sp <= irqstack_end)
+ stack_top = irqstack_end; /* The IRQ stack */
+ else
+ stack_top = (int *) ((((unsigned long) sp) | (__STACK_SIZE-1)) + 1); /* A normal thread stack */
+
+ for (x = stack_top - 1; x >= sp; x--)
+ {
+ printk(" [%8p] %8x\n", x, *x);
+ }
+ printk("End of stack\n");
+
+ in_dump = 0;
+}