| 1 | |
| 2 | * Hardware exception handling
|
| 3 | *
|
| 4 | * Copyright (C) 2010 Tobias Klauser <[email protected]>
|
| 5 | * Copyright (C) 2004 Microtronix Datacom Ltd.
|
| 6 | * Copyright (C) 2001 Vic Phillips
|
| 7 | *
|
| 8 | * This file is subject to the terms and conditions of the GNU General
|
| 9 | * Public License. See the file COPYING in the main directory of this
|
| 10 | * archive for more details.
|
| 11 |
|
| 12 |
|
| 13 | #include <linux/sched.h>
|
| 14 | #include <linux/sched/debug.h>
|
| 15 | #include <linux/kernel.h>
|
| 16 | #include <linux/signal.h>
|
| 17 | #include <linux/export.h>
|
| 18 | #include <linux/mm.h>
|
| 19 | #include <linux/ptrace.h>
|
| 20 |
|
| 21 | #include <asm/traps.h>
|
| 22 | #include <asm/sections.h>
|
| 23 | #include <linux/uaccess.h>
|
| 24 |
|
| 25 | static DEFINE_SPINLOCK(die_lock);
|
| 26 |
|
| 27 | static void _send_sig(int signo, int code, unsigned long addr)
|
| 28 | {
|
| 29 | force_sig_fault(signo, code, (void __user *) addr);
|
| 30 | }
|
| 31 |
|
| 32 | void die(const char *str, struct pt_regs *regs, long err)
|
| 33 | {
|
| 34 | console_verbose();
|
| 35 | spin_lock_irq(&die_lock);
|
| 36 | pr_warn("Oops: %s, sig: %ld\n", str, err);
|
| 37 | show_regs(regs);
|
| 38 | spin_unlock_irq(&die_lock);
|
| 39 | |
| 40 | * make_task_dead() should take care of panic'ing from an interrupt
|
| 41 | * context so we don't handle it here
|
| 42 |
|
| 43 | make_task_dead(err);
|
| 44 | }
|
| 45 |
|
| 46 | void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr)
|
| 47 | {
|
| 48 | if (!user_mode(regs))
|
| 49 | die("Exception in kernel mode", regs, signo);
|
| 50 |
|
| 51 | _send_sig(signo, code, addr);
|
| 52 | }
|
| 53 |
|
| 54 | |
| 55 | * The show_stack() is external API which we do not use ourselves.
|
| 56 |
|
| 57 |
|
| 58 | int kstack_depth_to_print = 48;
|
| 59 |
|
| 60 | void show_stack(struct task_struct *task, unsigned long *stack,
|
| 61 | const char *loglvl)
|
| 62 | {
|
| 63 | unsigned long *endstack, addr;
|
| 64 | int i;
|
| 65 |
|
| 66 | if (!stack) {
|
| 67 | if (task)
|
| 68 | stack = (unsigned long *)task->thread.ksp;
|
| 69 | else
|
| 70 | stack = (unsigned long *)&stack;
|
| 71 | }
|
| 72 |
|
| 73 | addr = (unsigned long) stack;
|
| 74 | endstack = (unsigned long *) PAGE_ALIGN(addr);
|
| 75 |
|
| 76 | printk("%sStack from %08lx:", loglvl, (unsigned long)stack);
|
| 77 | for (i = 0; i < kstack_depth_to_print; i++) {
|
| 78 | if (stack + 1 > endstack)
|
| 79 | break;
|
| 80 | if (i % 8 == 0)
|
| 81 | printk("%s\n ", loglvl);
|
| 82 | printk("%s %08lx", loglvl, *stack++);
|
| 83 | }
|
| 84 |
|
| 85 | printk("%s\nCall Trace:", loglvl);
|
| 86 | i = 0;
|
| 87 | while (stack + 1 <= endstack) {
|
| 88 | addr = *stack++;
|
| 89 | |
| 90 | * If the address is either in the text segment of the
|
| 91 | * kernel, or in the region which contains vmalloc'ed
|
| 92 | * memory, it *may* be the address of a calling
|
| 93 | * routine; if so, print it so that someone tracing
|
| 94 | * down the cause of the crash will be able to figure
|
| 95 | * out the call path that was taken.
|
| 96 |
|
| 97 | if (((addr >= (unsigned long) _stext) &&
|
| 98 | (addr <= (unsigned long) _etext))) {
|
| 99 | if (i % 4 == 0)
|
| 100 | pr_emerg("\n ");
|
| 101 | printk("%s [<%08lx>]", loglvl, addr);
|
| 102 | i++;
|
| 103 | }
|
| 104 | }
|
| 105 | printk("%s\n", loglvl);
|
| 106 | }
|
| 107 |
|
| 108 |
|
| 109 | asmlinkage void breakpoint_c(struct pt_regs *fp)
|
| 110 | {
|
| 111 | |
| 112 | * The breakpoint entry code has moved the PC on by 4 bytes, so we must
|
| 113 | * move it back. This could be done on the host but we do it here
|
| 114 | * because monitor.S of JTAG gdbserver does it too.
|
| 115 |
|
| 116 | fp->ea -= 4;
|
| 117 | _exception(SIGTRAP, fp, TRAP_BRKPT, fp->ea);
|
| 118 | }
|
| 119 |
|
| 120 | #ifndef CONFIG_NIOS2_ALIGNMENT_TRAP
|
| 121 |
|
| 122 | asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause)
|
| 123 | {
|
| 124 | unsigned long addr = RDCTL(CTL_BADADDR);
|
| 125 |
|
| 126 | cause >>= 2;
|
| 127 | fp->ea -= 4;
|
| 128 |
|
| 129 | if (fixup_exception(fp))
|
| 130 | return;
|
| 131 |
|
| 132 | if (!user_mode(fp)) {
|
| 133 | pr_alert("Unaligned access from kernel mode, this might be a hardware\n");
|
| 134 | pr_alert("problem, dump registers and restart the instruction\n");
|
| 135 | pr_alert(" BADADDR 0x%08lx\n", addr);
|
| 136 | pr_alert(" cause %d\n", cause);
|
| 137 | pr_alert(" op-code 0x%08lx\n", *(unsigned long *)(fp->ea));
|
| 138 | show_regs(fp);
|
| 139 | return;
|
| 140 | }
|
| 141 |
|
| 142 | _exception(SIGBUS, fp, BUS_ADRALN, addr);
|
| 143 | }
|
| 144 | #endif
|
| 145 |
|
| 146 |
|
| 147 | asmlinkage void handle_illegal_c(struct pt_regs *fp)
|
| 148 | {
|
| 149 | fp->ea -= 4;
|
| 150 | _exception(SIGILL, fp, ILL_ILLOPC, fp->ea);
|
| 151 | }
|
| 152 |
|
| 153 |
|
| 154 | asmlinkage void handle_supervisor_instr(struct pt_regs *fp)
|
| 155 | {
|
| 156 | fp->ea -= 4;
|
| 157 | _exception(SIGILL, fp, ILL_PRVOPC, fp->ea);
|
| 158 | }
|
| 159 |
|
| 160 |
|
| 161 | asmlinkage void handle_diverror_c(struct pt_regs *fp)
|
| 162 | {
|
| 163 | fp->ea -= 4;
|
| 164 | _exception(SIGFPE, fp, FPE_INTDIV, fp->ea);
|
| 165 | }
|
| 166 |
|
| 167 |
|
| 168 | asmlinkage void unhandled_exception(struct pt_regs *regs, int cause)
|
| 169 | {
|
| 170 | unsigned long addr = RDCTL(CTL_BADADDR);
|
| 171 |
|
| 172 | cause /= 4;
|
| 173 |
|
| 174 | pr_emerg("Unhandled exception #%d in %s mode (badaddr=0x%08lx)\n",
|
| 175 | cause, user_mode(regs) ? "user" : "kernel", addr);
|
| 176 |
|
| 177 | regs->ea -= 4;
|
| 178 | show_regs(regs);
|
| 179 |
|
| 180 | pr_emerg("opcode: 0x%08lx\n", *(unsigned long *)(regs->ea));
|
| 181 | }
|
| 182 |
|
| 183 | asmlinkage void handle_trap_1_c(struct pt_regs *fp)
|
| 184 | {
|
| 185 | _send_sig(SIGUSR1, 0, fp->ea);
|
| 186 | }
|
| 187 |
|
| 188 | asmlinkage void handle_trap_2_c(struct pt_regs *fp)
|
| 189 | {
|
| 190 | _send_sig(SIGUSR2, 0, fp->ea);
|
| 191 | }
|
| 192 |
|
| 193 | asmlinkage void handle_trap_3_c(struct pt_regs *fp)
|
| 194 | {
|
| 195 | _send_sig(SIGILL, ILL_ILLTRP, fp->ea);
|
| 196 | }
|