]> git.hungrycats.org Git - linux/commitdiff
[PATCH] i386 exceptions notifier for kprobes
authorPrasanna S. Panchamukhi <prasanna@in.ibm.com>
Tue, 31 Aug 2004 03:32:08 +0000 (20:32 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 31 Aug 2004 03:32:08 +0000 (20:32 -0700)
This patch provides notifiers for i386 architecture exceptions.  This patch
has been ported from x86_64 architecture as suggested by Andi Kleen.

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/kernel/i386_ksyms.c
arch/i386/kernel/traps.c
arch/i386/mm/fault.c
include/asm-i386/kdebug.h [new file with mode: 0644]

index 064d6fb8a094c88c140423b3e6a5865c829bace4..1d585afc326e16a3c573ae40d9834a33c5007b77 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/tlbflush.h>
 #include <asm/nmi.h>
 #include <asm/ist.h>
+#include <asm/kdebug.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern spinlock_t rtc_lock;
@@ -177,6 +178,7 @@ EXPORT_SYMBOL_GPL(unset_nmi_callback);
 extern int memcmp(const void *,const void *,__kernel_size_t);
 EXPORT_SYMBOL_NOVERS(memcmp);
 
+EXPORT_SYMBOL(register_die_notifier);
 #ifdef CONFIG_HAVE_DEC_LOCK
 EXPORT_SYMBOL(atomic_dec_and_lock);
 #endif
index b27f36d3d69319d505cb9843fd4dd38c02ad5b4e..f181c97ae0fd4a9c3bcc7fef6fd463b35f2e2bc6 100644 (file)
@@ -48,6 +48,7 @@
 
 #include <asm/smp.h>
 #include <asm/arch_hooks.h>
+#include <asm/kdebug.h>
 
 #include <linux/irq.h>
 #include <linux/module.h>
@@ -92,6 +93,18 @@ asmlinkage void spurious_interrupt_bug(void);
 asmlinkage void machine_check(void);
 
 static int kstack_depth_to_print = 24;
+struct notifier_block *i386die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+       int err = 0;
+       unsigned long flags;
+       spin_lock_irqsave(&die_notifier_lock, flags);
+       err = notifier_chain_register(&i386die_chain, nb);
+       spin_unlock_irqrestore(&die_notifier_lock, flags);
+       return err;
+}
 
 static int valid_stack_ptr(struct task_struct *task, void *p)
 {
@@ -333,6 +346,7 @@ void die(const char * str, struct pt_regs * regs, long err)
 #endif
                if (nl)
                        printk("\n");
+       notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
                show_registers(regs);
        } else
                printk(KERN_ERR "Recursive die() failure, output suppressed\n");
@@ -406,6 +420,9 @@ static inline void do_trap(int trapnr, int signr, char *str, int vm86,
 #define DO_ERROR(trapnr, signr, str, name) \
 asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
 { \
+       if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+                                               == NOTIFY_OK) \
+               return; \
        do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \
 }
 
@@ -417,12 +434,18 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
        info.si_errno = 0; \
        info.si_code = sicode; \
        info.si_addr = (void __user *)siaddr; \
+       if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+                                               == NOTIFY_BAD) \
+               return; \
        do_trap(trapnr, signr, str, 0, regs, error_code, &info); \
 }
 
 #define DO_VM86_ERROR(trapnr, signr, str, name) \
 asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
 { \
+       if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+                                               == NOTIFY_OK) \
+               return; \
        do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \
 }
 
@@ -434,6 +457,9 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
        info.si_errno = 0; \
        info.si_code = sicode; \
        info.si_addr = (void __user *)siaddr; \
+       if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+                                               == NOTIFY_OK) \
+               return; \
        do_trap(trapnr, signr, str, 1, regs, error_code, &info); \
 }
 
@@ -467,8 +493,12 @@ gp_in_vm86:
        return;
 
 gp_in_kernel:
-       if (!fixup_exception(regs))
+       if (!fixup_exception(regs)) {
+               if (notify_die(DIE_GPF, "general protection fault", regs,
+                               error_code, 13, SIGSEGV) == NOTIFY_OK);
+                       return;
                die("general protection fault", regs, error_code);
+       }
 }
 
 static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
@@ -538,6 +568,9 @@ static void default_do_nmi(struct pt_regs * regs)
        unsigned char reason = get_nmi_reason();
  
        if (!(reason & 0xc0)) {
+               if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT)
+                                                       == NOTIFY_BAD)
+                       return;
 #ifdef CONFIG_X86_LOCAL_APIC
                /*
                 * Ok, so this is none of the documented NMI sources,
@@ -551,6 +584,8 @@ static void default_do_nmi(struct pt_regs * regs)
                unknown_nmi_error(reason, regs);
                return;
        }
+       if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_BAD)
+               return;
        if (reason & 0x80)
                mem_parity_error(reason, regs);
        if (reason & 0x40)
@@ -624,6 +659,9 @@ asmlinkage void do_debug(struct pt_regs * regs, long error_code)
 
        __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
 
+       if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
+                                       SIGTRAP) == NOTIFY_OK)
+               return;
        /* It's safe to allow irq's after DR6 has been saved */
        if (regs->eflags & X86_EFLAGS_IF)
                local_irq_enable();
index 7c1bc5dc89ac34af10c57e0b4282baf3ed942e5b..f6599b30d46e8e8e0835bc1d708eb9e398849de6 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
 #include <asm/desc.h>
+#include <asm/kdebug.h>
 
 extern void die(const char *,struct pt_regs *,long);
 
@@ -226,6 +227,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
        /* get the address */
        __asm__("movl %%cr2,%0":"=r" (address));
 
+       if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+                                       SIGSEGV) == NOTIFY_OK)
+               return;
        /* It's safe to allow irq's after cr2 has been saved */
        if (regs->eflags & (X86_EFLAGS_IF|VM_MASK))
                local_irq_enable();
diff --git a/include/asm-i386/kdebug.h b/include/asm-i386/kdebug.h
new file mode 100644 (file)
index 0000000..de6498b
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _I386_KDEBUG_H
+#define _I386_KDEBUG_H 1
+
+/*
+ * Aug-05 2004 Ported by Prasanna S Panchamukhi <prasanna@in.ibm.com>
+ * from x86_64 architecture.
+ */
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+       struct pt_regs *regs;
+       const char *str;
+       long err;
+       int trapnr;
+       int signr;
+};
+
+/* Note - you should never unregister because that can race with NMIs.
+   If you really want to do it first unregister - then synchronize_kernel - then free.
+  */
+int register_die_notifier(struct notifier_block *nb);
+extern struct notifier_block *i386die_chain;
+
+
+/* Grossly misnamed. */
+enum die_val {
+       DIE_OOPS = 1,
+       DIE_INT3,
+       DIE_DEBUG,
+       DIE_PANIC,
+       DIE_NMI,
+       DIE_DIE,
+       DIE_NMIWATCHDOG,
+       DIE_KERNELDEBUG,
+       DIE_TRAP,
+       DIE_GPF,
+       DIE_CALL,
+       DIE_NMI_IPI,
+       DIE_PAGE_FAULT,
+};
+
+static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig)
+{
+       struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig };
+       return notifier_call_chain(&i386die_chain, val, &args);
+}
+
+#endif