]> err.no Git - linux-2.6/blobdiff - arch/i386/kernel/traps.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / arch / i386 / kernel / traps.c
index e4d4e2162c7a3873d36f5d0e64c451c96852889b..09a58cb6daa7050e018496a127458241f2fa0e30 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/ptrace.h>
 #include <linux/utsname.h>
 #include <linux/kprobes.h>
+#include <linux/kexec.h>
 
 #ifdef CONFIG_EISA
 #include <linux/ioport.h>
@@ -209,7 +210,7 @@ void show_registers(struct pt_regs *regs)
        unsigned short ss;
 
        esp = (unsigned long) (&regs->esp);
-       ss = __KERNEL_DS;
+       savesegment(ss, ss);
        if (user_mode(regs)) {
                in_kernel = 0;
                esp = regs->esp;
@@ -234,22 +235,22 @@ void show_registers(struct pt_regs *regs)
         * time of the fault..
         */
        if (in_kernel) {
-               u8 *eip;
+               u8 __user *eip;
 
                printk("\nStack: ");
                show_stack(NULL, (unsigned long*)esp);
 
                printk("Code: ");
 
-               eip = (u8 *)regs->eip - 43;
+               eip = (u8 __user *)regs->eip - 43;
                for (i = 0; i < 64; i++, eip++) {
                        unsigned char c;
 
-                       if (eip < (u8 *)PAGE_OFFSET || __get_user(c, eip)) {
+                       if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) {
                                printk(" Bad EIP value.");
                                break;
                        }
-                       if (eip == (u8 *)regs->eip)
+                       if (eip == (u8 __user *)regs->eip)
                                printk("<%02x> ", c);
                        else
                                printk("%02x ", c);
@@ -266,20 +267,17 @@ static void handle_BUG(struct pt_regs *regs)
        char c;
        unsigned long eip;
 
-       if (user_mode(regs))
-               goto no_bug;            /* Not in kernel */
-
        eip = regs->eip;
 
        if (eip < PAGE_OFFSET)
                goto no_bug;
-       if (__get_user(ud2, (unsigned short *)eip))
+       if (__get_user(ud2, (unsigned short __user *)eip))
                goto no_bug;
        if (ud2 != 0x0b0f)
                goto no_bug;
-       if (__get_user(line, (unsigned short *)(eip + 2)))
+       if (__get_user(line, (unsigned short __user *)(eip + 2)))
                goto bug;
-       if (__get_user(file, (char **)(eip + 4)) ||
+       if (__get_user(file, (char * __user *)(eip + 4)) ||
                (unsigned long)file < PAGE_OFFSET || __get_user(c, file))
                file = "<bad filename>";
 
@@ -294,6 +292,9 @@ bug:
        printk("Kernel BUG\n");
 }
 
+/* This is gone through when something in the kernel
+ * has done something bad and is about to be terminated.
+*/
 void die(const char * str, struct pt_regs * regs, long err)
 {
        static struct {
@@ -341,6 +342,10 @@ void die(const char * str, struct pt_regs * regs, long err)
        bust_spinlocks(0);
        die.lock_owner = -1;
        spin_unlock_irq(&die.lock);
+
+       if (kexec_should_crash(current))
+               crash_kexec(regs);
+
        if (in_interrupt())
                panic("Fatal exception in interrupt");
 
@@ -358,9 +363,14 @@ static inline void die_if_kernel(const char * str, struct pt_regs * regs, long e
                die(str, regs, err);
 }
 
-static void do_trap(int trapnr, int signr, char *str, int vm86,
-                          struct pt_regs * regs, long error_code, siginfo_t *info)
+static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86,
+                             struct pt_regs * regs, long error_code,
+                             siginfo_t *info)
 {
+       struct task_struct *tsk = current;
+       tsk->thread.error_code = error_code;
+       tsk->thread.trap_no = trapnr;
+
        if (regs->eflags & VM_MASK) {
                if (vm86)
                        goto vm86_trap;
@@ -371,9 +381,6 @@ static void do_trap(int trapnr, int signr, char *str, int vm86,
                goto kernel_trap;
 
        trap_signal: {
-               struct task_struct *tsk = current;
-               tsk->thread.error_code = error_code;
-               tsk->thread.trap_no = trapnr;
                if (info)
                        force_sig_info(signr, info, tsk);
                else
@@ -454,7 +461,8 @@ DO_ERROR(12, SIGBUS,  "stack segment", stack_segment)
 DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
 DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0)
 
-fastcall void do_general_protection(struct pt_regs * regs, long error_code)
+fastcall void __kprobes do_general_protection(struct pt_regs * regs,
+                                             long error_code)
 {
        int cpu = get_cpu();
        struct tss_struct *tss = &per_cpu(init_tss, cpu);
@@ -486,6 +494,9 @@ fastcall void do_general_protection(struct pt_regs * regs, long error_code)
        }
        put_cpu();
 
+       current->thread.error_code = error_code;
+       current->thread.trap_no = 13;
+
        if (regs->eflags & VM_MASK)
                goto gp_in_vm86;
 
@@ -556,6 +567,10 @@ static DEFINE_SPINLOCK(nmi_print_lock);
 
 void die_nmi (struct pt_regs *regs, const char *msg)
 {
+       if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 0, SIGINT) ==
+           NOTIFY_STOP)
+               return;
+
        spin_lock(&nmi_print_lock);
        /*
        * We are in trouble anyway, lets at least try
@@ -570,6 +585,15 @@ void die_nmi (struct pt_regs *regs, const char *msg)
        console_silent();
        spin_unlock(&nmi_print_lock);
        bust_spinlocks(0);
+
+       /* If we are in kernel we are probably nested up pretty bad
+        * and might aswell get out now while we still can.
+       */
+       if (!user_mode(regs)) {
+               current->thread.trap_no = 2;
+               crash_kexec(regs);
+       }
+
        do_exit(SIGSEGV);
 }
 
@@ -625,9 +649,17 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
        nmi_enter();
 
        cpu = smp_processor_id();
+
+#ifdef CONFIG_HOTPLUG_CPU
+       if (!cpu_online(cpu)) {
+               nmi_exit();
+               return;
+       }
+#endif
+
        ++nmi_count(cpu);
 
-       if (!nmi_callback(regs, cpu))
+       if (!rcu_dereference(nmi_callback)(regs, cpu))
                default_do_nmi(regs);
 
        nmi_exit();
@@ -635,7 +667,7 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
 
 void set_nmi_callback(nmi_callback_t callback)
 {
-       nmi_callback = callback;
+       rcu_assign_pointer(nmi_callback, callback);
 }
 EXPORT_SYMBOL_GPL(set_nmi_callback);
 
@@ -646,7 +678,7 @@ void unset_nmi_callback(void)
 EXPORT_SYMBOL_GPL(unset_nmi_callback);
 
 #ifdef CONFIG_KPROBES
-fastcall void do_int3(struct pt_regs *regs, long error_code)
+fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
 {
        if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
                        == NOTIFY_STOP)
@@ -680,7 +712,7 @@ fastcall void do_int3(struct pt_regs *regs, long error_code)
  * find every occurrence of the TF bit that could be saved away even
  * by user code)
  */
-fastcall void do_debug(struct pt_regs * regs, long error_code)
+fastcall void __kprobes do_debug(struct pt_regs * regs, long error_code)
 {
        unsigned int condition;
        struct task_struct *tsk = current;
@@ -774,15 +806,17 @@ void math_error(void __user *eip)
         */
        cwd = get_fpu_cwd(task);
        swd = get_fpu_swd(task);
-       switch (((~cwd) & swd & 0x3f) | (swd & 0x240)) {
+       switch (swd & ~cwd & 0x3f) {
                case 0x000:
                default:
                        break;
                case 0x001: /* Invalid Op */
-               case 0x041: /* Stack Fault */
-               case 0x241: /* Stack Fault | Direction */
+                       /*
+                        * swd & 0x240 == 0x040: Stack Underflow
+                        * swd & 0x240 == 0x240: Stack Overflow
+                        * User must clear the SF bit (0x40) if set
+                        */
                        info.si_code = FPE_FLTINV;
-                       /* Should we clear the SF or let user space do it ???? */
                        break;
                case 0x002: /* Denormalize */
                case 0x010: /* Underflow */
@@ -872,9 +906,9 @@ fastcall void do_simd_coprocessor_error(struct pt_regs * regs,
                                          error_code);
                        return;
                }
-               die_if_kernel("cache flush denied", regs, error_code);
                current->thread.trap_no = 19;
                current->thread.error_code = error_code;
+               die_if_kernel("cache flush denied", regs, error_code);
                force_sig(SIGSEGV, current);
        }
 }
@@ -977,7 +1011,7 @@ void __init trap_init_f00f_bug(void)
         * it uses the read-only mapped virtual address.
         */
        idt_descr.address = fix_to_virt(FIX_F00F_IDT);
-       __asm__ __volatile__("lidt %0" : : "m" (idt_descr));
+       load_idt(&idt_descr);
 }
 #endif