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 "entry.h" 22 23 int show_unhandled_signals = 1; 24 25 static inline void __user *get_trap_ip(struct pt_regs *regs) 26 { 27 #ifdef CONFIG_64BIT 28 unsigned long address; 29 30 if (regs->int_code & 0x200) 31 address = *(unsigned long *)(current->thread.trap_tdb + 24); 32 else 33 address = regs->psw.addr; 34 return (void __user *) 35 ((address - (regs->int_code >> 16)) & PSW_ADDR_INSN); 36 #else 37 return (void __user *) 38 ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); 39 #endif 40 } 41 42 static inline void report_user_fault(struct pt_regs *regs, int signr) 43 { 44 if ((task_pid_nr(current) > 1) && !show_unhandled_signals) 45 return; 46 if (!unhandled_signal(current, signr)) 47 return; 48 if (!printk_ratelimit()) 49 return; 50 printk("User process fault: interruption code 0x%X ", regs->int_code); 51 print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN); 52 printk("\n"); 53 show_regs(regs); 54 } 55 56 int is_valid_bugaddr(unsigned long addr) 57 { 58 return 1; 59 } 60 61 static void __kprobes do_trap(struct pt_regs *regs, 62 int si_signo, int si_code, char *str) 63 { 64 siginfo_t info; 65 66 if (notify_die(DIE_TRAP, str, regs, 0, 67 regs->int_code, si_signo) == NOTIFY_STOP) 68 return; 69 70 if (user_mode(regs)) { 71 info.si_signo = si_signo; 72 info.si_errno = 0; 73 info.si_code = si_code; 74 info.si_addr = get_trap_ip(regs); 75 force_sig_info(si_signo, &info, current); 76 report_user_fault(regs, si_signo); 77 } else { 78 const struct exception_table_entry *fixup; 79 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); 80 if (fixup) 81 regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE; 82 else { 83 enum bug_trap_type btt; 84 85 btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); 86 if (btt == BUG_TRAP_TYPE_WARN) 87 return; 88 die(regs, str); 89 } 90 } 91 } 92 93 void __kprobes do_per_trap(struct pt_regs *regs) 94 { 95 siginfo_t info; 96 97 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) 98 return; 99 if (!current->ptrace) 100 return; 101 info.si_signo = SIGTRAP; 102 info.si_errno = 0; 103 info.si_code = TRAP_HWBKPT; 104 info.si_addr = 105 (void __force __user *) current->thread.per_event.address; 106 force_sig_info(SIGTRAP, &info, current); 107 } 108 109 void default_trap_handler(struct pt_regs *regs) 110 { 111 if (user_mode(regs)) { 112 report_user_fault(regs, SIGSEGV); 113 do_exit(SIGSEGV); 114 } else 115 die(regs, "Unknown program exception"); 116 } 117 118 #define DO_ERROR_INFO(name, signr, sicode, str) \ 119 void name(struct pt_regs *regs) \ 120 { \ 121 do_trap(regs, signr, sicode, str); \ 122 } 123 124 DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, 125 "addressing exception") 126 DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, 127 "execute exception") 128 DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, 129 "fixpoint divide exception") 130 DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, 131 "fixpoint overflow exception") 132 DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, 133 "HFP overflow exception") 134 DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, 135 "HFP underflow exception") 136 DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, 137 "HFP significance exception") 138 DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, 139 "HFP divide exception") 140 DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, 141 "HFP square root exception") 142 DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, 143 "operand exception") 144 DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, 145 "privileged operation") 146 DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, 147 "special operation exception") 148 DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, 149 "translation exception") 150 151 #ifdef CONFIG_64BIT 152 DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, 153 "transaction constraint exception") 154 #endif 155 156 static inline void do_fp_trap(struct pt_regs *regs, int fpc) 157 { 158 int si_code = 0; 159 /* FPC[2] is Data Exception Code */ 160 if ((fpc & 0x00000300) == 0) { 161 /* bits 6 and 7 of DXC are 0 iff IEEE exception */ 162 if (fpc & 0x8000) /* invalid fp operation */ 163 si_code = FPE_FLTINV; 164 else if (fpc & 0x4000) /* div by 0 */ 165 si_code = FPE_FLTDIV; 166 else if (fpc & 0x2000) /* overflow */ 167 si_code = FPE_FLTOVF; 168 else if (fpc & 0x1000) /* underflow */ 169 si_code = FPE_FLTUND; 170 else if (fpc & 0x0800) /* inexact */ 171 si_code = FPE_FLTRES; 172 } 173 do_trap(regs, SIGFPE, si_code, "floating point exception"); 174 } 175 176 void __kprobes illegal_op(struct pt_regs *regs) 177 { 178 siginfo_t info; 179 __u8 opcode[6]; 180 __u16 __user *location; 181 int signal = 0; 182 183 location = get_trap_ip(regs); 184 185 if (user_mode(regs)) { 186 if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) 187 return; 188 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { 189 if (current->ptrace) { 190 info.si_signo = SIGTRAP; 191 info.si_errno = 0; 192 info.si_code = TRAP_BRKPT; 193 info.si_addr = location; 194 force_sig_info(SIGTRAP, &info, current); 195 } else 196 signal = SIGILL; 197 #ifdef CONFIG_MATHEMU 198 } else if (opcode[0] == 0xb3) { 199 if (get_user(*((__u16 *) (opcode+2)), location+1)) 200 return; 201 signal = math_emu_b3(opcode, regs); 202 } else if (opcode[0] == 0xed) { 203 if (get_user(*((__u32 *) (opcode+2)), 204 (__u32 __user *)(location+1))) 205 return; 206 signal = math_emu_ed(opcode, regs); 207 } else if (*((__u16 *) opcode) == 0xb299) { 208 if (get_user(*((__u16 *) (opcode+2)), location+1)) 209 return; 210 signal = math_emu_srnm(opcode, regs); 211 } else if (*((__u16 *) opcode) == 0xb29c) { 212 if (get_user(*((__u16 *) (opcode+2)), location+1)) 213 return; 214 signal = math_emu_stfpc(opcode, regs); 215 } else if (*((__u16 *) opcode) == 0xb29d) { 216 if (get_user(*((__u16 *) (opcode+2)), location+1)) 217 return; 218 signal = math_emu_lfpc(opcode, regs); 219 #endif 220 } else 221 signal = SIGILL; 222 } else { 223 /* 224 * If we get an illegal op in kernel mode, send it through the 225 * kprobes notifier. If kprobes doesn't pick it up, SIGILL 226 */ 227 if (notify_die(DIE_BPT, "bpt", regs, 0, 228 3, SIGTRAP) != NOTIFY_STOP) 229 signal = SIGILL; 230 } 231 232 #ifdef CONFIG_MATHEMU 233 if (signal == SIGFPE) 234 do_fp_trap(regs, current->thread.fp_regs.fpc); 235 else if (signal == SIGSEGV) 236 do_trap(regs, signal, SEGV_MAPERR, "user address fault"); 237 else 238 #endif 239 if (signal) 240 do_trap(regs, signal, ILL_ILLOPC, "illegal operation"); 241 } 242 243 244 #ifdef CONFIG_MATHEMU 245 void specification_exception(struct pt_regs *regs) 246 { 247 __u8 opcode[6]; 248 __u16 __user *location = NULL; 249 int signal = 0; 250 251 location = (__u16 __user *) get_trap_ip(regs); 252 253 if (user_mode(regs)) { 254 get_user(*((__u16 *) opcode), location); 255 switch (opcode[0]) { 256 case 0x28: /* LDR Rx,Ry */ 257 signal = math_emu_ldr(opcode); 258 break; 259 case 0x38: /* LER Rx,Ry */ 260 signal = math_emu_ler(opcode); 261 break; 262 case 0x60: /* STD R,D(X,B) */ 263 get_user(*((__u16 *) (opcode+2)), location+1); 264 signal = math_emu_std(opcode, regs); 265 break; 266 case 0x68: /* LD R,D(X,B) */ 267 get_user(*((__u16 *) (opcode+2)), location+1); 268 signal = math_emu_ld(opcode, regs); 269 break; 270 case 0x70: /* STE R,D(X,B) */ 271 get_user(*((__u16 *) (opcode+2)), location+1); 272 signal = math_emu_ste(opcode, regs); 273 break; 274 case 0x78: /* LE R,D(X,B) */ 275 get_user(*((__u16 *) (opcode+2)), location+1); 276 signal = math_emu_le(opcode, regs); 277 break; 278 default: 279 signal = SIGILL; 280 break; 281 } 282 } else 283 signal = SIGILL; 284 285 if (signal == SIGFPE) 286 do_fp_trap(regs, current->thread.fp_regs.fpc); 287 else if (signal) 288 do_trap(regs, signal, ILL_ILLOPN, "specification exception"); 289 } 290 #else 291 DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, 292 "specification exception"); 293 #endif 294 295 void data_exception(struct pt_regs *regs) 296 { 297 __u16 __user *location; 298 int signal = 0; 299 300 location = get_trap_ip(regs); 301 302 if (MACHINE_HAS_IEEE) 303 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); 304 305 #ifdef CONFIG_MATHEMU 306 else if (user_mode(regs)) { 307 __u8 opcode[6]; 308 get_user(*((__u16 *) opcode), location); 309 switch (opcode[0]) { 310 case 0x28: /* LDR Rx,Ry */ 311 signal = math_emu_ldr(opcode); 312 break; 313 case 0x38: /* LER Rx,Ry */ 314 signal = math_emu_ler(opcode); 315 break; 316 case 0x60: /* STD R,D(X,B) */ 317 get_user(*((__u16 *) (opcode+2)), location+1); 318 signal = math_emu_std(opcode, regs); 319 break; 320 case 0x68: /* LD R,D(X,B) */ 321 get_user(*((__u16 *) (opcode+2)), location+1); 322 signal = math_emu_ld(opcode, regs); 323 break; 324 case 0x70: /* STE R,D(X,B) */ 325 get_user(*((__u16 *) (opcode+2)), location+1); 326 signal = math_emu_ste(opcode, regs); 327 break; 328 case 0x78: /* LE R,D(X,B) */ 329 get_user(*((__u16 *) (opcode+2)), location+1); 330 signal = math_emu_le(opcode, regs); 331 break; 332 case 0xb3: 333 get_user(*((__u16 *) (opcode+2)), location+1); 334 signal = math_emu_b3(opcode, regs); 335 break; 336 case 0xed: 337 get_user(*((__u32 *) (opcode+2)), 338 (__u32 __user *)(location+1)); 339 signal = math_emu_ed(opcode, regs); 340 break; 341 case 0xb2: 342 if (opcode[1] == 0x99) { 343 get_user(*((__u16 *) (opcode+2)), location+1); 344 signal = math_emu_srnm(opcode, regs); 345 } else if (opcode[1] == 0x9c) { 346 get_user(*((__u16 *) (opcode+2)), location+1); 347 signal = math_emu_stfpc(opcode, regs); 348 } else if (opcode[1] == 0x9d) { 349 get_user(*((__u16 *) (opcode+2)), location+1); 350 signal = math_emu_lfpc(opcode, regs); 351 } else 352 signal = SIGILL; 353 break; 354 default: 355 signal = SIGILL; 356 break; 357 } 358 } 359 #endif 360 if (current->thread.fp_regs.fpc & FPC_DXC_MASK) 361 signal = SIGFPE; 362 else 363 signal = SIGILL; 364 if (signal == SIGFPE) 365 do_fp_trap(regs, current->thread.fp_regs.fpc); 366 else if (signal) 367 do_trap(regs, signal, ILL_ILLOPN, "data exception"); 368 } 369 370 void space_switch_exception(struct pt_regs *regs) 371 { 372 /* Set user psw back to home space mode. */ 373 if (user_mode(regs)) 374 regs->psw.mask |= PSW_ASC_HOME; 375 /* Send SIGILL. */ 376 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); 377 } 378 379 void __kprobes kernel_stack_overflow(struct pt_regs * regs) 380 { 381 bust_spinlocks(1); 382 printk("Kernel stack overflow.\n"); 383 show_regs(regs); 384 bust_spinlocks(0); 385 panic("Corrupt kernel stack, can't continue."); 386 } 387 388 void __init trap_init(void) 389 { 390 local_mcck_enable(); 391 } 392