diff options
author | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-07 11:59:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-07 11:59:11 -0500 |
commit | 4522d58275f124105819723e24e912c8e5bf3cdd (patch) | |
tree | b92c29014fadffe049c1925676037f0092b8d112 /arch/i386/kernel/traps.c | |
parent | 6cf24f031bc97cb5a7c9df3b6e73c45b628b2b28 (diff) | |
parent | 64a26a731235b59c9d73bbe82c1f896d57400d37 (diff) |
Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6
* 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6: (156 commits)
[PATCH] x86-64: Export smp_call_function_single
[PATCH] i386: Clean up smp_tune_scheduling()
[PATCH] unwinder: move .eh_frame to RODATA
[PATCH] unwinder: fully support linker generated .eh_frame_hdr section
[PATCH] x86-64: don't use set_irq_regs()
[PATCH] x86-64: check vector in setup_ioapic_dest to verify if need setup_IO_APIC_irq
[PATCH] x86-64: Make ix86 default to HIGHMEM4G instead of NOHIGHMEM
[PATCH] i386: replace kmalloc+memset with kzalloc
[PATCH] x86-64: remove remaining pc98 code
[PATCH] x86-64: remove unused variable
[PATCH] x86-64: Fix constraints in atomic_add_return()
[PATCH] x86-64: fix asm constraints in i386 atomic_add_return
[PATCH] x86-64: Correct documentation for bzImage protocol v2.05
[PATCH] x86-64: replace kmalloc+memset with kzalloc in MTRR code
[PATCH] x86-64: Fix numaq build error
[PATCH] x86-64: include/asm-x86_64/cpufeature.h isn't a userspace header
[PATCH] unwinder: Add debugging output to the Dwarf2 unwinder
[PATCH] x86-64: Clarify error message in GART code
[PATCH] x86-64: Fix interrupt race in idle callback (3rd try)
[PATCH] x86-64: Remove unwind stack pointer alignment forcing again
...
Fixed conflict in include/linux/uaccess.h manually
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/traps.c')
-rw-r--r-- | arch/i386/kernel/traps.c | 118 |
1 files changed, 52 insertions, 66 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 3124f1b04d67..68de48e498ca 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
@@ -29,6 +29,7 @@ | |||
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> | ||
32 | 33 | ||
33 | #ifdef CONFIG_EISA | 34 | #ifdef CONFIG_EISA |
34 | #include <linux/ioport.h> | 35 | #include <linux/ioport.h> |
@@ -61,9 +62,6 @@ int panic_on_unrecovered_nmi; | |||
61 | 62 | ||
62 | asmlinkage int system_call(void); | 63 | asmlinkage int system_call(void); |
63 | 64 | ||
64 | struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, | ||
65 | { 0, 0 }, { 0, 0 } }; | ||
66 | |||
67 | /* Do we ignore FPU interrupts ? */ | 65 | /* Do we ignore FPU interrupts ? */ |
68 | char ignore_fpu_irq = 0; | 66 | char ignore_fpu_irq = 0; |
69 | 67 | ||
@@ -94,7 +92,7 @@ asmlinkage void alignment_check(void); | |||
94 | asmlinkage void spurious_interrupt_bug(void); | 92 | asmlinkage void spurious_interrupt_bug(void); |
95 | asmlinkage void machine_check(void); | 93 | asmlinkage void machine_check(void); |
96 | 94 | ||
97 | static int kstack_depth_to_print = 24; | 95 | int kstack_depth_to_print = 24; |
98 | #ifdef CONFIG_STACK_UNWIND | 96 | #ifdef CONFIG_STACK_UNWIND |
99 | static int call_trace = 1; | 97 | static int call_trace = 1; |
100 | #else | 98 | #else |
@@ -163,16 +161,25 @@ dump_trace_unwind(struct unwind_frame_info *info, void *data) | |||
163 | { | 161 | { |
164 | struct ops_and_data *oad = (struct ops_and_data *)data; | 162 | struct ops_and_data *oad = (struct ops_and_data *)data; |
165 | int n = 0; | 163 | int n = 0; |
164 | unsigned long sp = UNW_SP(info); | ||
166 | 165 | ||
166 | if (arch_unw_user_mode(info)) | ||
167 | return -1; | ||
167 | while (unwind(info) == 0 && UNW_PC(info)) { | 168 | while (unwind(info) == 0 && UNW_PC(info)) { |
168 | n++; | 169 | n++; |
169 | oad->ops->address(oad->data, UNW_PC(info)); | 170 | oad->ops->address(oad->data, UNW_PC(info)); |
170 | if (arch_unw_user_mode(info)) | 171 | if (arch_unw_user_mode(info)) |
171 | break; | 172 | break; |
173 | if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1)) | ||
174 | && sp > UNW_SP(info)) | ||
175 | break; | ||
176 | sp = UNW_SP(info); | ||
172 | } | 177 | } |
173 | return n; | 178 | return n; |
174 | } | 179 | } |
175 | 180 | ||
181 | #define MSG(msg) ops->warning(data, msg) | ||
182 | |||
176 | void dump_trace(struct task_struct *task, struct pt_regs *regs, | 183 | void dump_trace(struct task_struct *task, struct pt_regs *regs, |
177 | unsigned long *stack, | 184 | unsigned long *stack, |
178 | struct stacktrace_ops *ops, void *data) | 185 | struct stacktrace_ops *ops, void *data) |
@@ -191,29 +198,31 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
191 | if (unwind_init_frame_info(&info, task, regs) == 0) | 198 | if (unwind_init_frame_info(&info, task, regs) == 0) |
192 | unw_ret = dump_trace_unwind(&info, &oad); | 199 | unw_ret = dump_trace_unwind(&info, &oad); |
193 | } else if (task == current) | 200 | } else if (task == current) |
194 | unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); | 201 | unw_ret = unwind_init_running(&info, dump_trace_unwind, |
202 | &oad); | ||
195 | else { | 203 | else { |
196 | if (unwind_init_blocked(&info, task) == 0) | 204 | if (unwind_init_blocked(&info, task) == 0) |
197 | unw_ret = dump_trace_unwind(&info, &oad); | 205 | unw_ret = dump_trace_unwind(&info, &oad); |
198 | } | 206 | } |
199 | if (unw_ret > 0) { | 207 | if (unw_ret > 0) { |
200 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { | 208 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { |
201 | ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", | 209 | ops->warning_symbol(data, |
210 | "DWARF2 unwinder stuck at %s", | ||
202 | UNW_PC(&info)); | 211 | UNW_PC(&info)); |
203 | if (UNW_SP(&info) >= PAGE_OFFSET) { | 212 | if (UNW_SP(&info) >= PAGE_OFFSET) { |
204 | ops->warning(data, "Leftover inexact backtrace:\n"); | 213 | MSG("Leftover inexact backtrace:"); |
205 | stack = (void *)UNW_SP(&info); | 214 | stack = (void *)UNW_SP(&info); |
206 | if (!stack) | 215 | if (!stack) |
207 | return; | 216 | return; |
208 | ebp = UNW_FP(&info); | 217 | ebp = UNW_FP(&info); |
209 | } else | 218 | } else |
210 | ops->warning(data, "Full inexact backtrace again:\n"); | 219 | MSG("Full inexact backtrace again:"); |
211 | } else if (call_trace >= 1) | 220 | } else if (call_trace >= 1) |
212 | return; | 221 | return; |
213 | else | 222 | else |
214 | ops->warning(data, "Full inexact backtrace again:\n"); | 223 | MSG("Full inexact backtrace again:"); |
215 | } else | 224 | } else |
216 | ops->warning(data, "Inexact backtrace:\n"); | 225 | MSG("Inexact backtrace:"); |
217 | } | 226 | } |
218 | if (!stack) { | 227 | if (!stack) { |
219 | unsigned long dummy; | 228 | unsigned long dummy; |
@@ -247,6 +256,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
247 | stack = (unsigned long*)context->previous_esp; | 256 | stack = (unsigned long*)context->previous_esp; |
248 | if (!stack) | 257 | if (!stack) |
249 | break; | 258 | break; |
259 | touch_nmi_watchdog(); | ||
250 | } | 260 | } |
251 | } | 261 | } |
252 | EXPORT_SYMBOL(dump_trace); | 262 | EXPORT_SYMBOL(dump_trace); |
@@ -379,7 +389,7 @@ void show_registers(struct pt_regs *regs) | |||
379 | * time of the fault.. | 389 | * time of the fault.. |
380 | */ | 390 | */ |
381 | if (in_kernel) { | 391 | if (in_kernel) { |
382 | u8 __user *eip; | 392 | u8 *eip; |
383 | int code_bytes = 64; | 393 | int code_bytes = 64; |
384 | unsigned char c; | 394 | unsigned char c; |
385 | 395 | ||
@@ -388,18 +398,20 @@ void show_registers(struct pt_regs *regs) | |||
388 | 398 | ||
389 | printk(KERN_EMERG "Code: "); | 399 | printk(KERN_EMERG "Code: "); |
390 | 400 | ||
391 | eip = (u8 __user *)regs->eip - 43; | 401 | eip = (u8 *)regs->eip - 43; |
392 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { | 402 | if (eip < (u8 *)PAGE_OFFSET || |
403 | probe_kernel_address(eip, c)) { | ||
393 | /* try starting at EIP */ | 404 | /* try starting at EIP */ |
394 | eip = (u8 __user *)regs->eip; | 405 | eip = (u8 *)regs->eip; |
395 | code_bytes = 32; | 406 | code_bytes = 32; |
396 | } | 407 | } |
397 | for (i = 0; i < code_bytes; i++, eip++) { | 408 | for (i = 0; i < code_bytes; i++, eip++) { |
398 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { | 409 | if (eip < (u8 *)PAGE_OFFSET || |
410 | probe_kernel_address(eip, c)) { | ||
399 | printk(" Bad EIP value."); | 411 | printk(" Bad EIP value."); |
400 | break; | 412 | break; |
401 | } | 413 | } |
402 | if (eip == (u8 __user *)regs->eip) | 414 | if (eip == (u8 *)regs->eip) |
403 | printk("<%02x> ", c); | 415 | printk("<%02x> ", c); |
404 | else | 416 | else |
405 | printk("%02x ", c); | 417 | printk("%02x ", c); |
@@ -415,7 +427,7 @@ static void handle_BUG(struct pt_regs *regs) | |||
415 | 427 | ||
416 | if (eip < PAGE_OFFSET) | 428 | if (eip < PAGE_OFFSET) |
417 | return; | 429 | return; |
418 | if (probe_kernel_address((unsigned short __user *)eip, ud2)) | 430 | if (probe_kernel_address((unsigned short *)eip, ud2)) |
419 | return; | 431 | return; |
420 | if (ud2 != 0x0b0f) | 432 | if (ud2 != 0x0b0f) |
421 | return; | 433 | return; |
@@ -428,11 +440,11 @@ static void handle_BUG(struct pt_regs *regs) | |||
428 | char *file; | 440 | char *file; |
429 | char c; | 441 | char c; |
430 | 442 | ||
431 | if (probe_kernel_address((unsigned short __user *)(eip + 2), | 443 | if (probe_kernel_address((unsigned short *)(eip + 2), line)) |
432 | line)) | ||
433 | break; | 444 | break; |
434 | if (__get_user(file, (char * __user *)(eip + 4)) || | 445 | if (probe_kernel_address((char **)(eip + 4), file) || |
435 | (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) | 446 | (unsigned long)file < PAGE_OFFSET || |
447 | probe_kernel_address(file, c)) | ||
436 | file = "<bad filename>"; | 448 | file = "<bad filename>"; |
437 | 449 | ||
438 | printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); | 450 | printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); |
@@ -707,8 +719,7 @@ mem_parity_error(unsigned char reason, struct pt_regs * regs) | |||
707 | { | 719 | { |
708 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " | 720 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " |
709 | "CPU %d.\n", reason, smp_processor_id()); | 721 | "CPU %d.\n", reason, smp_processor_id()); |
710 | printk(KERN_EMERG "You probably have a hardware problem with your RAM " | 722 | printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); |
711 | "chips\n"); | ||
712 | if (panic_on_unrecovered_nmi) | 723 | if (panic_on_unrecovered_nmi) |
713 | panic("NMI: Not continuing"); | 724 | panic("NMI: Not continuing"); |
714 | 725 | ||
@@ -773,7 +784,6 @@ void __kprobes die_nmi(struct pt_regs *regs, const char *msg) | |||
773 | printk(" on CPU%d, eip %08lx, registers:\n", | 784 | printk(" on CPU%d, eip %08lx, registers:\n", |
774 | smp_processor_id(), regs->eip); | 785 | smp_processor_id(), regs->eip); |
775 | show_registers(regs); | 786 | show_registers(regs); |
776 | printk(KERN_EMERG "console shuts up ...\n"); | ||
777 | console_silent(); | 787 | console_silent(); |
778 | spin_unlock(&nmi_print_lock); | 788 | spin_unlock(&nmi_print_lock); |
779 | bust_spinlocks(0); | 789 | bust_spinlocks(0); |
@@ -1088,49 +1098,24 @@ fastcall void do_spurious_interrupt_bug(struct pt_regs * regs, | |||
1088 | #endif | 1098 | #endif |
1089 | } | 1099 | } |
1090 | 1100 | ||
1091 | fastcall void setup_x86_bogus_stack(unsigned char * stk) | 1101 | fastcall unsigned long patch_espfix_desc(unsigned long uesp, |
1092 | { | 1102 | unsigned long kesp) |
1093 | unsigned long *switch16_ptr, *switch32_ptr; | ||
1094 | struct pt_regs *regs; | ||
1095 | unsigned long stack_top, stack_bot; | ||
1096 | unsigned short iret_frame16_off; | ||
1097 | int cpu = smp_processor_id(); | ||
1098 | /* reserve the space on 32bit stack for the magic switch16 pointer */ | ||
1099 | memmove(stk, stk + 8, sizeof(struct pt_regs)); | ||
1100 | switch16_ptr = (unsigned long *)(stk + sizeof(struct pt_regs)); | ||
1101 | regs = (struct pt_regs *)stk; | ||
1102 | /* now the switch32 on 16bit stack */ | ||
1103 | stack_bot = (unsigned long)&per_cpu(cpu_16bit_stack, cpu); | ||
1104 | stack_top = stack_bot + CPU_16BIT_STACK_SIZE; | ||
1105 | switch32_ptr = (unsigned long *)(stack_top - 8); | ||
1106 | iret_frame16_off = CPU_16BIT_STACK_SIZE - 8 - 20; | ||
1107 | /* copy iret frame on 16bit stack */ | ||
1108 | memcpy((void *)(stack_bot + iret_frame16_off), ®s->eip, 20); | ||
1109 | /* fill in the switch pointers */ | ||
1110 | switch16_ptr[0] = (regs->esp & 0xffff0000) | iret_frame16_off; | ||
1111 | switch16_ptr[1] = __ESPFIX_SS; | ||
1112 | switch32_ptr[0] = (unsigned long)stk + sizeof(struct pt_regs) + | ||
1113 | 8 - CPU_16BIT_STACK_SIZE; | ||
1114 | switch32_ptr[1] = __KERNEL_DS; | ||
1115 | } | ||
1116 | |||
1117 | fastcall unsigned char * fixup_x86_bogus_stack(unsigned short sp) | ||
1118 | { | 1103 | { |
1119 | unsigned long *switch32_ptr; | ||
1120 | unsigned char *stack16, *stack32; | ||
1121 | unsigned long stack_top, stack_bot; | ||
1122 | int len; | ||
1123 | int cpu = smp_processor_id(); | 1104 | int cpu = smp_processor_id(); |
1124 | stack_bot = (unsigned long)&per_cpu(cpu_16bit_stack, cpu); | 1105 | struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu); |
1125 | stack_top = stack_bot + CPU_16BIT_STACK_SIZE; | 1106 | struct desc_struct *gdt = (struct desc_struct *)cpu_gdt_descr->address; |
1126 | switch32_ptr = (unsigned long *)(stack_top - 8); | 1107 | unsigned long base = (kesp - uesp) & -THREAD_SIZE; |
1127 | /* copy the data from 16bit stack to 32bit stack */ | 1108 | unsigned long new_kesp = kesp - base; |
1128 | len = CPU_16BIT_STACK_SIZE - 8 - sp; | 1109 | unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT; |
1129 | stack16 = (unsigned char *)(stack_bot + sp); | 1110 | __u64 desc = *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS]; |
1130 | stack32 = (unsigned char *) | 1111 | /* Set up base for espfix segment */ |
1131 | (switch32_ptr[0] + CPU_16BIT_STACK_SIZE - 8 - len); | 1112 | desc &= 0x00f0ff0000000000ULL; |
1132 | memcpy(stack32, stack16, len); | 1113 | desc |= ((((__u64)base) << 16) & 0x000000ffffff0000ULL) | |
1133 | return stack32; | 1114 | ((((__u64)base) << 32) & 0xff00000000000000ULL) | |
1115 | ((((__u64)lim_pages) << 32) & 0x000f000000000000ULL) | | ||
1116 | (lim_pages & 0xffff); | ||
1117 | *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS] = desc; | ||
1118 | return new_kesp; | ||
1134 | } | 1119 | } |
1135 | 1120 | ||
1136 | /* | 1121 | /* |
@@ -1143,7 +1128,7 @@ fastcall unsigned char * fixup_x86_bogus_stack(unsigned short sp) | |||
1143 | * Must be called with kernel preemption disabled (in this case, | 1128 | * Must be called with kernel preemption disabled (in this case, |
1144 | * local interrupts are disabled at the call-site in entry.S). | 1129 | * local interrupts are disabled at the call-site in entry.S). |
1145 | */ | 1130 | */ |
1146 | asmlinkage void math_state_restore(struct pt_regs regs) | 1131 | asmlinkage void math_state_restore(void) |
1147 | { | 1132 | { |
1148 | struct thread_info *thread = current_thread_info(); | 1133 | struct thread_info *thread = current_thread_info(); |
1149 | struct task_struct *tsk = thread->task; | 1134 | struct task_struct *tsk = thread->task; |
@@ -1153,6 +1138,7 @@ asmlinkage void math_state_restore(struct pt_regs regs) | |||
1153 | init_fpu(tsk); | 1138 | init_fpu(tsk); |
1154 | restore_fpu(tsk); | 1139 | restore_fpu(tsk); |
1155 | thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ | 1140 | thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ |
1141 | tsk->fpu_counter++; | ||
1156 | } | 1142 | } |
1157 | 1143 | ||
1158 | #ifndef CONFIG_MATH_EMULATION | 1144 | #ifndef CONFIG_MATH_EMULATION |