Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 | 2 | * S390 version |
a53c8fab | 3 | * Copyright IBM Corp. 1999, 2000 |
1da177e4 LT |
4 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
5 | * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), | |
6 | * | |
7 | * Derived from "arch/i386/kernel/traps.c" | |
8 | * Copyright (C) 1991, 1992 Linus Torvalds | |
9 | */ | |
10 | ||
11 | /* | |
12 | * 'Traps.c' handles hardware traps and faults after we have saved some | |
13 | * state in 'asm.s'. | |
14 | */ | |
1da177e4 LT |
15 | #include <linux/sched.h> |
16 | #include <linux/kernel.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/errno.h> | |
73b7d40f | 19 | #include <linux/ptrace.h> |
1da177e4 LT |
20 | #include <linux/timer.h> |
21 | #include <linux/mm.h> | |
22 | #include <linux/smp.h> | |
1da177e4 LT |
23 | #include <linux/init.h> |
24 | #include <linux/interrupt.h> | |
df5f8314 | 25 | #include <linux/seq_file.h> |
1da177e4 LT |
26 | #include <linux/delay.h> |
27 | #include <linux/module.h> | |
1eeb66a1 | 28 | #include <linux/kdebug.h> |
1da177e4 | 29 | #include <linux/kallsyms.h> |
5d3f229f | 30 | #include <linux/reboot.h> |
4ba069b8 | 31 | #include <linux/kprobes.h> |
c0007f1a | 32 | #include <linux/bug.h> |
5c699714 | 33 | #include <linux/utsname.h> |
1da177e4 LT |
34 | #include <asm/uaccess.h> |
35 | #include <asm/io.h> | |
60063497 | 36 | #include <linux/atomic.h> |
1da177e4 LT |
37 | #include <asm/mathemu.h> |
38 | #include <asm/cpcmd.h> | |
1da177e4 LT |
39 | #include <asm/lowcore.h> |
40 | #include <asm/debug.h> | |
3ab121ab | 41 | #include <asm/ipl.h> |
a806170e | 42 | #include "entry.h" |
1da177e4 | 43 | |
02834eec | 44 | int show_unhandled_signals = 1; |
1da177e4 | 45 | |
1da177e4 LT |
46 | #define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; }) |
47 | ||
347a8dc3 | 48 | #ifndef CONFIG_64BIT |
d7fd5f1e | 49 | #define LONG "%08lx " |
1da177e4 LT |
50 | #define FOURLONG "%08lx %08lx %08lx %08lx\n" |
51 | static int kstack_depth_to_print = 12; | |
347a8dc3 | 52 | #else /* CONFIG_64BIT */ |
d7fd5f1e | 53 | #define LONG "%016lx " |
1da177e4 LT |
54 | #define FOURLONG "%016lx %016lx %016lx %016lx\n" |
55 | static int kstack_depth_to_print = 20; | |
347a8dc3 | 56 | #endif /* CONFIG_64BIT */ |
1da177e4 | 57 | |
d35339a4 MS |
58 | static inline void __user *get_trap_ip(struct pt_regs *regs) |
59 | { | |
60 | #ifdef CONFIG_64BIT | |
61 | unsigned long address; | |
62 | ||
63 | if (regs->int_code & 0x200) | |
64 | address = *(unsigned long *)(current->thread.trap_tdb + 24); | |
65 | else | |
66 | address = regs->psw.addr; | |
67 | return (void __user *) | |
68 | ((address - (regs->int_code >> 16)) & PSW_ADDR_INSN); | |
69 | #else | |
70 | return (void __user *) | |
71 | ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); | |
72 | #endif | |
73 | } | |
74 | ||
1da177e4 LT |
75 | /* |
76 | * For show_trace we have tree different stack to consider: | |
77 | * - the panic stack which is used if the kernel stack has overflown | |
78 | * - the asynchronous interrupt stack (cpu related) | |
79 | * - the synchronous kernel stack (process related) | |
80 | * The stack trace can start at any of the three stack and can potentially | |
81 | * touch all of them. The order is: panic stack, async stack, sync stack. | |
82 | */ | |
83 | static unsigned long | |
84 | __show_trace(unsigned long sp, unsigned long low, unsigned long high) | |
85 | { | |
86 | struct stack_frame *sf; | |
87 | struct pt_regs *regs; | |
88 | ||
89 | while (1) { | |
90 | sp = sp & PSW_ADDR_INSN; | |
91 | if (sp < low || sp > high - sizeof(*sf)) | |
92 | return sp; | |
93 | sf = (struct stack_frame *) sp; | |
94 | printk("([<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN); | |
95 | print_symbol("%s)\n", sf->gprs[8] & PSW_ADDR_INSN); | |
96 | /* Follow the backchain. */ | |
97 | while (1) { | |
98 | low = sp; | |
99 | sp = sf->back_chain & PSW_ADDR_INSN; | |
100 | if (!sp) | |
101 | break; | |
102 | if (sp <= low || sp > high - sizeof(*sf)) | |
103 | return sp; | |
104 | sf = (struct stack_frame *) sp; | |
105 | printk(" [<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN); | |
106 | print_symbol("%s\n", sf->gprs[8] & PSW_ADDR_INSN); | |
107 | } | |
108 | /* Zero backchain detected, check for interrupt frame. */ | |
109 | sp = (unsigned long) (sf + 1); | |
110 | if (sp <= low || sp > high - sizeof(*regs)) | |
111 | return sp; | |
112 | regs = (struct pt_regs *) sp; | |
113 | printk(" [<%016lx>] ", regs->psw.addr & PSW_ADDR_INSN); | |
114 | print_symbol("%s\n", regs->psw.addr & PSW_ADDR_INSN); | |
115 | low = sp; | |
116 | sp = regs->gprs[15]; | |
117 | } | |
118 | } | |
119 | ||
4e83be7b | 120 | static void show_trace(struct task_struct *task, unsigned long *stack) |
1da177e4 LT |
121 | { |
122 | register unsigned long __r15 asm ("15"); | |
123 | unsigned long sp; | |
124 | ||
125 | sp = (unsigned long) stack; | |
126 | if (!sp) | |
127 | sp = task ? task->thread.ksp : __r15; | |
128 | printk("Call Trace:\n"); | |
129 | #ifdef CONFIG_CHECK_STACK | |
130 | sp = __show_trace(sp, S390_lowcore.panic_stack - 4096, | |
131 | S390_lowcore.panic_stack); | |
132 | #endif | |
133 | sp = __show_trace(sp, S390_lowcore.async_stack - ASYNC_SIZE, | |
134 | S390_lowcore.async_stack); | |
135 | if (task) | |
30af7120 AV |
136 | __show_trace(sp, (unsigned long) task_stack_page(task), |
137 | (unsigned long) task_stack_page(task) + THREAD_SIZE); | |
1da177e4 LT |
138 | else |
139 | __show_trace(sp, S390_lowcore.thread_info, | |
140 | S390_lowcore.thread_info + THREAD_SIZE); | |
236257ee HC |
141 | if (!task) |
142 | task = current; | |
143 | debug_show_held_locks(task); | |
1da177e4 LT |
144 | } |
145 | ||
146 | void show_stack(struct task_struct *task, unsigned long *sp) | |
147 | { | |
148 | register unsigned long * __r15 asm ("15"); | |
149 | unsigned long *stack; | |
150 | int i; | |
151 | ||
1da177e4 | 152 | if (!sp) |
73805343 HC |
153 | stack = task ? (unsigned long *) task->thread.ksp : __r15; |
154 | else | |
155 | stack = sp; | |
1da177e4 | 156 | |
1da177e4 LT |
157 | for (i = 0; i < kstack_depth_to_print; i++) { |
158 | if (((addr_t) stack & (THREAD_SIZE-1)) == 0) | |
159 | break; | |
ddadfa8d HC |
160 | if ((i * sizeof(long) % 32) == 0) |
161 | printk("%s ", i == 0 ? "" : "\n"); | |
d7fd5f1e | 162 | printk(LONG, *stack++); |
1da177e4 LT |
163 | } |
164 | printk("\n"); | |
165 | show_trace(task, sp); | |
166 | } | |
167 | ||
4e83be7b | 168 | static void show_last_breaking_event(struct pt_regs *regs) |
9e74a6b8 | 169 | { |
4e83be7b | 170 | #ifdef CONFIG_64BIT |
9e74a6b8 CB |
171 | printk("Last Breaking-Event-Address:\n"); |
172 | printk(" [<%016lx>] ", regs->args[0] & PSW_ADDR_INSN); | |
173 | print_symbol("%s\n", regs->args[0] & PSW_ADDR_INSN); | |
9e74a6b8 | 174 | #endif |
4e83be7b | 175 | } |
9e74a6b8 | 176 | |
1da177e4 LT |
177 | /* |
178 | * The architecture-independent dump_stack generator | |
179 | */ | |
180 | void dump_stack(void) | |
181 | { | |
5c699714 HC |
182 | printk("CPU: %d %s %s %.*s\n", |
183 | task_thread_info(current)->cpu, print_tainted(), | |
184 | init_utsname()->release, | |
185 | (int)strcspn(init_utsname()->version, " "), | |
186 | init_utsname()->version); | |
187 | printk("Process %s (pid: %d, task: %p, ksp: %p)\n", | |
188 | current->comm, current->pid, current, | |
189 | (void *) current->thread.ksp); | |
d2c993d8 | 190 | show_stack(NULL, NULL); |
1da177e4 | 191 | } |
1da177e4 LT |
192 | EXPORT_SYMBOL(dump_stack); |
193 | ||
bb11e3bd MS |
194 | static inline int mask_bits(struct pt_regs *regs, unsigned long bits) |
195 | { | |
196 | return (regs->psw.mask & bits) / ((~bits + 1) & bits); | |
197 | } | |
198 | ||
1da177e4 LT |
199 | void show_registers(struct pt_regs *regs) |
200 | { | |
1da177e4 | 201 | char *mode; |
1da177e4 | 202 | |
7d256175 | 203 | mode = user_mode(regs) ? "User" : "Krnl"; |
1da177e4 LT |
204 | printk("%s PSW : %p %p", |
205 | mode, (void *) regs->psw.mask, | |
206 | (void *) regs->psw.addr); | |
207 | print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN); | |
bb11e3bd MS |
208 | printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x " |
209 | "P:%x AS:%x CC:%x PM:%x", mask_bits(regs, PSW_MASK_PER), | |
210 | mask_bits(regs, PSW_MASK_DAT), mask_bits(regs, PSW_MASK_IO), | |
211 | mask_bits(regs, PSW_MASK_EXT), mask_bits(regs, PSW_MASK_KEY), | |
212 | mask_bits(regs, PSW_MASK_MCHECK), mask_bits(regs, PSW_MASK_WAIT), | |
213 | mask_bits(regs, PSW_MASK_PSTATE), mask_bits(regs, PSW_MASK_ASC), | |
214 | mask_bits(regs, PSW_MASK_CC), mask_bits(regs, PSW_MASK_PM)); | |
215 | #ifdef CONFIG_64BIT | |
b50511e4 | 216 | printk(" EA:%x", mask_bits(regs, PSW_MASK_EA | PSW_MASK_BA)); |
bb11e3bd MS |
217 | #endif |
218 | printk("\n%s GPRS: " FOURLONG, mode, | |
1da177e4 LT |
219 | regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]); |
220 | printk(" " FOURLONG, | |
221 | regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]); | |
222 | printk(" " FOURLONG, | |
223 | regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]); | |
224 | printk(" " FOURLONG, | |
225 | regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]); | |
226 | ||
bb11e3bd | 227 | show_code(regs); |
1da177e4 LT |
228 | } |
229 | ||
4e83be7b HC |
230 | void show_regs(struct pt_regs *regs) |
231 | { | |
4e83be7b HC |
232 | printk("CPU: %d %s %s %.*s\n", |
233 | task_thread_info(current)->cpu, print_tainted(), | |
234 | init_utsname()->release, | |
235 | (int)strcspn(init_utsname()->version, " "), | |
236 | init_utsname()->version); | |
237 | printk("Process %s (pid: %d, task: %p, ksp: %p)\n", | |
238 | current->comm, current->pid, current, | |
239 | (void *) current->thread.ksp); | |
240 | show_registers(regs); | |
241 | /* Show stack backtrace if pt_regs is from kernel mode */ | |
7d256175 | 242 | if (!user_mode(regs)) |
4e83be7b HC |
243 | show_trace(NULL, (unsigned long *) regs->gprs[15]); |
244 | show_last_breaking_event(regs); | |
245 | } | |
246 | ||
2b67fc46 | 247 | static DEFINE_SPINLOCK(die_lock); |
1da177e4 | 248 | |
aa33c8cb | 249 | void die(struct pt_regs *regs, const char *str) |
1da177e4 LT |
250 | { |
251 | static int die_counter; | |
252 | ||
bca0fb86 | 253 | oops_enter(); |
3ab121ab | 254 | lgr_info_log(); |
1da177e4 LT |
255 | debug_stop_all(); |
256 | console_verbose(); | |
257 | spin_lock_irq(&die_lock); | |
258 | bust_spinlocks(1); | |
aa33c8cb | 259 | printk("%s: %04x [#%d] ", str, regs->int_code & 0xffff, ++die_counter); |
5c699714 HC |
260 | #ifdef CONFIG_PREEMPT |
261 | printk("PREEMPT "); | |
262 | #endif | |
263 | #ifdef CONFIG_SMP | |
2485579b HC |
264 | printk("SMP "); |
265 | #endif | |
266 | #ifdef CONFIG_DEBUG_PAGEALLOC | |
267 | printk("DEBUG_PAGEALLOC"); | |
5c699714 HC |
268 | #endif |
269 | printk("\n"); | |
aa33c8cb | 270 | notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV); |
02291257 | 271 | print_modules(); |
bca0fb86 | 272 | show_regs(regs); |
1da177e4 | 273 | bust_spinlocks(0); |
373d4d09 | 274 | add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); |
bca0fb86 | 275 | spin_unlock_irq(&die_lock); |
1da177e4 LT |
276 | if (in_interrupt()) |
277 | panic("Fatal exception in interrupt"); | |
278 | if (panic_on_oops) | |
279 | panic("Fatal exception: panic_on_oops"); | |
bca0fb86 HC |
280 | oops_exit(); |
281 | do_exit(SIGSEGV); | |
1da177e4 LT |
282 | } |
283 | ||
aa33c8cb | 284 | static inline void report_user_fault(struct pt_regs *regs, int signr) |
1da177e4 | 285 | { |
ab3c68ee | 286 | if ((task_pid_nr(current) > 1) && !show_unhandled_signals) |
1da177e4 | 287 | return; |
ab3c68ee HC |
288 | if (!unhandled_signal(current, signr)) |
289 | return; | |
290 | if (!printk_ratelimit()) | |
291 | return; | |
aa33c8cb | 292 | printk("User process fault: interruption code 0x%X ", regs->int_code); |
ab3c68ee HC |
293 | print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN); |
294 | printk("\n"); | |
1da177e4 | 295 | show_regs(regs); |
1da177e4 LT |
296 | } |
297 | ||
c0007f1a HC |
298 | int is_valid_bugaddr(unsigned long addr) |
299 | { | |
300 | return 1; | |
301 | } | |
302 | ||
aa33c8cb MS |
303 | static void __kprobes do_trap(struct pt_regs *regs, |
304 | int si_signo, int si_code, char *str) | |
305 | { | |
306 | siginfo_t info; | |
307 | ||
308 | if (notify_die(DIE_TRAP, str, regs, 0, | |
309 | regs->int_code, si_signo) == NOTIFY_STOP) | |
4ba069b8 MG |
310 | return; |
311 | ||
7d256175 | 312 | if (user_mode(regs)) { |
aa33c8cb MS |
313 | info.si_signo = si_signo; |
314 | info.si_errno = 0; | |
315 | info.si_code = si_code; | |
d35339a4 | 316 | info.si_addr = get_trap_ip(regs); |
aa33c8cb MS |
317 | force_sig_info(si_signo, &info, current); |
318 | report_user_fault(regs, si_signo); | |
1da177e4 LT |
319 | } else { |
320 | const struct exception_table_entry *fixup; | |
321 | fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); | |
322 | if (fixup) | |
eb608fb3 | 323 | regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE; |
c0007f1a HC |
324 | else { |
325 | enum bug_trap_type btt; | |
326 | ||
608e2619 | 327 | btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); |
c0007f1a HC |
328 | if (btt == BUG_TRAP_TYPE_WARN) |
329 | return; | |
aa33c8cb | 330 | die(regs, str); |
c0007f1a | 331 | } |
1da177e4 LT |
332 | } |
333 | } | |
334 | ||
5e9a2692 | 335 | void __kprobes do_per_trap(struct pt_regs *regs) |
1da177e4 | 336 | { |
73b7d40f MS |
337 | siginfo_t info; |
338 | ||
5e9a2692 | 339 | if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) |
4ba069b8 | 340 | return; |
73b7d40f MS |
341 | if (!current->ptrace) |
342 | return; | |
343 | info.si_signo = SIGTRAP; | |
344 | info.si_errno = 0; | |
345 | info.si_code = TRAP_HWBKPT; | |
3c52e49d MS |
346 | info.si_addr = |
347 | (void __force __user *) current->thread.per_event.address; | |
73b7d40f | 348 | force_sig_info(SIGTRAP, &info, current); |
1da177e4 LT |
349 | } |
350 | ||
b01a37a7 | 351 | void default_trap_handler(struct pt_regs *regs) |
1da177e4 | 352 | { |
7d256175 | 353 | if (user_mode(regs)) { |
aa33c8cb | 354 | report_user_fault(regs, SIGSEGV); |
6ea50968 | 355 | do_exit(SIGSEGV); |
1da177e4 | 356 | } else |
aa33c8cb | 357 | die(regs, "Unknown program exception"); |
1da177e4 LT |
358 | } |
359 | ||
1e54622e | 360 | #define DO_ERROR_INFO(name, signr, sicode, str) \ |
b01a37a7 HC |
361 | void name(struct pt_regs *regs) \ |
362 | { \ | |
363 | do_trap(regs, signr, sicode, str); \ | |
1da177e4 LT |
364 | } |
365 | ||
1e54622e MS |
366 | DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, |
367 | "addressing exception") | |
368 | DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, | |
369 | "execute exception") | |
370 | DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, | |
371 | "fixpoint divide exception") | |
372 | DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, | |
373 | "fixpoint overflow exception") | |
374 | DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, | |
375 | "HFP overflow exception") | |
376 | DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, | |
377 | "HFP underflow exception") | |
378 | DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, | |
379 | "HFP significance exception") | |
380 | DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, | |
381 | "HFP divide exception") | |
382 | DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, | |
383 | "HFP square root exception") | |
384 | DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, | |
385 | "operand exception") | |
386 | DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, | |
387 | "privileged operation") | |
388 | DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, | |
389 | "special operation exception") | |
390 | DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, | |
391 | "translation exception") | |
392 | ||
d35339a4 MS |
393 | #ifdef CONFIG_64BIT |
394 | DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, | |
395 | "transaction constraint exception") | |
396 | #endif | |
397 | ||
aa33c8cb | 398 | static inline void do_fp_trap(struct pt_regs *regs, int fpc) |
1da177e4 | 399 | { |
aa33c8cb | 400 | int si_code = 0; |
1da177e4 LT |
401 | /* FPC[2] is Data Exception Code */ |
402 | if ((fpc & 0x00000300) == 0) { | |
403 | /* bits 6 and 7 of DXC are 0 iff IEEE exception */ | |
404 | if (fpc & 0x8000) /* invalid fp operation */ | |
aa33c8cb | 405 | si_code = FPE_FLTINV; |
1da177e4 | 406 | else if (fpc & 0x4000) /* div by 0 */ |
aa33c8cb | 407 | si_code = FPE_FLTDIV; |
1da177e4 | 408 | else if (fpc & 0x2000) /* overflow */ |
aa33c8cb | 409 | si_code = FPE_FLTOVF; |
1da177e4 | 410 | else if (fpc & 0x1000) /* underflow */ |
aa33c8cb | 411 | si_code = FPE_FLTUND; |
1da177e4 | 412 | else if (fpc & 0x0800) /* inexact */ |
aa33c8cb | 413 | si_code = FPE_FLTRES; |
1da177e4 | 414 | } |
aa33c8cb | 415 | do_trap(regs, SIGFPE, si_code, "floating point exception"); |
1da177e4 LT |
416 | } |
417 | ||
b01a37a7 | 418 | void __kprobes illegal_op(struct pt_regs *regs) |
1da177e4 LT |
419 | { |
420 | siginfo_t info; | |
421 | __u8 opcode[6]; | |
d2c993d8 | 422 | __u16 __user *location; |
1da177e4 LT |
423 | int signal = 0; |
424 | ||
d35339a4 | 425 | location = get_trap_ip(regs); |
1da177e4 | 426 | |
7d256175 | 427 | if (user_mode(regs)) { |
12bae235 HC |
428 | if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) |
429 | return; | |
1da177e4 | 430 | if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { |
73b7d40f MS |
431 | if (current->ptrace) { |
432 | info.si_signo = SIGTRAP; | |
433 | info.si_errno = 0; | |
434 | info.si_code = TRAP_BRKPT; | |
435 | info.si_addr = location; | |
436 | force_sig_info(SIGTRAP, &info, current); | |
437 | } else | |
1da177e4 LT |
438 | signal = SIGILL; |
439 | #ifdef CONFIG_MATHEMU | |
440 | } else if (opcode[0] == 0xb3) { | |
12bae235 HC |
441 | if (get_user(*((__u16 *) (opcode+2)), location+1)) |
442 | return; | |
1da177e4 LT |
443 | signal = math_emu_b3(opcode, regs); |
444 | } else if (opcode[0] == 0xed) { | |
12bae235 HC |
445 | if (get_user(*((__u32 *) (opcode+2)), |
446 | (__u32 __user *)(location+1))) | |
447 | return; | |
1da177e4 LT |
448 | signal = math_emu_ed(opcode, regs); |
449 | } else if (*((__u16 *) opcode) == 0xb299) { | |
12bae235 HC |
450 | if (get_user(*((__u16 *) (opcode+2)), location+1)) |
451 | return; | |
1da177e4 LT |
452 | signal = math_emu_srnm(opcode, regs); |
453 | } else if (*((__u16 *) opcode) == 0xb29c) { | |
12bae235 HC |
454 | if (get_user(*((__u16 *) (opcode+2)), location+1)) |
455 | return; | |
1da177e4 LT |
456 | signal = math_emu_stfpc(opcode, regs); |
457 | } else if (*((__u16 *) opcode) == 0xb29d) { | |
12bae235 HC |
458 | if (get_user(*((__u16 *) (opcode+2)), location+1)) |
459 | return; | |
1da177e4 LT |
460 | signal = math_emu_lfpc(opcode, regs); |
461 | #endif | |
462 | } else | |
463 | signal = SIGILL; | |
35df8d53 HC |
464 | } else { |
465 | /* | |
466 | * If we get an illegal op in kernel mode, send it through the | |
467 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL | |
468 | */ | |
aa33c8cb | 469 | if (notify_die(DIE_BPT, "bpt", regs, 0, |
35df8d53 HC |
470 | 3, SIGTRAP) != NOTIFY_STOP) |
471 | signal = SIGILL; | |
472 | } | |
1da177e4 LT |
473 | |
474 | #ifdef CONFIG_MATHEMU | |
475 | if (signal == SIGFPE) | |
aa33c8cb MS |
476 | do_fp_trap(regs, current->thread.fp_regs.fpc); |
477 | else if (signal == SIGSEGV) | |
478 | do_trap(regs, signal, SEGV_MAPERR, "user address fault"); | |
479 | else | |
1da177e4 | 480 | #endif |
aa33c8cb MS |
481 | if (signal) |
482 | do_trap(regs, signal, ILL_ILLOPC, "illegal operation"); | |
1da177e4 LT |
483 | } |
484 | ||
485 | ||
486 | #ifdef CONFIG_MATHEMU | |
aa33c8cb | 487 | void specification_exception(struct pt_regs *regs) |
1da177e4 LT |
488 | { |
489 | __u8 opcode[6]; | |
5a42b81f | 490 | __u16 __user *location = NULL; |
1da177e4 LT |
491 | int signal = 0; |
492 | ||
d35339a4 | 493 | location = (__u16 __user *) get_trap_ip(regs); |
1da177e4 | 494 | |
7d256175 | 495 | if (user_mode(regs)) { |
1da177e4 LT |
496 | get_user(*((__u16 *) opcode), location); |
497 | switch (opcode[0]) { | |
498 | case 0x28: /* LDR Rx,Ry */ | |
499 | signal = math_emu_ldr(opcode); | |
500 | break; | |
501 | case 0x38: /* LER Rx,Ry */ | |
502 | signal = math_emu_ler(opcode); | |
503 | break; | |
504 | case 0x60: /* STD R,D(X,B) */ | |
505 | get_user(*((__u16 *) (opcode+2)), location+1); | |
506 | signal = math_emu_std(opcode, regs); | |
507 | break; | |
508 | case 0x68: /* LD R,D(X,B) */ | |
509 | get_user(*((__u16 *) (opcode+2)), location+1); | |
510 | signal = math_emu_ld(opcode, regs); | |
511 | break; | |
512 | case 0x70: /* STE R,D(X,B) */ | |
513 | get_user(*((__u16 *) (opcode+2)), location+1); | |
514 | signal = math_emu_ste(opcode, regs); | |
515 | break; | |
516 | case 0x78: /* LE R,D(X,B) */ | |
517 | get_user(*((__u16 *) (opcode+2)), location+1); | |
518 | signal = math_emu_le(opcode, regs); | |
519 | break; | |
520 | default: | |
521 | signal = SIGILL; | |
522 | break; | |
523 | } | |
524 | } else | |
525 | signal = SIGILL; | |
526 | ||
527 | if (signal == SIGFPE) | |
aa33c8cb MS |
528 | do_fp_trap(regs, current->thread.fp_regs.fpc); |
529 | else if (signal) | |
530 | do_trap(regs, signal, ILL_ILLOPN, "specification exception"); | |
1da177e4 LT |
531 | } |
532 | #else | |
1e54622e MS |
533 | DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, |
534 | "specification exception"); | |
1da177e4 LT |
535 | #endif |
536 | ||
b01a37a7 | 537 | void data_exception(struct pt_regs *regs) |
1da177e4 | 538 | { |
d2c993d8 | 539 | __u16 __user *location; |
1da177e4 LT |
540 | int signal = 0; |
541 | ||
d35339a4 | 542 | location = get_trap_ip(regs); |
1da177e4 LT |
543 | |
544 | if (MACHINE_HAS_IEEE) | |
94c12cc7 | 545 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); |
1da177e4 LT |
546 | |
547 | #ifdef CONFIG_MATHEMU | |
7d256175 | 548 | else if (user_mode(regs)) { |
1da177e4 LT |
549 | __u8 opcode[6]; |
550 | get_user(*((__u16 *) opcode), location); | |
551 | switch (opcode[0]) { | |
552 | case 0x28: /* LDR Rx,Ry */ | |
553 | signal = math_emu_ldr(opcode); | |
554 | break; | |
555 | case 0x38: /* LER Rx,Ry */ | |
556 | signal = math_emu_ler(opcode); | |
557 | break; | |
558 | case 0x60: /* STD R,D(X,B) */ | |
559 | get_user(*((__u16 *) (opcode+2)), location+1); | |
560 | signal = math_emu_std(opcode, regs); | |
561 | break; | |
562 | case 0x68: /* LD R,D(X,B) */ | |
563 | get_user(*((__u16 *) (opcode+2)), location+1); | |
564 | signal = math_emu_ld(opcode, regs); | |
565 | break; | |
566 | case 0x70: /* STE R,D(X,B) */ | |
567 | get_user(*((__u16 *) (opcode+2)), location+1); | |
568 | signal = math_emu_ste(opcode, regs); | |
569 | break; | |
570 | case 0x78: /* LE R,D(X,B) */ | |
571 | get_user(*((__u16 *) (opcode+2)), location+1); | |
572 | signal = math_emu_le(opcode, regs); | |
573 | break; | |
574 | case 0xb3: | |
575 | get_user(*((__u16 *) (opcode+2)), location+1); | |
576 | signal = math_emu_b3(opcode, regs); | |
577 | break; | |
578 | case 0xed: | |
579 | get_user(*((__u32 *) (opcode+2)), | |
5a42b81f | 580 | (__u32 __user *)(location+1)); |
1da177e4 LT |
581 | signal = math_emu_ed(opcode, regs); |
582 | break; | |
583 | case 0xb2: | |
584 | if (opcode[1] == 0x99) { | |
585 | get_user(*((__u16 *) (opcode+2)), location+1); | |
586 | signal = math_emu_srnm(opcode, regs); | |
587 | } else if (opcode[1] == 0x9c) { | |
588 | get_user(*((__u16 *) (opcode+2)), location+1); | |
589 | signal = math_emu_stfpc(opcode, regs); | |
590 | } else if (opcode[1] == 0x9d) { | |
591 | get_user(*((__u16 *) (opcode+2)), location+1); | |
592 | signal = math_emu_lfpc(opcode, regs); | |
593 | } else | |
594 | signal = SIGILL; | |
595 | break; | |
596 | default: | |
597 | signal = SIGILL; | |
598 | break; | |
599 | } | |
600 | } | |
601 | #endif | |
602 | if (current->thread.fp_regs.fpc & FPC_DXC_MASK) | |
603 | signal = SIGFPE; | |
604 | else | |
605 | signal = SIGILL; | |
606 | if (signal == SIGFPE) | |
aa33c8cb MS |
607 | do_fp_trap(regs, current->thread.fp_regs.fpc); |
608 | else if (signal) | |
609 | do_trap(regs, signal, ILL_ILLOPN, "data exception"); | |
1da177e4 LT |
610 | } |
611 | ||
b01a37a7 | 612 | void space_switch_exception(struct pt_regs *regs) |
1da177e4 | 613 | { |
1da177e4 | 614 | /* Set user psw back to home space mode. */ |
7d256175 | 615 | if (user_mode(regs)) |
1da177e4 LT |
616 | regs->psw.mask |= PSW_ASC_HOME; |
617 | /* Send SIGILL. */ | |
aa33c8cb | 618 | do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); |
1da177e4 LT |
619 | } |
620 | ||
fdb204d1 | 621 | void __kprobes kernel_stack_overflow(struct pt_regs * regs) |
1da177e4 | 622 | { |
77eb65cb HC |
623 | bust_spinlocks(1); |
624 | printk("Kernel stack overflow.\n"); | |
625 | show_regs(regs); | |
626 | bust_spinlocks(0); | |
1da177e4 LT |
627 | panic("Corrupt kernel stack, can't continue."); |
628 | } | |
629 | ||
1da177e4 LT |
630 | void __init trap_init(void) |
631 | { | |
f3e1a273 | 632 | local_mcck_enable(); |
1da177e4 | 633 | } |