diff options
Diffstat (limited to 'arch/i386/kernel/traps.c')
-rw-r--r-- | arch/i386/kernel/traps.c | 163 |
1 files changed, 67 insertions, 96 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 00489b706d27..2b30dbf8d117 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
@@ -29,6 +29,8 @@ | |||
29 | #include <linux/kexec.h> | 29 | #include <linux/kexec.h> |
30 | #include <linux/unwind.h> | 30 | #include <linux/unwind.h> |
31 | #include <linux/uaccess.h> | 31 | #include <linux/uaccess.h> |
32 | #include <linux/nmi.h> | ||
33 | #include <linux/bug.h> | ||
32 | 34 | ||
33 | #ifdef CONFIG_EISA | 35 | #ifdef CONFIG_EISA |
34 | #include <linux/ioport.h> | 36 | #include <linux/ioport.h> |
@@ -61,9 +63,6 @@ int panic_on_unrecovered_nmi; | |||
61 | 63 | ||
62 | asmlinkage int system_call(void); | 64 | asmlinkage int system_call(void); |
63 | 65 | ||
64 | struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, | ||
65 | { 0, 0 }, { 0, 0 } }; | ||
66 | |||
67 | /* Do we ignore FPU interrupts ? */ | 66 | /* Do we ignore FPU interrupts ? */ |
68 | char ignore_fpu_irq = 0; | 67 | char ignore_fpu_irq = 0; |
69 | 68 | ||
@@ -94,7 +93,7 @@ asmlinkage void alignment_check(void); | |||
94 | asmlinkage void spurious_interrupt_bug(void); | 93 | asmlinkage void spurious_interrupt_bug(void); |
95 | asmlinkage void machine_check(void); | 94 | asmlinkage void machine_check(void); |
96 | 95 | ||
97 | static int kstack_depth_to_print = 24; | 96 | int kstack_depth_to_print = 24; |
98 | #ifdef CONFIG_STACK_UNWIND | 97 | #ifdef CONFIG_STACK_UNWIND |
99 | static int call_trace = 1; | 98 | static int call_trace = 1; |
100 | #else | 99 | #else |
@@ -129,15 +128,19 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo, | |||
129 | 128 | ||
130 | #ifdef CONFIG_FRAME_POINTER | 129 | #ifdef CONFIG_FRAME_POINTER |
131 | while (valid_stack_ptr(tinfo, (void *)ebp)) { | 130 | while (valid_stack_ptr(tinfo, (void *)ebp)) { |
131 | unsigned long new_ebp; | ||
132 | addr = *(unsigned long *)(ebp + 4); | 132 | addr = *(unsigned long *)(ebp + 4); |
133 | ops->address(data, addr); | 133 | ops->address(data, addr); |
134 | /* | 134 | /* |
135 | * break out of recursive entries (such as | 135 | * break out of recursive entries (such as |
136 | * end_of_stack_stop_unwind_function): | 136 | * end_of_stack_stop_unwind_function). Also, |
137 | * we can never allow a frame pointer to | ||
138 | * move downwards! | ||
137 | */ | 139 | */ |
138 | if (ebp == *(unsigned long *)ebp) | 140 | new_ebp = *(unsigned long *)ebp; |
141 | if (new_ebp <= ebp) | ||
139 | break; | 142 | break; |
140 | ebp = *(unsigned long *)ebp; | 143 | ebp = new_ebp; |
141 | } | 144 | } |
142 | #else | 145 | #else |
143 | while (valid_stack_ptr(tinfo, stack)) { | 146 | while (valid_stack_ptr(tinfo, stack)) { |
@@ -159,16 +162,25 @@ dump_trace_unwind(struct unwind_frame_info *info, void *data) | |||
159 | { | 162 | { |
160 | struct ops_and_data *oad = (struct ops_and_data *)data; | 163 | struct ops_and_data *oad = (struct ops_and_data *)data; |
161 | int n = 0; | 164 | int n = 0; |
165 | unsigned long sp = UNW_SP(info); | ||
162 | 166 | ||
167 | if (arch_unw_user_mode(info)) | ||
168 | return -1; | ||
163 | while (unwind(info) == 0 && UNW_PC(info)) { | 169 | while (unwind(info) == 0 && UNW_PC(info)) { |
164 | n++; | 170 | n++; |
165 | oad->ops->address(oad->data, UNW_PC(info)); | 171 | oad->ops->address(oad->data, UNW_PC(info)); |
166 | if (arch_unw_user_mode(info)) | 172 | if (arch_unw_user_mode(info)) |
167 | break; | 173 | break; |
174 | if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1)) | ||
175 | && sp > UNW_SP(info)) | ||
176 | break; | ||
177 | sp = UNW_SP(info); | ||
168 | } | 178 | } |
169 | return n; | 179 | return n; |
170 | } | 180 | } |
171 | 181 | ||
182 | #define MSG(msg) ops->warning(data, msg) | ||
183 | |||
172 | void dump_trace(struct task_struct *task, struct pt_regs *regs, | 184 | void dump_trace(struct task_struct *task, struct pt_regs *regs, |
173 | unsigned long *stack, | 185 | unsigned long *stack, |
174 | struct stacktrace_ops *ops, void *data) | 186 | struct stacktrace_ops *ops, void *data) |
@@ -187,29 +199,31 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
187 | if (unwind_init_frame_info(&info, task, regs) == 0) | 199 | if (unwind_init_frame_info(&info, task, regs) == 0) |
188 | unw_ret = dump_trace_unwind(&info, &oad); | 200 | unw_ret = dump_trace_unwind(&info, &oad); |
189 | } else if (task == current) | 201 | } else if (task == current) |
190 | unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); | 202 | unw_ret = unwind_init_running(&info, dump_trace_unwind, |
203 | &oad); | ||
191 | else { | 204 | else { |
192 | if (unwind_init_blocked(&info, task) == 0) | 205 | if (unwind_init_blocked(&info, task) == 0) |
193 | unw_ret = dump_trace_unwind(&info, &oad); | 206 | unw_ret = dump_trace_unwind(&info, &oad); |
194 | } | 207 | } |
195 | if (unw_ret > 0) { | 208 | if (unw_ret > 0) { |
196 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { | 209 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { |
197 | ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", | 210 | ops->warning_symbol(data, |
211 | "DWARF2 unwinder stuck at %s", | ||
198 | UNW_PC(&info)); | 212 | UNW_PC(&info)); |
199 | if (UNW_SP(&info) >= PAGE_OFFSET) { | 213 | if (UNW_SP(&info) >= PAGE_OFFSET) { |
200 | ops->warning(data, "Leftover inexact backtrace:\n"); | 214 | MSG("Leftover inexact backtrace:"); |
201 | stack = (void *)UNW_SP(&info); | 215 | stack = (void *)UNW_SP(&info); |
202 | if (!stack) | 216 | if (!stack) |
203 | return; | 217 | return; |
204 | ebp = UNW_FP(&info); | 218 | ebp = UNW_FP(&info); |
205 | } else | 219 | } else |
206 | ops->warning(data, "Full inexact backtrace again:\n"); | 220 | MSG("Full inexact backtrace again:"); |
207 | } else if (call_trace >= 1) | 221 | } else if (call_trace >= 1) |
208 | return; | 222 | return; |
209 | else | 223 | else |
210 | ops->warning(data, "Full inexact backtrace again:\n"); | 224 | MSG("Full inexact backtrace again:"); |
211 | } else | 225 | } else |
212 | ops->warning(data, "Inexact backtrace:\n"); | 226 | MSG("Inexact backtrace:"); |
213 | } | 227 | } |
214 | if (!stack) { | 228 | if (!stack) { |
215 | unsigned long dummy; | 229 | unsigned long dummy; |
@@ -243,6 +257,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
243 | stack = (unsigned long*)context->previous_esp; | 257 | stack = (unsigned long*)context->previous_esp; |
244 | if (!stack) | 258 | if (!stack) |
245 | break; | 259 | break; |
260 | touch_nmi_watchdog(); | ||
246 | } | 261 | } |
247 | } | 262 | } |
248 | EXPORT_SYMBOL(dump_trace); | 263 | EXPORT_SYMBOL(dump_trace); |
@@ -375,7 +390,7 @@ void show_registers(struct pt_regs *regs) | |||
375 | * time of the fault.. | 390 | * time of the fault.. |
376 | */ | 391 | */ |
377 | if (in_kernel) { | 392 | if (in_kernel) { |
378 | u8 __user *eip; | 393 | u8 *eip; |
379 | int code_bytes = 64; | 394 | int code_bytes = 64; |
380 | unsigned char c; | 395 | unsigned char c; |
381 | 396 | ||
@@ -384,18 +399,20 @@ void show_registers(struct pt_regs *regs) | |||
384 | 399 | ||
385 | printk(KERN_EMERG "Code: "); | 400 | printk(KERN_EMERG "Code: "); |
386 | 401 | ||
387 | eip = (u8 __user *)regs->eip - 43; | 402 | eip = (u8 *)regs->eip - 43; |
388 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { | 403 | if (eip < (u8 *)PAGE_OFFSET || |
404 | probe_kernel_address(eip, c)) { | ||
389 | /* try starting at EIP */ | 405 | /* try starting at EIP */ |
390 | eip = (u8 __user *)regs->eip; | 406 | eip = (u8 *)regs->eip; |
391 | code_bytes = 32; | 407 | code_bytes = 32; |
392 | } | 408 | } |
393 | for (i = 0; i < code_bytes; i++, eip++) { | 409 | for (i = 0; i < code_bytes; i++, eip++) { |
394 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { | 410 | if (eip < (u8 *)PAGE_OFFSET || |
411 | probe_kernel_address(eip, c)) { | ||
395 | printk(" Bad EIP value."); | 412 | printk(" Bad EIP value."); |
396 | break; | 413 | break; |
397 | } | 414 | } |
398 | if (eip == (u8 __user *)regs->eip) | 415 | if (eip == (u8 *)regs->eip) |
399 | printk("<%02x> ", c); | 416 | printk("<%02x> ", c); |
400 | else | 417 | else |
401 | printk("%02x ", c); | 418 | printk("%02x ", c); |
@@ -404,43 +421,22 @@ void show_registers(struct pt_regs *regs) | |||
404 | printk("\n"); | 421 | printk("\n"); |
405 | } | 422 | } |
406 | 423 | ||
407 | static void handle_BUG(struct pt_regs *regs) | 424 | int is_valid_bugaddr(unsigned long eip) |
408 | { | 425 | { |
409 | unsigned long eip = regs->eip; | ||
410 | unsigned short ud2; | 426 | unsigned short ud2; |
411 | 427 | ||
412 | if (eip < PAGE_OFFSET) | 428 | if (eip < PAGE_OFFSET) |
413 | return; | 429 | return 0; |
414 | if (probe_kernel_address((unsigned short __user *)eip, ud2)) | 430 | if (probe_kernel_address((unsigned short *)eip, ud2)) |
415 | return; | 431 | return 0; |
416 | if (ud2 != 0x0b0f) | ||
417 | return; | ||
418 | |||
419 | printk(KERN_EMERG "------------[ cut here ]------------\n"); | ||
420 | |||
421 | #ifdef CONFIG_DEBUG_BUGVERBOSE | ||
422 | do { | ||
423 | unsigned short line; | ||
424 | char *file; | ||
425 | char c; | ||
426 | |||
427 | if (probe_kernel_address((unsigned short __user *)(eip + 2), | ||
428 | line)) | ||
429 | break; | ||
430 | if (__get_user(file, (char * __user *)(eip + 4)) || | ||
431 | (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) | ||
432 | file = "<bad filename>"; | ||
433 | 432 | ||
434 | printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); | 433 | return ud2 == 0x0b0f; |
435 | return; | ||
436 | } while (0); | ||
437 | #endif | ||
438 | printk(KERN_EMERG "Kernel BUG at [verbose debug info unavailable]\n"); | ||
439 | } | 434 | } |
440 | 435 | ||
441 | /* This is gone through when something in the kernel | 436 | /* |
442 | * has done something bad and is about to be terminated. | 437 | * This is gone through when something in the kernel has done something bad and |
443 | */ | 438 | * is about to be terminated. |
439 | */ | ||
444 | void die(const char * str, struct pt_regs * regs, long err) | 440 | void die(const char * str, struct pt_regs * regs, long err) |
445 | { | 441 | { |
446 | static struct { | 442 | static struct { |
@@ -448,7 +444,7 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
448 | u32 lock_owner; | 444 | u32 lock_owner; |
449 | int lock_owner_depth; | 445 | int lock_owner_depth; |
450 | } die = { | 446 | } die = { |
451 | .lock = SPIN_LOCK_UNLOCKED, | 447 | .lock = __SPIN_LOCK_UNLOCKED(die.lock), |
452 | .lock_owner = -1, | 448 | .lock_owner = -1, |
453 | .lock_owner_depth = 0 | 449 | .lock_owner_depth = 0 |
454 | }; | 450 | }; |
@@ -472,7 +468,8 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
472 | unsigned long esp; | 468 | unsigned long esp; |
473 | unsigned short ss; | 469 | unsigned short ss; |
474 | 470 | ||
475 | handle_BUG(regs); | 471 | report_bug(regs->eip); |
472 | |||
476 | printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); | 473 | printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); |
477 | #ifdef CONFIG_PREEMPT | 474 | #ifdef CONFIG_PREEMPT |
478 | printk(KERN_EMERG "PREEMPT "); | 475 | printk(KERN_EMERG "PREEMPT "); |
@@ -703,8 +700,7 @@ mem_parity_error(unsigned char reason, struct pt_regs * regs) | |||
703 | { | 700 | { |
704 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " | 701 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " |
705 | "CPU %d.\n", reason, smp_processor_id()); | 702 | "CPU %d.\n", reason, smp_processor_id()); |
706 | printk(KERN_EMERG "You probably have a hardware problem with your RAM " | 703 | printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); |
707 | "chips\n"); | ||
708 | if (panic_on_unrecovered_nmi) | 704 | if (panic_on_unrecovered_nmi) |
709 | panic("NMI: Not continuing"); | 705 | panic("NMI: Not continuing"); |
710 | 706 | ||
@@ -769,7 +765,6 @@ void __kprobes die_nmi(struct pt_regs *regs, const char *msg) | |||
769 | printk(" on CPU%d, eip %08lx, registers:\n", | 765 | printk(" on CPU%d, eip %08lx, registers:\n", |
770 | smp_processor_id(), regs->eip); | 766 | smp_processor_id(), regs->eip); |
771 | show_registers(regs); | 767 | show_registers(regs); |
772 | printk(KERN_EMERG "console shuts up ...\n"); | ||
773 | console_silent(); | 768 | console_silent(); |
774 | spin_unlock(&nmi_print_lock); | 769 | spin_unlock(&nmi_print_lock); |
775 | bust_spinlocks(0); | 770 | bust_spinlocks(0); |
@@ -1084,49 +1079,24 @@ fastcall void do_spurious_interrupt_bug(struct pt_regs * regs, | |||
1084 | #endif | 1079 | #endif |
1085 | } | 1080 | } |
1086 | 1081 | ||
1087 | fastcall void setup_x86_bogus_stack(unsigned char * stk) | 1082 | fastcall unsigned long patch_espfix_desc(unsigned long uesp, |
1088 | { | 1083 | unsigned long kesp) |
1089 | unsigned long *switch16_ptr, *switch32_ptr; | ||
1090 | struct pt_regs *regs; | ||
1091 | unsigned long stack_top, stack_bot; | ||
1092 | unsigned short iret_frame16_off; | ||
1093 | int cpu = smp_processor_id(); | ||
1094 | /* reserve the space on 32bit stack for the magic switch16 pointer */ | ||
1095 | memmove(stk, stk + 8, sizeof(struct pt_regs)); | ||
1096 | switch16_ptr = (unsigned long *)(stk + sizeof(struct pt_regs)); | ||
1097 | regs = (struct pt_regs *)stk; | ||
1098 | /* now the switch32 on 16bit stack */ | ||
1099 | stack_bot = (unsigned long)&per_cpu(cpu_16bit_stack, cpu); | ||
1100 | stack_top = stack_bot + CPU_16BIT_STACK_SIZE; | ||
1101 | switch32_ptr = (unsigned long *)(stack_top - 8); | ||
1102 | iret_frame16_off = CPU_16BIT_STACK_SIZE - 8 - 20; | ||
1103 | /* copy iret frame on 16bit stack */ | ||
1104 | memcpy((void *)(stack_bot + iret_frame16_off), ®s->eip, 20); | ||
1105 | /* fill in the switch pointers */ | ||
1106 | switch16_ptr[0] = (regs->esp & 0xffff0000) | iret_frame16_off; | ||
1107 | switch16_ptr[1] = __ESPFIX_SS; | ||
1108 | switch32_ptr[0] = (unsigned long)stk + sizeof(struct pt_regs) + | ||
1109 | 8 - CPU_16BIT_STACK_SIZE; | ||
1110 | switch32_ptr[1] = __KERNEL_DS; | ||
1111 | } | ||
1112 | |||
1113 | fastcall unsigned char * fixup_x86_bogus_stack(unsigned short sp) | ||
1114 | { | 1084 | { |
1115 | unsigned long *switch32_ptr; | ||
1116 | unsigned char *stack16, *stack32; | ||
1117 | unsigned long stack_top, stack_bot; | ||
1118 | int len; | ||
1119 | int cpu = smp_processor_id(); | 1085 | int cpu = smp_processor_id(); |
1120 | stack_bot = (unsigned long)&per_cpu(cpu_16bit_stack, cpu); | 1086 | struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu); |
1121 | stack_top = stack_bot + CPU_16BIT_STACK_SIZE; | 1087 | struct desc_struct *gdt = (struct desc_struct *)cpu_gdt_descr->address; |
1122 | switch32_ptr = (unsigned long *)(stack_top - 8); | 1088 | unsigned long base = (kesp - uesp) & -THREAD_SIZE; |
1123 | /* copy the data from 16bit stack to 32bit stack */ | 1089 | unsigned long new_kesp = kesp - base; |
1124 | len = CPU_16BIT_STACK_SIZE - 8 - sp; | 1090 | unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT; |
1125 | stack16 = (unsigned char *)(stack_bot + sp); | 1091 | __u64 desc = *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS]; |
1126 | stack32 = (unsigned char *) | 1092 | /* Set up base for espfix segment */ |
1127 | (switch32_ptr[0] + CPU_16BIT_STACK_SIZE - 8 - len); | 1093 | desc &= 0x00f0ff0000000000ULL; |
1128 | memcpy(stack32, stack16, len); | 1094 | desc |= ((((__u64)base) << 16) & 0x000000ffffff0000ULL) | |
1129 | return stack32; | 1095 | ((((__u64)base) << 32) & 0xff00000000000000ULL) | |
1096 | ((((__u64)lim_pages) << 32) & 0x000f000000000000ULL) | | ||
1097 | (lim_pages & 0xffff); | ||
1098 | *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS] = desc; | ||
1099 | return new_kesp; | ||
1130 | } | 1100 | } |
1131 | 1101 | ||
1132 | /* | 1102 | /* |
@@ -1139,7 +1109,7 @@ fastcall unsigned char * fixup_x86_bogus_stack(unsigned short sp) | |||
1139 | * Must be called with kernel preemption disabled (in this case, | 1109 | * Must be called with kernel preemption disabled (in this case, |
1140 | * local interrupts are disabled at the call-site in entry.S). | 1110 | * local interrupts are disabled at the call-site in entry.S). |
1141 | */ | 1111 | */ |
1142 | asmlinkage void math_state_restore(struct pt_regs regs) | 1112 | asmlinkage void math_state_restore(void) |
1143 | { | 1113 | { |
1144 | struct thread_info *thread = current_thread_info(); | 1114 | struct thread_info *thread = current_thread_info(); |
1145 | struct task_struct *tsk = thread->task; | 1115 | struct task_struct *tsk = thread->task; |
@@ -1149,6 +1119,7 @@ asmlinkage void math_state_restore(struct pt_regs regs) | |||
1149 | init_fpu(tsk); | 1119 | init_fpu(tsk); |
1150 | restore_fpu(tsk); | 1120 | restore_fpu(tsk); |
1151 | thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ | 1121 | thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ |
1122 | tsk->fpu_counter++; | ||
1152 | } | 1123 | } |
1153 | 1124 | ||
1154 | #ifndef CONFIG_MATH_EMULATION | 1125 | #ifndef CONFIG_MATH_EMULATION |