blob: dd97a3e8a34a8b3be07121f9ec4eb7be1bd7d971 [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>
Hendrik Bruecknerb0753902015-10-06 12:25:59 +020022#include <asm/fpu/api.h>
Heiko Carstensa8061702008-04-17 07:46:26 +020023#include "entry.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Martin Schwidefskyd35339a2012-07-31 11:03:04 +020025static inline void __user *get_trap_ip(struct pt_regs *regs)
26{
Martin Schwidefskyd35339a2012-07-31 11:03:04 +020027 unsigned long address;
28
29 if (regs->int_code & 0x200)
30 address = *(unsigned long *)(current->thread.trap_tdb + 24);
31 else
32 address = regs->psw.addr;
Heiko Carstens9cb1cce2016-01-18 13:12:19 +010033 return (void __user *) (address - (regs->int_code >> 16));
Martin Schwidefskyd35339a2012-07-31 11:03:04 +020034}
35
Heiko Carstensc0007f12007-04-27 16:01:42 +020036int is_valid_bugaddr(unsigned long addr)
37{
38 return 1;
39}
40
Jan Willeke2a0a5b22014-09-22 16:39:06 +020041void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010042{
43 siginfo_t info;
44
Heiko Carstens7d256172012-07-27 10:31:12 +020045 if (user_mode(regs)) {
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010046 info.si_signo = si_signo;
47 info.si_errno = 0;
48 info.si_code = si_code;
Martin Schwidefskyd35339a2012-07-31 11:03:04 +020049 info.si_addr = get_trap_ip(regs);
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010050 force_sig_info(si_signo, &info, current);
Heiko Carstens5d7ecce2016-02-24 14:27:46 +010051 report_user_fault(regs, si_signo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 } else {
53 const struct exception_table_entry *fixup;
Heiko Carstens9cb1cce2016-01-18 13:12:19 +010054 fixup = search_exception_tables(regs->psw.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 if (fixup)
Heiko Carstensfecc8682016-01-18 12:49:44 +010056 regs->psw.addr = extable_fixup(fixup);
Heiko Carstensc0007f12007-04-27 16:01:42 +020057 else {
58 enum bug_trap_type btt;
59
Heiko Carstens9cb1cce2016-01-18 13:12:19 +010060 btt = report_bug(regs->psw.addr, regs);
Heiko Carstensc0007f12007-04-27 16:01:42 +020061 if (btt == BUG_TRAP_TYPE_WARN)
62 return;
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +010063 die(regs, str);
Heiko Carstensc0007f12007-04-27 16:01:42 +020064 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 }
66}
67
Heiko Carstens7a5388d2014-10-22 12:42:38 +020068static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
Jan Willeke2a0a5b22014-09-22 16:39:06 +020069{
70 if (notify_die(DIE_TRAP, str, regs, 0,
71 regs->int_code, si_signo) == NOTIFY_STOP)
72 return;
73 do_report_trap(regs, si_signo, si_code, str);
74}
Heiko Carstens7a5388d2014-10-22 12:42:38 +020075NOKPROBE_SYMBOL(do_trap);
Jan Willeke2a0a5b22014-09-22 16:39:06 +020076
Heiko Carstens7a5388d2014-10-22 12:42:38 +020077void do_per_trap(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070078{
Martin Schwidefsky73b7d402011-07-24 10:48:33 +020079 siginfo_t info;
80
Martin Schwidefsky5e9a2692011-01-05 12:48:10 +010081 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
Michael Grundy4ba069b2006-09-20 15:58:39 +020082 return;
Martin Schwidefsky73b7d402011-07-24 10:48:33 +020083 if (!current->ptrace)
84 return;
85 info.si_signo = SIGTRAP;
86 info.si_errno = 0;
87 info.si_code = TRAP_HWBKPT;
Martin Schwidefsky3c52e492011-10-30 15:17:15 +010088 info.si_addr =
89 (void __force __user *) current->thread.per_event.address;
Martin Schwidefsky73b7d402011-07-24 10:48:33 +020090 force_sig_info(SIGTRAP, &info, current);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091}
Heiko Carstens7a5388d2014-10-22 12:42:38 +020092NOKPROBE_SYMBOL(do_per_trap);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Heiko Carstensb01a37a2012-10-18 18:10:06 +020094void default_trap_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
Heiko Carstens7d256172012-07-27 10:31:12 +020096 if (user_mode(regs)) {
Heiko Carstens5d7ecce2016-02-24 14:27:46 +010097 report_user_fault(regs, SIGSEGV, 0);
Heiko Carstens6ea50962010-05-17 10:00:13 +020098 do_exit(SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 } else
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100100 die(regs, "Unknown program exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
Martin Schwidefsky1e546222010-10-25 16:10:37 +0200103#define DO_ERROR_INFO(name, signr, sicode, str) \
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200104void name(struct pt_regs *regs) \
105{ \
106 do_trap(regs, signr, sicode, str); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
Martin Schwidefsky1e546222010-10-25 16:10:37 +0200109DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
110 "addressing exception")
111DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
112 "execute exception")
113DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
114 "fixpoint divide exception")
115DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
116 "fixpoint overflow exception")
117DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
118 "HFP overflow exception")
119DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
120 "HFP underflow exception")
121DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
122 "HFP significance exception")
123DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
124 "HFP divide exception")
125DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
126 "HFP square root exception")
127DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
128 "operand exception")
129DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
130 "privileged operation")
131DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
132 "special operation exception")
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200133DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
134 "transaction constraint exception")
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200135
Hendrik Brueckner9977e882015-06-10 12:53:42 +0200136static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100138 int si_code = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 /* FPC[2] is Data Exception Code */
140 if ((fpc & 0x00000300) == 0) {
141 /* bits 6 and 7 of DXC are 0 iff IEEE exception */
142 if (fpc & 0x8000) /* invalid fp operation */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100143 si_code = FPE_FLTINV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 else if (fpc & 0x4000) /* div by 0 */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100145 si_code = FPE_FLTDIV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 else if (fpc & 0x2000) /* overflow */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100147 si_code = FPE_FLTOVF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 else if (fpc & 0x1000) /* underflow */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100149 si_code = FPE_FLTUND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 else if (fpc & 0x0800) /* inexact */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100151 si_code = FPE_FLTRES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 }
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100153 do_trap(regs, SIGFPE, si_code, "floating point exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154}
155
Heiko Carstense56da342014-11-19 14:05:52 +0100156void translation_exception(struct pt_regs *regs)
157{
158 /* May never happen. */
Heiko Carstense1d12d72015-02-18 14:17:14 +0100159 panic("Translation exception");
Heiko Carstense56da342014-11-19 14:05:52 +0100160}
161
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200162void illegal_op(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
164 siginfo_t info;
165 __u8 opcode[6];
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200166 __u16 __user *location;
Jan Willeke2a0a5b22014-09-22 16:39:06 +0200167 int is_uprobe_insn = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 int signal = 0;
169
Martin Schwidefskyd35339a2012-07-31 11:03:04 +0200170 location = get_trap_ip(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
Heiko Carstens7d256172012-07-27 10:31:12 +0200172 if (user_mode(regs)) {
Heiko Carstens12bae232006-10-27 12:39:22 +0200173 if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
174 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
Martin Schwidefsky73b7d402011-07-24 10:48:33 +0200176 if (current->ptrace) {
177 info.si_signo = SIGTRAP;
178 info.si_errno = 0;
179 info.si_code = TRAP_BRKPT;
180 info.si_addr = location;
181 force_sig_info(SIGTRAP, &info, current);
182 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 signal = SIGILL;
Jan Willeke2a0a5b22014-09-22 16:39:06 +0200184#ifdef CONFIG_UPROBES
185 } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) {
186 is_uprobe_insn = 1;
187#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 } else
189 signal = SIGILL;
Jan Willeke2a0a5b22014-09-22 16:39:06 +0200190 }
191 /*
192 * We got either an illegal op in kernel mode, or user space trapped
193 * on a uprobes illegal instruction. See if kprobes or uprobes picks
194 * it up. If not, SIGILL.
195 */
196 if (is_uprobe_insn || !user_mode(regs)) {
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100197 if (notify_die(DIE_BPT, "bpt", regs, 0,
Heiko Carstens35df8d52007-02-05 21:17:29 +0100198 3, SIGTRAP) != NOTIFY_STOP)
199 signal = SIGILL;
200 }
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100201 if (signal)
202 do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200204NOKPROBE_SYMBOL(illegal_op);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
Martin Schwidefsky1e546222010-10-25 16:10:37 +0200206DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
207 "specification exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208
Martin Schwidefsky80703612014-10-06 17:53:53 +0200209void vector_exception(struct pt_regs *regs)
210{
211 int si_code, vic;
212
213 if (!MACHINE_HAS_VX) {
214 do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation");
215 return;
216 }
217
218 /* get vector interrupt code from fpc */
Hendrik Bruecknerd0164ee2015-06-29 16:43:06 +0200219 save_fpu_regs();
Hendrik Brueckner904818e2015-06-11 15:33:54 +0200220 vic = (current->thread.fpu.fpc & 0xf00) >> 8;
Martin Schwidefsky80703612014-10-06 17:53:53 +0200221 switch (vic) {
222 case 1: /* invalid vector operation */
223 si_code = FPE_FLTINV;
224 break;
225 case 2: /* division by zero */
226 si_code = FPE_FLTDIV;
227 break;
228 case 3: /* overflow */
229 si_code = FPE_FLTOVF;
230 break;
231 case 4: /* underflow */
232 si_code = FPE_FLTUND;
233 break;
234 case 5: /* inexact */
235 si_code = FPE_FLTRES;
236 break;
237 default: /* unknown cause */
238 si_code = 0;
239 }
240 do_trap(regs, SIGFPE, si_code, "vector exception");
241}
242
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200243void data_exception(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 int signal = 0;
246
Hendrik Bruecknerd0164ee2015-06-29 16:43:06 +0200247 save_fpu_regs();
Hendrik Brueckner904818e2015-06-11 15:33:54 +0200248 if (current->thread.fpu.fpc & FPC_DXC_MASK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 signal = SIGFPE;
250 else
251 signal = SIGILL;
Heiko Carstens5a798592015-02-12 13:08:27 +0100252 if (signal == SIGFPE)
Hendrik Brueckner904818e2015-06-11 15:33:54 +0200253 do_fp_trap(regs, current->thread.fpu.fpc);
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100254 else if (signal)
255 do_trap(regs, signal, ILL_ILLOPN, "data exception");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256}
257
Heiko Carstensb01a37a2012-10-18 18:10:06 +0200258void space_switch_exception(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 /* Set user psw back to home space mode. */
Heiko Carstens7d256172012-07-27 10:31:12 +0200261 if (user_mode(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 regs->psw.mask |= PSW_ASC_HOME;
263 /* Send SIGILL. */
Martin Schwidefskyaa33c8c2011-12-27 11:27:18 +0100264 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265}
266
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200267void kernel_stack_overflow(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
Heiko Carstens77eb65c2005-06-21 17:16:28 -0700269 bust_spinlocks(1);
270 printk("Kernel stack overflow.\n");
271 show_regs(regs);
272 bust_spinlocks(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 panic("Corrupt kernel stack, can't continue.");
274}
Heiko Carstens7a5388d2014-10-22 12:42:38 +0200275NOKPROBE_SYMBOL(kernel_stack_overflow);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277void __init trap_init(void)
278{
Heiko Carstensf3e1a272011-01-05 12:48:00 +0100279 local_mcck_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}