| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/xtensa/kernel/traps.c | 
|  | 3 | * | 
|  | 4 | * Exception handling. | 
|  | 5 | * | 
|  | 6 | * Derived from code with the following copyrights: | 
|  | 7 | * Copyright (C) 1994 - 1999 by Ralf Baechle | 
|  | 8 | * Modified for R3000 by Paul M. Antoine, 1995, 1996 | 
|  | 9 | * Complete output from die() by Ulf Carlsson, 1998 | 
|  | 10 | * Copyright (C) 1999 Silicon Graphics, Inc. | 
|  | 11 | * | 
|  | 12 | * Essentially rewritten for the Xtensa architecture port. | 
|  | 13 | * | 
|  | 14 | * Copyright (C) 2001 - 2005 Tensilica Inc. | 
|  | 15 | * | 
|  | 16 | * Joe Taylor	<joe@tensilica.com, joetylr@yahoo.com> | 
|  | 17 | * Chris Zankel	<chris@zankel.net> | 
|  | 18 | * Marc Gauthier<marc@tensilica.com, marc@alumni.uwaterloo.ca> | 
|  | 19 | * Kevin Chea | 
|  | 20 | * | 
|  | 21 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 22 | * License.  See the file "COPYING" in the main directory of this archive | 
|  | 23 | * for more details. | 
|  | 24 | */ | 
|  | 25 |  | 
|  | 26 | #include <linux/kernel.h> | 
|  | 27 | #include <linux/sched.h> | 
|  | 28 | #include <linux/init.h> | 
|  | 29 | #include <linux/module.h> | 
|  | 30 | #include <linux/stringify.h> | 
|  | 31 | #include <linux/kallsyms.h> | 
| Nishanth Aravamudan | 5c888d5 | 2005-07-12 13:58:26 -0700 | [diff] [blame] | 32 | #include <linux/delay.h> | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 33 |  | 
|  | 34 | #include <asm/ptrace.h> | 
|  | 35 | #include <asm/timex.h> | 
|  | 36 | #include <asm/uaccess.h> | 
|  | 37 | #include <asm/pgtable.h> | 
|  | 38 | #include <asm/processor.h> | 
|  | 39 |  | 
|  | 40 | #ifdef CONFIG_KGDB | 
|  | 41 | extern int gdb_enter; | 
|  | 42 | extern int return_from_debug_flag; | 
|  | 43 | #endif | 
|  | 44 |  | 
|  | 45 | /* | 
|  | 46 | * Machine specific interrupt handlers | 
|  | 47 | */ | 
|  | 48 |  | 
|  | 49 | extern void kernel_exception(void); | 
|  | 50 | extern void user_exception(void); | 
|  | 51 |  | 
|  | 52 | extern void fast_syscall_kernel(void); | 
|  | 53 | extern void fast_syscall_user(void); | 
|  | 54 | extern void fast_alloca(void); | 
|  | 55 | extern void fast_unaligned(void); | 
|  | 56 | extern void fast_second_level_miss(void); | 
|  | 57 | extern void fast_store_prohibited(void); | 
|  | 58 | extern void fast_coprocessor(void); | 
|  | 59 |  | 
|  | 60 | extern void do_illegal_instruction (struct pt_regs*); | 
|  | 61 | extern void do_interrupt (struct pt_regs*); | 
|  | 62 | extern void do_unaligned_user (struct pt_regs*); | 
|  | 63 | extern void do_multihit (struct pt_regs*, unsigned long); | 
|  | 64 | extern void do_page_fault (struct pt_regs*, unsigned long); | 
|  | 65 | extern void do_debug (struct pt_regs*); | 
|  | 66 | extern void system_call (struct pt_regs*); | 
|  | 67 |  | 
|  | 68 | /* | 
|  | 69 | * The vector table must be preceded by a save area (which | 
|  | 70 | * implies it must be in RAM, unless one places RAM immediately | 
|  | 71 | * before a ROM and puts the vector at the start of the ROM (!)) | 
|  | 72 | */ | 
|  | 73 |  | 
|  | 74 | #define KRNL		0x01 | 
|  | 75 | #define USER		0x02 | 
|  | 76 |  | 
|  | 77 | #define COPROCESSOR(x)							\ | 
| Chris Zankel | 173d668 | 2006-12-10 02:18:48 -0800 | [diff] [blame] | 78 | { EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor } | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 79 |  | 
|  | 80 | typedef struct { | 
|  | 81 | int cause; | 
|  | 82 | int fast; | 
|  | 83 | void* handler; | 
|  | 84 | } dispatch_init_table_t; | 
|  | 85 |  | 
| Chris Zankel | b91dc33 | 2007-08-03 15:54:36 -0700 | [diff] [blame] | 86 | static dispatch_init_table_t __initdata dispatch_init_table[] = { | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 87 |  | 
| Chris Zankel | 173d668 | 2006-12-10 02:18:48 -0800 | [diff] [blame] | 88 | { EXCCAUSE_ILLEGAL_INSTRUCTION,	0,	   do_illegal_instruction}, | 
|  | 89 | { EXCCAUSE_SYSTEM_CALL,		KRNL,	   fast_syscall_kernel }, | 
|  | 90 | { EXCCAUSE_SYSTEM_CALL,		USER,	   fast_syscall_user }, | 
|  | 91 | { EXCCAUSE_SYSTEM_CALL,		0,	   system_call }, | 
|  | 92 | /* EXCCAUSE_INSTRUCTION_FETCH unhandled */ | 
|  | 93 | /* EXCCAUSE_LOAD_STORE_ERROR unhandled*/ | 
|  | 94 | { EXCCAUSE_LEVEL1_INTERRUPT,	0,	   do_interrupt }, | 
|  | 95 | { EXCCAUSE_ALLOCA,		USER|KRNL, fast_alloca }, | 
|  | 96 | /* EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */ | 
|  | 97 | /* EXCCAUSE_PRIVILEGED unhandled */ | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 98 | #if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION | 
|  | 99 | #ifdef CONFIG_UNALIGNED_USER | 
| Chris Zankel | 173d668 | 2006-12-10 02:18:48 -0800 | [diff] [blame] | 100 | { EXCCAUSE_UNALIGNED,		USER,	   fast_unaligned }, | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 101 | #else | 
| Chris Zankel | 173d668 | 2006-12-10 02:18:48 -0800 | [diff] [blame] | 102 | { EXCCAUSE_UNALIGNED,		0,	   do_unaligned_user }, | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 103 | #endif | 
| Chris Zankel | 173d668 | 2006-12-10 02:18:48 -0800 | [diff] [blame] | 104 | { EXCCAUSE_UNALIGNED,		KRNL,	   fast_unaligned }, | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 105 | #endif | 
| Chris Zankel | 173d668 | 2006-12-10 02:18:48 -0800 | [diff] [blame] | 106 | { EXCCAUSE_ITLB_MISS,		0,	   do_page_fault }, | 
|  | 107 | { EXCCAUSE_ITLB_MISS,		USER|KRNL, fast_second_level_miss}, | 
|  | 108 | { EXCCAUSE_ITLB_MULTIHIT,		0,	   do_multihit }, | 
|  | 109 | { EXCCAUSE_ITLB_PRIVILEGE,	0,	   do_page_fault }, | 
|  | 110 | /* EXCCAUSE_SIZE_RESTRICTION unhandled */ | 
|  | 111 | { EXCCAUSE_FETCH_CACHE_ATTRIBUTE,	0,	   do_page_fault }, | 
|  | 112 | { EXCCAUSE_DTLB_MISS,		USER|KRNL, fast_second_level_miss}, | 
|  | 113 | { EXCCAUSE_DTLB_MISS,		0,	   do_page_fault }, | 
|  | 114 | { EXCCAUSE_DTLB_MULTIHIT,		0,	   do_multihit }, | 
|  | 115 | { EXCCAUSE_DTLB_PRIVILEGE,	0,	   do_page_fault }, | 
|  | 116 | /* EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */ | 
|  | 117 | { EXCCAUSE_STORE_CACHE_ATTRIBUTE,	USER|KRNL, fast_store_prohibited }, | 
|  | 118 | { EXCCAUSE_STORE_CACHE_ATTRIBUTE,	0,	   do_page_fault }, | 
|  | 119 | { EXCCAUSE_LOAD_CACHE_ATTRIBUTE,	0,	   do_page_fault }, | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 120 | /* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */ | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 121 | #if XTENSA_HAVE_COPROCESSOR(0) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 122 | COPROCESSOR(0), | 
|  | 123 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 124 | #if XTENSA_HAVE_COPROCESSOR(1) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 125 | COPROCESSOR(1), | 
|  | 126 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 127 | #if XTENSA_HAVE_COPROCESSOR(2) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 128 | COPROCESSOR(2), | 
|  | 129 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 130 | #if XTENSA_HAVE_COPROCESSOR(3) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 131 | COPROCESSOR(3), | 
|  | 132 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 133 | #if XTENSA_HAVE_COPROCESSOR(4) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 134 | COPROCESSOR(4), | 
|  | 135 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 136 | #if XTENSA_HAVE_COPROCESSOR(5) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 137 | COPROCESSOR(5), | 
|  | 138 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 139 | #if XTENSA_HAVE_COPROCESSOR(6) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 140 | COPROCESSOR(6), | 
|  | 141 | #endif | 
| Chris Zankel | c658eac | 2008-02-12 13:17:07 -0800 | [diff] [blame] | 142 | #if XTENSA_HAVE_COPROCESSOR(7) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 143 | COPROCESSOR(7), | 
|  | 144 | #endif | 
|  | 145 | { EXCCAUSE_MAPPED_DEBUG,		0,		do_debug }, | 
|  | 146 | { -1, -1, 0 } | 
|  | 147 |  | 
|  | 148 | }; | 
|  | 149 |  | 
|  | 150 | /* The exception table <exc_table> serves two functions: | 
|  | 151 | * 1. it contains three dispatch tables (fast_user, fast_kernel, default-c) | 
|  | 152 | * 2. it is a temporary memory buffer for the exception handlers. | 
|  | 153 | */ | 
|  | 154 |  | 
|  | 155 | unsigned long exc_table[EXC_TABLE_SIZE/4]; | 
|  | 156 |  | 
|  | 157 | void die(const char*, struct pt_regs*, long); | 
|  | 158 |  | 
|  | 159 | static inline void | 
|  | 160 | __die_if_kernel(const char *str, struct pt_regs *regs, long err) | 
|  | 161 | { | 
|  | 162 | if (!user_mode(regs)) | 
|  | 163 | die(str, regs, err); | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | /* | 
|  | 167 | * Unhandled Exceptions. Kill user task or panic if in kernel space. | 
|  | 168 | */ | 
|  | 169 |  | 
|  | 170 | void do_unhandled(struct pt_regs *regs, unsigned long exccause) | 
|  | 171 | { | 
|  | 172 | __die_if_kernel("Caught unhandled exception - should not happen", | 
|  | 173 | regs, SIGKILL); | 
|  | 174 |  | 
|  | 175 | /* If in user mode, send SIGILL signal to current process */ | 
|  | 176 | printk("Caught unhandled exception in '%s' " | 
|  | 177 | "(pid = %d, pc = %#010lx) - should not happen\n" | 
|  | 178 | "\tEXCCAUSE is %ld\n", | 
| Alexey Dobriyan | 19c5870 | 2007-10-18 23:40:41 -0700 | [diff] [blame] | 179 | current->comm, task_pid_nr(current), regs->pc, exccause); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 180 | force_sig(SIGILL, current); | 
|  | 181 | } | 
|  | 182 |  | 
|  | 183 | /* | 
|  | 184 | * Multi-hit exception. This if fatal! | 
|  | 185 | */ | 
|  | 186 |  | 
|  | 187 | void do_multihit(struct pt_regs *regs, unsigned long exccause) | 
|  | 188 | { | 
|  | 189 | die("Caught multihit exception", regs, SIGKILL); | 
|  | 190 | } | 
|  | 191 |  | 
|  | 192 | /* | 
|  | 193 | * Level-1 interrupt. | 
|  | 194 | * We currently have no priority encoding. | 
|  | 195 | */ | 
|  | 196 |  | 
|  | 197 | unsigned long ignored_level1_interrupts; | 
|  | 198 | extern void do_IRQ(int, struct pt_regs *); | 
|  | 199 |  | 
|  | 200 | void do_interrupt (struct pt_regs *regs) | 
|  | 201 | { | 
|  | 202 | unsigned long intread = get_sr (INTREAD); | 
|  | 203 | unsigned long intenable = get_sr (INTENABLE); | 
|  | 204 | int i, mask; | 
|  | 205 |  | 
|  | 206 | /* Handle all interrupts (no priorities). | 
|  | 207 | * (Clear the interrupt before processing, in case it's | 
|  | 208 | *  edge-triggered or software-generated) | 
|  | 209 | */ | 
|  | 210 |  | 
|  | 211 | for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) { | 
|  | 212 | if (mask & (intread & intenable)) { | 
|  | 213 | set_sr (mask, INTCLEAR); | 
|  | 214 | do_IRQ (i,regs); | 
|  | 215 | } | 
|  | 216 | } | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | /* | 
|  | 220 | * Illegal instruction. Fatal if in kernel space. | 
|  | 221 | */ | 
|  | 222 |  | 
|  | 223 | void | 
|  | 224 | do_illegal_instruction(struct pt_regs *regs) | 
|  | 225 | { | 
|  | 226 | __die_if_kernel("Illegal instruction in kernel", regs, SIGKILL); | 
|  | 227 |  | 
|  | 228 | /* If in user mode, send SIGILL signal to current process. */ | 
|  | 229 |  | 
|  | 230 | printk("Illegal Instruction in '%s' (pid = %d, pc = %#010lx)\n", | 
| Alexey Dobriyan | 19c5870 | 2007-10-18 23:40:41 -0700 | [diff] [blame] | 231 | current->comm, task_pid_nr(current), regs->pc); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 232 | force_sig(SIGILL, current); | 
|  | 233 | } | 
|  | 234 |  | 
|  | 235 |  | 
|  | 236 | /* | 
|  | 237 | * Handle unaligned memory accesses from user space. Kill task. | 
|  | 238 | * | 
|  | 239 | * If CONFIG_UNALIGNED_USER is not set, we don't allow unaligned memory | 
|  | 240 | * accesses causes from user space. | 
|  | 241 | */ | 
|  | 242 |  | 
|  | 243 | #if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION | 
|  | 244 | #ifndef CONFIG_UNALIGNED_USER | 
|  | 245 | void | 
|  | 246 | do_unaligned_user (struct pt_regs *regs) | 
|  | 247 | { | 
|  | 248 | siginfo_t info; | 
|  | 249 |  | 
|  | 250 | __die_if_kernel("Unhandled unaligned exception in kernel", | 
|  | 251 | regs, SIGKILL); | 
|  | 252 |  | 
|  | 253 | current->thread.bad_vaddr = regs->excvaddr; | 
|  | 254 | current->thread.error_code = -3; | 
|  | 255 | printk("Unaligned memory access to %08lx in '%s' " | 
|  | 256 | "(pid = %d, pc = %#010lx)\n", | 
| Alexey Dobriyan | 19c5870 | 2007-10-18 23:40:41 -0700 | [diff] [blame] | 257 | regs->excvaddr, current->comm, task_pid_nr(current), regs->pc); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 258 | info.si_signo = SIGBUS; | 
|  | 259 | info.si_errno = 0; | 
|  | 260 | info.si_code = BUS_ADRALN; | 
|  | 261 | info.si_addr = (void *) regs->excvaddr; | 
|  | 262 | force_sig_info(SIGSEGV, &info, current); | 
|  | 263 |  | 
|  | 264 | } | 
|  | 265 | #endif | 
|  | 266 | #endif | 
|  | 267 |  | 
|  | 268 | void | 
|  | 269 | do_debug(struct pt_regs *regs) | 
|  | 270 | { | 
|  | 271 | #ifdef CONFIG_KGDB | 
|  | 272 | /* If remote debugging is configured AND enabled, we give control to | 
|  | 273 | * kgdb.  Otherwise, we fall through, perhaps giving control to the | 
|  | 274 | * native debugger. | 
|  | 275 | */ | 
|  | 276 |  | 
|  | 277 | if (gdb_enter) { | 
|  | 278 | extern void gdb_handle_exception(struct pt_regs *); | 
|  | 279 | gdb_handle_exception(regs); | 
|  | 280 | return_from_debug_flag = 1; | 
|  | 281 | return; | 
|  | 282 | } | 
|  | 283 | #endif | 
|  | 284 |  | 
|  | 285 | __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); | 
|  | 286 |  | 
|  | 287 | /* If in user mode, send SIGTRAP signal to current process */ | 
|  | 288 |  | 
|  | 289 | force_sig(SIGTRAP, current); | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 |  | 
|  | 293 | /* | 
|  | 294 | * Initialize dispatch tables. | 
|  | 295 | * | 
|  | 296 | * The exception vectors are stored compressed the __init section in the | 
|  | 297 | * dispatch_init_table. This function initializes the following three tables | 
|  | 298 | * from that compressed table: | 
|  | 299 | * - fast user		first dispatch table for user exceptions | 
|  | 300 | * - fast kernel	first dispatch table for kernel exceptions | 
|  | 301 | * - default C-handler	C-handler called by the default fast handler. | 
|  | 302 | * | 
|  | 303 | * See vectors.S for more details. | 
|  | 304 | */ | 
|  | 305 |  | 
|  | 306 | #define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler)) | 
|  | 307 |  | 
| Chris Zankel | b91dc33 | 2007-08-03 15:54:36 -0700 | [diff] [blame] | 308 | void __init trap_init(void) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 309 | { | 
|  | 310 | int i; | 
|  | 311 |  | 
|  | 312 | /* Setup default vectors. */ | 
|  | 313 |  | 
|  | 314 | for(i = 0; i < 64; i++) { | 
|  | 315 | set_handler(EXC_TABLE_FAST_USER/4   + i, user_exception); | 
|  | 316 | set_handler(EXC_TABLE_FAST_KERNEL/4 + i, kernel_exception); | 
|  | 317 | set_handler(EXC_TABLE_DEFAULT/4 + i, do_unhandled); | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 | /* Setup specific handlers. */ | 
|  | 321 |  | 
|  | 322 | for(i = 0; dispatch_init_table[i].cause >= 0; i++) { | 
|  | 323 |  | 
|  | 324 | int fast = dispatch_init_table[i].fast; | 
|  | 325 | int cause = dispatch_init_table[i].cause; | 
|  | 326 | void *handler = dispatch_init_table[i].handler; | 
|  | 327 |  | 
|  | 328 | if (fast == 0) | 
|  | 329 | set_handler (EXC_TABLE_DEFAULT/4 + cause, handler); | 
|  | 330 | if (fast && fast & USER) | 
|  | 331 | set_handler (EXC_TABLE_FAST_USER/4 + cause, handler); | 
|  | 332 | if (fast && fast & KRNL) | 
|  | 333 | set_handler (EXC_TABLE_FAST_KERNEL/4 + cause, handler); | 
|  | 334 | } | 
|  | 335 |  | 
|  | 336 | /* Initialize EXCSAVE_1 to hold the address of the exception table. */ | 
|  | 337 |  | 
|  | 338 | i = (unsigned long)exc_table; | 
|  | 339 | __asm__ __volatile__("wsr  %0, "__stringify(EXCSAVE_1)"\n" : : "a" (i)); | 
|  | 340 | } | 
|  | 341 |  | 
|  | 342 | /* | 
|  | 343 | * This function dumps the current valid window frame and other base registers. | 
|  | 344 | */ | 
|  | 345 |  | 
|  | 346 | void show_regs(struct pt_regs * regs) | 
|  | 347 | { | 
|  | 348 | int i, wmask; | 
|  | 349 |  | 
|  | 350 | wmask = regs->wmask & ~1; | 
|  | 351 |  | 
| Chris Zankel | 8d7e824 | 2008-02-12 11:55:32 -0800 | [diff] [blame] | 352 | for (i = 0; i < 16; i++) { | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 353 | if ((i % 8) == 0) | 
|  | 354 | printk ("\n" KERN_INFO "a%02d: ", i); | 
|  | 355 | printk("%08lx ", regs->areg[i]); | 
|  | 356 | } | 
|  | 357 | printk("\n"); | 
|  | 358 |  | 
|  | 359 | printk("pc: %08lx, ps: %08lx, depc: %08lx, excvaddr: %08lx\n", | 
|  | 360 | regs->pc, regs->ps, regs->depc, regs->excvaddr); | 
|  | 361 | printk("lbeg: %08lx, lend: %08lx lcount: %08lx, sar: %08lx\n", | 
|  | 362 | regs->lbeg, regs->lend, regs->lcount, regs->sar); | 
|  | 363 | if (user_mode(regs)) | 
|  | 364 | printk("wb: %08lx, ws: %08lx, wmask: %08lx, syscall: %ld\n", | 
|  | 365 | regs->windowbase, regs->windowstart, regs->wmask, | 
|  | 366 | regs->syscall); | 
|  | 367 | } | 
|  | 368 |  | 
|  | 369 | void show_trace(struct task_struct *task, unsigned long *sp) | 
|  | 370 | { | 
|  | 371 | unsigned long a0, a1, pc; | 
|  | 372 | unsigned long sp_start, sp_end; | 
|  | 373 |  | 
|  | 374 | a1 = (unsigned long)sp; | 
|  | 375 |  | 
|  | 376 | if (a1 == 0) | 
|  | 377 | __asm__ __volatile__ ("mov %0, a1\n" : "=a"(a1)); | 
|  | 378 |  | 
|  | 379 |  | 
|  | 380 | sp_start = a1 & ~(THREAD_SIZE-1); | 
|  | 381 | sp_end = sp_start + THREAD_SIZE; | 
|  | 382 |  | 
|  | 383 | printk("Call Trace:"); | 
|  | 384 | #ifdef CONFIG_KALLSYMS | 
|  | 385 | printk("\n"); | 
|  | 386 | #endif | 
|  | 387 | spill_registers(); | 
|  | 388 |  | 
|  | 389 | while (a1 > sp_start && a1 < sp_end) { | 
|  | 390 | sp = (unsigned long*)a1; | 
|  | 391 |  | 
|  | 392 | a0 = *(sp - 4); | 
|  | 393 | a1 = *(sp - 3); | 
|  | 394 |  | 
|  | 395 | if (a1 <= (unsigned long) sp) | 
|  | 396 | break; | 
|  | 397 |  | 
|  | 398 | pc = MAKE_PC_FROM_RA(a0, a1); | 
|  | 399 |  | 
|  | 400 | if (kernel_text_address(pc)) { | 
|  | 401 | printk(" [<%08lx>] ", pc); | 
|  | 402 | print_symbol("%s\n", pc); | 
|  | 403 | } | 
|  | 404 | } | 
|  | 405 | printk("\n"); | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | /* | 
|  | 409 | * This routine abuses get_user()/put_user() to reference pointers | 
|  | 410 | * with at least a bit of error checking ... | 
|  | 411 | */ | 
|  | 412 |  | 
|  | 413 | static int kstack_depth_to_print = 24; | 
|  | 414 |  | 
|  | 415 | void show_stack(struct task_struct *task, unsigned long *sp) | 
|  | 416 | { | 
|  | 417 | int i = 0; | 
|  | 418 | unsigned long *stack; | 
|  | 419 |  | 
|  | 420 | if (sp == 0) | 
|  | 421 | __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp)); | 
|  | 422 |  | 
|  | 423 | stack = sp; | 
|  | 424 |  | 
|  | 425 | printk("\nStack: "); | 
|  | 426 |  | 
|  | 427 | for (i = 0; i < kstack_depth_to_print; i++) { | 
|  | 428 | if (kstack_end(sp)) | 
|  | 429 | break; | 
|  | 430 | if (i && ((i % 8) == 0)) | 
|  | 431 | printk("\n       "); | 
|  | 432 | printk("%08lx ", *sp++); | 
|  | 433 | } | 
|  | 434 | printk("\n"); | 
|  | 435 | show_trace(task, stack); | 
|  | 436 | } | 
|  | 437 |  | 
|  | 438 | void dump_stack(void) | 
|  | 439 | { | 
|  | 440 | show_stack(current, NULL); | 
|  | 441 | } | 
|  | 442 |  | 
|  | 443 | EXPORT_SYMBOL(dump_stack); | 
|  | 444 |  | 
|  | 445 |  | 
|  | 446 | void show_code(unsigned int *pc) | 
|  | 447 | { | 
|  | 448 | long i; | 
|  | 449 |  | 
|  | 450 | printk("\nCode:"); | 
|  | 451 |  | 
|  | 452 | for(i = -3 ; i < 6 ; i++) { | 
|  | 453 | unsigned long insn; | 
|  | 454 | if (__get_user(insn, pc + i)) { | 
|  | 455 | printk(" (Bad address in pc)\n"); | 
|  | 456 | break; | 
|  | 457 | } | 
|  | 458 | printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>')); | 
|  | 459 | } | 
|  | 460 | } | 
|  | 461 |  | 
| Ingo Molnar | 34af946 | 2006-06-27 02:53:55 -0700 | [diff] [blame] | 462 | DEFINE_SPINLOCK(die_lock); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 463 |  | 
|  | 464 | void die(const char * str, struct pt_regs * regs, long err) | 
|  | 465 | { | 
|  | 466 | static int die_counter; | 
|  | 467 | int nl = 0; | 
|  | 468 |  | 
|  | 469 | console_verbose(); | 
|  | 470 | spin_lock_irq(&die_lock); | 
|  | 471 |  | 
|  | 472 | printk("%s: sig: %ld [#%d]\n", str, err, ++die_counter); | 
|  | 473 | #ifdef CONFIG_PREEMPT | 
|  | 474 | printk("PREEMPT "); | 
|  | 475 | nl = 1; | 
|  | 476 | #endif | 
|  | 477 | if (nl) | 
|  | 478 | printk("\n"); | 
|  | 479 | show_regs(regs); | 
|  | 480 | if (!user_mode(regs)) | 
|  | 481 | show_stack(NULL, (unsigned long*)regs->areg[1]); | 
|  | 482 |  | 
| Pavel Emelianov | bcdcd8e | 2007-07-17 04:03:42 -0700 | [diff] [blame] | 483 | add_taint(TAINT_DIE); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 484 | spin_unlock_irq(&die_lock); | 
|  | 485 |  | 
|  | 486 | if (in_interrupt()) | 
|  | 487 | panic("Fatal exception in interrupt"); | 
|  | 488 |  | 
| Horms | cea6a4b | 2006-07-30 03:03:34 -0700 | [diff] [blame] | 489 | if (panic_on_oops) | 
| Horms | 012c437 | 2006-08-13 23:24:22 -0700 | [diff] [blame] | 490 | panic("Fatal exception"); | 
| Horms | cea6a4b | 2006-07-30 03:03:34 -0700 | [diff] [blame] | 491 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 492 | do_exit(err); | 
|  | 493 | } | 
|  | 494 |  | 
|  | 495 |  |