diff options
author | Tejun Heo <tj@kernel.org> | 2009-02-09 08:17:39 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-09 18:39:14 -0500 |
commit | d315760ffa261c15ff92699ac6f514112543d7ca (patch) | |
tree | 3f7d185cf97a06e6686521a23a1c78d1809a51c4 | |
parent | ae6af41f5a4841f06eb92bc86ad020ad44ae2a30 (diff) |
x86: fix math_emu register frame access
do_device_not_available() is the handler for #NM and it declares that
it takes a unsigned long and calls math_emu(), which takes a long
argument and surprisingly expects the stack frame starting at the zero
argument would match struct math_emu_info, which isn't true regardless
of configuration in the current code.
This patch makes do_device_not_available() take struct pt_regs like
other exception handlers and initialize struct math_emu_info with
pointer to it and pass pointer to the math_emu_info to math_emulate()
like normal C functions do. This way, unless gcc makes a copy of
struct pt_regs in do_device_not_available(), the register frame is
correctly accessed regardless of kernel configuration or compiler
used.
This doesn't fix all math_emu problems but it at least gets it
somewhat working.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/include/asm/math_emu.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/traps.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 15 | ||||
-rw-r--r-- | arch/x86/math-emu/fpu_entry.c | 4 | ||||
-rw-r--r-- | arch/x86/math-emu/fpu_proto.h | 2 | ||||
-rw-r--r-- | arch/x86/math-emu/fpu_system.h | 16 | ||||
-rw-r--r-- | arch/x86/math-emu/get_address.c | 66 |
7 files changed, 55 insertions, 56 deletions
diff --git a/arch/x86/include/asm/math_emu.h b/arch/x86/include/asm/math_emu.h index 302492c77956..031f6266f425 100644 --- a/arch/x86/include/asm/math_emu.h +++ b/arch/x86/include/asm/math_emu.h | |||
@@ -11,8 +11,8 @@ | |||
11 | struct math_emu_info { | 11 | struct math_emu_info { |
12 | long ___orig_eip; | 12 | long ___orig_eip; |
13 | union { | 13 | union { |
14 | struct pt_regs regs; | 14 | struct pt_regs *regs; |
15 | struct kernel_vm86_regs vm86; | 15 | struct kernel_vm86_regs *vm86; |
16 | }; | 16 | }; |
17 | }; | 17 | }; |
18 | #endif /* _ASM_X86_MATH_EMU_H */ | 18 | #endif /* _ASM_X86_MATH_EMU_H */ |
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 2ee0a3bceedf..cf3bb053da0b 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h | |||
@@ -41,7 +41,7 @@ dotraplinkage void do_int3(struct pt_regs *, long); | |||
41 | dotraplinkage void do_overflow(struct pt_regs *, long); | 41 | dotraplinkage void do_overflow(struct pt_regs *, long); |
42 | dotraplinkage void do_bounds(struct pt_regs *, long); | 42 | dotraplinkage void do_bounds(struct pt_regs *, long); |
43 | dotraplinkage void do_invalid_op(struct pt_regs *, long); | 43 | dotraplinkage void do_invalid_op(struct pt_regs *, long); |
44 | dotraplinkage void do_device_not_available(struct pt_regs *, long); | 44 | dotraplinkage void do_device_not_available(struct pt_regs); |
45 | dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *, long); | 45 | dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *, long); |
46 | dotraplinkage void do_invalid_TSS(struct pt_regs *, long); | 46 | dotraplinkage void do_invalid_TSS(struct pt_regs *, long); |
47 | dotraplinkage void do_segment_not_present(struct pt_regs *, long); | 47 | dotraplinkage void do_segment_not_present(struct pt_regs *, long); |
@@ -77,7 +77,7 @@ extern int panic_on_unrecovered_nmi; | |||
77 | extern int kstack_depth_to_print; | 77 | extern int kstack_depth_to_print; |
78 | 78 | ||
79 | void math_error(void __user *); | 79 | void math_error(void __user *); |
80 | asmlinkage void math_emulate(long); | 80 | void math_emulate(struct math_emu_info *); |
81 | #ifdef CONFIG_X86_32 | 81 | #ifdef CONFIG_X86_32 |
82 | unsigned long patch_espfix_desc(unsigned long, unsigned long); | 82 | unsigned long patch_espfix_desc(unsigned long, unsigned long); |
83 | #else | 83 | #else |
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 98c2d055284b..7932338d7cb3 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -896,7 +896,7 @@ asmlinkage void math_state_restore(void) | |||
896 | EXPORT_SYMBOL_GPL(math_state_restore); | 896 | EXPORT_SYMBOL_GPL(math_state_restore); |
897 | 897 | ||
898 | #ifndef CONFIG_MATH_EMULATION | 898 | #ifndef CONFIG_MATH_EMULATION |
899 | asmlinkage void math_emulate(long arg) | 899 | void math_emulate(struct math_emu_info *info) |
900 | { | 900 | { |
901 | printk(KERN_EMERG | 901 | printk(KERN_EMERG |
902 | "math-emulation not enabled and no coprocessor found.\n"); | 902 | "math-emulation not enabled and no coprocessor found.\n"); |
@@ -906,16 +906,19 @@ asmlinkage void math_emulate(long arg) | |||
906 | } | 906 | } |
907 | #endif /* CONFIG_MATH_EMULATION */ | 907 | #endif /* CONFIG_MATH_EMULATION */ |
908 | 908 | ||
909 | dotraplinkage void __kprobes | 909 | dotraplinkage void __kprobes do_device_not_available(struct pt_regs regs) |
910 | do_device_not_available(struct pt_regs *regs, long error) | ||
911 | { | 910 | { |
912 | #ifdef CONFIG_X86_32 | 911 | #ifdef CONFIG_X86_32 |
913 | if (read_cr0() & X86_CR0_EM) { | 912 | if (read_cr0() & X86_CR0_EM) { |
914 | conditional_sti(regs); | 913 | struct math_emu_info info = { }; |
915 | math_emulate(0); | 914 | |
915 | conditional_sti(®s); | ||
916 | |||
917 | info.regs = ®s; | ||
918 | math_emulate(&info); | ||
916 | } else { | 919 | } else { |
917 | math_state_restore(); /* interrupts still off */ | 920 | math_state_restore(); /* interrupts still off */ |
918 | conditional_sti(regs); | 921 | conditional_sti(®s); |
919 | } | 922 | } |
920 | #else | 923 | #else |
921 | math_state_restore(); | 924 | math_state_restore(); |
diff --git a/arch/x86/math-emu/fpu_entry.c b/arch/x86/math-emu/fpu_entry.c index c268abe72253..5d87f586f8d7 100644 --- a/arch/x86/math-emu/fpu_entry.c +++ b/arch/x86/math-emu/fpu_entry.c | |||
@@ -131,7 +131,7 @@ u_char emulating = 0; | |||
131 | static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip, | 131 | static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip, |
132 | overrides * override); | 132 | overrides * override); |
133 | 133 | ||
134 | asmlinkage void math_emulate(long arg) | 134 | void math_emulate(struct math_emu_info *info) |
135 | { | 135 | { |
136 | u_char FPU_modrm, byte1; | 136 | u_char FPU_modrm, byte1; |
137 | unsigned short code; | 137 | unsigned short code; |
@@ -161,7 +161,7 @@ asmlinkage void math_emulate(long arg) | |||
161 | RE_ENTRANT_CHECK_ON; | 161 | RE_ENTRANT_CHECK_ON; |
162 | #endif /* RE_ENTRANT_CHECKING */ | 162 | #endif /* RE_ENTRANT_CHECKING */ |
163 | 163 | ||
164 | SETUP_DATA_AREA(arg); | 164 | FPU_info = info; |
165 | 165 | ||
166 | FPU_ORIG_EIP = FPU_EIP; | 166 | FPU_ORIG_EIP = FPU_EIP; |
167 | 167 | ||
diff --git a/arch/x86/math-emu/fpu_proto.h b/arch/x86/math-emu/fpu_proto.h index 51bfbb61c5b1..9779df436b7d 100644 --- a/arch/x86/math-emu/fpu_proto.h +++ b/arch/x86/math-emu/fpu_proto.h | |||
@@ -51,7 +51,7 @@ extern void ffreep(void); | |||
51 | extern void fst_i_(void); | 51 | extern void fst_i_(void); |
52 | extern void fstp_i(void); | 52 | extern void fstp_i(void); |
53 | /* fpu_entry.c */ | 53 | /* fpu_entry.c */ |
54 | asmlinkage extern void math_emulate(long arg); | 54 | extern void math_emulate(struct math_emu_info *info); |
55 | extern void math_abort(struct math_emu_info *info, unsigned int signal); | 55 | extern void math_abort(struct math_emu_info *info, unsigned int signal); |
56 | /* fpu_etc.c */ | 56 | /* fpu_etc.c */ |
57 | extern void FPU_etc(void); | 57 | extern void FPU_etc(void); |
diff --git a/arch/x86/math-emu/fpu_system.h b/arch/x86/math-emu/fpu_system.h index 6729c6a31348..50fa0ec2c8a5 100644 --- a/arch/x86/math-emu/fpu_system.h +++ b/arch/x86/math-emu/fpu_system.h | |||
@@ -16,10 +16,6 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/mm.h> | 17 | #include <linux/mm.h> |
18 | 18 | ||
19 | /* This sets the pointer FPU_info to point to the argument part | ||
20 | of the stack frame of math_emulate() */ | ||
21 | #define SETUP_DATA_AREA(arg) FPU_info = (struct math_emu_info *) &arg | ||
22 | |||
23 | /* s is always from a cpu register, and the cpu does bounds checking | 19 | /* s is always from a cpu register, and the cpu does bounds checking |
24 | * during register load --> no further bounds checks needed */ | 20 | * during register load --> no further bounds checks needed */ |
25 | #define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->context.ldt)[(s) >> 3]) | 21 | #define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->context.ldt)[(s) >> 3]) |
@@ -38,12 +34,12 @@ | |||
38 | #define I387 (current->thread.xstate) | 34 | #define I387 (current->thread.xstate) |
39 | #define FPU_info (I387->soft.info) | 35 | #define FPU_info (I387->soft.info) |
40 | 36 | ||
41 | #define FPU_CS (*(unsigned short *) &(FPU_info->regs.cs)) | 37 | #define FPU_CS (*(unsigned short *) &(FPU_info->regs->cs)) |
42 | #define FPU_SS (*(unsigned short *) &(FPU_info->regs.ss)) | 38 | #define FPU_SS (*(unsigned short *) &(FPU_info->regs->ss)) |
43 | #define FPU_DS (*(unsigned short *) &(FPU_info->regs.ds)) | 39 | #define FPU_DS (*(unsigned short *) &(FPU_info->regs->ds)) |
44 | #define FPU_EAX (FPU_info->regs.ax) | 40 | #define FPU_EAX (FPU_info->regs->ax) |
45 | #define FPU_EFLAGS (FPU_info->regs.flags) | 41 | #define FPU_EFLAGS (FPU_info->regs->flags) |
46 | #define FPU_EIP (FPU_info->regs.ip) | 42 | #define FPU_EIP (FPU_info->regs->ip) |
47 | #define FPU_ORIG_EIP (FPU_info->___orig_eip) | 43 | #define FPU_ORIG_EIP (FPU_info->___orig_eip) |
48 | 44 | ||
49 | #define FPU_lookahead (I387->soft.lookahead) | 45 | #define FPU_lookahead (I387->soft.lookahead) |
diff --git a/arch/x86/math-emu/get_address.c b/arch/x86/math-emu/get_address.c index 62daa7fcc44c..420b3b6e3915 100644 --- a/arch/x86/math-emu/get_address.c +++ b/arch/x86/math-emu/get_address.c | |||
@@ -29,43 +29,43 @@ | |||
29 | #define FPU_WRITE_BIT 0x10 | 29 | #define FPU_WRITE_BIT 0x10 |
30 | 30 | ||
31 | static int reg_offset[] = { | 31 | static int reg_offset[] = { |
32 | offsetof(struct math_emu_info, regs.ax), | 32 | offsetof(struct pt_regs, ax), |
33 | offsetof(struct math_emu_info, regs.cx), | 33 | offsetof(struct pt_regs, cx), |
34 | offsetof(struct math_emu_info, regs.dx), | 34 | offsetof(struct pt_regs, dx), |
35 | offsetof(struct math_emu_info, regs.bx), | 35 | offsetof(struct pt_regs, bx), |
36 | offsetof(struct math_emu_info, regs.sp), | 36 | offsetof(struct pt_regs, sp), |
37 | offsetof(struct math_emu_info, regs.bp), | 37 | offsetof(struct pt_regs, bp), |
38 | offsetof(struct math_emu_info, regs.si), | 38 | offsetof(struct pt_regs, si), |
39 | offsetof(struct math_emu_info, regs.di) | 39 | offsetof(struct pt_regs, di) |
40 | }; | 40 | }; |
41 | 41 | ||
42 | #define REG_(x) (*(long *)(reg_offset[(x)]+(u_char *) FPU_info)) | 42 | #define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs)) |
43 | 43 | ||
44 | static int reg_offset_vm86[] = { | 44 | static int reg_offset_vm86[] = { |
45 | offsetof(struct math_emu_info, regs.cs), | 45 | offsetof(struct pt_regs, cs), |
46 | offsetof(struct math_emu_info, vm86.ds), | 46 | offsetof(struct kernel_vm86_regs, ds), |
47 | offsetof(struct math_emu_info, vm86.es), | 47 | offsetof(struct kernel_vm86_regs, es), |
48 | offsetof(struct math_emu_info, vm86.fs), | 48 | offsetof(struct kernel_vm86_regs, fs), |
49 | offsetof(struct math_emu_info, vm86.gs), | 49 | offsetof(struct kernel_vm86_regs, gs), |
50 | offsetof(struct math_emu_info, regs.ss), | 50 | offsetof(struct pt_regs, ss), |
51 | offsetof(struct math_emu_info, vm86.ds) | 51 | offsetof(struct kernel_vm86_regs, ds) |
52 | }; | 52 | }; |
53 | 53 | ||
54 | #define VM86_REG_(x) (*(unsigned short *) \ | 54 | #define VM86_REG_(x) (*(unsigned short *) \ |
55 | (reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info)) | 55 | (reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs)) |
56 | 56 | ||
57 | static int reg_offset_pm[] = { | 57 | static int reg_offset_pm[] = { |
58 | offsetof(struct math_emu_info, regs.cs), | 58 | offsetof(struct pt_regs, cs), |
59 | offsetof(struct math_emu_info, regs.ds), | 59 | offsetof(struct pt_regs, ds), |
60 | offsetof(struct math_emu_info, regs.es), | 60 | offsetof(struct pt_regs, es), |
61 | offsetof(struct math_emu_info, regs.fs), | 61 | offsetof(struct pt_regs, fs), |
62 | offsetof(struct math_emu_info, regs.ds), /* dummy, not saved on stack */ | 62 | offsetof(struct pt_regs, ds), /* dummy, not saved on stack */ |
63 | offsetof(struct math_emu_info, regs.ss), | 63 | offsetof(struct pt_regs, ss), |
64 | offsetof(struct math_emu_info, regs.ds) | 64 | offsetof(struct pt_regs, ds) |
65 | }; | 65 | }; |
66 | 66 | ||
67 | #define PM_REG_(x) (*(unsigned short *) \ | 67 | #define PM_REG_(x) (*(unsigned short *) \ |
68 | (reg_offset_pm[((unsigned)x)]+(u_char *) FPU_info)) | 68 | (reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs)) |
69 | 69 | ||
70 | /* Decode the SIB byte. This function assumes mod != 0 */ | 70 | /* Decode the SIB byte. This function assumes mod != 0 */ |
71 | static int sib(int mod, unsigned long *fpu_eip) | 71 | static int sib(int mod, unsigned long *fpu_eip) |
@@ -346,34 +346,34 @@ void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, | |||
346 | } | 346 | } |
347 | switch (rm) { | 347 | switch (rm) { |
348 | case 0: | 348 | case 0: |
349 | address += FPU_info->regs.bx + FPU_info->regs.si; | 349 | address += FPU_info->regs->bx + FPU_info->regs->si; |
350 | break; | 350 | break; |
351 | case 1: | 351 | case 1: |
352 | address += FPU_info->regs.bx + FPU_info->regs.di; | 352 | address += FPU_info->regs->bx + FPU_info->regs->di; |
353 | break; | 353 | break; |
354 | case 2: | 354 | case 2: |
355 | address += FPU_info->regs.bp + FPU_info->regs.si; | 355 | address += FPU_info->regs->bp + FPU_info->regs->si; |
356 | if (addr_modes.override.segment == PREFIX_DEFAULT) | 356 | if (addr_modes.override.segment == PREFIX_DEFAULT) |
357 | addr_modes.override.segment = PREFIX_SS_; | 357 | addr_modes.override.segment = PREFIX_SS_; |
358 | break; | 358 | break; |
359 | case 3: | 359 | case 3: |
360 | address += FPU_info->regs.bp + FPU_info->regs.di; | 360 | address += FPU_info->regs->bp + FPU_info->regs->di; |
361 | if (addr_modes.override.segment == PREFIX_DEFAULT) | 361 | if (addr_modes.override.segment == PREFIX_DEFAULT) |
362 | addr_modes.override.segment = PREFIX_SS_; | 362 | addr_modes.override.segment = PREFIX_SS_; |
363 | break; | 363 | break; |
364 | case 4: | 364 | case 4: |
365 | address += FPU_info->regs.si; | 365 | address += FPU_info->regs->si; |
366 | break; | 366 | break; |
367 | case 5: | 367 | case 5: |
368 | address += FPU_info->regs.di; | 368 | address += FPU_info->regs->di; |
369 | break; | 369 | break; |
370 | case 6: | 370 | case 6: |
371 | address += FPU_info->regs.bp; | 371 | address += FPU_info->regs->bp; |
372 | if (addr_modes.override.segment == PREFIX_DEFAULT) | 372 | if (addr_modes.override.segment == PREFIX_DEFAULT) |
373 | addr_modes.override.segment = PREFIX_SS_; | 373 | addr_modes.override.segment = PREFIX_SS_; |
374 | break; | 374 | break; |
375 | case 7: | 375 | case 7: |
376 | address += FPU_info->regs.bx; | 376 | address += FPU_info->regs->bx; |
377 | break; | 377 | break; |
378 | } | 378 | } |
379 | 379 | ||