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/fpu/api.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 unsigned long address; 30 31 if (regs->int_code & 0x200) 32 address = *(unsigned long *)(current->thread.trap_tdb + 24); 33 else 34 address = regs->psw.addr; 35 return (void __user *) (address - (regs->int_code >> 16)); 36 } 37 38 static inline void report_user_fault(struct pt_regs *regs, int signr) 39 { 40 if ((task_pid_nr(current) > 1) && !show_unhandled_signals) 41 return; 42 if (!unhandled_signal(current, signr)) 43 return; 44 if (!printk_ratelimit()) 45 return; 46 printk("User process fault: interruption code %04x ilc:%d ", 47 regs->int_code & 0xffff, regs->int_code >> 17); 48 print_vma_addr("in ", regs->psw.addr); 49 printk("\n"); 50 show_regs(regs); 51 } 52 53 int is_valid_bugaddr(unsigned long addr) 54 { 55 return 1; 56 } 57 58 void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) 59 { 60 siginfo_t info; 61 62 if (user_mode(regs)) { 63 info.si_signo = si_signo; 64 info.si_errno = 0; 65 info.si_code = si_code; 66 info.si_addr = get_trap_ip(regs); 67 force_sig_info(si_signo, &info, current); 68 report_user_fault(regs, si_signo); 69 } else { 70 const struct exception_table_entry *fixup; 71 fixup = search_exception_tables(regs->psw.addr); 72 if (fixup) 73 regs->psw.addr = extable_fixup(fixup); 74 else { 75 enum bug_trap_type btt; 76 77 btt = report_bug(regs->psw.addr, regs); 78 if (btt == BUG_TRAP_TYPE_WARN) 79 return; 80 die(regs, str); 81 } 82 } 83 } 84 85 static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) 86 { 87 if (notify_die(DIE_TRAP, str, regs, 0, 88 regs->int_code, si_signo) == NOTIFY_STOP) 89 return; 90 do_report_trap(regs, si_signo, si_code, str); 91 } 92 NOKPROBE_SYMBOL(do_trap); 93 94 void do_per_trap(struct pt_regs *regs) 95 { 96 siginfo_t info; 97 98 if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) 99 return; 100 if (!current->ptrace) 101 return; 102 info.si_signo = SIGTRAP; 103 info.si_errno = 0; 104 info.si_code = TRAP_HWBKPT; 105 info.si_addr = 106 (void __force __user *) current->thread.per_event.address; 107 force_sig_info(SIGTRAP, &info, current); 108 } 109 NOKPROBE_SYMBOL(do_per_trap); 110 111 void default_trap_handler(struct pt_regs *regs) 112 { 113 if (user_mode(regs)) { 114 report_user_fault(regs, SIGSEGV); 115 do_exit(SIGSEGV); 116 } else 117 die(regs, "Unknown program exception"); 118 } 119 120 #define DO_ERROR_INFO(name, signr, sicode, str) \ 121 void name(struct pt_regs *regs) \ 122 { \ 123 do_trap(regs, signr, sicode, str); \ 124 } 125 126 DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, 127 "addressing exception") 128 DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, 129 "execute exception") 130 DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, 131 "fixpoint divide exception") 132 DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, 133 "fixpoint overflow exception") 134 DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, 135 "HFP overflow exception") 136 DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, 137 "HFP underflow exception") 138 DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, 139 "HFP significance exception") 140 DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, 141 "HFP divide exception") 142 DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, 143 "HFP square root exception") 144 DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, 145 "operand exception") 146 DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, 147 "privileged operation") 148 DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, 149 "special operation exception") 150 DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, 151 "transaction constraint exception") 152 153 static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc) 154 { 155 int si_code = 0; 156 /* FPC[2] is Data Exception Code */ 157 if ((fpc & 0x00000300) == 0) { 158 /* bits 6 and 7 of DXC are 0 iff IEEE exception */ 159 if (fpc & 0x8000) /* invalid fp operation */ 160 si_code = FPE_FLTINV; 161 else if (fpc & 0x4000) /* div by 0 */ 162 si_code = FPE_FLTDIV; 163 else if (fpc & 0x2000) /* overflow */ 164 si_code = FPE_FLTOVF; 165 else if (fpc & 0x1000) /* underflow */ 166 si_code = FPE_FLTUND; 167 else if (fpc & 0x0800) /* inexact */ 168 si_code = FPE_FLTRES; 169 } 170 do_trap(regs, SIGFPE, si_code, "floating point exception"); 171 } 172 173 void translation_exception(struct pt_regs *regs) 174 { 175 /* May never happen. */ 176 panic("Translation exception"); 177 } 178 179 void illegal_op(struct pt_regs *regs) 180 { 181 siginfo_t info; 182 __u8 opcode[6]; 183 __u16 __user *location; 184 int is_uprobe_insn = 0; 185 int signal = 0; 186 187 location = get_trap_ip(regs); 188 189 if (user_mode(regs)) { 190 if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) 191 return; 192 if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { 193 if (current->ptrace) { 194 info.si_signo = SIGTRAP; 195 info.si_errno = 0; 196 info.si_code = TRAP_BRKPT; 197 info.si_addr = location; 198 force_sig_info(SIGTRAP, &info, current); 199 } else 200 signal = SIGILL; 201 #ifdef CONFIG_UPROBES 202 } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { 203 is_uprobe_insn = 1; 204 #endif 205 } else 206 signal = SIGILL; 207 } 208 /* 209 * We got either an illegal op in kernel mode, or user space trapped 210 * on a uprobes illegal instruction. See if kprobes or uprobes picks 211 * it up. If not, SIGILL. 212 */ 213 if (is_uprobe_insn || !user_mode(regs)) { 214 if (notify_die(DIE_BPT, "bpt", regs, 0, 215 3, SIGTRAP) != NOTIFY_STOP) 216 signal = SIGILL; 217 } 218 if (signal) 219 do_trap(regs, signal, ILL_ILLOPC, "illegal operation"); 220 } 221 NOKPROBE_SYMBOL(illegal_op); 222 223 DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, 224 "specification exception"); 225 226 void vector_exception(struct pt_regs *regs) 227 { 228 int si_code, vic; 229 230 if (!MACHINE_HAS_VX) { 231 do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation"); 232 return; 233 } 234 235 /* get vector interrupt code from fpc */ 236 save_fpu_regs(); 237 vic = (current->thread.fpu.fpc & 0xf00) >> 8; 238 switch (vic) { 239 case 1: /* invalid vector operation */ 240 si_code = FPE_FLTINV; 241 break; 242 case 2: /* division by zero */ 243 si_code = FPE_FLTDIV; 244 break; 245 case 3: /* overflow */ 246 si_code = FPE_FLTOVF; 247 break; 248 case 4: /* underflow */ 249 si_code = FPE_FLTUND; 250 break; 251 case 5: /* inexact */ 252 si_code = FPE_FLTRES; 253 break; 254 default: /* unknown cause */ 255 si_code = 0; 256 } 257 do_trap(regs, SIGFPE, si_code, "vector exception"); 258 } 259 260 void data_exception(struct pt_regs *regs) 261 { 262 int signal = 0; 263 264 save_fpu_regs(); 265 if (current->thread.fpu.fpc & FPC_DXC_MASK) 266 signal = SIGFPE; 267 else 268 signal = SIGILL; 269 if (signal == SIGFPE) 270 do_fp_trap(regs, current->thread.fpu.fpc); 271 else if (signal) 272 do_trap(regs, signal, ILL_ILLOPN, "data exception"); 273 } 274 275 void space_switch_exception(struct pt_regs *regs) 276 { 277 /* Set user psw back to home space mode. */ 278 if (user_mode(regs)) 279 regs->psw.mask |= PSW_ASC_HOME; 280 /* Send SIGILL. */ 281 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); 282 } 283 284 void kernel_stack_overflow(struct pt_regs *regs) 285 { 286 bust_spinlocks(1); 287 printk("Kernel stack overflow.\n"); 288 show_regs(regs); 289 bust_spinlocks(0); 290 panic("Corrupt kernel stack, can't continue."); 291 } 292 NOKPROBE_SYMBOL(kernel_stack_overflow); 293 294 void __init trap_init(void) 295 { 296 local_mcck_enable(); 297 } 298