X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fi386%2Fkernel%2Ftraps.c;fp=arch%2Fi386%2Fkernel%2Ftraps.c;h=610d2f92879f9d87f05bdaae5447fb37c1ec118c;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=05ab8b0fe22c8cd317eead9537aac17febd31e73;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 05ab8b0fe..610d2f928 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -11,6 +11,7 @@ * 'Traps.c' handles hardware traps and faults after we have saved some * state in 'asm.s'. */ +#include #include #include #include @@ -27,7 +28,6 @@ #include #include #include -#include #ifdef CONFIG_EISA #include @@ -47,11 +47,10 @@ #include #include #include -#include + #include #include #include -#include #include #include @@ -94,27 +93,19 @@ 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 -ATOMIC_NOTIFIER_HEAD(i386die_chain); - -extern char last_sysfs_file[]; +struct notifier_block *i386die_chain; +static DEFINE_SPINLOCK(die_notifier_lock); int register_die_notifier(struct notifier_block *nb) { - vmalloc_sync_all(); - return atomic_notifier_chain_register(&i386die_chain, nb); -} -EXPORT_SYMBOL(register_die_notifier); /* used modular by kdb */ - -int unregister_die_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&i386die_chain, 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; } -EXPORT_SYMBOL(unregister_die_notifier); /* used modular by kdb */ +EXPORT_SYMBOL(register_die_notifier); static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) { @@ -122,182 +113,72 @@ static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) p < (void *)tinfo + THREAD_SIZE - 3; } +static void print_addr_and_symbol(unsigned long addr, char *log_lvl) +{ + printk(log_lvl); + printk(" [<%08lx>] ", addr); + print_symbol("%s", addr); + printk("\n"); +} + static inline unsigned long print_context_stack(struct thread_info *tinfo, unsigned long *stack, unsigned long ebp, - struct stacktrace_ops *ops, void *data) + char *log_lvl) { unsigned long addr; #ifdef CONFIG_FRAME_POINTER while (valid_stack_ptr(tinfo, (void *)ebp)) { addr = *(unsigned long *)(ebp + 4); - ops->address(data, addr); - /* - * break out of recursive entries (such as - * end_of_stack_stop_unwind_function): - */ - if (ebp == *(unsigned long *)ebp) - break; + print_addr_and_symbol(addr, log_lvl); ebp = *(unsigned long *)ebp; } #else while (valid_stack_ptr(tinfo, stack)) { addr = *stack++; if (__kernel_text_address(addr)) - ops->address(data, addr); + print_addr_and_symbol(addr, log_lvl); } #endif 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; -} - -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, - struct stacktrace_ops *ops, void *data) +static void show_trace_log_lvl(struct task_struct *task, + unsigned long *stack, char *log_lvl) { - unsigned long ebp = 0; + unsigned long ebp; 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; - if (task && task != current) - stack = (unsigned long *)task->thread.esp; - } - -#ifdef CONFIG_FRAME_POINTER - if (!ebp) { - if (task == current) { - /* Grab ebp right from our regs */ - asm ("movl %%ebp, %0" : "=r" (ebp) : ); - } else { - /* ebp is the last reg pushed by switch_to */ - ebp = *(unsigned long *) task->thread.esp; - } + if (task == current) { + /* Grab ebp right from our regs */ + asm ("movl %%ebp, %0" : "=r" (ebp) : ); + } else { + /* ebp is the last reg pushed by switch_to */ + ebp = *(unsigned long *) task->thread.esp; } -#endif while (1) { struct thread_info *context; context = (struct thread_info *) ((unsigned long)stack & (~(THREAD_SIZE - 1))); - ebp = print_context_stack(context, stack, ebp, ops, data); - /* Should be after the line below, but somewhere - in early boot context comes out corrupted and we - can't reference it -AK */ - if (ops->stack(data, "IRQ") < 0) - break; + ebp = print_context_stack(context, stack, ebp, log_lvl); stack = (unsigned long*)context->previous_esp; if (!stack) break; + printk(log_lvl); + printk(" =======================\n"); } } -EXPORT_SYMBOL(dump_trace); - -static void -print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) -{ - printk(data); - print_symbol(msg, symbol); - printk("\n"); -} - -static void print_trace_warning(void *data, char *msg) -{ - printk("%s%s\n", (char *)data, msg); -} - -static int print_trace_stack(void *data, char *name) -{ - return 0; -} - -/* - * Print one address/symbol entries per line. - */ -static void print_trace_address(void *data, unsigned long addr) -{ - printk("%s [<%08lx>] ", (char *)data, addr); - print_symbol("%s\n", addr); -} - -static struct stacktrace_ops print_trace_ops = { - .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, char *log_lvl) -{ - dump_trace(task, regs, stack, &print_trace_ops, log_lvl); - printk("%s =======================\n", log_lvl); -} -void show_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long * stack) +void show_trace(struct task_struct *task, unsigned long * stack) { - show_trace_log_lvl(task, regs, stack, ""); + show_trace_log_lvl(task, stack, ""); } -static void show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *esp, char *log_lvl) +static void show_stack_log_lvl(struct task_struct *task, unsigned long *esp, + char *log_lvl) { unsigned long *stack; int i; @@ -310,21 +191,26 @@ static void show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, } stack = esp; + printk(log_lvl); for(i = 0; i < kstack_depth_to_print; i++) { if (kstack_end(stack)) break; - if (i && ((i % 8) == 0)) - printk("\n%s ", log_lvl); + if (i && ((i % 8) == 0)) { + printk("\n"); + printk(log_lvl); + printk(" "); + } printk("%08lx ", *stack++); } - printk("\n%sCall Trace:\n", log_lvl); - show_trace_log_lvl(task, regs, esp, log_lvl); + printk("\n"); + printk(log_lvl); + printk("Call Trace:\n"); + show_trace_log_lvl(task, esp, log_lvl); } void show_stack(struct task_struct *task, unsigned long *esp) { - printk(" "); - show_stack_log_lvl(task, NULL, esp, ""); + show_stack_log_lvl(task, esp, ""); } /* @@ -334,7 +220,7 @@ void dump_stack(void) { unsigned long stack; - show_trace(current, NULL, &stack); + show_trace(current, &stack); } EXPORT_SYMBOL(dump_stack); @@ -348,7 +234,7 @@ void show_registers(struct pt_regs *regs) esp = (unsigned long) (®s->esp); savesegment(ss, ss); - if (user_mode_vm(regs)) { + if (user_mode(regs)) { in_kernel = 0; esp = regs->esp; ss = regs->xss & 0xffff; @@ -367,9 +253,9 @@ void show_registers(struct pt_regs *regs) regs->esi, regs->edi, regs->ebp, esp); printk(KERN_EMERG "ds: %04x es: %04x ss: %04x\n", regs->xds & 0xffff, regs->xes & 0xffff, ss); - printk(KERN_EMERG "Process %.*s (pid: %d[#%u], ti=%p task=%p task.ti=%p)", - TASK_COMM_LEN, current->comm, current->pid, current->xid, - current_thread_info(), current, current->thread_info); + printk(KERN_EMERG "Process %s (pid: %d[#%u], threadinfo=%p task=%p)", + current->comm, current->pid, current->xid, + current_thread_info(), current); /* * When in-kernel, we also print out the stack and code at the * time of the fault.. @@ -378,7 +264,7 @@ void show_registers(struct pt_regs *regs) u8 __user *eip; printk("\n" KERN_EMERG "Stack: "); - show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG); + show_stack_log_lvl(NULL, (unsigned long *)esp, KERN_EMERG); printk(KERN_EMERG "Code: "); @@ -401,35 +287,35 @@ void show_registers(struct pt_regs *regs) static void handle_BUG(struct pt_regs *regs) { - unsigned long eip = regs->eip; unsigned short ud2; + unsigned short line; + char *file; + char c; + unsigned long eip; + + eip = regs->eip; if (eip < PAGE_OFFSET) - return; + goto no_bug; if (__get_user(ud2, (unsigned short __user *)eip)) - return; + goto no_bug; if (ud2 != 0x0b0f) - return; + goto no_bug; + if (__get_user(line, (unsigned short __user *)(eip + 2))) + goto bug; + if (__get_user(file, (char * __user *)(eip + 4)) || + (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) + file = ""; printk(KERN_EMERG "------------[ cut here ]------------\n"); + printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); -#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 = ""; +no_bug: + return; - 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"); + /* Here we know it was a BUG but file-n-line is unavailable */ +bug: + printk(KERN_EMERG "Kernel BUG\n"); } /* This is gone through when something in the kernel @@ -449,8 +335,6 @@ void die(const char * str, struct pt_regs * regs, long err) static int die_counter; unsigned long flags; - oops_enter(); - vxh_throw_oops(); if (die.lock_owner != raw_smp_processor_id()) { @@ -465,9 +349,6 @@ void die(const char * str, struct pt_regs * regs, long err) if (++die.lock_owner_depth < 3) { int nl = 0; - unsigned long esp; - unsigned short ss; - handle_BUG(regs); printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); #ifdef CONFIG_PREEMPT @@ -488,26 +369,9 @@ void die(const char * str, struct pt_regs * regs, long err) #endif if (nl) printk("\n"); -#ifdef CONFIG_SYSFS - printk(KERN_ALERT "last sysfs file: %s\n", last_sysfs_file); -#endif - if (notify_die(DIE_OOPS, str, regs, err, - current->thread.trap_no, SIGSEGV) != NOTIFY_STOP) { - show_registers(regs); - vxh_dump_history(); - /* Executive summary in case the oops scrolled away */ - esp = (unsigned long) (®s->esp); - savesegment(ss, ss); - if (user_mode(regs)) { - esp = regs->esp; - ss = regs->xss & 0xffff; - } - printk(KERN_EMERG "EIP: [<%08lx>] ", regs->eip); - print_symbol("%s", regs->eip); - printk(" SS:ESP %04x:%08lx\n", ss, esp); - } - else - regs = NULL; + notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV); + show_registers(regs); + vxh_dump_history(); } else printk(KERN_EMERG "Recursive die() failure, output suppressed\n"); @@ -515,19 +379,17 @@ void die(const char * str, struct pt_regs * regs, long err) die.lock_owner = -1; spin_unlock_irqrestore(&die.lock, flags); - if (!regs) - return; - if (kexec_should_crash(current)) crash_kexec(regs); if (in_interrupt()) panic("Fatal exception in interrupt"); - if (panic_on_oops) + if (panic_on_oops) { + printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); + ssleep(5); panic("Fatal exception"); - - oops_exit(); + } do_exit(SIGSEGV); } @@ -633,82 +495,7 @@ 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_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) - - -/* - * lazy-check for CS validity on exec-shield binaries: - * - * the original non-exec stack patch was written by - * Solar Designer . Thanks! - */ -static int -check_lazy_exec_limit(int cpu, struct pt_regs *regs, long error_code) -{ - struct desc_struct *desc1, *desc2; - struct vm_area_struct *vma; - unsigned long limit; - - if (current->mm == NULL) - return 0; - - limit = -1UL; - if (current->mm->context.exec_limit != -1UL) { - limit = PAGE_SIZE; - spin_lock(¤t->mm->page_table_lock); - for (vma = current->mm->mmap; vma; vma = vma->vm_next) - if ((vma->vm_flags & VM_EXEC) && (vma->vm_end > limit)) - limit = vma->vm_end; - spin_unlock(¤t->mm->page_table_lock); - if (limit >= TASK_SIZE) - limit = -1UL; - current->mm->context.exec_limit = limit; - } - set_user_cs(¤t->mm->context.user_cs, limit); - - desc1 = ¤t->mm->context.user_cs; - desc2 = get_cpu_gdt_table(cpu) + GDT_ENTRY_DEFAULT_USER_CS; - - if (desc1->a != desc2->a || desc1->b != desc2->b) { - /* - * The CS was not in sync - reload it and retry the - * instruction. If the instruction still faults then - * we won't hit this branch next time around. - */ - if (print_fatal_signals >= 2) { - printk("#GPF fixup (%ld[seg:%lx]) at %08lx, CPU#%d.\n", error_code, error_code/8, regs->eip, smp_processor_id()); - printk(" exec_limit: %08lx, user_cs: %08lx/%08lx, CPU_cs: %08lx/%08lx.\n", current->mm->context.exec_limit, desc1->a, desc1->b, desc2->a, desc2->b); - } - load_user_cs_desc(cpu, current->mm); - return 1; - } - - return 0; -} - -/* - * The fixup code for errors in iret jumps to here (iret_exc). It loses - * the original trap number and error code. The bogus trap 32 and error - * code 0 are what the vanilla kernel delivers via: - * DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0) - * - * In case of a general protection fault in the iret instruction, we - * need to check for a lazy CS update for exec-shield. - */ -fastcall void do_iret_error(struct pt_regs *regs, long error_code) -{ - int ok = check_lazy_exec_limit(get_cpu(), regs, error_code); - put_cpu(); - if (!ok && notify_die(DIE_TRAP, "iret exception", regs, - error_code, 32, SIGSEGV) != NOTIFY_STOP) { - siginfo_t info; - info.si_signo = SIGSEGV; - info.si_errno = 0; - info.si_code = ILL_BADSTK; - info.si_addr = 0; - do_trap(32, SIGSEGV, "iret exception", 0, regs, error_code, - &info); - } -} +DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0) fastcall void __kprobes do_general_protection(struct pt_regs * regs, long error_code) @@ -716,7 +503,6 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs, int cpu = get_cpu(); struct tss_struct *tss = &per_cpu(init_tss, cpu); struct thread_struct *thread = ¤t->thread; - int ok; /* * Perform the lazy TSS's I/O bitmap copy. If the TSS has an @@ -743,6 +529,7 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs, put_cpu(); return; } + put_cpu(); current->thread.error_code = error_code; current->thread.trap_no = 13; @@ -753,31 +540,17 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs, if (!user_mode(regs)) goto gp_in_kernel; - ok = check_lazy_exec_limit(cpu, regs, error_code); - - put_cpu(); - - if (ok) - return; - - if (print_fatal_signals) { - printk("#GPF(%ld[seg:%lx]) at %08lx, CPU#%d.\n", error_code, error_code/8, regs->eip, smp_processor_id()); - printk(" exec_limit: %08lx, user_cs: %08lx/%08lx.\n", current->mm->context.exec_limit, current->mm->context.user_cs.a, current->mm->context.user_cs.b); - } - current->thread.error_code = error_code; current->thread.trap_no = 13; force_sig(SIGSEGV, current); return; gp_in_vm86: - put_cpu(); local_irq_enable(); handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); return; gp_in_kernel: - put_cpu(); if (!fixup_exception(regs)) { if (notify_die(DIE_GPF, "general protection fault", regs, error_code, 13, SIGSEGV) == NOTIFY_STOP) @@ -799,11 +572,18 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs) static void io_check_error(unsigned char reason, struct pt_regs * regs) { + unsigned long i; + printk(KERN_EMERG "NMI: IOCK error (debug interrupt?)\n"); show_registers(regs); /* Re-enable the IOCK line, wait for a few seconds */ - clear_io_check_error(reason); + reason = (reason & 0xf) | 8; + outb(reason, 0x61); + i = 2000; + while (--i) udelay(1000); + reason &= ~8; + outb(reason, 0x61); } static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) @@ -826,7 +606,7 @@ static DEFINE_SPINLOCK(nmi_print_lock); void die_nmi (struct pt_regs *regs, const char *msg) { - if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) == + if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 0, SIGINT) == NOTIFY_STOP) return; @@ -840,6 +620,7 @@ void die_nmi (struct pt_regs *regs, const char *msg) printk(" on CPU%d, eip %08lx, registers:\n", smp_processor_id(), regs->eip); show_registers(regs); + printk(KERN_EMERG "console shuts up ...\n"); console_silent(); spin_unlock(&nmi_print_lock); bust_spinlocks(0); @@ -847,7 +628,7 @@ void die_nmi (struct pt_regs *regs, const char *msg) /* 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_vm(regs)) { + if (!user_mode(regs)) { current->thread.trap_no = 2; crash_kexec(regs); } @@ -864,7 +645,7 @@ static void default_do_nmi(struct pt_regs * regs) reason = get_nmi_reason(); if (!(reason & 0xc0)) { - if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT) + if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT) == NOTIFY_STOP) return; #ifdef CONFIG_X86_LOCAL_APIC @@ -880,7 +661,7 @@ static void default_do_nmi(struct pt_regs * regs) unknown_nmi_error(reason, regs); return; } - if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) + if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_STOP) return; if (reason & 0x80) mem_parity_error(reason, regs); @@ -918,7 +699,6 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code) void set_nmi_callback(nmi_callback_t callback) { - vmalloc_sync_all(); rcu_assign_pointer(nmi_callback, callback); } EXPORT_SYMBOL_GPL(set_nmi_callback); @@ -1389,22 +1169,6 @@ void __init trap_init(void) static int __init kstack_setup(char *s) { kstack_depth_to_print = simple_strtoul(s, NULL, 0); - return 1; + return 0; } __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