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