1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * S390 version |
4 | * Copyright IBM Corp. 1999, 2000 |
5 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
6 | * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), |
7 | * |
8 | * Derived from "arch/i386/kernel/traps.c" |
9 | * Copyright (C) 1991, 1992 Linus Torvalds |
10 | */ |
11 | |
12 | /* |
13 | * 'Traps.c' handles hardware traps and faults after we have saved some |
14 | * state in 'asm.s'. |
15 | */ |
16 | #include "asm/irqflags.h" |
17 | #include "asm/ptrace.h" |
18 | #include <linux/kprobes.h> |
19 | #include <linux/kdebug.h> |
20 | #include <linux/randomize_kstack.h> |
21 | #include <linux/extable.h> |
22 | #include <linux/ptrace.h> |
23 | #include <linux/sched.h> |
24 | #include <linux/sched/debug.h> |
25 | #include <linux/mm.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/uaccess.h> |
28 | #include <linux/cpu.h> |
29 | #include <linux/entry-common.h> |
30 | #include <asm/asm-extable.h> |
31 | #include <asm/vtime.h> |
32 | #include <asm/fpu.h> |
33 | #include "entry.h" |
34 | |
35 | static inline void __user *get_trap_ip(struct pt_regs *regs) |
36 | { |
37 | unsigned long address; |
38 | |
39 | if (regs->int_code & 0x200) |
40 | address = current->thread.trap_tdb.data[3]; |
41 | else |
42 | address = regs->psw.addr; |
43 | return (void __user *) (address - (regs->int_code >> 16)); |
44 | } |
45 | |
46 | #ifdef CONFIG_GENERIC_BUG |
47 | int is_valid_bugaddr(unsigned long addr) |
48 | { |
49 | return 1; |
50 | } |
51 | #endif |
52 | |
53 | void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) |
54 | { |
55 | if (user_mode(regs)) { |
56 | force_sig_fault(sig: si_signo, code: si_code, addr: get_trap_ip(regs)); |
57 | report_user_fault(regs, si_signo, 0); |
58 | } else { |
59 | if (!fixup_exception(regs)) |
60 | die(regs, str); |
61 | } |
62 | } |
63 | |
64 | static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) |
65 | { |
66 | if (notify_die(val: DIE_TRAP, str, regs, err: 0, |
67 | trap: regs->int_code, sig: si_signo) == NOTIFY_STOP) |
68 | return; |
69 | do_report_trap(regs, si_signo, si_code, str); |
70 | } |
71 | NOKPROBE_SYMBOL(do_trap); |
72 | |
73 | void do_per_trap(struct pt_regs *regs) |
74 | { |
75 | if (notify_die(val: DIE_SSTEP, str: "sstep" , regs, err: 0, trap: 0, SIGTRAP) == NOTIFY_STOP) |
76 | return; |
77 | if (!current->ptrace) |
78 | return; |
79 | force_sig_fault(SIGTRAP, TRAP_HWBKPT, |
80 | addr: (void __force __user *) current->thread.per_event.address); |
81 | } |
82 | NOKPROBE_SYMBOL(do_per_trap); |
83 | |
84 | static void default_trap_handler(struct pt_regs *regs) |
85 | { |
86 | if (user_mode(regs)) { |
87 | report_user_fault(regs, SIGSEGV, 0); |
88 | force_exit_sig(SIGSEGV); |
89 | } else |
90 | die(regs, "Unknown program exception" ); |
91 | } |
92 | |
93 | #define DO_ERROR_INFO(name, signr, sicode, str) \ |
94 | static void name(struct pt_regs *regs) \ |
95 | { \ |
96 | do_trap(regs, signr, sicode, str); \ |
97 | } |
98 | |
99 | DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, |
100 | "addressing exception" ) |
101 | DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, |
102 | "execute exception" ) |
103 | DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, |
104 | "fixpoint divide exception" ) |
105 | DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, |
106 | "fixpoint overflow exception" ) |
107 | DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, |
108 | "HFP overflow exception" ) |
109 | DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, |
110 | "HFP underflow exception" ) |
111 | DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, |
112 | "HFP significance exception" ) |
113 | DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, |
114 | "HFP divide exception" ) |
115 | DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, |
116 | "HFP square root exception" ) |
117 | DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, |
118 | "operand exception" ) |
119 | DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, |
120 | "privileged operation" ) |
121 | DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, |
122 | "special operation exception" ) |
123 | DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, |
124 | "transaction constraint exception" ) |
125 | |
126 | static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc) |
127 | { |
128 | int si_code = 0; |
129 | /* FPC[2] is Data Exception Code */ |
130 | if ((fpc & 0x00000300) == 0) { |
131 | /* bits 6 and 7 of DXC are 0 iff IEEE exception */ |
132 | if (fpc & 0x8000) /* invalid fp operation */ |
133 | si_code = FPE_FLTINV; |
134 | else if (fpc & 0x4000) /* div by 0 */ |
135 | si_code = FPE_FLTDIV; |
136 | else if (fpc & 0x2000) /* overflow */ |
137 | si_code = FPE_FLTOVF; |
138 | else if (fpc & 0x1000) /* underflow */ |
139 | si_code = FPE_FLTUND; |
140 | else if (fpc & 0x0800) /* inexact */ |
141 | si_code = FPE_FLTRES; |
142 | } |
143 | do_trap(regs, SIGFPE, si_code, str: "floating point exception" ); |
144 | } |
145 | |
146 | static void translation_specification_exception(struct pt_regs *regs) |
147 | { |
148 | /* May never happen. */ |
149 | panic(fmt: "Translation-Specification Exception" ); |
150 | } |
151 | |
152 | static void illegal_op(struct pt_regs *regs) |
153 | { |
154 | __u8 opcode[6]; |
155 | __u16 __user *location; |
156 | int is_uprobe_insn = 0; |
157 | int signal = 0; |
158 | |
159 | location = get_trap_ip(regs); |
160 | |
161 | if (user_mode(regs)) { |
162 | if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) |
163 | return; |
164 | if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { |
165 | if (current->ptrace) |
166 | force_sig_fault(SIGTRAP, TRAP_BRKPT, addr: location); |
167 | else |
168 | signal = SIGILL; |
169 | #ifdef CONFIG_UPROBES |
170 | } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { |
171 | is_uprobe_insn = 1; |
172 | #endif |
173 | } else |
174 | signal = SIGILL; |
175 | } |
176 | /* |
177 | * We got either an illegal op in kernel mode, or user space trapped |
178 | * on a uprobes illegal instruction. See if kprobes or uprobes picks |
179 | * it up. If not, SIGILL. |
180 | */ |
181 | if (is_uprobe_insn || !user_mode(regs)) { |
182 | if (notify_die(val: DIE_BPT, str: "bpt" , regs, err: 0, |
183 | trap: 3, SIGTRAP) != NOTIFY_STOP) |
184 | signal = SIGILL; |
185 | } |
186 | if (signal) |
187 | do_trap(regs, si_signo: signal, ILL_ILLOPC, str: "illegal operation" ); |
188 | } |
189 | NOKPROBE_SYMBOL(illegal_op); |
190 | |
191 | DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, |
192 | "specification exception" ); |
193 | |
194 | static void vector_exception(struct pt_regs *regs) |
195 | { |
196 | int si_code, vic; |
197 | |
198 | if (!cpu_has_vx()) { |
199 | do_trap(regs, SIGILL, ILL_ILLOPN, str: "illegal operation" ); |
200 | return; |
201 | } |
202 | |
203 | /* get vector interrupt code from fpc */ |
204 | save_user_fpu_regs(); |
205 | vic = (current->thread.ufpu.fpc & 0xf00) >> 8; |
206 | switch (vic) { |
207 | case 1: /* invalid vector operation */ |
208 | si_code = FPE_FLTINV; |
209 | break; |
210 | case 2: /* division by zero */ |
211 | si_code = FPE_FLTDIV; |
212 | break; |
213 | case 3: /* overflow */ |
214 | si_code = FPE_FLTOVF; |
215 | break; |
216 | case 4: /* underflow */ |
217 | si_code = FPE_FLTUND; |
218 | break; |
219 | case 5: /* inexact */ |
220 | si_code = FPE_FLTRES; |
221 | break; |
222 | default: /* unknown cause */ |
223 | si_code = 0; |
224 | } |
225 | do_trap(regs, SIGFPE, si_code, str: "vector exception" ); |
226 | } |
227 | |
228 | static void data_exception(struct pt_regs *regs) |
229 | { |
230 | save_user_fpu_regs(); |
231 | if (current->thread.ufpu.fpc & FPC_DXC_MASK) |
232 | do_fp_trap(regs, current->thread.fpc: ufpu.fpc); |
233 | else |
234 | do_trap(regs, SIGILL, ILL_ILLOPN, str: "data exception" ); |
235 | } |
236 | |
237 | static void space_switch_exception(struct pt_regs *regs) |
238 | { |
239 | /* Set user psw back to home space mode. */ |
240 | if (user_mode(regs)) |
241 | regs->psw.mask |= PSW_ASC_HOME; |
242 | /* Send SIGILL. */ |
243 | do_trap(regs, SIGILL, ILL_PRVOPC, str: "space switch event" ); |
244 | } |
245 | |
246 | static void monitor_event_exception(struct pt_regs *regs) |
247 | { |
248 | if (user_mode(regs)) |
249 | return; |
250 | |
251 | switch (report_bug(bug_addr: regs->psw.addr - (regs->int_code >> 16), regs)) { |
252 | case BUG_TRAP_TYPE_NONE: |
253 | fixup_exception(regs); |
254 | break; |
255 | case BUG_TRAP_TYPE_WARN: |
256 | break; |
257 | case BUG_TRAP_TYPE_BUG: |
258 | die(regs, "monitor event" ); |
259 | break; |
260 | } |
261 | } |
262 | |
263 | void kernel_stack_overflow(struct pt_regs *regs) |
264 | { |
265 | bust_spinlocks(yes: 1); |
266 | printk("Kernel stack overflow.\n" ); |
267 | show_regs(regs); |
268 | bust_spinlocks(yes: 0); |
269 | panic(fmt: "Corrupt kernel stack, can't continue." ); |
270 | } |
271 | NOKPROBE_SYMBOL(kernel_stack_overflow); |
272 | |
273 | static void __init test_monitor_call(void) |
274 | { |
275 | int val = 1; |
276 | |
277 | if (!IS_ENABLED(CONFIG_BUG)) |
278 | return; |
279 | asm volatile( |
280 | " mc 0,0\n" |
281 | "0: xgr %0,%0\n" |
282 | "1:\n" |
283 | EX_TABLE(0b,1b) |
284 | : "+d" (val)); |
285 | if (!val) |
286 | panic(fmt: "Monitor call doesn't work!\n" ); |
287 | } |
288 | |
289 | void __init trap_init(void) |
290 | { |
291 | unsigned long flags; |
292 | struct ctlreg cr0; |
293 | |
294 | local_irq_save(flags); |
295 | cr0 = local_ctl_clear_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT); |
296 | psw_bits(S390_lowcore.external_new_psw).mcheck = 1; |
297 | psw_bits(S390_lowcore.program_new_psw).mcheck = 1; |
298 | psw_bits(S390_lowcore.svc_new_psw).mcheck = 1; |
299 | psw_bits(S390_lowcore.io_new_psw).mcheck = 1; |
300 | local_ctl_load(0, &cr0); |
301 | local_irq_restore(flags); |
302 | local_mcck_enable(); |
303 | test_monitor_call(); |
304 | } |
305 | |
306 | static void (*pgm_check_table[128])(struct pt_regs *regs); |
307 | |
308 | void noinstr __do_pgm_check(struct pt_regs *regs) |
309 | { |
310 | unsigned int trapnr; |
311 | irqentry_state_t state; |
312 | |
313 | regs->int_code = S390_lowcore.pgm_int_code; |
314 | regs->int_parm_long = S390_lowcore.trans_exc_code; |
315 | |
316 | state = irqentry_enter(regs); |
317 | |
318 | if (user_mode(regs)) { |
319 | update_timer_sys(); |
320 | if (!static_branch_likely(&cpu_has_bear)) { |
321 | if (regs->last_break < 4096) |
322 | regs->last_break = 1; |
323 | } |
324 | current->thread.last_break = regs->last_break; |
325 | } |
326 | |
327 | if (S390_lowcore.pgm_code & 0x0200) { |
328 | /* transaction abort */ |
329 | current->thread.trap_tdb = S390_lowcore.pgm_tdb; |
330 | } |
331 | |
332 | if (S390_lowcore.pgm_code & PGM_INT_CODE_PER) { |
333 | if (user_mode(regs)) { |
334 | struct per_event *ev = ¤t->thread.per_event; |
335 | |
336 | set_thread_flag(TIF_PER_TRAP); |
337 | ev->address = S390_lowcore.per_address; |
338 | ev->cause = S390_lowcore.per_code_combined; |
339 | ev->paid = S390_lowcore.per_access_id; |
340 | } else { |
341 | /* PER event in kernel is kprobes */ |
342 | __arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER); |
343 | do_per_trap(regs); |
344 | goto out; |
345 | } |
346 | } |
347 | |
348 | if (!irqs_disabled_flags(regs->psw.mask)) |
349 | trace_hardirqs_on(); |
350 | __arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER); |
351 | |
352 | trapnr = regs->int_code & PGM_INT_CODE_MASK; |
353 | if (trapnr) |
354 | pgm_check_table[trapnr](regs); |
355 | out: |
356 | local_irq_disable(); |
357 | irqentry_exit(regs, state); |
358 | } |
359 | |
360 | /* |
361 | * The program check table contains exactly 128 (0x00-0x7f) entries. Each |
362 | * line defines the function to be called corresponding to the program check |
363 | * interruption code. |
364 | */ |
365 | static void (*pgm_check_table[128])(struct pt_regs *regs) = { |
366 | [0x00] = default_trap_handler, |
367 | [0x01] = illegal_op, |
368 | [0x02] = privileged_op, |
369 | [0x03] = execute_exception, |
370 | [0x04] = do_protection_exception, |
371 | [0x05] = addressing_exception, |
372 | [0x06] = specification_exception, |
373 | [0x07] = data_exception, |
374 | [0x08] = overflow_exception, |
375 | [0x09] = divide_exception, |
376 | [0x0a] = overflow_exception, |
377 | [0x0b] = divide_exception, |
378 | [0x0c] = hfp_overflow_exception, |
379 | [0x0d] = hfp_underflow_exception, |
380 | [0x0e] = hfp_significance_exception, |
381 | [0x0f] = hfp_divide_exception, |
382 | [0x10] = do_dat_exception, |
383 | [0x11] = do_dat_exception, |
384 | [0x12] = translation_specification_exception, |
385 | [0x13] = special_op_exception, |
386 | [0x14] = default_trap_handler, |
387 | [0x15] = operand_exception, |
388 | [0x16] = default_trap_handler, |
389 | [0x17] = default_trap_handler, |
390 | [0x18] = transaction_exception, |
391 | [0x19] = default_trap_handler, |
392 | [0x1a] = default_trap_handler, |
393 | [0x1b] = vector_exception, |
394 | [0x1c] = space_switch_exception, |
395 | [0x1d] = hfp_sqrt_exception, |
396 | [0x1e ... 0x37] = default_trap_handler, |
397 | [0x38] = do_dat_exception, |
398 | [0x39] = do_dat_exception, |
399 | [0x3a] = do_dat_exception, |
400 | [0x3b] = do_dat_exception, |
401 | [0x3c] = default_trap_handler, |
402 | [0x3d] = do_secure_storage_access, |
403 | [0x3e] = do_non_secure_storage_access, |
404 | [0x3f] = do_secure_storage_violation, |
405 | [0x40] = monitor_event_exception, |
406 | [0x41 ... 0x7f] = default_trap_handler, |
407 | }; |
408 | |
409 | #define COND_TRAP(x) asm( \ |
410 | ".weak " __stringify(x) "\n\t" \ |
411 | ".set " __stringify(x) "," \ |
412 | __stringify(default_trap_handler)) |
413 | |
414 | COND_TRAP(do_secure_storage_access); |
415 | COND_TRAP(do_non_secure_storage_access); |
416 | COND_TRAP(do_secure_storage_violation); |
417 | |