Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[pandora-kernel.git] / arch / x86 / kernel / traps_32.c
index 08d752d..03df8e4 100644 (file)
@@ -1,5 +1,6 @@
 /*
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
  *
  *  Pentium III FXSR, SSE support
  *     Gareth Hughes <gareth@valinux.com>, May 2000
 #include <asm/nmi.h>
 #include <asm/smp.h>
 #include <asm/io.h>
+#include <asm/traps.h>
 
 #include "mach_traps.h"
 
-int panic_on_unrecovered_nmi;
-
 DECLARE_BITMAP(used_vectors, NR_VECTORS);
 EXPORT_SYMBOL_GPL(used_vectors);
 
@@ -78,39 +78,22 @@ char ignore_fpu_irq;
 gate_desc idt_table[256]
        __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, };
 
-asmlinkage void divide_error(void);
-asmlinkage void debug(void);
-asmlinkage void nmi(void);
-asmlinkage void int3(void);
-asmlinkage void overflow(void);
-asmlinkage void bounds(void);
-asmlinkage void invalid_op(void);
-asmlinkage void device_not_available(void);
-asmlinkage void coprocessor_segment_overrun(void);
-asmlinkage void invalid_TSS(void);
-asmlinkage void segment_not_present(void);
-asmlinkage void stack_segment(void);
-asmlinkage void general_protection(void);
-asmlinkage void page_fault(void);
-asmlinkage void coprocessor_error(void);
-asmlinkage void simd_coprocessor_error(void);
-asmlinkage void alignment_check(void);
-asmlinkage void spurious_interrupt_bug(void);
-asmlinkage void machine_check(void);
-
+int panic_on_unrecovered_nmi;
 int kstack_depth_to_print = 24;
 static unsigned int code_bytes = 64;
+static int ignore_nmis;
+static int die_counter;
 
 void printk_address(unsigned long address, int reliable)
 {
 #ifdef CONFIG_KALLSYMS
-       char namebuf[KSYM_NAME_LEN];
        unsigned long offset = 0;
        unsigned long symsize;
        const char *symname;
-       char reliab[4] = "";
-       char *delim = ":";
        char *modname;
+       char *delim = ":";
+       char namebuf[KSYM_NAME_LEN];
+       char reliab[4] = "";
 
        symname = kallsyms_lookup(address, &symsize, &offset,
                                        &modname, namebuf);
@@ -130,22 +113,23 @@ void printk_address(unsigned long address, int reliable)
 #endif
 }
 
-static inline int valid_stack_ptr(struct thread_info *tinfo, void *p, unsigned size)
+static inline int valid_stack_ptr(struct thread_info *tinfo,
+                       void *p, unsigned int size)
 {
-       return  p > (void *)tinfo &&
-               p <= (void *)tinfo + THREAD_SIZE - size;
+       void *t = tinfo;
+       return  p > t && p <= t + THREAD_SIZE - size;
 }
 
 /* The form of the top of the frame on the stack */
 struct stack_frame {
-       struct stack_frame      *next_frame;
-       unsigned long           return_address;
+       struct stack_frame *next_frame;
+       unsigned long return_address;
 };
 
 static inline unsigned long
 print_context_stack(struct thread_info *tinfo,
-                   unsigned long *stack, unsigned long bp,
-                   const struct stacktrace_ops *ops, void *data)
+               unsigned long *stack, unsigned long bp,
+               const struct stacktrace_ops *ops, void *data)
 {
        struct stack_frame *frame = (struct stack_frame *)bp;
 
@@ -167,8 +151,6 @@ print_context_stack(struct thread_info *tinfo,
        return bp;
 }
 
-#define MSG(msg)               ops->warning(data, msg)
-
 void dump_trace(struct task_struct *task, struct pt_regs *regs,
                unsigned long *stack, unsigned long bp,
                const struct stacktrace_ops *ops, void *data)
@@ -178,7 +160,6 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
 
        if (!stack) {
                unsigned long dummy;
-
                stack = &dummy;
                if (task != current)
                        stack = (unsigned long *)task->thread.sp;
@@ -196,7 +177,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
        }
 #endif
 
-       while (1) {
+       for (;;) {
                struct thread_info *context;
 
                context = (struct thread_info *)
@@ -248,15 +229,15 @@ static void print_trace_address(void *data, unsigned long addr, int reliable)
 }
 
 static const struct stacktrace_ops print_trace_ops = {
-       .warning                = print_trace_warning,
-       .warning_symbol         = print_trace_warning_symbol,
-       .stack                  = print_trace_stack,
-       .address                = print_trace_address,
+       .warning = print_trace_warning,
+       .warning_symbol = print_trace_warning_symbol,
+       .stack = print_trace_stack,
+       .address = print_trace_address,
 };
 
 static void
 show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
-                  unsigned long *stack, unsigned long bp, char *log_lvl)
+               unsigned long *stack, unsigned long bp, char *log_lvl)
 {
        dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
        printk("%s =======================\n", log_lvl);
@@ -351,15 +332,14 @@ void show_registers(struct pt_regs *regs)
                printk(KERN_EMERG "Code: ");
 
                ip = (u8 *)regs->ip - code_prologue;
-               if (ip < (u8 *)PAGE_OFFSET ||
-                       probe_kernel_address(ip, c)) {
+               if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
                        /* try starting at EIP */
                        ip = (u8 *)regs->ip;
                        code_len = code_len - code_prologue + 1;
                }
                for (i = 0; i < code_len; i++, ip++) {
                        if (ip < (u8 *)PAGE_OFFSET ||
-                               probe_kernel_address(ip, c)) {
+                                       probe_kernel_address(ip, c)) {
                                printk(" Bad EIP value.");
                                break;
                        }
@@ -384,7 +364,53 @@ int is_valid_bugaddr(unsigned long ip)
        return ud2 == 0x0b0f;
 }
 
-static int die_counter;
+static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
+static int die_owner = -1;
+static unsigned int die_nest_count;
+
+unsigned __kprobes long oops_begin(void)
+{
+       unsigned long flags;
+
+       oops_enter();
+
+       if (die_owner != raw_smp_processor_id()) {
+               console_verbose();
+               raw_local_irq_save(flags);
+               __raw_spin_lock(&die_lock);
+               die_owner = smp_processor_id();
+               die_nest_count = 0;
+               bust_spinlocks(1);
+       } else {
+               raw_local_irq_save(flags);
+       }
+       die_nest_count++;
+       return flags;
+}
+
+void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
+{
+       bust_spinlocks(0);
+       die_owner = -1;
+       add_taint(TAINT_DIE);
+       __raw_spin_unlock(&die_lock);
+       raw_local_irq_restore(flags);
+
+       if (!regs)
+               return;
+
+       if (kexec_should_crash(current))
+               crash_kexec(regs);
+
+       if (in_interrupt())
+               panic("Fatal exception in interrupt");
+
+       if (panic_on_oops)
+               panic("Fatal exception");
+
+       oops_exit();
+       do_exit(signr);
+}
 
 int __kprobes __die(const char *str, struct pt_regs *regs, long err)
 {
@@ -402,26 +428,22 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
        printk("DEBUG_PAGEALLOC");
 #endif
        printk("\n");
-
        if (notify_die(DIE_OOPS, str, regs, err,
-                       current->thread.trap_no, SIGSEGV) != NOTIFY_STOP) {
-
-               show_registers(regs);
-               /* Executive summary in case the oops scrolled away */
-               sp = (unsigned long) (&regs->sp);
-               savesegment(ss, ss);
-               if (user_mode(regs)) {
-                       sp = regs->sp;
-                       ss = regs->ss & 0xffff;
-               }
-               printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip);
-               print_symbol("%s", regs->ip);
-               printk(" SS:ESP %04x:%08lx\n", ss, sp);
+                       current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
+               return 1;
 
-               return 0;
+       show_registers(regs);
+       /* Executive summary in case the oops scrolled away */
+       sp = (unsigned long) (&regs->sp);
+       savesegment(ss, ss);
+       if (user_mode(regs)) {
+               sp = regs->sp;
+               ss = regs->ss & 0xffff;
        }
-
-       return 1;
+       printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip);
+       print_symbol("%s", regs->ip);
+       printk(" SS:ESP %04x:%08lx\n", ss, sp);
+       return 0;
 }
 
 /*
@@ -430,31 +452,9 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
  */
 void die(const char *str, struct pt_regs *regs, long err)
 {
-       static struct {
-               raw_spinlock_t lock;
-               u32 lock_owner;
-               int lock_owner_depth;
-       } die = {
-               .lock =                 __RAW_SPIN_LOCK_UNLOCKED,
-               .lock_owner =           -1,
-               .lock_owner_depth =     0
-       };
-       unsigned long flags;
-
-       oops_enter();
-
-       if (die.lock_owner != raw_smp_processor_id()) {
-               console_verbose();
-               raw_local_irq_save(flags);
-               __raw_spin_lock(&die.lock);
-               die.lock_owner = smp_processor_id();
-               die.lock_owner_depth = 0;
-               bust_spinlocks(1);
-       } else {
-               raw_local_irq_save(flags);
-       }
+       unsigned long flags = oops_begin();
 
-       if (++die.lock_owner_depth < 3) {
+       if (die_nest_count < 3) {
                report_bug(regs->ip, regs);
 
                if (__die(str, regs, err))
@@ -463,26 +463,7 @@ void die(const char *str, struct pt_regs *regs, long err)
                printk(KERN_EMERG "Recursive die() failure, output suppressed\n");
        }
 
-       bust_spinlocks(0);
-       die.lock_owner = -1;
-       add_taint(TAINT_DIE);
-       __raw_spin_unlock(&die.lock);
-       raw_local_irq_restore(flags);
-
-       if (!regs)
-               return;
-
-       if (kexec_should_crash(current))
-               crash_kexec(regs);
-
-       if (in_interrupt())
-               panic("Fatal exception in interrupt");
-
-       if (panic_on_oops)
-               panic("Fatal exception");
-
-       oops_exit();
-       do_exit(SIGSEGV);
+       oops_end(flags, regs, SIGSEGV);
 }
 
 static inline void
@@ -546,7 +527,7 @@ void do_##name(struct pt_regs *regs, long error_code)                       \
 {                                                                      \
        trace_hardirqs_fixup();                                         \
        if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr)  \
-                                               == NOTIFY_STOP)         \
+                                                       == NOTIFY_STOP) \
                return;                                                 \
        do_trap(trapnr, signr, str, 0, regs, error_code, NULL);         \
 }
@@ -562,7 +543,7 @@ void do_##name(struct pt_regs *regs, long error_code)                       \
        info.si_code = sicode;                                          \
        info.si_addr = (void __user *)siaddr;                           \
        if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr)  \
-                                               == NOTIFY_STOP)         \
+                                                       == NOTIFY_STOP) \
                return;                                                 \
        do_trap(trapnr, signr, str, 0, regs, error_code, &info);        \
 }
@@ -571,7 +552,7 @@ void do_##name(struct pt_regs *regs, long error_code)                       \
 void do_##name(struct pt_regs *regs, long error_code)                  \
 {                                                                      \
        if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr)  \
-                                               == NOTIFY_STOP)         \
+                                                       == NOTIFY_STOP) \
                return;                                                 \
        do_trap(trapnr, signr, str, 1, regs, error_code, NULL);         \
 }
@@ -586,27 +567,29 @@ void do_##name(struct pt_regs *regs, long error_code)                     \
        info.si_addr = (void __user *)siaddr;                           \
        trace_hardirqs_fixup();                                         \
        if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr)  \
-                                               == NOTIFY_STOP)         \
+                                                       == NOTIFY_STOP) \
                return;                                                 \
        do_trap(trapnr, signr, str, 1, regs, error_code, &info);        \
 }
 
-DO_VM86_ERROR_INFO(0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->ip)
+DO_VM86_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip)
 #ifndef CONFIG_KPROBES
 DO_VM86_ERROR(3, SIGTRAP, "int3", int3)
 #endif
 DO_VM86_ERROR(4, SIGSEGV, "overflow", overflow)
 DO_VM86_ERROR(5, SIGSEGV, "bounds", bounds)
-DO_ERROR_INFO(6, SIGILL,  "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip, 0)
-DO_ERROR(9, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun)
+DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip, 0)
+DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun)
 DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
-DO_ERROR(11, SIGBUS,  "segment not present", segment_not_present)
-DO_ERROR(12, SIGBUS,  "stack segment", stack_segment)
+DO_ERROR(11, SIGBUS, "segment not present", segment_not_present)
+DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
 DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0, 0)
 DO_ERROR_INFO(32, SIGILL, "iret exception", iret_error, ILL_BADSTK, 0, 1)
 
-void __kprobes do_general_protection(struct pt_regs *regs, long error_code)
+void __kprobes
+do_general_protection(struct pt_regs *regs, long error_code)
 {
+       struct task_struct *tsk;
        struct thread_struct *thread;
        struct tss_struct *tss;
        int cpu;
@@ -647,23 +630,24 @@ void __kprobes do_general_protection(struct pt_regs *regs, long error_code)
        if (regs->flags & X86_VM_MASK)
                goto gp_in_vm86;
 
+       tsk = current;
        if (!user_mode(regs))
                goto gp_in_kernel;
 
-       current->thread.error_code = error_code;
-       current->thread.trap_no = 13;
+       tsk->thread.error_code = error_code;
+       tsk->thread.trap_no = 13;
 
-       if (show_unhandled_signals && unhandled_signal(current, SIGSEGV) &&
-           printk_ratelimit()) {
+       if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
+                       printk_ratelimit()) {
                printk(KERN_INFO
-                   "%s[%d] general protection ip:%lx sp:%lx error:%lx",
-                   current->comm, task_pid_nr(current),
-                   regs->ip, regs->sp, error_code);
+                       "%s[%d] general protection ip:%lx sp:%lx error:%lx",
+                       tsk->comm, task_pid_nr(tsk),
+                       regs->ip, regs->sp, error_code);
                print_vma_addr(" in ", regs->ip);
                printk("\n");
        }
 
-       force_sig(SIGSEGV, current);
+       force_sig(SIGSEGV, tsk);
        return;
 
 gp_in_vm86:
@@ -672,14 +656,15 @@ gp_in_vm86:
        return;
 
 gp_in_kernel:
-       if (!fixup_exception(regs)) {
-               current->thread.error_code = error_code;
-               current->thread.trap_no = 13;
-               if (notify_die(DIE_GPF, "general protection fault", regs,
+       if (fixup_exception(regs))
+               return;
+
+       tsk->thread.error_code = error_code;
+       tsk->thread.trap_no = 13;
+       if (notify_die(DIE_GPF, "general protection fault", regs,
                                error_code, 13, SIGSEGV) == NOTIFY_STOP)
-                       return;
-               die("general protection fault", regs, error_code);
-       }
+               return;
+       die("general protection fault", regs, error_code);
 }
 
 static notrace __kprobes void
@@ -756,9 +741,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 
 static DEFINE_SPINLOCK(nmi_print_lock);
 
-void notrace __kprobes die_nmi(struct pt_regs *regs, const char *msg)
+void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
 {
-       if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) == NOTIFY_STOP)
+       if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
                return;
 
        spin_lock(&nmi_print_lock);
@@ -767,10 +752,12 @@ void notrace __kprobes die_nmi(struct pt_regs *regs, const char *msg)
        * to get a message out:
        */
        bust_spinlocks(1);
-       printk(KERN_EMERG "%s", msg);
+       printk(KERN_EMERG "%s", str);
        printk(" on CPU%d, ip %08lx, registers:\n",
                smp_processor_id(), regs->ip);
        show_registers(regs);
+       if (do_panic)
+               panic("Non maskable interrupt");
        console_silent();
        spin_unlock(&nmi_print_lock);
        bust_spinlocks(0);
@@ -790,14 +777,17 @@ void notrace __kprobes die_nmi(struct pt_regs *regs, const char *msg)
 static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
 {
        unsigned char reason = 0;
+       int cpu;
 
-       /* Only the BSP gets external NMIs from the system: */
-       if (!smp_processor_id())
+       cpu = smp_processor_id();
+
+       /* Only the BSP gets external NMIs from the system. */
+       if (!cpu)
                reason = get_nmi_reason();
 
        if (!(reason & 0xc0)) {
                if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT)
-                                                       == NOTIFY_STOP)
+                                                               == NOTIFY_STOP)
                        return;
 #ifdef CONFIG_X86_LOCAL_APIC
                /*
@@ -806,7 +796,7 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
                 */
                if (nmi_watchdog_tick(regs, reason))
                        return;
-               if (!do_nmi_callback(regs, smp_processor_id()))
+               if (!do_nmi_callback(regs, cpu))
                        unknown_nmi_error(reason, regs);
 #else
                unknown_nmi_error(reason, regs);
@@ -816,6 +806,8 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
        }
        if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
                return;
+
+       /* AK: following checks seem to be broken on modern chipsets. FIXME */
        if (reason & 0x80)
                mem_parity_error(reason, regs);
        if (reason & 0x40)
@@ -827,8 +819,6 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
        reassert_nmi();
 }
 
-static int ignore_nmis;
-
 notrace __kprobes void do_nmi(struct pt_regs *regs, long error_code)
 {
        int cpu;
@@ -913,7 +903,7 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code)
        tsk->thread.debugctlmsr = 0;
 
        if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
-                                       SIGTRAP) == NOTIFY_STOP)
+                                               SIGTRAP) == NOTIFY_STOP)
                return;
        /* It's safe to allow irq's after DR6 has been saved */
        if (regs->flags & X86_EFLAGS_IF)
@@ -974,9 +964,8 @@ clear_TF_reenable:
 void math_error(void __user *ip)
 {
        struct task_struct *task;
-       unsigned short cwd;
-       unsigned short swd;
        siginfo_t info;
+       unsigned short cwd, swd;
 
        /*
         * Save the info for the exception handler and clear the error.
@@ -995,7 +984,7 @@ void math_error(void __user *ip)
         * C1 reg you need in case of a stack fault, 0x040 is the stack
         * fault bit.  We should only be taking one exception at a time,
         * so if this combination doesn't produce any single exception,
-        * then we have a bad program that isn't syncronizing its FPU usage
+        * then we have a bad program that isn't synchronizing its FPU usage
         * and it will suffer the consequences since we won't be able to
         * fully reproduce the context of the exception
         */
@@ -1004,7 +993,7 @@ void math_error(void __user *ip)
        switch (swd & ~cwd & 0x3f) {
        case 0x000: /* No unmasked exception */
                return;
-       default:    /* Multiple exceptions */
+       default: /* Multiple exceptions */
                break;
        case 0x001: /* Invalid Op */
                /*
@@ -1040,8 +1029,8 @@ void do_coprocessor_error(struct pt_regs *regs, long error_code)
 static void simd_math_error(void __user *ip)
 {
        struct task_struct *task;
-       unsigned short mxcsr;
        siginfo_t info;
+       unsigned short mxcsr;
 
        /*
         * Save the info for the exception handler and clear the error.
@@ -1117,7 +1106,7 @@ void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code)
 
 unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp)
 {
-       struct desc_struct *gdt = __get_cpu_var(gdt_page).gdt;
+       struct desc_struct *gdt = get_cpu_gdt_table(smp_processor_id());
        unsigned long base = (kesp - uesp) & -THREAD_SIZE;
        unsigned long new_kesp = kesp - base;
        unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT;
@@ -1196,19 +1185,16 @@ void __init trap_init(void)
        early_iounmap(p, 4);
 #endif
 
-#ifdef CONFIG_X86_LOCAL_APIC
-       init_apic_mappings();
-#endif
-       set_trap_gate(0,  &divide_error);
-       set_intr_gate(1,  &debug);
-       set_intr_gate(2,  &nmi);
-       set_system_intr_gate(3, &int3); /* int3/4 can be called from all */
-       set_system_gate(4, &overflow);
-       set_trap_gate(5,  &bounds);
-       set_trap_gate(6,  &invalid_op);
-       set_trap_gate(7,  &device_not_available);
-       set_task_gate(8,  GDT_ENTRY_DOUBLEFAULT_TSS);
-       set_trap_gate(9,  &coprocessor_segment_overrun);
+       set_trap_gate(0, &divide_error);
+       set_intr_gate(1, &debug);
+       set_intr_gate(2, &nmi);
+       set_system_intr_gate(3, &int3); /* int3 can be called from all */
+       set_system_gate(4, &overflow); /* int4 can be called from all */
+       set_trap_gate(5, &bounds);
+       set_trap_gate(6, &invalid_op);
+       set_trap_gate(7, &device_not_available);
+       set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);
+       set_trap_gate(9, &coprocessor_segment_overrun);
        set_trap_gate(10, &invalid_TSS);
        set_trap_gate(11, &segment_not_present);
        set_trap_gate(12, &stack_segment);