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 "entry.h"
  22
  23int show_unhandled_signals = 1;
  24
  25static 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
  42static 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
  56int is_valid_bugaddr(unsigned long addr)
  57{
  58        return 1;
  59}
  60
  61static 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
  93void __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
 109void 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) \
 119void name(struct pt_regs *regs)                 \
 120{                                               \
 121        do_trap(regs, signr, sicode, str);      \
 122}
 123
 124DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
 125              "addressing exception")
 126DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
 127              "execute exception")
 128DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
 129              "fixpoint divide exception")
 130DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
 131              "fixpoint overflow exception")
 132DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
 133              "HFP overflow exception")
 134DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
 135              "HFP underflow exception")
 136DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
 137              "HFP significance exception")
 138DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
 139              "HFP divide exception")
 140DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
 141              "HFP square root exception")
 142DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
 143              "operand exception")
 144DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
 145              "privileged operation")
 146DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
 147              "special operation exception")
 148DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
 149              "translation exception")
 150
 151#ifdef CONFIG_64BIT
 152DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
 153              "transaction constraint exception")
 154#endif
 155
 156static 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
 176void __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
 245void 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
 291DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
 292              "specification exception");
 293#endif
 294
 295void 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
 370void 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
 379void __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
 388void __init trap_init(void)
 389{
 390        local_mcck_enable();
 391}
 392