blob: f081cf1157c358dcc6c2f43f838d5037cf52def3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * S390 version
Heiko Carstensa53c8fa2012-07-20 11:15:04 +02003 * Copyright IBM Corp. 1999, 2000
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * 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 */
Michael Grundy4ba069b2006-09-20 15:58:39 +020015#include <linux/kprobes.h>
Heiko Carstens1bca09f2013-03-14 13:44:25 +010016#include <linux/kdebug.h>
17#include <linux/module.h>
18#include <linux/ptrace.h>
19#include <linux/sched.h>
20#include <linux/mm.h>
Martin Schwidefsky80703612014-10-06 17:53:53 +020021#include <linux/slab.h>
22#include <asm/switch_to.h>
Heiko Carstensa8061702008-04-17 07:46:26 +020023#include "entry.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Heiko Carstens02834ee2011-12-27 11:27:31 +010025int show_unhandled_signals = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Martin Schwidefskyd35339a2012-07-31 11:03:04 +020027static inline void __user *get_trap_ip(struct pt_regs *regs)
28{
29#ifdef CONFIG_64BIT
30 unsigned long address;
31
32 if (regs->int_code & 0x200)
33 address = *(unsigned long *)(current->thread.trap_tdb + 24);
34 else
35 address = regs->psw.addr;
36 return (void __user *)
37 ((address - (regs->int_code >> 16)) & PSW_ADDR_INSN);
38#else
39 return (void __user *)
40 ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
41#endif
42}
43
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010044static inline void report_user_fault(struct pt_regs *regs, int signr)
Linus Torvalds1da177e2005-04-16 15:20:36 -070045{
Heiko Carstensab3c68e2010-05-17 10:00:21 +020046 if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 return;
Heiko Carstensab3c68e2010-05-17 10:00:21 +020048 if (!unhandled_signal(current, signr))
49 return;
50 if (!printk_ratelimit())
51 return;
Heiko Carstens413d4042014-11-19 13:31:08 +010052 printk("User process fault: interruption code %04x ilc:%d ",
53 regs->int_code & 0xffff, regs->int_code >> 17);
Heiko Carstensab3c68e2010-05-17 10:00:21 +020054 print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN);
55 printk("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 show_regs(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057}
58
Heiko Carstensc0007f12007-04-27 16:01:42 +020059int is_valid_bugaddr(unsigned long addr)
60{
61 return 1;
62}
63
Jan Willeke2a0a5b22014-09-22 16:39:06 +020064void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010065{
66 siginfo_t info;
67
Heiko Carstens7d256172012-07-27 10:31:12 +020068 if (user_mode(regs)) {
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010069 info.si_signo = si_signo;
70 info.si_errno = 0;
71 info.si_code = si_code;
Martin Schwidefskyd35339a2012-07-31 11:03:04 +020072 info.si_addr = get_trap_ip(regs);
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010073 force_sig_info(si_signo, &info, current);
74 report_user_fault(regs, si_signo);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 } else {
76 const struct exception_table_entry *fixup;
77 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
78 if (fixup)
Heiko Carstenseb608fb2012-09-05 13:26:11 +020079 regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE;
Heiko Carstensc0007f12007-04-27 16:01:42 +020080 else {
81 enum bug_trap_type btt;
82
Heiko Carstens608e2612007-07-15 23:41:39 -070083 btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
Heiko Carstensc0007f12007-04-27 16:01:42 +020084 if (btt == BUG_TRAP_TYPE_WARN)
85 return;
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010086 die(regs, str);
Heiko Carstensc0007f12007-04-27 16:01:42 +020087 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 }
89}
90
Heiko Carstens7a5388d2014-10-22 12:42:38 +020091static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
Jan Willeke2a0a5b22014-09-22 16:39:06 +020092{
93 if (notify_die(DIE_TRAP, str, regs, 0,
94 regs->int_code, si_signo) == NOTIFY_STOP)
95 return;
96 do_report_trap(regs, si_signo, si_code, str);
97}
Heiko Carstens7a5388d2014-10-22 12:42:38 +020098NOKPROBE_SYMBOL(do_trap);
Jan Willeke2a0a5b22014-09-22 16:39:06 +020099
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200100void do_per_trap(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101{
Martin Schwidefsky73b7d402011-07-24 10:48:33 +0200102 siginfo_t info;
103
Martin Schwidefsky5e9a2692011-01-05 12:48:10 +0100104 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
Michael Grundy4ba069b2006-09-20 15:58:39 +0200105 return;
Martin Schwidefsky73b7d402011-07-24 10:48:33 +0200106 if (!current->ptrace)
107 return;
108 info.si_signo = SIGTRAP;
109 info.si_errno = 0;
110 info.si_code = TRAP_HWBKPT;
Martin Schwidefsky3c52e492011-10-30 15:17:15 +0100111 info.si_addr =
112 (void __force __user *) current->thread.per_event.address;
Martin Schwidefsky73b7d402011-07-24 10:48:33 +0200113 force_sig_info(SIGTRAP, &info, current);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114}
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200115NOKPROBE_SYMBOL(do_per_trap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200117void default_trap_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118{
Heiko Carstens7d256172012-07-27 10:31:12 +0200119 if (user_mode(regs)) {
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100120 report_user_fault(regs, SIGSEGV);
Heiko Carstens6ea50962010-05-17 10:00:13 +0200121 do_exit(SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 } else
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100123 die(regs, "Unknown program exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124}
125
Martin Schwidefsky1e546222010-10-25 16:10:37 +0200126#define DO_ERROR_INFO(name, signr, sicode, str) \
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200127void name(struct pt_regs *regs) \
128{ \
129 do_trap(regs, signr, sicode, str); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130}
131
Martin Schwidefsky1e546222010-10-25 16:10:37 +0200132DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
133 "addressing exception")
134DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
135 "execute exception")
136DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
137 "fixpoint divide exception")
138DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
139 "fixpoint overflow exception")
140DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
141 "HFP overflow exception")
142DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
143 "HFP underflow exception")
144DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
145 "HFP significance exception")
146DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
147 "HFP divide exception")
148DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
149 "HFP square root exception")
150DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
151 "operand exception")
152DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
153 "privileged operation")
154DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
155 "special operation exception")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200157#ifdef CONFIG_64BIT
158DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
159 "transaction constraint exception")
160#endif
161
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100162static inline void do_fp_trap(struct pt_regs *regs, int fpc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100164 int si_code = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 /* FPC[2] is Data Exception Code */
166 if ((fpc & 0x00000300) == 0) {
167 /* bits 6 and 7 of DXC are 0 iff IEEE exception */
168 if (fpc & 0x8000) /* invalid fp operation */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100169 si_code = FPE_FLTINV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 else if (fpc & 0x4000) /* div by 0 */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100171 si_code = FPE_FLTDIV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 else if (fpc & 0x2000) /* overflow */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100173 si_code = FPE_FLTOVF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 else if (fpc & 0x1000) /* underflow */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100175 si_code = FPE_FLTUND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 else if (fpc & 0x0800) /* inexact */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100177 si_code = FPE_FLTRES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 }
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100179 do_trap(regs, SIGFPE, si_code, "floating point exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Heiko Carstense56da342014-11-19 14:05:52 +0100182void translation_exception(struct pt_regs *regs)
183{
184 /* May never happen. */
185 die(regs, "Translation exception");
186}
187
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200188void illegal_op(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
190 siginfo_t info;
191 __u8 opcode[6];
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200192 __u16 __user *location;
Jan Willeke2a0a5b22014-09-22 16:39:06 +0200193 int is_uprobe_insn = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 int signal = 0;
195
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200196 location = get_trap_ip(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Heiko Carstens7d256172012-07-27 10:31:12 +0200198 if (user_mode(regs)) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200199 if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
200 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
Martin Schwidefsky73b7d402011-07-24 10:48:33 +0200202 if (current->ptrace) {
203 info.si_signo = SIGTRAP;
204 info.si_errno = 0;
205 info.si_code = TRAP_BRKPT;
206 info.si_addr = location;
207 force_sig_info(SIGTRAP, &info, current);
208 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 signal = SIGILL;
Jan Willeke2a0a5b22014-09-22 16:39:06 +0200210#ifdef CONFIG_UPROBES
211 } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) {
212 is_uprobe_insn = 1;
213#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214#ifdef CONFIG_MATHEMU
215 } else if (opcode[0] == 0xb3) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200216 if (get_user(*((__u16 *) (opcode+2)), location+1))
217 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 signal = math_emu_b3(opcode, regs);
219 } else if (opcode[0] == 0xed) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200220 if (get_user(*((__u32 *) (opcode+2)),
221 (__u32 __user *)(location+1)))
222 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 signal = math_emu_ed(opcode, regs);
224 } else if (*((__u16 *) opcode) == 0xb299) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200225 if (get_user(*((__u16 *) (opcode+2)), location+1))
226 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 signal = math_emu_srnm(opcode, regs);
228 } else if (*((__u16 *) opcode) == 0xb29c) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200229 if (get_user(*((__u16 *) (opcode+2)), location+1))
230 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 signal = math_emu_stfpc(opcode, regs);
232 } else if (*((__u16 *) opcode) == 0xb29d) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200233 if (get_user(*((__u16 *) (opcode+2)), location+1))
234 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 signal = math_emu_lfpc(opcode, regs);
236#endif
237 } else
238 signal = SIGILL;
Jan Willeke2a0a5b22014-09-22 16:39:06 +0200239 }
240 /*
241 * We got either an illegal op in kernel mode, or user space trapped
242 * on a uprobes illegal instruction. See if kprobes or uprobes picks
243 * it up. If not, SIGILL.
244 */
245 if (is_uprobe_insn || !user_mode(regs)) {
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100246 if (notify_die(DIE_BPT, "bpt", regs, 0,
Heiko Carstens35df8d52007-02-05 21:17:29 +0100247 3, SIGTRAP) != NOTIFY_STOP)
248 signal = SIGILL;
249 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
251#ifdef CONFIG_MATHEMU
252 if (signal == SIGFPE)
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100253 do_fp_trap(regs, current->thread.fp_regs.fpc);
254 else if (signal == SIGSEGV)
255 do_trap(regs, signal, SEGV_MAPERR, "user address fault");
256 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257#endif
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100258 if (signal)
259 do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260}
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200261NOKPROBE_SYMBOL(illegal_op);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263#ifdef CONFIG_MATHEMU
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100264void specification_exception(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
266 __u8 opcode[6];
Al Viro5a42b812006-10-09 20:28:03 +0100267 __u16 __user *location = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 int signal = 0;
269
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200270 location = (__u16 __user *) get_trap_ip(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
Heiko Carstens7d256172012-07-27 10:31:12 +0200272 if (user_mode(regs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 get_user(*((__u16 *) opcode), location);
274 switch (opcode[0]) {
275 case 0x28: /* LDR Rx,Ry */
276 signal = math_emu_ldr(opcode);
277 break;
278 case 0x38: /* LER Rx,Ry */
279 signal = math_emu_ler(opcode);
280 break;
281 case 0x60: /* STD R,D(X,B) */
282 get_user(*((__u16 *) (opcode+2)), location+1);
283 signal = math_emu_std(opcode, regs);
284 break;
285 case 0x68: /* LD R,D(X,B) */
286 get_user(*((__u16 *) (opcode+2)), location+1);
287 signal = math_emu_ld(opcode, regs);
288 break;
289 case 0x70: /* STE R,D(X,B) */
290 get_user(*((__u16 *) (opcode+2)), location+1);
291 signal = math_emu_ste(opcode, regs);
292 break;
293 case 0x78: /* LE R,D(X,B) */
294 get_user(*((__u16 *) (opcode+2)), location+1);
295 signal = math_emu_le(opcode, regs);
296 break;
297 default:
298 signal = SIGILL;
299 break;
300 }
301 } else
302 signal = SIGILL;
303
304 if (signal == SIGFPE)
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100305 do_fp_trap(regs, current->thread.fp_regs.fpc);
306 else if (signal)
307 do_trap(regs, signal, ILL_ILLOPN, "specification exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308}
309#else
Martin Schwidefsky1e546222010-10-25 16:10:37 +0200310DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
311 "specification exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312#endif
313
Martin Schwidefsky80703612014-10-06 17:53:53 +0200314#ifdef CONFIG_64BIT
315int alloc_vector_registers(struct task_struct *tsk)
316{
317 __vector128 *vxrs;
318 int i;
319
320 /* Allocate vector register save area. */
321 vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS,
322 GFP_KERNEL|__GFP_REPEAT);
323 if (!vxrs)
324 return -ENOMEM;
325 preempt_disable();
326 if (tsk == current)
327 save_fp_regs(tsk->thread.fp_regs.fprs);
328 /* Copy the 16 floating point registers */
329 for (i = 0; i < 16; i++)
330 *(freg_t *) &vxrs[i] = tsk->thread.fp_regs.fprs[i];
331 tsk->thread.vxrs = vxrs;
332 if (tsk == current) {
333 __ctl_set_bit(0, 17);
334 restore_vx_regs(vxrs);
335 }
336 preempt_enable();
337 return 0;
338}
339
340void vector_exception(struct pt_regs *regs)
341{
342 int si_code, vic;
343
344 if (!MACHINE_HAS_VX) {
345 do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation");
346 return;
347 }
348
349 /* get vector interrupt code from fpc */
350 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
351 vic = (current->thread.fp_regs.fpc & 0xf00) >> 8;
352 switch (vic) {
353 case 1: /* invalid vector operation */
354 si_code = FPE_FLTINV;
355 break;
356 case 2: /* division by zero */
357 si_code = FPE_FLTDIV;
358 break;
359 case 3: /* overflow */
360 si_code = FPE_FLTOVF;
361 break;
362 case 4: /* underflow */
363 si_code = FPE_FLTUND;
364 break;
365 case 5: /* inexact */
366 si_code = FPE_FLTRES;
367 break;
368 default: /* unknown cause */
369 si_code = 0;
370 }
371 do_trap(regs, SIGFPE, si_code, "vector exception");
372}
373
374static int __init disable_vector_extension(char *str)
375{
376 S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
377 return 1;
378}
379__setup("novx", disable_vector_extension);
380#endif
381
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200382void data_exception(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200384 __u16 __user *location;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 int signal = 0;
386
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200387 location = get_trap_ip(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389 if (MACHINE_HAS_IEEE)
Martin Schwidefsky94c12cc2006-09-28 16:56:43 +0200390 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392#ifdef CONFIG_MATHEMU
Heiko Carstens7d256172012-07-27 10:31:12 +0200393 else if (user_mode(regs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 __u8 opcode[6];
395 get_user(*((__u16 *) opcode), location);
396 switch (opcode[0]) {
397 case 0x28: /* LDR Rx,Ry */
398 signal = math_emu_ldr(opcode);
399 break;
400 case 0x38: /* LER Rx,Ry */
401 signal = math_emu_ler(opcode);
402 break;
403 case 0x60: /* STD R,D(X,B) */
404 get_user(*((__u16 *) (opcode+2)), location+1);
405 signal = math_emu_std(opcode, regs);
406 break;
407 case 0x68: /* LD R,D(X,B) */
408 get_user(*((__u16 *) (opcode+2)), location+1);
409 signal = math_emu_ld(opcode, regs);
410 break;
411 case 0x70: /* STE R,D(X,B) */
412 get_user(*((__u16 *) (opcode+2)), location+1);
413 signal = math_emu_ste(opcode, regs);
414 break;
415 case 0x78: /* LE R,D(X,B) */
416 get_user(*((__u16 *) (opcode+2)), location+1);
417 signal = math_emu_le(opcode, regs);
418 break;
419 case 0xb3:
420 get_user(*((__u16 *) (opcode+2)), location+1);
421 signal = math_emu_b3(opcode, regs);
422 break;
423 case 0xed:
424 get_user(*((__u32 *) (opcode+2)),
Al Viro5a42b812006-10-09 20:28:03 +0100425 (__u32 __user *)(location+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 signal = math_emu_ed(opcode, regs);
427 break;
428 case 0xb2:
429 if (opcode[1] == 0x99) {
430 get_user(*((__u16 *) (opcode+2)), location+1);
431 signal = math_emu_srnm(opcode, regs);
432 } else if (opcode[1] == 0x9c) {
433 get_user(*((__u16 *) (opcode+2)), location+1);
434 signal = math_emu_stfpc(opcode, regs);
435 } else if (opcode[1] == 0x9d) {
436 get_user(*((__u16 *) (opcode+2)), location+1);
437 signal = math_emu_lfpc(opcode, regs);
438 } else
439 signal = SIGILL;
440 break;
441 default:
442 signal = SIGILL;
443 break;
444 }
445 }
446#endif
Martin Schwidefsky80703612014-10-06 17:53:53 +0200447#ifdef CONFIG_64BIT
448 /* Check for vector register enablement */
449 if (MACHINE_HAS_VX && !current->thread.vxrs &&
450 (current->thread.fp_regs.fpc & FPC_DXC_MASK) == 0xfe00) {
451 alloc_vector_registers(current);
452 /* Vector data exception is suppressing, rewind psw. */
453 regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
454 clear_pt_regs_flag(regs, PIF_PER_TRAP);
455 return;
456 }
457#endif
458
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (current->thread.fp_regs.fpc & FPC_DXC_MASK)
460 signal = SIGFPE;
461 else
462 signal = SIGILL;
463 if (signal == SIGFPE)
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100464 do_fp_trap(regs, current->thread.fp_regs.fpc);
465 else if (signal)
466 do_trap(regs, signal, ILL_ILLOPN, "data exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467}
468
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200469void space_switch_exception(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 /* Set user psw back to home space mode. */
Heiko Carstens7d256172012-07-27 10:31:12 +0200472 if (user_mode(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 regs->psw.mask |= PSW_ASC_HOME;
474 /* Send SIGILL. */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100475 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476}
477
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200478void kernel_stack_overflow(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
Heiko Carstens77eb65c2005-06-21 17:16:28 -0700480 bust_spinlocks(1);
481 printk("Kernel stack overflow.\n");
482 show_regs(regs);
483 bust_spinlocks(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 panic("Corrupt kernel stack, can't continue.");
485}
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200486NOKPROBE_SYMBOL(kernel_stack_overflow);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488void __init trap_init(void)
489{
Heiko Carstensf3e1a272011-01-05 12:48:00 +0100490 local_mcck_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491}