diff options
Diffstat (limited to 'arch/x86/math-emu/load_store.c')
-rw-r--r-- | arch/x86/math-emu/load_store.c | 448 |
1 files changed, 229 insertions, 219 deletions
diff --git a/arch/x86/math-emu/load_store.c b/arch/x86/math-emu/load_store.c index eebd6fb1c8a8..2931ff355218 100644 --- a/arch/x86/math-emu/load_store.c +++ b/arch/x86/math-emu/load_store.c | |||
@@ -26,247 +26,257 @@ | |||
26 | #include "status_w.h" | 26 | #include "status_w.h" |
27 | #include "control_w.h" | 27 | #include "control_w.h" |
28 | 28 | ||
29 | 29 | #define _NONE_ 0 /* st0_ptr etc not needed */ | |
30 | #define _NONE_ 0 /* st0_ptr etc not needed */ | 30 | #define _REG0_ 1 /* Will be storing st(0) */ |
31 | #define _REG0_ 1 /* Will be storing st(0) */ | 31 | #define _PUSH_ 3 /* Need to check for space to push onto stack */ |
32 | #define _PUSH_ 3 /* Need to check for space to push onto stack */ | 32 | #define _null_ 4 /* Function illegal or not implemented */ |
33 | #define _null_ 4 /* Function illegal or not implemented */ | ||
34 | 33 | ||
35 | #define pop_0() { FPU_settag0(TAG_Empty); top++; } | 34 | #define pop_0() { FPU_settag0(TAG_Empty); top++; } |
36 | 35 | ||
37 | |||
38 | static u_char const type_table[32] = { | 36 | static u_char const type_table[32] = { |
39 | _PUSH_, _PUSH_, _PUSH_, _PUSH_, | 37 | _PUSH_, _PUSH_, _PUSH_, _PUSH_, |
40 | _null_, _null_, _null_, _null_, | 38 | _null_, _null_, _null_, _null_, |
41 | _REG0_, _REG0_, _REG0_, _REG0_, | 39 | _REG0_, _REG0_, _REG0_, _REG0_, |
42 | _REG0_, _REG0_, _REG0_, _REG0_, | 40 | _REG0_, _REG0_, _REG0_, _REG0_, |
43 | _NONE_, _null_, _NONE_, _PUSH_, | 41 | _NONE_, _null_, _NONE_, _PUSH_, |
44 | _NONE_, _PUSH_, _null_, _PUSH_, | 42 | _NONE_, _PUSH_, _null_, _PUSH_, |
45 | _NONE_, _null_, _NONE_, _REG0_, | 43 | _NONE_, _null_, _NONE_, _REG0_, |
46 | _NONE_, _REG0_, _NONE_, _REG0_ | 44 | _NONE_, _REG0_, _NONE_, _REG0_ |
47 | }; | 45 | }; |
48 | 46 | ||
49 | u_char const data_sizes_16[32] = { | 47 | u_char const data_sizes_16[32] = { |
50 | 4, 4, 8, 2, 0, 0, 0, 0, | 48 | 4, 4, 8, 2, 0, 0, 0, 0, |
51 | 4, 4, 8, 2, 4, 4, 8, 2, | 49 | 4, 4, 8, 2, 4, 4, 8, 2, |
52 | 14, 0, 94, 10, 2, 10, 0, 8, | 50 | 14, 0, 94, 10, 2, 10, 0, 8, |
53 | 14, 0, 94, 10, 2, 10, 2, 8 | 51 | 14, 0, 94, 10, 2, 10, 2, 8 |
54 | }; | 52 | }; |
55 | 53 | ||
56 | static u_char const data_sizes_32[32] = { | 54 | static u_char const data_sizes_32[32] = { |
57 | 4, 4, 8, 2, 0, 0, 0, 0, | 55 | 4, 4, 8, 2, 0, 0, 0, 0, |
58 | 4, 4, 8, 2, 4, 4, 8, 2, | 56 | 4, 4, 8, 2, 4, 4, 8, 2, |
59 | 28, 0,108, 10, 2, 10, 0, 8, | 57 | 28, 0, 108, 10, 2, 10, 0, 8, |
60 | 28, 0,108, 10, 2, 10, 2, 8 | 58 | 28, 0, 108, 10, 2, 10, 2, 8 |
61 | }; | 59 | }; |
62 | 60 | ||
63 | int FPU_load_store(u_char type, fpu_addr_modes addr_modes, | 61 | int FPU_load_store(u_char type, fpu_addr_modes addr_modes, |
64 | void __user *data_address) | 62 | void __user * data_address) |
65 | { | 63 | { |
66 | FPU_REG loaded_data; | 64 | FPU_REG loaded_data; |
67 | FPU_REG *st0_ptr; | 65 | FPU_REG *st0_ptr; |
68 | u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */ | 66 | u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */ |
69 | u_char loaded_tag; | 67 | u_char loaded_tag; |
70 | 68 | ||
71 | st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ | 69 | st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ |
72 | 70 | ||
73 | if ( addr_modes.default_mode & PROTECTED ) | 71 | if (addr_modes.default_mode & PROTECTED) { |
74 | { | 72 | if (addr_modes.default_mode == SEG32) { |
75 | if ( addr_modes.default_mode == SEG32 ) | 73 | if (access_limit < data_sizes_32[type]) |
76 | { | 74 | math_abort(FPU_info, SIGSEGV); |
77 | if ( access_limit < data_sizes_32[type] ) | 75 | } else if (addr_modes.default_mode == PM16) { |
78 | math_abort(FPU_info,SIGSEGV); | 76 | if (access_limit < data_sizes_16[type]) |
79 | } | 77 | math_abort(FPU_info, SIGSEGV); |
80 | else if ( addr_modes.default_mode == PM16 ) | 78 | } |
81 | { | ||
82 | if ( access_limit < data_sizes_16[type] ) | ||
83 | math_abort(FPU_info,SIGSEGV); | ||
84 | } | ||
85 | #ifdef PARANOID | 79 | #ifdef PARANOID |
86 | else | 80 | else |
87 | EXCEPTION(EX_INTERNAL|0x140); | 81 | EXCEPTION(EX_INTERNAL | 0x140); |
88 | #endif /* PARANOID */ | 82 | #endif /* PARANOID */ |
89 | } | 83 | } |
90 | 84 | ||
91 | switch ( type_table[type] ) | 85 | switch (type_table[type]) { |
92 | { | 86 | case _NONE_: |
93 | case _NONE_: | 87 | break; |
94 | break; | 88 | case _REG0_: |
95 | case _REG0_: | 89 | st0_ptr = &st(0); /* Some of these instructions pop after |
96 | st0_ptr = &st(0); /* Some of these instructions pop after | 90 | storing */ |
97 | storing */ | 91 | st0_tag = FPU_gettag0(); |
98 | st0_tag = FPU_gettag0(); | 92 | break; |
99 | break; | 93 | case _PUSH_: |
100 | case _PUSH_: | 94 | { |
101 | { | 95 | if (FPU_gettagi(-1) != TAG_Empty) { |
102 | if ( FPU_gettagi(-1) != TAG_Empty ) | 96 | FPU_stack_overflow(); |
103 | { FPU_stack_overflow(); return 0; } | 97 | return 0; |
104 | top--; | 98 | } |
105 | st0_ptr = &st(0); | 99 | top--; |
106 | } | 100 | st0_ptr = &st(0); |
107 | break; | 101 | } |
108 | case _null_: | 102 | break; |
109 | FPU_illegal(); | 103 | case _null_: |
110 | return 0; | 104 | FPU_illegal(); |
105 | return 0; | ||
111 | #ifdef PARANOID | 106 | #ifdef PARANOID |
112 | default: | 107 | default: |
113 | EXCEPTION(EX_INTERNAL|0x141); | 108 | EXCEPTION(EX_INTERNAL | 0x141); |
114 | return 0; | 109 | return 0; |
115 | #endif /* PARANOID */ | 110 | #endif /* PARANOID */ |
116 | } | ||
117 | |||
118 | switch ( type ) | ||
119 | { | ||
120 | case 000: /* fld m32real */ | ||
121 | clear_C1(); | ||
122 | loaded_tag = FPU_load_single((float __user *)data_address, &loaded_data); | ||
123 | if ( (loaded_tag == TAG_Special) | ||
124 | && isNaN(&loaded_data) | ||
125 | && (real_1op_NaN(&loaded_data) < 0) ) | ||
126 | { | ||
127 | top++; | ||
128 | break; | ||
129 | } | ||
130 | FPU_copy_to_reg0(&loaded_data, loaded_tag); | ||
131 | break; | ||
132 | case 001: /* fild m32int */ | ||
133 | clear_C1(); | ||
134 | loaded_tag = FPU_load_int32((long __user *)data_address, &loaded_data); | ||
135 | FPU_copy_to_reg0(&loaded_data, loaded_tag); | ||
136 | break; | ||
137 | case 002: /* fld m64real */ | ||
138 | clear_C1(); | ||
139 | loaded_tag = FPU_load_double((double __user *)data_address, &loaded_data); | ||
140 | if ( (loaded_tag == TAG_Special) | ||
141 | && isNaN(&loaded_data) | ||
142 | && (real_1op_NaN(&loaded_data) < 0) ) | ||
143 | { | ||
144 | top++; | ||
145 | break; | ||
146 | } | 111 | } |
147 | FPU_copy_to_reg0(&loaded_data, loaded_tag); | 112 | |
148 | break; | 113 | switch (type) { |
149 | case 003: /* fild m16int */ | 114 | case 000: /* fld m32real */ |
150 | clear_C1(); | 115 | clear_C1(); |
151 | loaded_tag = FPU_load_int16((short __user *)data_address, &loaded_data); | 116 | loaded_tag = |
152 | FPU_copy_to_reg0(&loaded_data, loaded_tag); | 117 | FPU_load_single((float __user *)data_address, &loaded_data); |
153 | break; | 118 | if ((loaded_tag == TAG_Special) |
154 | case 010: /* fst m32real */ | 119 | && isNaN(&loaded_data) |
155 | clear_C1(); | 120 | && (real_1op_NaN(&loaded_data) < 0)) { |
156 | FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address); | 121 | top++; |
157 | break; | 122 | break; |
158 | case 011: /* fist m32int */ | 123 | } |
159 | clear_C1(); | 124 | FPU_copy_to_reg0(&loaded_data, loaded_tag); |
160 | FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address); | 125 | break; |
161 | break; | 126 | case 001: /* fild m32int */ |
162 | case 012: /* fst m64real */ | 127 | clear_C1(); |
163 | clear_C1(); | 128 | loaded_tag = |
164 | FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address); | 129 | FPU_load_int32((long __user *)data_address, &loaded_data); |
165 | break; | 130 | FPU_copy_to_reg0(&loaded_data, loaded_tag); |
166 | case 013: /* fist m16int */ | 131 | break; |
167 | clear_C1(); | 132 | case 002: /* fld m64real */ |
168 | FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address); | 133 | clear_C1(); |
169 | break; | 134 | loaded_tag = |
170 | case 014: /* fstp m32real */ | 135 | FPU_load_double((double __user *)data_address, |
171 | clear_C1(); | 136 | &loaded_data); |
172 | if ( FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address) ) | 137 | if ((loaded_tag == TAG_Special) |
173 | pop_0(); /* pop only if the number was actually stored | 138 | && isNaN(&loaded_data) |
174 | (see the 80486 manual p16-28) */ | 139 | && (real_1op_NaN(&loaded_data) < 0)) { |
175 | break; | 140 | top++; |
176 | case 015: /* fistp m32int */ | 141 | break; |
177 | clear_C1(); | 142 | } |
178 | if ( FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address) ) | 143 | FPU_copy_to_reg0(&loaded_data, loaded_tag); |
179 | pop_0(); /* pop only if the number was actually stored | 144 | break; |
180 | (see the 80486 manual p16-28) */ | 145 | case 003: /* fild m16int */ |
181 | break; | 146 | clear_C1(); |
182 | case 016: /* fstp m64real */ | 147 | loaded_tag = |
183 | clear_C1(); | 148 | FPU_load_int16((short __user *)data_address, &loaded_data); |
184 | if ( FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address) ) | 149 | FPU_copy_to_reg0(&loaded_data, loaded_tag); |
185 | pop_0(); /* pop only if the number was actually stored | 150 | break; |
186 | (see the 80486 manual p16-28) */ | 151 | case 010: /* fst m32real */ |
187 | break; | 152 | clear_C1(); |
188 | case 017: /* fistp m16int */ | 153 | FPU_store_single(st0_ptr, st0_tag, |
189 | clear_C1(); | 154 | (float __user *)data_address); |
190 | if ( FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address) ) | 155 | break; |
191 | pop_0(); /* pop only if the number was actually stored | 156 | case 011: /* fist m32int */ |
192 | (see the 80486 manual p16-28) */ | 157 | clear_C1(); |
193 | break; | 158 | FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address); |
194 | case 020: /* fldenv m14/28byte */ | 159 | break; |
195 | fldenv(addr_modes, (u_char __user *)data_address); | 160 | case 012: /* fst m64real */ |
196 | /* Ensure that the values just loaded are not changed by | 161 | clear_C1(); |
197 | fix-up operations. */ | 162 | FPU_store_double(st0_ptr, st0_tag, |
198 | return 1; | 163 | (double __user *)data_address); |
199 | case 022: /* frstor m94/108byte */ | 164 | break; |
200 | frstor(addr_modes, (u_char __user *)data_address); | 165 | case 013: /* fist m16int */ |
201 | /* Ensure that the values just loaded are not changed by | 166 | clear_C1(); |
202 | fix-up operations. */ | 167 | FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address); |
203 | return 1; | 168 | break; |
204 | case 023: /* fbld m80dec */ | 169 | case 014: /* fstp m32real */ |
205 | clear_C1(); | 170 | clear_C1(); |
206 | loaded_tag = FPU_load_bcd((u_char __user *)data_address); | 171 | if (FPU_store_single |
207 | FPU_settag0(loaded_tag); | 172 | (st0_ptr, st0_tag, (float __user *)data_address)) |
208 | break; | 173 | pop_0(); /* pop only if the number was actually stored |
209 | case 024: /* fldcw */ | 174 | (see the 80486 manual p16-28) */ |
210 | RE_ENTRANT_CHECK_OFF; | 175 | break; |
211 | FPU_access_ok(VERIFY_READ, data_address, 2); | 176 | case 015: /* fistp m32int */ |
212 | FPU_get_user(control_word, (unsigned short __user *) data_address); | 177 | clear_C1(); |
213 | RE_ENTRANT_CHECK_ON; | 178 | if (FPU_store_int32 |
214 | if ( partial_status & ~control_word & CW_Exceptions ) | 179 | (st0_ptr, st0_tag, (long __user *)data_address)) |
215 | partial_status |= (SW_Summary | SW_Backward); | 180 | pop_0(); /* pop only if the number was actually stored |
216 | else | 181 | (see the 80486 manual p16-28) */ |
217 | partial_status &= ~(SW_Summary | SW_Backward); | 182 | break; |
183 | case 016: /* fstp m64real */ | ||
184 | clear_C1(); | ||
185 | if (FPU_store_double | ||
186 | (st0_ptr, st0_tag, (double __user *)data_address)) | ||
187 | pop_0(); /* pop only if the number was actually stored | ||
188 | (see the 80486 manual p16-28) */ | ||
189 | break; | ||
190 | case 017: /* fistp m16int */ | ||
191 | clear_C1(); | ||
192 | if (FPU_store_int16 | ||
193 | (st0_ptr, st0_tag, (short __user *)data_address)) | ||
194 | pop_0(); /* pop only if the number was actually stored | ||
195 | (see the 80486 manual p16-28) */ | ||
196 | break; | ||
197 | case 020: /* fldenv m14/28byte */ | ||
198 | fldenv(addr_modes, (u_char __user *) data_address); | ||
199 | /* Ensure that the values just loaded are not changed by | ||
200 | fix-up operations. */ | ||
201 | return 1; | ||
202 | case 022: /* frstor m94/108byte */ | ||
203 | frstor(addr_modes, (u_char __user *) data_address); | ||
204 | /* Ensure that the values just loaded are not changed by | ||
205 | fix-up operations. */ | ||
206 | return 1; | ||
207 | case 023: /* fbld m80dec */ | ||
208 | clear_C1(); | ||
209 | loaded_tag = FPU_load_bcd((u_char __user *) data_address); | ||
210 | FPU_settag0(loaded_tag); | ||
211 | break; | ||
212 | case 024: /* fldcw */ | ||
213 | RE_ENTRANT_CHECK_OFF; | ||
214 | FPU_access_ok(VERIFY_READ, data_address, 2); | ||
215 | FPU_get_user(control_word, | ||
216 | (unsigned short __user *)data_address); | ||
217 | RE_ENTRANT_CHECK_ON; | ||
218 | if (partial_status & ~control_word & CW_Exceptions) | ||
219 | partial_status |= (SW_Summary | SW_Backward); | ||
220 | else | ||
221 | partial_status &= ~(SW_Summary | SW_Backward); | ||
218 | #ifdef PECULIAR_486 | 222 | #ifdef PECULIAR_486 |
219 | control_word |= 0x40; /* An 80486 appears to always set this bit */ | 223 | control_word |= 0x40; /* An 80486 appears to always set this bit */ |
220 | #endif /* PECULIAR_486 */ | 224 | #endif /* PECULIAR_486 */ |
221 | return 1; | 225 | return 1; |
222 | case 025: /* fld m80real */ | 226 | case 025: /* fld m80real */ |
223 | clear_C1(); | 227 | clear_C1(); |
224 | loaded_tag = FPU_load_extended((long double __user *)data_address, 0); | 228 | loaded_tag = |
225 | FPU_settag0(loaded_tag); | 229 | FPU_load_extended((long double __user *)data_address, 0); |
226 | break; | 230 | FPU_settag0(loaded_tag); |
227 | case 027: /* fild m64int */ | 231 | break; |
228 | clear_C1(); | 232 | case 027: /* fild m64int */ |
229 | loaded_tag = FPU_load_int64((long long __user *)data_address); | 233 | clear_C1(); |
230 | if (loaded_tag == TAG_Error) | 234 | loaded_tag = FPU_load_int64((long long __user *)data_address); |
235 | if (loaded_tag == TAG_Error) | ||
236 | return 0; | ||
237 | FPU_settag0(loaded_tag); | ||
238 | break; | ||
239 | case 030: /* fstenv m14/28byte */ | ||
240 | fstenv(addr_modes, (u_char __user *) data_address); | ||
241 | return 1; | ||
242 | case 032: /* fsave */ | ||
243 | fsave(addr_modes, (u_char __user *) data_address); | ||
244 | return 1; | ||
245 | case 033: /* fbstp m80dec */ | ||
246 | clear_C1(); | ||
247 | if (FPU_store_bcd | ||
248 | (st0_ptr, st0_tag, (u_char __user *) data_address)) | ||
249 | pop_0(); /* pop only if the number was actually stored | ||
250 | (see the 80486 manual p16-28) */ | ||
251 | break; | ||
252 | case 034: /* fstcw m16int */ | ||
253 | RE_ENTRANT_CHECK_OFF; | ||
254 | FPU_access_ok(VERIFY_WRITE, data_address, 2); | ||
255 | FPU_put_user(control_word, | ||
256 | (unsigned short __user *)data_address); | ||
257 | RE_ENTRANT_CHECK_ON; | ||
258 | return 1; | ||
259 | case 035: /* fstp m80real */ | ||
260 | clear_C1(); | ||
261 | if (FPU_store_extended | ||
262 | (st0_ptr, st0_tag, (long double __user *)data_address)) | ||
263 | pop_0(); /* pop only if the number was actually stored | ||
264 | (see the 80486 manual p16-28) */ | ||
265 | break; | ||
266 | case 036: /* fstsw m2byte */ | ||
267 | RE_ENTRANT_CHECK_OFF; | ||
268 | FPU_access_ok(VERIFY_WRITE, data_address, 2); | ||
269 | FPU_put_user(status_word(), | ||
270 | (unsigned short __user *)data_address); | ||
271 | RE_ENTRANT_CHECK_ON; | ||
272 | return 1; | ||
273 | case 037: /* fistp m64int */ | ||
274 | clear_C1(); | ||
275 | if (FPU_store_int64 | ||
276 | (st0_ptr, st0_tag, (long long __user *)data_address)) | ||
277 | pop_0(); /* pop only if the number was actually stored | ||
278 | (see the 80486 manual p16-28) */ | ||
279 | break; | ||
280 | } | ||
231 | return 0; | 281 | return 0; |
232 | FPU_settag0(loaded_tag); | ||
233 | break; | ||
234 | case 030: /* fstenv m14/28byte */ | ||
235 | fstenv(addr_modes, (u_char __user *)data_address); | ||
236 | return 1; | ||
237 | case 032: /* fsave */ | ||
238 | fsave(addr_modes, (u_char __user *)data_address); | ||
239 | return 1; | ||
240 | case 033: /* fbstp m80dec */ | ||
241 | clear_C1(); | ||
242 | if ( FPU_store_bcd(st0_ptr, st0_tag, (u_char __user *)data_address) ) | ||
243 | pop_0(); /* pop only if the number was actually stored | ||
244 | (see the 80486 manual p16-28) */ | ||
245 | break; | ||
246 | case 034: /* fstcw m16int */ | ||
247 | RE_ENTRANT_CHECK_OFF; | ||
248 | FPU_access_ok(VERIFY_WRITE,data_address,2); | ||
249 | FPU_put_user(control_word, (unsigned short __user *) data_address); | ||
250 | RE_ENTRANT_CHECK_ON; | ||
251 | return 1; | ||
252 | case 035: /* fstp m80real */ | ||
253 | clear_C1(); | ||
254 | if ( FPU_store_extended(st0_ptr, st0_tag, (long double __user *)data_address) ) | ||
255 | pop_0(); /* pop only if the number was actually stored | ||
256 | (see the 80486 manual p16-28) */ | ||
257 | break; | ||
258 | case 036: /* fstsw m2byte */ | ||
259 | RE_ENTRANT_CHECK_OFF; | ||
260 | FPU_access_ok(VERIFY_WRITE,data_address,2); | ||
261 | FPU_put_user(status_word(),(unsigned short __user *) data_address); | ||
262 | RE_ENTRANT_CHECK_ON; | ||
263 | return 1; | ||
264 | case 037: /* fistp m64int */ | ||
265 | clear_C1(); | ||
266 | if ( FPU_store_int64(st0_ptr, st0_tag, (long long __user *)data_address) ) | ||
267 | pop_0(); /* pop only if the number was actually stored | ||
268 | (see the 80486 manual p16-28) */ | ||
269 | break; | ||
270 | } | ||
271 | return 0; | ||
272 | } | 282 | } |