linux/arch/s390/kernel/traps.c
<<
>>
Prefs
   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
  25int show_unhandled_signals = 1;
  26
  27static 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
  39static 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
  54int is_valid_bugaddr(unsigned long addr)
  55{
  56        return 1;
  57}
  58
  59void 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
  86static 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}
  93NOKPROBE_SYMBOL(do_trap);
  94
  95void 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}
 110NOKPROBE_SYMBOL(do_per_trap);
 111
 112void 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) \
 122void name(struct pt_regs *regs)                 \
 123{                                               \
 124        do_trap(regs, signr, sicode, str);      \
 125}
 126
 127DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
 128              "addressing exception")
 129DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
 130              "execute exception")
 131DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
 132              "fixpoint divide exception")
 133DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
 134              "fixpoint overflow exception")
 135DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
 136              "HFP overflow exception")
 137DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
 138              "HFP underflow exception")
 139DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
 140              "HFP significance exception")
 141DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
 142              "HFP divide exception")
 143DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
 144              "HFP square root exception")
 145DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
 146              "operand exception")
 147DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
 148              "privileged operation")
 149DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
 150              "special operation exception")
 151DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
 152              "transaction constraint exception")
 153
 154static 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
 174void translation_exception(struct pt_regs *regs)
 175{
 176        /* May never happen. */
 177        panic("Translation exception");
 178}
 179
 180void 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}
 222NOKPROBE_SYMBOL(illegal_op);
 223
 224DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
 225              "specification exception");
 226
 227int 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
 252void 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
 286static 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
 293void 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
 320void 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
 329void 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}
 337NOKPROBE_SYMBOL(kernel_stack_overflow);
 338
 339void __init trap_init(void)
 340{
 341        local_mcck_enable();
 342}
 343