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 0x%X ", regs->int_code); 53 print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN); 54 printk("\n"); 55 show_regs(regs); 56 } 57 58 int is_valid_bugaddr(unsigned long addr) 59 { 60 return 1; 61 } 62 63 void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) 64 { 65 siginfo_t info; 66 67 if (user_mode(regs)) { 68 info.si_signo = si_signo; 69 info.si_errno = 0; 70 info.si_code = si_code; 71 info.si_addr = get_trap_ip(regs); 72 force_sig_info(si_signo, &info, current); 73 report_user_fault(regs, si_signo); 74 } else { 75 const struct exception_table_entry *fixup; 76 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); 77 if (fixup) 78 regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE; 79 else { 80 enum bug_trap_type btt; 81 82 btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); 83 if (btt == BUG_TRAP_TYPE_WARN) 84 return; 85 die(regs, str); 86 } 87 } 88 } 89 90 static void __kprobes do_trap(struct pt_regs *regs, int si_signo, int si_code, 91 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 99 void __kprobes do_per_trap(struct pt_regs *regs) 100 { 101 siginfo_t info; 102 103 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) 104 return; 105 if (!current->ptrace) 106 return; 107 info.si_signo = SIGTRAP; 108 info.si_errno = 0; 109 info.si_code = TRAP_HWBKPT; 110 info.si_addr = 111 (void __force __user *) current->thread.per_event.address; 112 force_sig_info(SIGTRAP, &info, current); 113 } 114 115 void default_trap_handler(struct pt_regs *regs) 116 { 117 if (user_mode(regs)) { 118 report_user_fault(regs, SIGSEGV); 119 do_exit(SIGSEGV); 120 } else 121 die(regs, "Unknown program exception"); 122 } 123 124 #define DO_ERROR_INFO(name, signr, sicode, str) \ 125 void name(struct pt_regs *regs) \ 126 { \ 127 do_trap(regs, signr, sicode, str); \ 128 } 129 130 DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, 131 "addressing exception") 132 DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, 133 "execute exception") 134 DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, 135 "fixpoint divide exception") 136 DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, 137 "fixpoint overflow exception") 138 DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, 139 "HFP overflow exception") 140 DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, 141 "HFP underflow exception") 142 DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, 143 "HFP significance exception") 144 DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, 145 "HFP divide exception") 146 DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, 147 "HFP square root exception") 148 DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, 149 "operand exception") 150 DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, 151 "privileged operation") 152 DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, 153 "special operation exception") 154 DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, 155 "translation 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 __kprobes illegal_op(struct pt_regs *regs) 183 { 184 siginfo_t info; 185 __u8 opcode[6]; 186 __u16 __user *location; 187 int is_uprobe_insn = 0; 188 int signal = 0; 189 190 location = get_trap_ip(regs); 191 192 if (user_mode(regs)) { 193 if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) 194 return; 195 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { 196 if (current->ptrace) { 197 info.si_signo = SIGTRAP; 198 info.si_errno = 0; 199 info.si_code = TRAP_BRKPT; 200 info.si_addr = location; 201 force_sig_info(SIGTRAP, &info, current); 202 } else 203 signal = SIGILL; 204 #ifdef CONFIG_UPROBES 205 } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { 206 is_uprobe_insn = 1; 207 #endif 208 #ifdef CONFIG_MATHEMU 209 } else if (opcode[0] == 0xb3) { 210 if (get_user(*((__u16 *) (opcode+2)), location+1)) 211 return; 212 signal = math_emu_b3(opcode, regs); 213 } else if (opcode[0] == 0xed) { 214 if (get_user(*((__u32 *) (opcode+2)), 215 (__u32 __user *)(location+1))) 216 return; 217 signal = math_emu_ed(opcode, regs); 218 } else if (*((__u16 *) opcode) == 0xb299) { 219 if (get_user(*((__u16 *) (opcode+2)), location+1)) 220 return; 221 signal = math_emu_srnm(opcode, regs); 222 } else if (*((__u16 *) opcode) == 0xb29c) { 223 if (get_user(*((__u16 *) (opcode+2)), location+1)) 224 return; 225 signal = math_emu_stfpc(opcode, regs); 226 } else if (*((__u16 *) opcode) == 0xb29d) { 227 if (get_user(*((__u16 *) (opcode+2)), location+1)) 228 return; 229 signal = math_emu_lfpc(opcode, regs); 230 #endif 231 } else 232 signal = SIGILL; 233 } 234 /* 235 * We got either an illegal op in kernel mode, or user space trapped 236 * on a uprobes illegal instruction. See if kprobes or uprobes picks 237 * it up. If not, SIGILL. 238 */ 239 if (is_uprobe_insn || !user_mode(regs)) { 240 if (notify_die(DIE_BPT, "bpt", regs, 0, 241 3, SIGTRAP) != NOTIFY_STOP) 242 signal = SIGILL; 243 } 244 245 #ifdef CONFIG_MATHEMU 246 if (signal == SIGFPE) 247 do_fp_trap(regs, current->thread.fp_regs.fpc); 248 else if (signal == SIGSEGV) 249 do_trap(regs, signal, SEGV_MAPERR, "user address fault"); 250 else 251 #endif 252 if (signal) 253 do_trap(regs, signal, ILL_ILLOPC, "illegal operation"); 254 } 255 256 257 #ifdef CONFIG_MATHEMU 258 void specification_exception(struct pt_regs *regs) 259 { 260 __u8 opcode[6]; 261 __u16 __user *location = NULL; 262 int signal = 0; 263 264 location = (__u16 __user *) get_trap_ip(regs); 265 266 if (user_mode(regs)) { 267 get_user(*((__u16 *) opcode), location); 268 switch (opcode[0]) { 269 case 0x28: /* LDR Rx,Ry */ 270 signal = math_emu_ldr(opcode); 271 break; 272 case 0x38: /* LER Rx,Ry */ 273 signal = math_emu_ler(opcode); 274 break; 275 case 0x60: /* STD R,D(X,B) */ 276 get_user(*((__u16 *) (opcode+2)), location+1); 277 signal = math_emu_std(opcode, regs); 278 break; 279 case 0x68: /* LD R,D(X,B) */ 280 get_user(*((__u16 *) (opcode+2)), location+1); 281 signal = math_emu_ld(opcode, regs); 282 break; 283 case 0x70: /* STE R,D(X,B) */ 284 get_user(*((__u16 *) (opcode+2)), location+1); 285 signal = math_emu_ste(opcode, regs); 286 break; 287 case 0x78: /* LE R,D(X,B) */ 288 get_user(*((__u16 *) (opcode+2)), location+1); 289 signal = math_emu_le(opcode, regs); 290 break; 291 default: 292 signal = SIGILL; 293 break; 294 } 295 } else 296 signal = SIGILL; 297 298 if (signal == SIGFPE) 299 do_fp_trap(regs, current->thread.fp_regs.fpc); 300 else if (signal) 301 do_trap(regs, signal, ILL_ILLOPN, "specification exception"); 302 } 303 #else 304 DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, 305 "specification exception"); 306 #endif 307 308 #ifdef CONFIG_64BIT 309 int alloc_vector_registers(struct task_struct *tsk) 310 { 311 __vector128 *vxrs; 312 int i; 313 314 /* Allocate vector register save area. */ 315 vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS, 316 GFP_KERNEL|__GFP_REPEAT); 317 if (!vxrs) 318 return -ENOMEM; 319 preempt_disable(); 320 if (tsk == current) 321 save_fp_regs(tsk->thread.fp_regs.fprs); 322 /* Copy the 16 floating point registers */ 323 for (i = 0; i < 16; i++) 324 *(freg_t *) &vxrs[i] = tsk->thread.fp_regs.fprs[i]; 325 tsk->thread.vxrs = vxrs; 326 if (tsk == current) { 327 __ctl_set_bit(0, 17); 328 restore_vx_regs(vxrs); 329 } 330 preempt_enable(); 331 return 0; 332 } 333 334 void vector_exception(struct pt_regs *regs) 335 { 336 int si_code, vic; 337 338 if (!MACHINE_HAS_VX) { 339 do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation"); 340 return; 341 } 342 343 /* get vector interrupt code from fpc */ 344 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); 345 vic = (current->thread.fp_regs.fpc & 0xf00) >> 8; 346 switch (vic) { 347 case 1: /* invalid vector operation */ 348 si_code = FPE_FLTINV; 349 break; 350 case 2: /* division by zero */ 351 si_code = FPE_FLTDIV; 352 break; 353 case 3: /* overflow */ 354 si_code = FPE_FLTOVF; 355 break; 356 case 4: /* underflow */ 357 si_code = FPE_FLTUND; 358 break; 359 case 5: /* inexact */ 360 si_code = FPE_FLTRES; 361 break; 362 default: /* unknown cause */ 363 si_code = 0; 364 } 365 do_trap(regs, SIGFPE, si_code, "vector exception"); 366 } 367 368 static int __init disable_vector_extension(char *str) 369 { 370 S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; 371 return 1; 372 } 373 __setup("novx", disable_vector_extension); 374 #endif 375 376 void data_exception(struct pt_regs *regs) 377 { 378 __u16 __user *location; 379 int signal = 0; 380 381 location = get_trap_ip(regs); 382 383 if (MACHINE_HAS_IEEE) 384 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); 385 386 #ifdef CONFIG_MATHEMU 387 else if (user_mode(regs)) { 388 __u8 opcode[6]; 389 get_user(*((__u16 *) opcode), location); 390 switch (opcode[0]) { 391 case 0x28: /* LDR Rx,Ry */ 392 signal = math_emu_ldr(opcode); 393 break; 394 case 0x38: /* LER Rx,Ry */ 395 signal = math_emu_ler(opcode); 396 break; 397 case 0x60: /* STD R,D(X,B) */ 398 get_user(*((__u16 *) (opcode+2)), location+1); 399 signal = math_emu_std(opcode, regs); 400 break; 401 case 0x68: /* LD R,D(X,B) */ 402 get_user(*((__u16 *) (opcode+2)), location+1); 403 signal = math_emu_ld(opcode, regs); 404 break; 405 case 0x70: /* STE R,D(X,B) */ 406 get_user(*((__u16 *) (opcode+2)), location+1); 407 signal = math_emu_ste(opcode, regs); 408 break; 409 case 0x78: /* LE R,D(X,B) */ 410 get_user(*((__u16 *) (opcode+2)), location+1); 411 signal = math_emu_le(opcode, regs); 412 break; 413 case 0xb3: 414 get_user(*((__u16 *) (opcode+2)), location+1); 415 signal = math_emu_b3(opcode, regs); 416 break; 417 case 0xed: 418 get_user(*((__u32 *) (opcode+2)), 419 (__u32 __user *)(location+1)); 420 signal = math_emu_ed(opcode, regs); 421 break; 422 case 0xb2: 423 if (opcode[1] == 0x99) { 424 get_user(*((__u16 *) (opcode+2)), location+1); 425 signal = math_emu_srnm(opcode, regs); 426 } else if (opcode[1] == 0x9c) { 427 get_user(*((__u16 *) (opcode+2)), location+1); 428 signal = math_emu_stfpc(opcode, regs); 429 } else if (opcode[1] == 0x9d) { 430 get_user(*((__u16 *) (opcode+2)), location+1); 431 signal = math_emu_lfpc(opcode, regs); 432 } else 433 signal = SIGILL; 434 break; 435 default: 436 signal = SIGILL; 437 break; 438 } 439 } 440 #endif 441 #ifdef CONFIG_64BIT 442 /* Check for vector register enablement */ 443 if (MACHINE_HAS_VX && !current->thread.vxrs && 444 (current->thread.fp_regs.fpc & FPC_DXC_MASK) == 0xfe00) { 445 alloc_vector_registers(current); 446 /* Vector data exception is suppressing, rewind psw. */ 447 regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); 448 clear_pt_regs_flag(regs, PIF_PER_TRAP); 449 return; 450 } 451 #endif 452 453 if (current->thread.fp_regs.fpc & FPC_DXC_MASK) 454 signal = SIGFPE; 455 else 456 signal = SIGILL; 457 if (signal == SIGFPE) 458 do_fp_trap(regs, current->thread.fp_regs.fpc); 459 else if (signal) 460 do_trap(regs, signal, ILL_ILLOPN, "data exception"); 461 } 462 463 void space_switch_exception(struct pt_regs *regs) 464 { 465 /* Set user psw back to home space mode. */ 466 if (user_mode(regs)) 467 regs->psw.mask |= PSW_ASC_HOME; 468 /* Send SIGILL. */ 469 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); 470 } 471 472 void __kprobes kernel_stack_overflow(struct pt_regs * regs) 473 { 474 bust_spinlocks(1); 475 printk("Kernel stack overflow.\n"); 476 show_regs(regs); 477 bust_spinlocks(0); 478 panic("Corrupt kernel stack, can't continue."); 479 } 480 481 void __init trap_init(void) 482 { 483 local_mcck_enable(); 484 } 485