]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - arch/s390/kernel/traps.c
Merge remote-tracking branches 'asoc/topic/atmel', 'asoc/topic/bcm2835' and 'asoc...
[mirror_ubuntu-zesty-kernel.git] / arch / s390 / kernel / traps.c
1 /*
2 * S390 version
3 * Copyright IBM Corp. 1999, 2000
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 */
15 #include <linux/kprobes.h>
16 #include <linux/kdebug.h>
17 #include <linux/module.h>
18 #include <linux/ptrace.h>
19 #include <linux/sched.h>
20 #include <linux/mm.h>
21 #include <linux/slab.h>
22 #include <asm/fpu/api.h>
23 #include "entry.h"
24
25 int show_unhandled_signals = 1;
26
27 static inline void __user *get_trap_ip(struct pt_regs *regs)
28 {
29 unsigned long address;
30
31 if (regs->int_code & 0x200)
32 address = *(unsigned long *)(current->thread.trap_tdb + 24);
33 else
34 address = regs->psw.addr;
35 return (void __user *) (address - (regs->int_code >> 16));
36 }
37
38 static inline void report_user_fault(struct pt_regs *regs, int signr)
39 {
40 if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
41 return;
42 if (!unhandled_signal(current, signr))
43 return;
44 if (!printk_ratelimit())
45 return;
46 printk("User process fault: interruption code %04x ilc:%d ",
47 regs->int_code & 0xffff, regs->int_code >> 17);
48 print_vma_addr("in ", regs->psw.addr);
49 printk("\n");
50 show_regs(regs);
51 }
52
53 int is_valid_bugaddr(unsigned long addr)
54 {
55 return 1;
56 }
57
58 void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
59 {
60 siginfo_t info;
61
62 if (user_mode(regs)) {
63 info.si_signo = si_signo;
64 info.si_errno = 0;
65 info.si_code = si_code;
66 info.si_addr = get_trap_ip(regs);
67 force_sig_info(si_signo, &info, current);
68 report_user_fault(regs, si_signo);
69 } else {
70 const struct exception_table_entry *fixup;
71 fixup = search_exception_tables(regs->psw.addr);
72 if (fixup)
73 regs->psw.addr = extable_fixup(fixup);
74 else {
75 enum bug_trap_type btt;
76
77 btt = report_bug(regs->psw.addr, regs);
78 if (btt == BUG_TRAP_TYPE_WARN)
79 return;
80 die(regs, str);
81 }
82 }
83 }
84
85 static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
86 {
87 if (notify_die(DIE_TRAP, str, regs, 0,
88 regs->int_code, si_signo) == NOTIFY_STOP)
89 return;
90 do_report_trap(regs, si_signo, si_code, str);
91 }
92 NOKPROBE_SYMBOL(do_trap);
93
94 void do_per_trap(struct pt_regs *regs)
95 {
96 siginfo_t info;
97
98 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
99 return;
100 if (!current->ptrace)
101 return;
102 info.si_signo = SIGTRAP;
103 info.si_errno = 0;
104 info.si_code = TRAP_HWBKPT;
105 info.si_addr =
106 (void __force __user *) current->thread.per_event.address;
107 force_sig_info(SIGTRAP, &info, current);
108 }
109 NOKPROBE_SYMBOL(do_per_trap);
110
111 void default_trap_handler(struct pt_regs *regs)
112 {
113 if (user_mode(regs)) {
114 report_user_fault(regs, SIGSEGV);
115 do_exit(SIGSEGV);
116 } else
117 die(regs, "Unknown program exception");
118 }
119
120 #define DO_ERROR_INFO(name, signr, sicode, str) \
121 void name(struct pt_regs *regs) \
122 { \
123 do_trap(regs, signr, sicode, str); \
124 }
125
126 DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
127 "addressing exception")
128 DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
129 "execute exception")
130 DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
131 "fixpoint divide exception")
132 DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
133 "fixpoint overflow exception")
134 DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
135 "HFP overflow exception")
136 DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
137 "HFP underflow exception")
138 DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
139 "HFP significance exception")
140 DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
141 "HFP divide exception")
142 DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
143 "HFP square root exception")
144 DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
145 "operand exception")
146 DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
147 "privileged operation")
148 DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
149 "special operation exception")
150 DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
151 "transaction constraint exception")
152
153 static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc)
154 {
155 int si_code = 0;
156 /* FPC[2] is Data Exception Code */
157 if ((fpc & 0x00000300) == 0) {
158 /* bits 6 and 7 of DXC are 0 iff IEEE exception */
159 if (fpc & 0x8000) /* invalid fp operation */
160 si_code = FPE_FLTINV;
161 else if (fpc & 0x4000) /* div by 0 */
162 si_code = FPE_FLTDIV;
163 else if (fpc & 0x2000) /* overflow */
164 si_code = FPE_FLTOVF;
165 else if (fpc & 0x1000) /* underflow */
166 si_code = FPE_FLTUND;
167 else if (fpc & 0x0800) /* inexact */
168 si_code = FPE_FLTRES;
169 }
170 do_trap(regs, SIGFPE, si_code, "floating point exception");
171 }
172
173 void translation_exception(struct pt_regs *regs)
174 {
175 /* May never happen. */
176 panic("Translation exception");
177 }
178
179 void illegal_op(struct pt_regs *regs)
180 {
181 siginfo_t info;
182 __u8 opcode[6];
183 __u16 __user *location;
184 int is_uprobe_insn = 0;
185 int signal = 0;
186
187 location = get_trap_ip(regs);
188
189 if (user_mode(regs)) {
190 if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
191 return;
192 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
193 if (current->ptrace) {
194 info.si_signo = SIGTRAP;
195 info.si_errno = 0;
196 info.si_code = TRAP_BRKPT;
197 info.si_addr = location;
198 force_sig_info(SIGTRAP, &info, current);
199 } else
200 signal = SIGILL;
201 #ifdef CONFIG_UPROBES
202 } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) {
203 is_uprobe_insn = 1;
204 #endif
205 } else
206 signal = SIGILL;
207 }
208 /*
209 * We got either an illegal op in kernel mode, or user space trapped
210 * on a uprobes illegal instruction. See if kprobes or uprobes picks
211 * it up. If not, SIGILL.
212 */
213 if (is_uprobe_insn || !user_mode(regs)) {
214 if (notify_die(DIE_BPT, "bpt", regs, 0,
215 3, SIGTRAP) != NOTIFY_STOP)
216 signal = SIGILL;
217 }
218 if (signal)
219 do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
220 }
221 NOKPROBE_SYMBOL(illegal_op);
222
223 DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
224 "specification exception");
225
226 void vector_exception(struct pt_regs *regs)
227 {
228 int si_code, vic;
229
230 if (!MACHINE_HAS_VX) {
231 do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation");
232 return;
233 }
234
235 /* get vector interrupt code from fpc */
236 save_fpu_regs();
237 vic = (current->thread.fpu.fpc & 0xf00) >> 8;
238 switch (vic) {
239 case 1: /* invalid vector operation */
240 si_code = FPE_FLTINV;
241 break;
242 case 2: /* division by zero */
243 si_code = FPE_FLTDIV;
244 break;
245 case 3: /* overflow */
246 si_code = FPE_FLTOVF;
247 break;
248 case 4: /* underflow */
249 si_code = FPE_FLTUND;
250 break;
251 case 5: /* inexact */
252 si_code = FPE_FLTRES;
253 break;
254 default: /* unknown cause */
255 si_code = 0;
256 }
257 do_trap(regs, SIGFPE, si_code, "vector exception");
258 }
259
260 void data_exception(struct pt_regs *regs)
261 {
262 int signal = 0;
263
264 save_fpu_regs();
265 if (current->thread.fpu.fpc & FPC_DXC_MASK)
266 signal = SIGFPE;
267 else
268 signal = SIGILL;
269 if (signal == SIGFPE)
270 do_fp_trap(regs, current->thread.fpu.fpc);
271 else if (signal)
272 do_trap(regs, signal, ILL_ILLOPN, "data exception");
273 }
274
275 void space_switch_exception(struct pt_regs *regs)
276 {
277 /* Set user psw back to home space mode. */
278 if (user_mode(regs))
279 regs->psw.mask |= PSW_ASC_HOME;
280 /* Send SIGILL. */
281 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
282 }
283
284 void kernel_stack_overflow(struct pt_regs *regs)
285 {
286 bust_spinlocks(1);
287 printk("Kernel stack overflow.\n");
288 show_regs(regs);
289 bust_spinlocks(0);
290 panic("Corrupt kernel stack, can't continue.");
291 }
292 NOKPROBE_SYMBOL(kernel_stack_overflow);
293
294 void __init trap_init(void)
295 {
296 local_mcck_enable();
297 }