Merge ../linux-2.6-watchdog-mm
[linux-drm-fsl-dcu.git] / arch / s390 / kernel / traps.c
index bde1d1d598586cc483bcb84ca0211008d3a70711..92ecffbc8d8233352589b700f5eefa4f165fe832 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/kallsyms.h>
 #include <linux/reboot.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -39,6 +40,7 @@
 #include <asm/s390_ext.h>
 #include <asm/lowcore.h>
 #include <asm/debug.h>
+#include <asm/kdebug.h>
 
 /* Called from entry.S only */
 extern void handle_per_exception(struct pt_regs *regs);
@@ -59,7 +61,7 @@ extern pgm_check_handler_t do_dat_exception;
 #ifdef CONFIG_PFAULT
 extern int pfault_init(void);
 extern void pfault_fini(void);
-extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
+extern void pfault_interrupt(__u16 error_code);
 static ext_int_info_t ext_int_pfault;
 #endif
 extern pgm_check_handler_t do_monitor_call;
@@ -74,6 +76,20 @@ static int kstack_depth_to_print = 12;
 static int kstack_depth_to_print = 20;
 #endif /* CONFIG_64BIT */
 
+ATOMIC_NOTIFIER_HEAD(s390die_chain);
+
+int register_die_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&s390die_chain, nb);
+}
+EXPORT_SYMBOL(register_die_notifier);
+
+int unregister_die_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&s390die_chain, nb);
+}
+EXPORT_SYMBOL(unregister_die_notifier);
+
 /*
  * For show_trace we have tree different stack to consider:
  *   - the panic stack which is used if the kernel stack has overflown
@@ -305,8 +321,9 @@ report_user_fault(long interruption_code, struct pt_regs *regs)
 #endif
 }
 
-static void inline do_trap(long interruption_code, int signr, char *str,
-                           struct pt_regs *regs, siginfo_t *info)
+static void __kprobes inline do_trap(long interruption_code, int signr,
+                                       char *str, struct pt_regs *regs,
+                                       siginfo_t *info)
 {
        /*
         * We got all needed information from the lowcore and can
@@ -315,6 +332,10 @@ static void inline do_trap(long interruption_code, int signr, char *str,
         if (regs->psw.mask & PSW_MASK_PSTATE)
                local_irq_enable();
 
+       if (notify_die(DIE_TRAP, str, regs, interruption_code,
+                               interruption_code, signr) == NOTIFY_STOP)
+               return;
+
         if (regs->psw.mask & PSW_MASK_PSTATE) {
                 struct task_struct *tsk = current;
 
@@ -336,8 +357,12 @@ static inline void __user *get_check_address(struct pt_regs *regs)
        return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
 }
 
-void do_single_step(struct pt_regs *regs)
+void __kprobes do_single_step(struct pt_regs *regs)
 {
+       if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0,
+                                       SIGTRAP) == NOTIFY_STOP){
+               return;
+       }
        if ((current->ptrace & PT_PTRACED) != 0)
                force_sig(SIGTRAP, current);
 }
@@ -437,7 +462,8 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
                local_irq_enable();
 
        if (regs->psw.mask & PSW_MASK_PSTATE) {
-               get_user(*((__u16 *) opcode), (__u16 __user *) location);
+               if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
+                       return;
                if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
                        if (current->ptrace & PT_PTRACED)
                                force_sig(SIGTRAP, current);
@@ -445,20 +471,25 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
                                signal = SIGILL;
 #ifdef CONFIG_MATHEMU
                } else if (opcode[0] == 0xb3) {
-                       get_user(*((__u16 *) (opcode+2)), location+1);
+                       if (get_user(*((__u16 *) (opcode+2)), location+1))
+                               return;
                        signal = math_emu_b3(opcode, regs);
                 } else if (opcode[0] == 0xed) {
-                       get_user(*((__u32 *) (opcode+2)),
-                                (__u32 *)(location+1));
+                       if (get_user(*((__u32 *) (opcode+2)),
+                                    (__u32 __user *)(location+1)))
+                               return;
                        signal = math_emu_ed(opcode, regs);
                } else if (*((__u16 *) opcode) == 0xb299) {
-                       get_user(*((__u16 *) (opcode+2)), location+1);
+                       if (get_user(*((__u16 *) (opcode+2)), location+1))
+                               return;
                        signal = math_emu_srnm(opcode, regs);
                } else if (*((__u16 *) opcode) == 0xb29c) {
-                       get_user(*((__u16 *) (opcode+2)), location+1);
+                       if (get_user(*((__u16 *) (opcode+2)), location+1))
+                               return;
                        signal = math_emu_stfpc(opcode, regs);
                } else if (*((__u16 *) opcode) == 0xb29d) {
-                       get_user(*((__u16 *) (opcode+2)), location+1);
+                       if (get_user(*((__u16 *) (opcode+2)), location+1))
+                               return;
                        signal = math_emu_lfpc(opcode, regs);
 #endif
                } else
@@ -474,7 +505,7 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
                info.si_signo = signal;
                info.si_errno = 0;
                info.si_code = SEGV_MAPERR;
-               info.si_addr = (void *) location;
+               info.si_addr = (void __user *) location;
                do_trap(interruption_code, signal,
                        "user address fault", regs, &info);
        } else
@@ -495,10 +526,10 @@ asmlinkage void
 specification_exception(struct pt_regs * regs, long interruption_code)
 {
         __u8 opcode[6];
-       __u16 *location = NULL;
+       __u16 __user *location = NULL;
        int signal = 0;
 
-       location = (__u16 *) get_check_address(regs);
+       location = (__u16 __user *) get_check_address(regs);
 
        /*
         * We got all needed information from the lowcore and can
@@ -572,8 +603,7 @@ asmlinkage void data_exception(struct pt_regs * regs, long interruption_code)
                local_irq_enable();
 
        if (MACHINE_HAS_IEEE)
-               __asm__ volatile ("stfpc %0\n\t" 
-                                 : "=m" (current->thread.fp_regs.fpc));
+               asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
 
 #ifdef CONFIG_MATHEMU
         else if (regs->psw.mask & PSW_MASK_PSTATE) {
@@ -608,7 +638,7 @@ asmlinkage void data_exception(struct pt_regs * regs, long interruption_code)
                        break;
                 case 0xed:
                        get_user(*((__u32 *) (opcode+2)),
-                                (__u32 *)(location+1));
+                                (__u32 __user *)(location+1));
                        signal = math_emu_ed(opcode, regs);
                        break;
                case 0xb2: