fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / i386 / kernel / traps.c
index 05ab8b0..cc3bc38 100644 (file)
@@ -28,6 +28,9 @@
 #include <linux/kprobes.h>
 #include <linux/kexec.h>
 #include <linux/unwind.h>
+#include <linux/uaccess.h>
+#include <linux/nmi.h>
+#include <linux/bug.h>
 
 #ifdef CONFIG_EISA
 #include <linux/ioport.h>
@@ -40,7 +43,6 @@
 
 #include <asm/processor.h>
 #include <asm/system.h>
-#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/atomic.h>
 #include <asm/debugreg.h>
 #include <asm/stacktrace.h>
 
 #include <linux/module.h>
-#include <linux/vserver/debug.h>
+#include <linux/vs_context.h>
+#include <linux/vserver/history.h>
 
 #include "mach_traps.h"
 
-asmlinkage int system_call(void);
+int panic_on_unrecovered_nmi;
 
-struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
-               { 0, 0 }, { 0, 0 } };
+asmlinkage int system_call(void);
 
 /* Do we ignore FPU interrupts ? */
 char ignore_fpu_irq = 0;
@@ -93,12 +95,7 @@ asmlinkage void alignment_check(void);
 asmlinkage void spurious_interrupt_bug(void);
 asmlinkage void machine_check(void);
 
-static int kstack_depth_to_print = 24;
-#ifdef CONFIG_STACK_UNWIND
-static int call_trace = 1;
-#else
-#define call_trace (-1)
-#endif
+int kstack_depth_to_print = 24;
 ATOMIC_NOTIFIER_HEAD(i386die_chain);
 
 extern char last_sysfs_file[];
@@ -130,15 +127,19 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo,
 
 #ifdef CONFIG_FRAME_POINTER
        while (valid_stack_ptr(tinfo, (void *)ebp)) {
+               unsigned long new_ebp;
                addr = *(unsigned long *)(ebp + 4);
                ops->address(data, addr);
                /*
                 * break out of recursive entries (such as
-                * end_of_stack_stop_unwind_function):
+                * end_of_stack_stop_unwind_function). Also,
+                * we can never allow a frame pointer to
+                * move downwards!
                 */
-               if (ebp == *(unsigned long *)ebp)
+               new_ebp = *(unsigned long *)ebp;
+               if (new_ebp <= ebp)
                        break;
-               ebp = *(unsigned long *)ebp;
+               ebp = new_ebp;
        }
 #else
        while (valid_stack_ptr(tinfo, stack)) {
@@ -150,25 +151,7 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo,
        return ebp;
 }
 
-struct ops_and_data {
-       struct stacktrace_ops *ops;
-       void *data;
-};
-
-static asmlinkage int
-dump_trace_unwind(struct unwind_frame_info *info, void *data)
-{
-       struct ops_and_data *oad = (struct ops_and_data *)data;
-       int n = 0;
-
-       while (unwind(info) == 0 && UNW_PC(info)) {
-               n++;
-               oad->ops->address(oad->data, UNW_PC(info));
-               if (arch_unw_user_mode(info))
-                       break;
-       }
-       return n;
-}
+#define MSG(msg) ops->warning(data, msg)
 
 void dump_trace(struct task_struct *task, struct pt_regs *regs,
                unsigned long *stack,
@@ -179,39 +162,6 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
        if (!task)
                task = current;
 
-       if (call_trace >= 0) {
-               int unw_ret = 0;
-               struct unwind_frame_info info;
-               struct ops_and_data oad = { .ops = ops, .data = data };
-
-               if (regs) {
-                       if (unwind_init_frame_info(&info, task, regs) == 0)
-                               unw_ret = dump_trace_unwind(&info, &oad);
-               } else if (task == current)
-                       unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad);
-               else {
-                       if (unwind_init_blocked(&info, task) == 0)
-                               unw_ret = dump_trace_unwind(&info, &oad);
-               }
-               if (unw_ret > 0) {
-                       if (call_trace == 1 && !arch_unw_user_mode(&info)) {
-                               ops->warning_symbol(data, "DWARF2 unwinder stuck at %s",
-                                            UNW_PC(&info));
-                               if (UNW_SP(&info) >= PAGE_OFFSET) {
-                                       ops->warning(data, "Leftover inexact backtrace:");
-                                       stack = (void *)UNW_SP(&info);
-                                       if (!stack)
-                                               return;
-                                       ebp = UNW_FP(&info);
-                               } else
-                                       ops->warning(data, "Full inexact backtrace again:");
-                       } else if (call_trace >= 1)
-                               return;
-                       else
-                               ops->warning(data, "Full inexact backtrace again:");
-               } else
-                       ops->warning(data, "Inexact backtrace:");
-       }
        if (!stack) {
                unsigned long dummy;
                stack = &dummy;
@@ -244,6 +194,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
                stack = (unsigned long*)context->previous_esp;
                if (!stack)
                        break;
+               touch_nmi_watchdog();
        }
 }
 EXPORT_SYMBOL(dump_trace);
@@ -354,12 +305,13 @@ void show_registers(struct pt_regs *regs)
                ss = regs->xss & 0xffff;
        }
        print_modules();
-       printk(KERN_EMERG "CPU:    %d\nEIP:    %04x:[<%08lx>]    %s VLI\n"
-                       "EFLAGS: %08lx   (%s %.*s) \n",
+       printk(KERN_EMERG "CPU:    %d\n"
+               KERN_EMERG "EIP:    %04x:[<%08lx>]    %s VLI\n"
+               KERN_EMERG "EFLAGS: %08lx   (%s %.*s)\n",
                smp_processor_id(), 0xffff & regs->xcs, regs->eip,
-               print_tainted(), regs->eflags, system_utsname.release,
-               (int)strcspn(system_utsname.version, " "),
-               system_utsname.version);
+               print_tainted(), regs->eflags, init_utsname()->release,
+               (int)strcspn(init_utsname()->version, " "),
+               init_utsname()->version);
        print_symbol(KERN_EMERG "EIP is at %s\n", regs->eip);
        printk(KERN_EMERG "eax: %08lx   ebx: %08lx   ecx: %08lx   edx: %08lx\n",
                regs->eax, regs->ebx, regs->ecx, regs->edx);
@@ -375,22 +327,29 @@ void show_registers(struct pt_regs *regs)
         * time of the fault..
         */
        if (in_kernel) {
-               u8 __user *eip;
+               u8 *eip;
+               int code_bytes = 64;
+               unsigned char c;
 
                printk("\n" KERN_EMERG "Stack: ");
                show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG);
 
                printk(KERN_EMERG "Code: ");
 
-               eip = (u8 __user *)regs->eip - 43;
-               for (i = 0; i < 64; i++, eip++) {
-                       unsigned char c;
-
-                       if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) {
+               eip = (u8 *)regs->eip - 43;
+               if (eip < (u8 *)PAGE_OFFSET ||
+                       probe_kernel_address(eip, c)) {
+                       /* try starting at EIP */
+                       eip = (u8 *)regs->eip;
+                       code_bytes = 32;
+               }
+               for (i = 0; i < code_bytes; i++, eip++) {
+                       if (eip < (u8 *)PAGE_OFFSET ||
+                               probe_kernel_address(eip, c)) {
                                printk(" Bad EIP value.");
                                break;
                        }
-                       if (eip == (u8 __user *)regs->eip)
+                       if (eip == (u8 *)regs->eip)
                                printk("<%02x> ", c);
                        else
                                printk("%02x ", c);
@@ -399,42 +358,22 @@ void show_registers(struct pt_regs *regs)
        printk("\n");
 }      
 
-static void handle_BUG(struct pt_regs *regs)
+int is_valid_bugaddr(unsigned long eip)
 {
-       unsigned long eip = regs->eip;
        unsigned short ud2;
 
        if (eip < PAGE_OFFSET)
-               return;
-       if (__get_user(ud2, (unsigned short __user *)eip))
-               return;
-       if (ud2 != 0x0b0f)
-               return;
-
-       printk(KERN_EMERG "------------[ cut here ]------------\n");
-
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-       do {
-               unsigned short line;
-               char *file;
-               char c;
-
-               if (__get_user(line, (unsigned short __user *)(eip + 2)))
-                       break;
-               if (__get_user(file, (char * __user *)(eip + 4)) ||
-                   (unsigned long)file < PAGE_OFFSET || __get_user(c, file))
-                       file = "<bad filename>";
+               return 0;
+       if (probe_kernel_address((unsigned short *)eip, ud2))
+               return 0;
 
-               printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line);
-               return;
-       } while (0);
-#endif
-       printk(KERN_EMERG "Kernel BUG at [verbose debug info unavailable]\n");
+       return ud2 == 0x0b0f;
 }
 
-/* This is gone through when something in the kernel
- * has done something bad and is about to be terminated.
-*/
+/*
+ * 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 {
@@ -442,7 +381,7 @@ void die(const char * str, struct pt_regs * regs, long err)
                u32 lock_owner;
                int lock_owner_depth;
        } die = {
-               .lock =                 SPIN_LOCK_UNLOCKED,
+               .lock =                 __SPIN_LOCK_UNLOCKED(die.lock),
                .lock_owner =           -1,
                .lock_owner_depth =     0
        };
@@ -468,7 +407,8 @@ void die(const char * str, struct pt_regs * regs, long err)
                unsigned long esp;
                unsigned short ss;
 
-               handle_BUG(regs);
+               report_bug(regs->eip);
+
                printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
 #ifdef CONFIG_PREEMPT
                printk(KERN_EMERG "PREEMPT ");
@@ -786,18 +726,23 @@ gp_in_kernel:
        }
 }
 
-static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
+static __kprobes void
+mem_parity_error(unsigned char reason, struct pt_regs * regs)
 {
-       printk(KERN_EMERG "Uhhuh. NMI received. Dazed and confused, but trying "
-                       "to continue\n");
-       printk(KERN_EMERG "You probably have a hardware problem with your RAM "
-                       "chips\n");
+       printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on "
+               "CPU %d.\n", reason, smp_processor_id());
+       printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n");
+       if (panic_on_unrecovered_nmi)
+                panic("NMI: Not continuing");
+
+       printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
 
        /* Clear and disable the memory parity error line. */
        clear_mem_error(reason);
 }
 
-static void io_check_error(unsigned char reason, struct pt_regs * regs)
+static __kprobes void
+io_check_error(unsigned char reason, struct pt_regs * regs)
 {
        printk(KERN_EMERG "NMI: IOCK error (debug interrupt?)\n");
        show_registers(regs);
@@ -806,7 +751,8 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs)
        clear_io_check_error(reason);
 }
 
-static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
+static __kprobes void
+unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
 {
 #ifdef CONFIG_MCA
        /* Might actually be able to figure out what the guilty party
@@ -816,15 +762,18 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
                return;
        }
 #endif
-       printk("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
-               reason, smp_processor_id());
-       printk("Dazed and confused, but trying to continue\n");
-       printk("Do you have a strange power saving mode enabled?\n");
+       printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on "
+               "CPU %d.\n", reason, smp_processor_id());
+       printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n");
+       if (panic_on_unrecovered_nmi)
+                panic("NMI: Not continuing");
+
+       printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
 }
 
 static DEFINE_SPINLOCK(nmi_print_lock);
 
-void die_nmi (struct pt_regs *regs, const char *msg)
+void __kprobes die_nmi(struct pt_regs *regs, const char *msg)
 {
        if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) ==
            NOTIFY_STOP)
@@ -855,7 +804,7 @@ void die_nmi (struct pt_regs *regs, const char *msg)
        do_exit(SIGSEGV);
 }
 
-static void default_do_nmi(struct pt_regs * regs)
+static __kprobes void default_do_nmi(struct pt_regs * regs)
 {
        unsigned char reason = 0;
 
@@ -872,12 +821,12 @@ static void default_do_nmi(struct pt_regs * regs)
                 * Ok, so this is none of the documented NMI sources,
                 * so it must be the NMI watchdog.
                 */
-               if (nmi_watchdog) {
-                       nmi_watchdog_tick(regs);
+               if (nmi_watchdog_tick(regs, reason))
                        return;
-               }
+               if (!do_nmi_callback(regs, smp_processor_id()))
 #endif
-               unknown_nmi_error(reason, regs);
+                       unknown_nmi_error(reason, regs);
+
                return;
        }
        if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
@@ -893,14 +842,7 @@ static void default_do_nmi(struct pt_regs * regs)
        reassert_nmi();
 }
 
-static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
-{
-       return 0;
-}
-static nmi_callback_t nmi_callback = dummy_nmi_callback;
-fastcall void do_nmi(struct pt_regs * regs, long error_code)
+fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
 {
        int cpu;
 
@@ -910,25 +852,11 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
 
        ++nmi_count(cpu);
 
-       if (!rcu_dereference(nmi_callback)(regs, cpu))
-               default_do_nmi(regs);
+       default_do_nmi(regs);
 
        nmi_exit();
 }
 
-void set_nmi_callback(nmi_callback_t callback)
-{
-       vmalloc_sync_all();
-       rcu_assign_pointer(nmi_callback, callback);
-}
-EXPORT_SYMBOL_GPL(set_nmi_callback);
-
-void unset_nmi_callback(void)
-{
-       nmi_callback = dummy_nmi_callback;
-}
-EXPORT_SYMBOL_GPL(unset_nmi_callback);
-
 #ifdef CONFIG_KPROBES
 fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
 {
@@ -1175,49 +1103,24 @@ fastcall void do_spurious_interrupt_bug(struct pt_regs * regs,
 #endif
 }
 
-fastcall void setup_x86_bogus_stack(unsigned char * stk)
-{
-       unsigned long *switch16_ptr, *switch32_ptr;
-       struct pt_regs *regs;
-       unsigned long stack_top, stack_bot;
-       unsigned short iret_frame16_off;
-       int cpu = smp_processor_id();
-       /* reserve the space on 32bit stack for the magic switch16 pointer */
-       memmove(stk, stk + 8, sizeof(struct pt_regs));
-       switch16_ptr = (unsigned long *)(stk + sizeof(struct pt_regs));
-       regs = (struct pt_regs *)stk;
-       /* now the switch32 on 16bit stack */
-       stack_bot = (unsigned long)&per_cpu(cpu_16bit_stack, cpu);
-       stack_top = stack_bot + CPU_16BIT_STACK_SIZE;
-       switch32_ptr = (unsigned long *)(stack_top - 8);
-       iret_frame16_off = CPU_16BIT_STACK_SIZE - 8 - 20;
-       /* copy iret frame on 16bit stack */
-       memcpy((void *)(stack_bot + iret_frame16_off), &regs->eip, 20);
-       /* fill in the switch pointers */
-       switch16_ptr[0] = (regs->esp & 0xffff0000) | iret_frame16_off;
-       switch16_ptr[1] = __ESPFIX_SS;
-       switch32_ptr[0] = (unsigned long)stk + sizeof(struct pt_regs) +
-               8 - CPU_16BIT_STACK_SIZE;
-       switch32_ptr[1] = __KERNEL_DS;
-}
-
-fastcall unsigned char * fixup_x86_bogus_stack(unsigned short sp)
+fastcall unsigned long patch_espfix_desc(unsigned long uesp,
+                                         unsigned long kesp)
 {
-       unsigned long *switch32_ptr;
-       unsigned char *stack16, *stack32;
-       unsigned long stack_top, stack_bot;
-       int len;
        int cpu = smp_processor_id();
-       stack_bot = (unsigned long)&per_cpu(cpu_16bit_stack, cpu);
-       stack_top = stack_bot + CPU_16BIT_STACK_SIZE;
-       switch32_ptr = (unsigned long *)(stack_top - 8);
-       /* copy the data from 16bit stack to 32bit stack */
-       len = CPU_16BIT_STACK_SIZE - 8 - sp;
-       stack16 = (unsigned char *)(stack_bot + sp);
-       stack32 = (unsigned char *)
-               (switch32_ptr[0] + CPU_16BIT_STACK_SIZE - 8 - len);
-       memcpy(stack32, stack16, len);
-       return stack32;
+       struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
+       struct desc_struct *gdt = (struct desc_struct *)cpu_gdt_descr->address;
+       unsigned long base = (kesp - uesp) & -THREAD_SIZE;
+       unsigned long new_kesp = kesp - base;
+       unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT;
+       __u64 desc = *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS];
+       /* Set up base for espfix segment */
+       desc &= 0x00f0ff0000000000ULL;
+       desc |= ((((__u64)base) << 16) & 0x000000ffffff0000ULL) |
+               ((((__u64)base) << 32) & 0xff00000000000000ULL) |
+               ((((__u64)lim_pages) << 32) & 0x000f000000000000ULL) |
+               (lim_pages & 0xffff);
+       *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS] = desc;
+       return new_kesp;
 }
 
 /*
@@ -1230,7 +1133,7 @@ fastcall unsigned char * fixup_x86_bogus_stack(unsigned short sp)
  * Must be called with kernel preemption disabled (in this case,
  * local interrupts are disabled at the call-site in entry.S).
  */
-asmlinkage void math_state_restore(struct pt_regs regs)
+asmlinkage void math_state_restore(void)
 {
        struct thread_info *thread = current_thread_info();
        struct task_struct *tsk = thread->task;
@@ -1240,6 +1143,7 @@ asmlinkage void math_state_restore(struct pt_regs regs)
                init_fpu(tsk);
        restore_fpu(tsk);
        thread->status |= TS_USEDFPU;   /* So we fnsave on switch_to() */
+       tsk->fpu_counter++;
 }
 
 #ifndef CONFIG_MATH_EMULATION
@@ -1268,20 +1172,6 @@ void __init trap_init_f00f_bug(void)
 }
 #endif
 
-#define _set_gate(gate_addr,type,dpl,addr,seg) \
-do { \
-  int __d0, __d1; \
-  __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
-       "movw %4,%%dx\n\t" \
-       "movl %%eax,%0\n\t" \
-       "movl %%edx,%1" \
-       :"=m" (*((long *) (gate_addr))), \
-        "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
-       :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
-        "3" ((char *) (addr)),"2" ((seg) << 16)); \
-} while (0)
-
-
 /*
  * This needs to use 'idt_table' rather than 'idt', and
  * thus use the _nonmapped_ version of the IDT, as the
@@ -1290,7 +1180,7 @@ do { \
  */
 void set_intr_gate(unsigned int n, void *addr)
 {
-       _set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
+       _set_gate(n, DESCTYPE_INT, addr, __KERNEL_CS);
 }
 
 /*
@@ -1298,22 +1188,22 @@ void set_intr_gate(unsigned int n, void *addr)
  */
 static inline void set_system_intr_gate(unsigned int n, void *addr)
 {
-       _set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS);
+       _set_gate(n, DESCTYPE_INT | DESCTYPE_DPL3, addr, __KERNEL_CS);
 }
 
 static void __init set_trap_gate(unsigned int n, void *addr)
 {
-       _set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
+       _set_gate(n, DESCTYPE_TRAP, addr, __KERNEL_CS);
 }
 
 static void __init set_system_gate(unsigned int n, void *addr)
 {
-       _set_gate(idt_table+n,15,3,addr,__KERNEL_CS);
+       _set_gate(n, DESCTYPE_TRAP | DESCTYPE_DPL3, addr, __KERNEL_CS);
 }
 
 static void __init set_task_gate(unsigned int n, unsigned int gdt_entry)
 {
-       _set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
+       _set_gate(n, DESCTYPE_TASK, (void *)0, (gdt_entry<<3));
 }
 
 
@@ -1392,19 +1282,3 @@ static int __init kstack_setup(char *s)
        return 1;
 }
 __setup("kstack=", kstack_setup);
-
-#ifdef CONFIG_STACK_UNWIND
-static int __init call_trace_setup(char *s)
-{
-       if (strcmp(s, "old") == 0)
-               call_trace = -1;
-       else if (strcmp(s, "both") == 0)
-               call_trace = 0;
-       else if (strcmp(s, "newfallback") == 0)
-               call_trace = 1;
-       else if (strcmp(s, "new") == 2)
-               call_trace = 2;
-       return 1;
-}
-__setup("call_trace=", call_trace_setup);
-#endif