diff options
Diffstat (limited to 'arch/x86/math-emu/get_address.c')
-rw-r--r-- | arch/x86/math-emu/get_address.c | 650 |
1 files changed, 308 insertions, 342 deletions
diff --git a/arch/x86/math-emu/get_address.c b/arch/x86/math-emu/get_address.c index 2e2c51a8bd3a..d701e2b39e44 100644 --- a/arch/x86/math-emu/get_address.c +++ b/arch/x86/math-emu/get_address.c | |||
@@ -17,7 +17,6 @@ | |||
17 | | other processes using the emulator while swapping is in progress. | | 17 | | other processes using the emulator while swapping is in progress. | |
18 | +---------------------------------------------------------------------------*/ | 18 | +---------------------------------------------------------------------------*/ |
19 | 19 | ||
20 | |||
21 | #include <linux/stddef.h> | 20 | #include <linux/stddef.h> |
22 | 21 | ||
23 | #include <asm/uaccess.h> | 22 | #include <asm/uaccess.h> |
@@ -27,31 +26,30 @@ | |||
27 | #include "exception.h" | 26 | #include "exception.h" |
28 | #include "fpu_emu.h" | 27 | #include "fpu_emu.h" |
29 | 28 | ||
30 | |||
31 | #define FPU_WRITE_BIT 0x10 | 29 | #define FPU_WRITE_BIT 0x10 |
32 | 30 | ||
33 | static int reg_offset[] = { | 31 | static int reg_offset[] = { |
34 | offsetof(struct info,___eax), | 32 | offsetof(struct info, ___eax), |
35 | offsetof(struct info,___ecx), | 33 | offsetof(struct info, ___ecx), |
36 | offsetof(struct info,___edx), | 34 | offsetof(struct info, ___edx), |
37 | offsetof(struct info,___ebx), | 35 | offsetof(struct info, ___ebx), |
38 | offsetof(struct info,___esp), | 36 | offsetof(struct info, ___esp), |
39 | offsetof(struct info,___ebp), | 37 | offsetof(struct info, ___ebp), |
40 | offsetof(struct info,___esi), | 38 | offsetof(struct info, ___esi), |
41 | offsetof(struct info,___edi) | 39 | offsetof(struct info, ___edi) |
42 | }; | 40 | }; |
43 | 41 | ||
44 | #define REG_(x) (*(long *)(reg_offset[(x)]+(u_char *) FPU_info)) | 42 | #define REG_(x) (*(long *)(reg_offset[(x)]+(u_char *) FPU_info)) |
45 | 43 | ||
46 | static int reg_offset_vm86[] = { | 44 | static int reg_offset_vm86[] = { |
47 | offsetof(struct info,___cs), | 45 | offsetof(struct info, ___cs), |
48 | offsetof(struct info,___vm86_ds), | 46 | offsetof(struct info, ___vm86_ds), |
49 | offsetof(struct info,___vm86_es), | 47 | offsetof(struct info, ___vm86_es), |
50 | offsetof(struct info,___vm86_fs), | 48 | offsetof(struct info, ___vm86_fs), |
51 | offsetof(struct info,___vm86_gs), | 49 | offsetof(struct info, ___vm86_gs), |
52 | offsetof(struct info,___ss), | 50 | offsetof(struct info, ___ss), |
53 | offsetof(struct info,___vm86_ds) | 51 | offsetof(struct info, ___vm86_ds) |
54 | }; | 52 | }; |
55 | 53 | ||
56 | #define VM86_REG_(x) (*(unsigned short *) \ | 54 | #define VM86_REG_(x) (*(unsigned short *) \ |
57 | (reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info)) | 55 | (reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info)) |
@@ -60,158 +58,141 @@ static int reg_offset_vm86[] = { | |||
60 | #define ___GS ___ds | 58 | #define ___GS ___ds |
61 | 59 | ||
62 | static int reg_offset_pm[] = { | 60 | static int reg_offset_pm[] = { |
63 | offsetof(struct info,___cs), | 61 | offsetof(struct info, ___cs), |
64 | offsetof(struct info,___ds), | 62 | offsetof(struct info, ___ds), |
65 | offsetof(struct info,___es), | 63 | offsetof(struct info, ___es), |
66 | offsetof(struct info,___fs), | 64 | offsetof(struct info, ___fs), |
67 | offsetof(struct info,___GS), | 65 | offsetof(struct info, ___GS), |
68 | offsetof(struct info,___ss), | 66 | offsetof(struct info, ___ss), |
69 | offsetof(struct info,___ds) | 67 | offsetof(struct info, ___ds) |
70 | }; | 68 | }; |
71 | 69 | ||
72 | #define PM_REG_(x) (*(unsigned short *) \ | 70 | #define PM_REG_(x) (*(unsigned short *) \ |
73 | (reg_offset_pm[((unsigned)x)]+(u_char *) FPU_info)) | 71 | (reg_offset_pm[((unsigned)x)]+(u_char *) FPU_info)) |
74 | 72 | ||
75 | |||
76 | /* Decode the SIB byte. This function assumes mod != 0 */ | 73 | /* Decode the SIB byte. This function assumes mod != 0 */ |
77 | static int sib(int mod, unsigned long *fpu_eip) | 74 | static int sib(int mod, unsigned long *fpu_eip) |
78 | { | 75 | { |
79 | u_char ss,index,base; | 76 | u_char ss, index, base; |
80 | long offset; | 77 | long offset; |
81 | 78 | ||
82 | RE_ENTRANT_CHECK_OFF; | 79 | RE_ENTRANT_CHECK_OFF; |
83 | FPU_code_access_ok(1); | 80 | FPU_code_access_ok(1); |
84 | FPU_get_user(base, (u_char __user *) (*fpu_eip)); /* The SIB byte */ | 81 | FPU_get_user(base, (u_char __user *) (*fpu_eip)); /* The SIB byte */ |
85 | RE_ENTRANT_CHECK_ON; | 82 | RE_ENTRANT_CHECK_ON; |
86 | (*fpu_eip)++; | 83 | (*fpu_eip)++; |
87 | ss = base >> 6; | 84 | ss = base >> 6; |
88 | index = (base >> 3) & 7; | 85 | index = (base >> 3) & 7; |
89 | base &= 7; | 86 | base &= 7; |
90 | 87 | ||
91 | if ((mod == 0) && (base == 5)) | 88 | if ((mod == 0) && (base == 5)) |
92 | offset = 0; /* No base register */ | 89 | offset = 0; /* No base register */ |
93 | else | 90 | else |
94 | offset = REG_(base); | 91 | offset = REG_(base); |
95 | 92 | ||
96 | if (index == 4) | 93 | if (index == 4) { |
97 | { | 94 | /* No index register */ |
98 | /* No index register */ | 95 | /* A non-zero ss is illegal */ |
99 | /* A non-zero ss is illegal */ | 96 | if (ss) |
100 | if ( ss ) | 97 | EXCEPTION(EX_Invalid); |
101 | EXCEPTION(EX_Invalid); | 98 | } else { |
102 | } | 99 | offset += (REG_(index)) << ss; |
103 | else | 100 | } |
104 | { | 101 | |
105 | offset += (REG_(index)) << ss; | 102 | if (mod == 1) { |
106 | } | 103 | /* 8 bit signed displacement */ |
107 | 104 | long displacement; | |
108 | if (mod == 1) | 105 | RE_ENTRANT_CHECK_OFF; |
109 | { | 106 | FPU_code_access_ok(1); |
110 | /* 8 bit signed displacement */ | 107 | FPU_get_user(displacement, (signed char __user *)(*fpu_eip)); |
111 | long displacement; | 108 | offset += displacement; |
112 | RE_ENTRANT_CHECK_OFF; | 109 | RE_ENTRANT_CHECK_ON; |
113 | FPU_code_access_ok(1); | 110 | (*fpu_eip)++; |
114 | FPU_get_user(displacement, (signed char __user *) (*fpu_eip)); | 111 | } else if (mod == 2 || base == 5) { /* The second condition also has mod==0 */ |
115 | offset += displacement; | 112 | /* 32 bit displacement */ |
116 | RE_ENTRANT_CHECK_ON; | 113 | long displacement; |
117 | (*fpu_eip)++; | 114 | RE_ENTRANT_CHECK_OFF; |
118 | } | 115 | FPU_code_access_ok(4); |
119 | else if (mod == 2 || base == 5) /* The second condition also has mod==0 */ | 116 | FPU_get_user(displacement, (long __user *)(*fpu_eip)); |
120 | { | 117 | offset += displacement; |
121 | /* 32 bit displacement */ | 118 | RE_ENTRANT_CHECK_ON; |
122 | long displacement; | 119 | (*fpu_eip) += 4; |
123 | RE_ENTRANT_CHECK_OFF; | 120 | } |
124 | FPU_code_access_ok(4); | ||
125 | FPU_get_user(displacement, (long __user *) (*fpu_eip)); | ||
126 | offset += displacement; | ||
127 | RE_ENTRANT_CHECK_ON; | ||
128 | (*fpu_eip) += 4; | ||
129 | } | ||
130 | |||
131 | return offset; | ||
132 | } | ||
133 | 121 | ||
122 | return offset; | ||
123 | } | ||
134 | 124 | ||
135 | static unsigned long vm86_segment(u_char segment, | 125 | static unsigned long vm86_segment(u_char segment, struct address *addr) |
136 | struct address *addr) | ||
137 | { | 126 | { |
138 | segment--; | 127 | segment--; |
139 | #ifdef PARANOID | 128 | #ifdef PARANOID |
140 | if ( segment > PREFIX_SS_ ) | 129 | if (segment > PREFIX_SS_) { |
141 | { | 130 | EXCEPTION(EX_INTERNAL | 0x130); |
142 | EXCEPTION(EX_INTERNAL|0x130); | 131 | math_abort(FPU_info, SIGSEGV); |
143 | math_abort(FPU_info,SIGSEGV); | 132 | } |
144 | } | ||
145 | #endif /* PARANOID */ | 133 | #endif /* PARANOID */ |
146 | addr->selector = VM86_REG_(segment); | 134 | addr->selector = VM86_REG_(segment); |
147 | return (unsigned long)VM86_REG_(segment) << 4; | 135 | return (unsigned long)VM86_REG_(segment) << 4; |
148 | } | 136 | } |
149 | 137 | ||
150 | |||
151 | /* This should work for 16 and 32 bit protected mode. */ | 138 | /* This should work for 16 and 32 bit protected mode. */ |
152 | static long pm_address(u_char FPU_modrm, u_char segment, | 139 | static long pm_address(u_char FPU_modrm, u_char segment, |
153 | struct address *addr, long offset) | 140 | struct address *addr, long offset) |
154 | { | 141 | { |
155 | struct desc_struct descriptor; | 142 | struct desc_struct descriptor; |
156 | unsigned long base_address, limit, address, seg_top; | 143 | unsigned long base_address, limit, address, seg_top; |
157 | 144 | ||
158 | segment--; | 145 | segment--; |
159 | 146 | ||
160 | #ifdef PARANOID | 147 | #ifdef PARANOID |
161 | /* segment is unsigned, so this also detects if segment was 0: */ | 148 | /* segment is unsigned, so this also detects if segment was 0: */ |
162 | if ( segment > PREFIX_SS_ ) | 149 | if (segment > PREFIX_SS_) { |
163 | { | 150 | EXCEPTION(EX_INTERNAL | 0x132); |
164 | EXCEPTION(EX_INTERNAL|0x132); | 151 | math_abort(FPU_info, SIGSEGV); |
165 | math_abort(FPU_info,SIGSEGV); | 152 | } |
166 | } | ||
167 | #endif /* PARANOID */ | 153 | #endif /* PARANOID */ |
168 | 154 | ||
169 | switch ( segment ) | 155 | switch (segment) { |
170 | { | 156 | /* gs isn't used by the kernel, so it still has its |
171 | /* gs isn't used by the kernel, so it still has its | 157 | user-space value. */ |
172 | user-space value. */ | 158 | case PREFIX_GS_ - 1: |
173 | case PREFIX_GS_-1: | 159 | /* N.B. - movl %seg, mem is a 2 byte write regardless of prefix */ |
174 | /* N.B. - movl %seg, mem is a 2 byte write regardless of prefix */ | 160 | savesegment(gs, addr->selector); |
175 | savesegment(gs, addr->selector); | 161 | break; |
176 | break; | 162 | default: |
177 | default: | 163 | addr->selector = PM_REG_(segment); |
178 | addr->selector = PM_REG_(segment); | ||
179 | } | ||
180 | |||
181 | descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); | ||
182 | base_address = SEG_BASE_ADDR(descriptor); | ||
183 | address = base_address + offset; | ||
184 | limit = base_address | ||
185 | + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1; | ||
186 | if ( limit < base_address ) limit = 0xffffffff; | ||
187 | |||
188 | if ( SEG_EXPAND_DOWN(descriptor) ) | ||
189 | { | ||
190 | if ( SEG_G_BIT(descriptor) ) | ||
191 | seg_top = 0xffffffff; | ||
192 | else | ||
193 | { | ||
194 | seg_top = base_address + (1 << 20); | ||
195 | if ( seg_top < base_address ) seg_top = 0xffffffff; | ||
196 | } | 164 | } |
197 | access_limit = | ||
198 | (address <= limit) || (address >= seg_top) ? 0 : | ||
199 | ((seg_top-address) >= 255 ? 255 : seg_top-address); | ||
200 | } | ||
201 | else | ||
202 | { | ||
203 | access_limit = | ||
204 | (address > limit) || (address < base_address) ? 0 : | ||
205 | ((limit-address) >= 254 ? 255 : limit-address+1); | ||
206 | } | ||
207 | if ( SEG_EXECUTE_ONLY(descriptor) || | ||
208 | (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) ) | ||
209 | { | ||
210 | access_limit = 0; | ||
211 | } | ||
212 | return address; | ||
213 | } | ||
214 | 165 | ||
166 | descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); | ||
167 | base_address = SEG_BASE_ADDR(descriptor); | ||
168 | address = base_address + offset; | ||
169 | limit = base_address | ||
170 | + (SEG_LIMIT(descriptor) + 1) * SEG_GRANULARITY(descriptor) - 1; | ||
171 | if (limit < base_address) | ||
172 | limit = 0xffffffff; | ||
173 | |||
174 | if (SEG_EXPAND_DOWN(descriptor)) { | ||
175 | if (SEG_G_BIT(descriptor)) | ||
176 | seg_top = 0xffffffff; | ||
177 | else { | ||
178 | seg_top = base_address + (1 << 20); | ||
179 | if (seg_top < base_address) | ||
180 | seg_top = 0xffffffff; | ||
181 | } | ||
182 | access_limit = | ||
183 | (address <= limit) || (address >= seg_top) ? 0 : | ||
184 | ((seg_top - address) >= 255 ? 255 : seg_top - address); | ||
185 | } else { | ||
186 | access_limit = | ||
187 | (address > limit) || (address < base_address) ? 0 : | ||
188 | ((limit - address) >= 254 ? 255 : limit - address + 1); | ||
189 | } | ||
190 | if (SEG_EXECUTE_ONLY(descriptor) || | ||
191 | (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT))) { | ||
192 | access_limit = 0; | ||
193 | } | ||
194 | return address; | ||
195 | } | ||
215 | 196 | ||
216 | /* | 197 | /* |
217 | MOD R/M byte: MOD == 3 has a special use for the FPU | 198 | MOD R/M byte: MOD == 3 has a special use for the FPU |
@@ -221,7 +202,6 @@ static long pm_address(u_char FPU_modrm, u_char segment, | |||
221 | ..... ......... ......... | 202 | ..... ......... ......... |
222 | MOD OPCODE(2) R/M | 203 | MOD OPCODE(2) R/M |
223 | 204 | ||
224 | |||
225 | SIB byte | 205 | SIB byte |
226 | 206 | ||
227 | 7 6 5 4 3 2 1 0 | 207 | 7 6 5 4 3 2 1 0 |
@@ -231,208 +211,194 @@ static long pm_address(u_char FPU_modrm, u_char segment, | |||
231 | */ | 211 | */ |
232 | 212 | ||
233 | void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, | 213 | void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, |
234 | struct address *addr, | 214 | struct address *addr, fpu_addr_modes addr_modes) |
235 | fpu_addr_modes addr_modes) | 215 | { |
216 | u_char mod; | ||
217 | unsigned rm = FPU_modrm & 7; | ||
218 | long *cpu_reg_ptr; | ||
219 | int address = 0; /* Initialized just to stop compiler warnings. */ | ||
220 | |||
221 | /* Memory accessed via the cs selector is write protected | ||
222 | in `non-segmented' 32 bit protected mode. */ | ||
223 | if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) | ||
224 | && (addr_modes.override.segment == PREFIX_CS_)) { | ||
225 | math_abort(FPU_info, SIGSEGV); | ||
226 | } | ||
227 | |||
228 | addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ | ||
229 | |||
230 | mod = (FPU_modrm >> 6) & 3; | ||
231 | |||
232 | if (rm == 4 && mod != 3) { | ||
233 | address = sib(mod, fpu_eip); | ||
234 | } else { | ||
235 | cpu_reg_ptr = ®_(rm); | ||
236 | switch (mod) { | ||
237 | case 0: | ||
238 | if (rm == 5) { | ||
239 | /* Special case: disp32 */ | ||
240 | RE_ENTRANT_CHECK_OFF; | ||
241 | FPU_code_access_ok(4); | ||
242 | FPU_get_user(address, | ||
243 | (unsigned long __user | ||
244 | *)(*fpu_eip)); | ||
245 | (*fpu_eip) += 4; | ||
246 | RE_ENTRANT_CHECK_ON; | ||
247 | addr->offset = address; | ||
248 | return (void __user *)address; | ||
249 | } else { | ||
250 | address = *cpu_reg_ptr; /* Just return the contents | ||
251 | of the cpu register */ | ||
252 | addr->offset = address; | ||
253 | return (void __user *)address; | ||
254 | } | ||
255 | case 1: | ||
256 | /* 8 bit signed displacement */ | ||
257 | RE_ENTRANT_CHECK_OFF; | ||
258 | FPU_code_access_ok(1); | ||
259 | FPU_get_user(address, (signed char __user *)(*fpu_eip)); | ||
260 | RE_ENTRANT_CHECK_ON; | ||
261 | (*fpu_eip)++; | ||
262 | break; | ||
263 | case 2: | ||
264 | /* 32 bit displacement */ | ||
265 | RE_ENTRANT_CHECK_OFF; | ||
266 | FPU_code_access_ok(4); | ||
267 | FPU_get_user(address, (long __user *)(*fpu_eip)); | ||
268 | (*fpu_eip) += 4; | ||
269 | RE_ENTRANT_CHECK_ON; | ||
270 | break; | ||
271 | case 3: | ||
272 | /* Not legal for the FPU */ | ||
273 | EXCEPTION(EX_Invalid); | ||
274 | } | ||
275 | address += *cpu_reg_ptr; | ||
276 | } | ||
277 | |||
278 | addr->offset = address; | ||
279 | |||
280 | switch (addr_modes.default_mode) { | ||
281 | case 0: | ||
282 | break; | ||
283 | case VM86: | ||
284 | address += vm86_segment(addr_modes.override.segment, addr); | ||
285 | break; | ||
286 | case PM16: | ||
287 | case SEG32: | ||
288 | address = pm_address(FPU_modrm, addr_modes.override.segment, | ||
289 | addr, address); | ||
290 | break; | ||
291 | default: | ||
292 | EXCEPTION(EX_INTERNAL | 0x133); | ||
293 | } | ||
294 | |||
295 | return (void __user *)address; | ||
296 | } | ||
297 | |||
298 | void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, | ||
299 | struct address *addr, fpu_addr_modes addr_modes) | ||
236 | { | 300 | { |
237 | u_char mod; | 301 | u_char mod; |
238 | unsigned rm = FPU_modrm & 7; | 302 | unsigned rm = FPU_modrm & 7; |
239 | long *cpu_reg_ptr; | 303 | int address = 0; /* Default used for mod == 0 */ |
240 | int address = 0; /* Initialized just to stop compiler warnings. */ | 304 | |
241 | 305 | /* Memory accessed via the cs selector is write protected | |
242 | /* Memory accessed via the cs selector is write protected | 306 | in `non-segmented' 32 bit protected mode. */ |
243 | in `non-segmented' 32 bit protected mode. */ | 307 | if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) |
244 | if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) | 308 | && (addr_modes.override.segment == PREFIX_CS_)) { |
245 | && (addr_modes.override.segment == PREFIX_CS_) ) | 309 | math_abort(FPU_info, SIGSEGV); |
246 | { | 310 | } |
247 | math_abort(FPU_info,SIGSEGV); | 311 | |
248 | } | 312 | addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ |
249 | 313 | ||
250 | addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ | 314 | mod = (FPU_modrm >> 6) & 3; |
251 | 315 | ||
252 | mod = (FPU_modrm >> 6) & 3; | 316 | switch (mod) { |
253 | |||
254 | if (rm == 4 && mod != 3) | ||
255 | { | ||
256 | address = sib(mod, fpu_eip); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | cpu_reg_ptr = & REG_(rm); | ||
261 | switch (mod) | ||
262 | { | ||
263 | case 0: | 317 | case 0: |
264 | if (rm == 5) | 318 | if (rm == 6) { |
265 | { | 319 | /* Special case: disp16 */ |
266 | /* Special case: disp32 */ | 320 | RE_ENTRANT_CHECK_OFF; |
267 | RE_ENTRANT_CHECK_OFF; | 321 | FPU_code_access_ok(2); |
268 | FPU_code_access_ok(4); | 322 | FPU_get_user(address, |
269 | FPU_get_user(address, (unsigned long __user *) (*fpu_eip)); | 323 | (unsigned short __user *)(*fpu_eip)); |
270 | (*fpu_eip) += 4; | 324 | (*fpu_eip) += 2; |
271 | RE_ENTRANT_CHECK_ON; | 325 | RE_ENTRANT_CHECK_ON; |
272 | addr->offset = address; | 326 | goto add_segment; |
273 | return (void __user *) address; | 327 | } |
274 | } | 328 | break; |
275 | else | ||
276 | { | ||
277 | address = *cpu_reg_ptr; /* Just return the contents | ||
278 | of the cpu register */ | ||
279 | addr->offset = address; | ||
280 | return (void __user *) address; | ||
281 | } | ||
282 | case 1: | 329 | case 1: |
283 | /* 8 bit signed displacement */ | 330 | /* 8 bit signed displacement */ |
284 | RE_ENTRANT_CHECK_OFF; | 331 | RE_ENTRANT_CHECK_OFF; |
285 | FPU_code_access_ok(1); | 332 | FPU_code_access_ok(1); |
286 | FPU_get_user(address, (signed char __user *) (*fpu_eip)); | 333 | FPU_get_user(address, (signed char __user *)(*fpu_eip)); |
287 | RE_ENTRANT_CHECK_ON; | 334 | RE_ENTRANT_CHECK_ON; |
288 | (*fpu_eip)++; | 335 | (*fpu_eip)++; |
289 | break; | 336 | break; |
290 | case 2: | 337 | case 2: |
291 | /* 32 bit displacement */ | 338 | /* 16 bit displacement */ |
292 | RE_ENTRANT_CHECK_OFF; | 339 | RE_ENTRANT_CHECK_OFF; |
293 | FPU_code_access_ok(4); | 340 | FPU_code_access_ok(2); |
294 | FPU_get_user(address, (long __user *) (*fpu_eip)); | 341 | FPU_get_user(address, (unsigned short __user *)(*fpu_eip)); |
295 | (*fpu_eip) += 4; | 342 | (*fpu_eip) += 2; |
296 | RE_ENTRANT_CHECK_ON; | 343 | RE_ENTRANT_CHECK_ON; |
297 | break; | 344 | break; |
298 | case 3: | 345 | case 3: |
299 | /* Not legal for the FPU */ | 346 | /* Not legal for the FPU */ |
300 | EXCEPTION(EX_Invalid); | 347 | EXCEPTION(EX_Invalid); |
348 | break; | ||
349 | } | ||
350 | switch (rm) { | ||
351 | case 0: | ||
352 | address += FPU_info->___ebx + FPU_info->___esi; | ||
353 | break; | ||
354 | case 1: | ||
355 | address += FPU_info->___ebx + FPU_info->___edi; | ||
356 | break; | ||
357 | case 2: | ||
358 | address += FPU_info->___ebp + FPU_info->___esi; | ||
359 | if (addr_modes.override.segment == PREFIX_DEFAULT) | ||
360 | addr_modes.override.segment = PREFIX_SS_; | ||
361 | break; | ||
362 | case 3: | ||
363 | address += FPU_info->___ebp + FPU_info->___edi; | ||
364 | if (addr_modes.override.segment == PREFIX_DEFAULT) | ||
365 | addr_modes.override.segment = PREFIX_SS_; | ||
366 | break; | ||
367 | case 4: | ||
368 | address += FPU_info->___esi; | ||
369 | break; | ||
370 | case 5: | ||
371 | address += FPU_info->___edi; | ||
372 | break; | ||
373 | case 6: | ||
374 | address += FPU_info->___ebp; | ||
375 | if (addr_modes.override.segment == PREFIX_DEFAULT) | ||
376 | addr_modes.override.segment = PREFIX_SS_; | ||
377 | break; | ||
378 | case 7: | ||
379 | address += FPU_info->___ebx; | ||
380 | break; | ||
301 | } | 381 | } |
302 | address += *cpu_reg_ptr; | ||
303 | } | ||
304 | |||
305 | addr->offset = address; | ||
306 | |||
307 | switch ( addr_modes.default_mode ) | ||
308 | { | ||
309 | case 0: | ||
310 | break; | ||
311 | case VM86: | ||
312 | address += vm86_segment(addr_modes.override.segment, addr); | ||
313 | break; | ||
314 | case PM16: | ||
315 | case SEG32: | ||
316 | address = pm_address(FPU_modrm, addr_modes.override.segment, | ||
317 | addr, address); | ||
318 | break; | ||
319 | default: | ||
320 | EXCEPTION(EX_INTERNAL|0x133); | ||
321 | } | ||
322 | |||
323 | return (void __user *)address; | ||
324 | } | ||
325 | 382 | ||
383 | add_segment: | ||
384 | address &= 0xffff; | ||
326 | 385 | ||
327 | void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, | 386 | addr->offset = address; |
328 | struct address *addr, | 387 | |
329 | fpu_addr_modes addr_modes) | 388 | switch (addr_modes.default_mode) { |
330 | { | 389 | case 0: |
331 | u_char mod; | 390 | break; |
332 | unsigned rm = FPU_modrm & 7; | 391 | case VM86: |
333 | int address = 0; /* Default used for mod == 0 */ | 392 | address += vm86_segment(addr_modes.override.segment, addr); |
334 | 393 | break; | |
335 | /* Memory accessed via the cs selector is write protected | 394 | case PM16: |
336 | in `non-segmented' 32 bit protected mode. */ | 395 | case SEG32: |
337 | if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) | 396 | address = pm_address(FPU_modrm, addr_modes.override.segment, |
338 | && (addr_modes.override.segment == PREFIX_CS_) ) | 397 | addr, address); |
339 | { | 398 | break; |
340 | math_abort(FPU_info,SIGSEGV); | 399 | default: |
341 | } | 400 | EXCEPTION(EX_INTERNAL | 0x131); |
342 | |||
343 | addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ | ||
344 | |||
345 | mod = (FPU_modrm >> 6) & 3; | ||
346 | |||
347 | switch (mod) | ||
348 | { | ||
349 | case 0: | ||
350 | if (rm == 6) | ||
351 | { | ||
352 | /* Special case: disp16 */ | ||
353 | RE_ENTRANT_CHECK_OFF; | ||
354 | FPU_code_access_ok(2); | ||
355 | FPU_get_user(address, (unsigned short __user *) (*fpu_eip)); | ||
356 | (*fpu_eip) += 2; | ||
357 | RE_ENTRANT_CHECK_ON; | ||
358 | goto add_segment; | ||
359 | } | 401 | } |
360 | break; | 402 | |
361 | case 1: | 403 | return (void __user *)address; |
362 | /* 8 bit signed displacement */ | ||
363 | RE_ENTRANT_CHECK_OFF; | ||
364 | FPU_code_access_ok(1); | ||
365 | FPU_get_user(address, (signed char __user *) (*fpu_eip)); | ||
366 | RE_ENTRANT_CHECK_ON; | ||
367 | (*fpu_eip)++; | ||
368 | break; | ||
369 | case 2: | ||
370 | /* 16 bit displacement */ | ||
371 | RE_ENTRANT_CHECK_OFF; | ||
372 | FPU_code_access_ok(2); | ||
373 | FPU_get_user(address, (unsigned short __user *) (*fpu_eip)); | ||
374 | (*fpu_eip) += 2; | ||
375 | RE_ENTRANT_CHECK_ON; | ||
376 | break; | ||
377 | case 3: | ||
378 | /* Not legal for the FPU */ | ||
379 | EXCEPTION(EX_Invalid); | ||
380 | break; | ||
381 | } | ||
382 | switch ( rm ) | ||
383 | { | ||
384 | case 0: | ||
385 | address += FPU_info->___ebx + FPU_info->___esi; | ||
386 | break; | ||
387 | case 1: | ||
388 | address += FPU_info->___ebx + FPU_info->___edi; | ||
389 | break; | ||
390 | case 2: | ||
391 | address += FPU_info->___ebp + FPU_info->___esi; | ||
392 | if ( addr_modes.override.segment == PREFIX_DEFAULT ) | ||
393 | addr_modes.override.segment = PREFIX_SS_; | ||
394 | break; | ||
395 | case 3: | ||
396 | address += FPU_info->___ebp + FPU_info->___edi; | ||
397 | if ( addr_modes.override.segment == PREFIX_DEFAULT ) | ||
398 | addr_modes.override.segment = PREFIX_SS_; | ||
399 | break; | ||
400 | case 4: | ||
401 | address += FPU_info->___esi; | ||
402 | break; | ||
403 | case 5: | ||
404 | address += FPU_info->___edi; | ||
405 | break; | ||
406 | case 6: | ||
407 | address += FPU_info->___ebp; | ||
408 | if ( addr_modes.override.segment == PREFIX_DEFAULT ) | ||
409 | addr_modes.override.segment = PREFIX_SS_; | ||
410 | break; | ||
411 | case 7: | ||
412 | address += FPU_info->___ebx; | ||
413 | break; | ||
414 | } | ||
415 | |||
416 | add_segment: | ||
417 | address &= 0xffff; | ||
418 | |||
419 | addr->offset = address; | ||
420 | |||
421 | switch ( addr_modes.default_mode ) | ||
422 | { | ||
423 | case 0: | ||
424 | break; | ||
425 | case VM86: | ||
426 | address += vm86_segment(addr_modes.override.segment, addr); | ||
427 | break; | ||
428 | case PM16: | ||
429 | case SEG32: | ||
430 | address = pm_address(FPU_modrm, addr_modes.override.segment, | ||
431 | addr, address); | ||
432 | break; | ||
433 | default: | ||
434 | EXCEPTION(EX_INTERNAL|0x131); | ||
435 | } | ||
436 | |||
437 | return (void __user *)address ; | ||
438 | } | 404 | } |