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/switch_to.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 #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 44 static inline void report_user_fault(struct pt_regs *regs, int signr) 45 { 46 if ((task_pid_nr(current) > 1) && !show_unhandled_signals) 47 return; 48 if (!unhandled_signal(current, signr)) 49 return; 50 if (!printk_ratelimit()) 51 return; 52 printk("User process fault: interruption code %04x ilc:%d ", 53 regs->int_code & 0xffff, regs->int_code >> 17); 54 print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN); 55 printk("\n"); 56 show_regs(regs); 57 } 58 59 int is_valid_bugaddr(unsigned long addr) 60 { 61 return 1; 62 } 63 64 void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) 65 { 66 siginfo_t info; 67 68 if (user_mode(regs)) { 69 info.si_signo = si_signo; 70 info.si_errno = 0; 71 info.si_code = si_code; 72 info.si_addr = get_trap_ip(regs); 73 force_sig_info(si_signo, &info, current); 74 report_user_fault(regs, si_signo); 75 } else { 76 const struct exception_table_entry *fixup; 77 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); 78 if (fixup) 79 regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE; 80 else { 81 enum bug_trap_type btt; 82 83 btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); 84 if (btt == BUG_TRAP_TYPE_WARN) 85 return; 86 die(regs, str); 87 } 88 } 89 } 90 91 static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) 92 { 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 } 98 NOKPROBE_SYMBOL(do_trap); 99 100 void do_per_trap(struct pt_regs *regs) 101 { 102 siginfo_t info; 103 104 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) 105 return; 106 if (!current->ptrace) 107 return; 108 info.si_signo = SIGTRAP; 109 info.si_errno = 0; 110 info.si_code = TRAP_HWBKPT; 111 info.si_addr = 112 (void __force __user *) current->thread.per_event.address; 113 force_sig_info(SIGTRAP, &info, current); 114 } 115 NOKPROBE_SYMBOL(do_per_trap); 116 117 void default_trap_handler(struct pt_regs *regs) 118 { 119 if (user_mode(regs)) { 120 report_user_fault(regs, SIGSEGV); 121 do_exit(SIGSEGV); 122 } else 123 die(regs, "Unknown program exception"); 124 } 125 126 #define DO_ERROR_INFO(name, signr, sicode, str) \ 127 void name(struct pt_regs *regs) \ 128 { \ 129 do_trap(regs, signr, sicode, str); \ 130 } 131 132 DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, 133 "addressing exception") 134 DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, 135 "execute exception") 136 DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, 137 "fixpoint divide exception") 138 DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, 139 "fixpoint overflow exception") 140 DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, 141 "HFP overflow exception") 142 DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, 143 "HFP underflow exception") 144 DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, 145 "HFP significance exception") 146 DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, 147 "HFP divide exception") 148 DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, 149 "HFP square root exception") 150 DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, 151 "operand exception") 152 DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, 153 "privileged operation") 154 DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, 155 "special operation exception") 156 157 #ifdef CONFIG_64BIT 158 DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, 159 "transaction constraint exception") 160 #endif 161 162 static inline void do_fp_trap(struct pt_regs *regs, int fpc) 163 { 164 int si_code = 0; 165 /* 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 */ 169 si_code = FPE_FLTINV; 170 else if (fpc & 0x4000) /* div by 0 */ 171 si_code = FPE_FLTDIV; 172 else if (fpc & 0x2000) /* overflow */ 173 si_code = FPE_FLTOVF; 174 else if (fpc & 0x1000) /* underflow */ 175 si_code = FPE_FLTUND; 176 else if (fpc & 0x0800) /* inexact */ 177 si_code = FPE_FLTRES; 178 } 179 do_trap(regs, SIGFPE, si_code, "floating point exception"); 180 } 181 182 void translation_exception(struct pt_regs *regs) 183 { 184 /* May never happen. */ 185 die(regs, "Translation exception"); 186 } 187 188 void illegal_op(struct pt_regs *regs) 189 { 190 siginfo_t info; 191 __u8 opcode[6]; 192 __u16 __user *location; 193 int is_uprobe_insn = 0; 194 int signal = 0; 195 196 location = get_trap_ip(regs); 197 198 if (user_mode(regs)) { 199 if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) 200 return; 201 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { 202 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 209 signal = SIGILL; 210 #ifdef CONFIG_UPROBES 211 } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { 212 is_uprobe_insn = 1; 213 #endif 214 #ifdef CONFIG_MATHEMU 215 } else if (opcode[0] == 0xb3) { 216 if (get_user(*((__u16 *) (opcode+2)), location+1)) 217 return; 218 signal = math_emu_b3(opcode, regs); 219 } else if (opcode[0] == 0xed) { 220 if (get_user(*((__u32 *) (opcode+2)), 221 (__u32 __user *)(location+1))) 222 return; 223 signal = math_emu_ed(opcode, regs); 224 } else if (*((__u16 *) opcode) == 0xb299) { 225 if (get_user(*((__u16 *) (opcode+2)), location+1)) 226 return; 227 signal = math_emu_srnm(opcode, regs); 228 } else if (*((__u16 *) opcode) == 0xb29c) { 229 if (get_user(*((__u16 *) (opcode+2)), location+1)) 230 return; 231 signal = math_emu_stfpc(opcode, regs); 232 } else if (*((__u16 *) opcode) == 0xb29d) { 233 if (get_user(*((__u16 *) (opcode+2)), location+1)) 234 return; 235 signal = math_emu_lfpc(opcode, regs); 236 #endif 237 } else 238 signal = SIGILL; 239 } 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)) { 246 if (notify_die(DIE_BPT, "bpt", regs, 0, 247 3, SIGTRAP) != NOTIFY_STOP) 248 signal = SIGILL; 249 } 250 251 #ifdef CONFIG_MATHEMU 252 if (signal == SIGFPE) 253 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 257 #endif 258 if (signal) 259 do_trap(regs, signal, ILL_ILLOPC, "illegal operation"); 260 } 261 NOKPROBE_SYMBOL(illegal_op); 262 263 #ifdef CONFIG_MATHEMU 264 void specification_exception(struct pt_regs *regs) 265 { 266 __u8 opcode[6]; 267 __u16 __user *location = NULL; 268 int signal = 0; 269 270 location = (__u16 __user *) get_trap_ip(regs); 271 272 if (user_mode(regs)) { 273 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) 305 do_fp_trap(regs, current->thread.fp_regs.fpc); 306 else if (signal) 307 do_trap(regs, signal, ILL_ILLOPN, "specification exception"); 308 } 309 #else 310 DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, 311 "specification exception"); 312 #endif 313 314 #ifdef CONFIG_64BIT 315 int 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 340 void 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 374 static 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 382 void data_exception(struct pt_regs *regs) 383 { 384 __u16 __user *location; 385 int signal = 0; 386 387 location = get_trap_ip(regs); 388 389 if (MACHINE_HAS_IEEE) 390 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); 391 392 #ifdef CONFIG_MATHEMU 393 else if (user_mode(regs)) { 394 __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)), 425 (__u32 __user *)(location+1)); 426 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 447 #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 459 if (current->thread.fp_regs.fpc & FPC_DXC_MASK) 460 signal = SIGFPE; 461 else 462 signal = SIGILL; 463 if (signal == SIGFPE) 464 do_fp_trap(regs, current->thread.fp_regs.fpc); 465 else if (signal) 466 do_trap(regs, signal, ILL_ILLOPN, "data exception"); 467 } 468 469 void space_switch_exception(struct pt_regs *regs) 470 { 471 /* Set user psw back to home space mode. */ 472 if (user_mode(regs)) 473 regs->psw.mask |= PSW_ASC_HOME; 474 /* Send SIGILL. */ 475 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); 476 } 477 478 void kernel_stack_overflow(struct pt_regs *regs) 479 { 480 bust_spinlocks(1); 481 printk("Kernel stack overflow.\n"); 482 show_regs(regs); 483 bust_spinlocks(0); 484 panic("Corrupt kernel stack, can't continue."); 485 } 486 NOKPROBE_SYMBOL(kernel_stack_overflow); 487 488 void __init trap_init(void) 489 { 490 local_mcck_enable(); 491 } 492