/src/xen/xen/arch/x86/x86_emulate/fpu.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | | /****************************************************************************** |
3 | | * fpu.c |
4 | | * |
5 | | * Generic x86 (32-bit and 64-bit) instruction decoder and emulator. |
6 | | * |
7 | | * Copyright (c) 2005-2007 Keir Fraser |
8 | | * Copyright (c) 2005-2007 XenSource Inc. |
9 | | */ |
10 | | |
11 | | #include "private.h" |
12 | | |
13 | | #ifdef __XEN__ |
14 | | # include <asm/amd.h> |
15 | | # define cpu_has_amd_erratum(nr) \ |
16 | | cpu_has_amd_erratum(¤t_cpu_data, AMD_ERRATUM_##nr) |
17 | | #else |
18 | | # define cpu_has_amd_erratum(nr) 0 |
19 | | #endif |
20 | | |
21 | | /* Floating point status word definitions. */ |
22 | 3.40k | #define FSW_ES (1U << 7) |
23 | | |
24 | | static inline bool fpu_check_write(void) |
25 | 3.40k | { |
26 | 3.40k | uint16_t fsw; |
27 | | |
28 | 3.40k | asm ( "fnstsw %0" : "=am" (fsw) ); |
29 | | |
30 | 3.40k | return !(fsw & FSW_ES); |
31 | 3.40k | } |
32 | | |
33 | 4.36k | #define emulate_fpu_insn_memdst(opc, ext, arg) \ |
34 | 4.36k | do { \ |
35 | 4.36k | void *_p = get_stub(stub); \ |
36 | 4.36k | /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \ |
37 | 4.36k | *insn_bytes = 2; \ |
38 | 4.36k | memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \ |
39 | 4.36k | place_ret(_p); \ |
40 | 4.36k | invoke_stub("", "", "+m" (arg) : "a" (&(arg))); \ |
41 | 4.36k | put_stub(stub); \ |
42 | 4.36k | } while (0) |
43 | | |
44 | 3.38k | #define emulate_fpu_insn_memsrc(opc, ext, arg) \ |
45 | 3.38k | do { \ |
46 | 3.38k | void *_p = get_stub(stub); \ |
47 | 3.38k | /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \ |
48 | 3.38k | memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \ |
49 | 3.38k | place_ret(_p); \ |
50 | 3.38k | invoke_stub("", "", "=m" (dummy) : "m" (arg), "a" (&(arg))); \ |
51 | 3.38k | put_stub(stub); \ |
52 | 3.38k | } while (0) |
53 | | |
54 | 35.3k | #define emulate_fpu_insn_stub(bytes...) \ |
55 | 35.3k | do { \ |
56 | 35.3k | void *_p = get_stub(stub); \ |
57 | 35.3k | unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \ |
58 | 35.3k | memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \ |
59 | 35.3k | place_ret(_p); \ |
60 | 35.3k | invoke_stub("", "", "=m" (dummy) : "i" (0)); \ |
61 | 35.3k | put_stub(stub); \ |
62 | 35.3k | } while (0) |
63 | | |
64 | 23.6k | #define emulate_fpu_insn_stub_eflags(bytes...) \ |
65 | 23.6k | do { \ |
66 | 23.6k | void *_p = get_stub(stub); \ |
67 | 23.6k | unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \ |
68 | 23.6k | unsigned long tmp_; \ |
69 | 23.6k | memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \ |
70 | 23.6k | place_ret(_p); \ |
71 | 23.6k | invoke_stub(_PRE_EFLAGS("[eflags]", "[mask]", "[tmp]"), \ |
72 | 23.6k | _POST_EFLAGS("[eflags]", "[mask]", "[tmp]"), \ |
73 | 23.6k | [eflags] "+g" (regs->eflags), [tmp] "=&r" (tmp_) \ |
74 | 23.6k | : [mask] "i" (X86_EFLAGS_ZF|X86_EFLAGS_PF|X86_EFLAGS_CF)); \ |
75 | 23.6k | put_stub(stub); \ |
76 | 23.6k | } while (0) |
77 | | |
78 | | int x86emul_fpu(struct x86_emulate_state *s, |
79 | | struct cpu_user_regs *regs, |
80 | | struct operand *dst, |
81 | | struct operand *src, |
82 | | struct x86_emulate_ctxt *ctxt, |
83 | | const struct x86_emulate_ops *ops, |
84 | | unsigned int *insn_bytes, |
85 | | enum x86_emulate_fpu_type *fpu_type, |
86 | 66.8k | #define fpu_type (*fpu_type) /* for get_fpu() */ |
87 | | mmval_t *mmvalp) |
88 | | #define stub_exn (*s->stub_exn) /* for invoke_stub() */ |
89 | 66.8k | { |
90 | 66.8k | uint8_t b; |
91 | 66.8k | int rc; |
92 | 66.8k | struct x86_emulate_stub stub = {}; |
93 | | |
94 | 66.8k | switch ( b = ctxt->opcode ) |
95 | 66.8k | { |
96 | 0 | unsigned long dummy; |
97 | | |
98 | 2 | case 0x9b: /* wait/fwait */ |
99 | 2 | vcpu_must_have(fpu); |
100 | 2 | get_fpu(X86EMUL_FPU_wait); |
101 | 0 | emulate_fpu_insn_stub(b); |
102 | 0 | break; |
103 | | |
104 | 591 | case 0xd8: /* FPU 0xd8 */ |
105 | 591 | vcpu_must_have(fpu); |
106 | 591 | get_fpu(X86EMUL_FPU_fpu); |
107 | 589 | switch ( s->modrm ) |
108 | 589 | { |
109 | 2 | case 0xc0 ... 0xc7: /* fadd %stN,%st */ |
110 | 22 | case 0xc8 ... 0xcf: /* fmul %stN,%st */ |
111 | 22 | case 0xd0 ... 0xd7: /* fcom %stN,%st */ |
112 | 262 | case 0xd8 ... 0xdf: /* fcomp %stN,%st */ |
113 | 265 | case 0xe0 ... 0xe7: /* fsub %stN,%st */ |
114 | 269 | case 0xe8 ... 0xef: /* fsubr %stN,%st */ |
115 | 288 | case 0xf0 ... 0xf7: /* fdiv %stN,%st */ |
116 | 320 | case 0xf8 ... 0xff: /* fdivr %stN,%st */ |
117 | 320 | emulate_fpu_insn_stub(0xd8, s->modrm); |
118 | 320 | break; |
119 | 320 | default: |
120 | 1.34k | fpu_memsrc32: |
121 | 1.34k | ASSERT(s->ea.type == OP_MEM); |
122 | 1.34k | if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, &src->val, |
123 | 1.34k | 4, ctxt)) != X86EMUL_OKAY ) |
124 | 75 | goto done; |
125 | 1.27k | emulate_fpu_insn_memsrc(b, s->modrm_reg & 7, src->val); |
126 | 1.27k | break; |
127 | 589 | } |
128 | 1.59k | break; |
129 | | |
130 | 11.9k | case 0xd9: /* FPU 0xd9 */ |
131 | 11.9k | vcpu_must_have(fpu); |
132 | 11.9k | get_fpu(X86EMUL_FPU_fpu); |
133 | 11.9k | switch ( s->modrm ) |
134 | 11.9k | { |
135 | 186 | case 0xfb: /* fsincos */ |
136 | 186 | fail_if(cpu_has_amd_erratum(573)); |
137 | | /* fall through */ |
138 | 1.80k | case 0xc0 ... 0xc7: /* fld %stN */ |
139 | 3.48k | case 0xc8 ... 0xcf: /* fxch %stN */ |
140 | 3.65k | case 0xd0: /* fnop */ |
141 | 5.70k | case 0xd8 ... 0xdf: /* fstp %stN (alternative encoding) */ |
142 | 5.86k | case 0xe0: /* fchs */ |
143 | 6.45k | case 0xe1: /* fabs */ |
144 | 6.79k | case 0xe4: /* ftst */ |
145 | 7.11k | case 0xe5: /* fxam */ |
146 | 7.31k | case 0xe8: /* fld1 */ |
147 | 7.48k | case 0xe9: /* fldl2t */ |
148 | 7.55k | case 0xea: /* fldl2e */ |
149 | 7.74k | case 0xeb: /* fldpi */ |
150 | 7.94k | case 0xec: /* fldlg2 */ |
151 | 8.03k | case 0xed: /* fldln2 */ |
152 | 8.22k | case 0xee: /* fldz */ |
153 | 8.47k | case 0xf0: /* f2xm1 */ |
154 | 8.68k | case 0xf1: /* fyl2x */ |
155 | 8.73k | case 0xf2: /* fptan */ |
156 | 8.92k | case 0xf3: /* fpatan */ |
157 | 9.08k | case 0xf4: /* fxtract */ |
158 | 9.28k | case 0xf5: /* fprem1 */ |
159 | 9.47k | case 0xf6: /* fdecstp */ |
160 | 9.67k | case 0xf7: /* fincstp */ |
161 | 9.88k | case 0xf8: /* fprem */ |
162 | 10.0k | case 0xf9: /* fyl2xp1 */ |
163 | 10.1k | case 0xfa: /* fsqrt */ |
164 | 10.4k | case 0xfc: /* frndint */ |
165 | 10.6k | case 0xfd: /* fscale */ |
166 | 10.7k | case 0xfe: /* fsin */ |
167 | 10.8k | case 0xff: /* fcos */ |
168 | 10.8k | emulate_fpu_insn_stub(0xd9, s->modrm); |
169 | 10.8k | break; |
170 | 10.8k | default: |
171 | 1.14k | generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD); |
172 | 1.14k | switch ( s->modrm_reg & 7 ) |
173 | 1.14k | { |
174 | 349 | case 0: /* fld m32fp */ |
175 | 349 | goto fpu_memsrc32; |
176 | 92 | case 2: /* fst m32fp */ |
177 | 335 | case 3: /* fstp m32fp */ |
178 | 1.06k | fpu_memdst32: |
179 | 1.06k | *dst = s->ea; |
180 | 1.06k | dst->bytes = 4; |
181 | 1.06k | emulate_fpu_insn_memdst(b, s->modrm_reg & 7, dst->val); |
182 | 1.06k | break; |
183 | 1.06k | case 4: /* fldenv */ |
184 | | /* Raise #MF now if there are pending unmasked exceptions. */ |
185 | 1 | emulate_fpu_insn_stub(0xd9, 0xd0 /* fnop */); |
186 | | /* fall through */ |
187 | 3 | case 6: /* fnstenv */ |
188 | 3 | fail_if(!ops->blk); |
189 | 0 | s->blk = s->modrm_reg & 2 ? blk_fst : blk_fld; |
190 | | /* |
191 | | * REX is meaningless for these insns by this point - (ab)use |
192 | | * the field to communicate real vs protected mode to ->blk(). |
193 | | */ |
194 | 0 | s->rex_prefix = in_protmode(ctxt, ops); |
195 | 0 | if ( (rc = ops->blk(s->ea.mem.seg, s->ea.mem.off, NULL, |
196 | 0 | s->op_bytes > 2 ? sizeof(struct x87_env32) |
197 | 0 | : sizeof(struct x87_env16), |
198 | 0 | ®s->eflags, |
199 | 0 | s, ctxt)) != X86EMUL_OKAY ) |
200 | 0 | goto done; |
201 | 0 | s->fpu_ctrl = true; |
202 | 0 | break; |
203 | 243 | case 5: /* fldcw m2byte */ |
204 | 243 | s->fpu_ctrl = true; |
205 | 718 | fpu_memsrc16: |
206 | 718 | if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, &src->val, |
207 | 718 | 2, ctxt)) != X86EMUL_OKAY ) |
208 | 32 | goto done; |
209 | 686 | emulate_fpu_insn_memsrc(b, s->modrm_reg & 7, src->val); |
210 | 686 | break; |
211 | 686 | case 7: /* fnstcw m2byte */ |
212 | 213 | s->fpu_ctrl = true; |
213 | 1.58k | fpu_memdst16: |
214 | 1.58k | *dst = s->ea; |
215 | 1.58k | dst->bytes = 2; |
216 | 1.58k | emulate_fpu_insn_memdst(b, s->modrm_reg & 7, dst->val); |
217 | 1.58k | break; |
218 | 1.58k | default: |
219 | 2 | generate_exception(X86_EXC_UD); |
220 | 1.14k | } |
221 | | /* |
222 | | * Control instructions can't raise FPU exceptions, so we need |
223 | | * to consider suppressing writes only for non-control ones. |
224 | | */ |
225 | 3.33k | if ( dst->type == OP_MEM && !s->fpu_ctrl && !fpu_check_write() ) |
226 | 0 | dst->type = OP_NONE; |
227 | 3.33k | break; |
228 | 11.9k | } |
229 | 14.1k | break; |
230 | | |
231 | 14.1k | case 0xda: /* FPU 0xda */ |
232 | 8.10k | vcpu_must_have(fpu); |
233 | 8.10k | get_fpu(X86EMUL_FPU_fpu); |
234 | 8.10k | switch ( s->modrm ) |
235 | 8.10k | { |
236 | 1.89k | case 0xc0 ... 0xc7: /* fcmovb %stN */ |
237 | 3.54k | case 0xc8 ... 0xcf: /* fcmove %stN */ |
238 | 5.59k | case 0xd0 ... 0xd7: /* fcmovbe %stN */ |
239 | 7.64k | case 0xd8 ... 0xdf: /* fcmovu %stN */ |
240 | 7.64k | vcpu_must_have(cmov); |
241 | 7.64k | emulate_fpu_insn_stub_eflags(0xda, s->modrm); |
242 | 7.64k | break; |
243 | 7.64k | case 0xe9: /* fucompp */ |
244 | 186 | emulate_fpu_insn_stub(0xda, s->modrm); |
245 | 186 | break; |
246 | 272 | default: |
247 | 272 | generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD); |
248 | 272 | goto fpu_memsrc32; |
249 | 8.10k | } |
250 | 7.82k | break; |
251 | | |
252 | 15.3k | case 0xdb: /* FPU 0xdb */ |
253 | 15.3k | vcpu_must_have(fpu); |
254 | 15.3k | get_fpu(X86EMUL_FPU_fpu); |
255 | 15.3k | switch ( s->modrm ) |
256 | 15.3k | { |
257 | 1.43k | case 0xc0 ... 0xc7: /* fcmovnb %stN */ |
258 | 3.72k | case 0xc8 ... 0xcf: /* fcmovne %stN */ |
259 | 6.28k | case 0xd0 ... 0xd7: /* fcmovnbe %stN */ |
260 | 8.07k | case 0xd8 ... 0xdf: /* fcmovnu %stN */ |
261 | 10.1k | case 0xe8 ... 0xef: /* fucomi %stN */ |
262 | 12.1k | case 0xf0 ... 0xf7: /* fcomi %stN */ |
263 | 12.1k | vcpu_must_have(cmov); |
264 | 12.1k | emulate_fpu_insn_stub_eflags(0xdb, s->modrm); |
265 | 12.1k | break; |
266 | 12.1k | case 0xe0: /* fneni - 8087 only, ignored by 287 */ |
267 | 444 | case 0xe1: /* fndisi - 8087 only, ignored by 287 */ |
268 | 670 | case 0xe2: /* fnclex */ |
269 | 871 | case 0xe3: /* fninit */ |
270 | 1.06k | case 0xe4: /* fnsetpm - 287 only, ignored by 387 */ |
271 | | /* case 0xe5: frstpm - 287 only, #UD on 387 */ |
272 | 1.06k | s->fpu_ctrl = true; |
273 | 1.06k | emulate_fpu_insn_stub(0xdb, s->modrm); |
274 | 1.06k | break; |
275 | 2.13k | default: |
276 | 2.13k | generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD); |
277 | 2.13k | switch ( s->modrm_reg & 7 ) |
278 | 2.13k | { |
279 | 458 | case 0: /* fild m32i */ |
280 | 458 | goto fpu_memsrc32; |
281 | 306 | case 1: /* fisttp m32i */ |
282 | 306 | vcpu_must_have(sse3); |
283 | | /* fall through */ |
284 | 524 | case 2: /* fist m32i */ |
285 | 728 | case 3: /* fistp m32i */ |
286 | 728 | goto fpu_memdst32; |
287 | 489 | case 5: /* fld m80fp */ |
288 | 755 | fpu_memsrc80: |
289 | 755 | if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, mmvalp, |
290 | 755 | 10, ctxt)) != X86EMUL_OKAY ) |
291 | 16 | goto done; |
292 | 739 | emulate_fpu_insn_memsrc(b, s->modrm_reg & 7, *mmvalp); |
293 | 739 | break; |
294 | 739 | case 7: /* fstp m80fp */ |
295 | 763 | fpu_memdst80: |
296 | 763 | fail_if(!ops->write); |
297 | 762 | emulate_fpu_insn_memdst(b, s->modrm_reg & 7, *mmvalp); |
298 | 762 | if ( fpu_check_write() && |
299 | 762 | (rc = ops->write(s->ea.mem.seg, s->ea.mem.off, mmvalp, |
300 | 762 | 10, ctxt)) != X86EMUL_OKAY ) |
301 | 6 | goto done; |
302 | 756 | break; |
303 | 756 | default: |
304 | 2 | generate_exception(X86_EXC_UD); |
305 | 2.13k | } |
306 | 1.49k | break; |
307 | 15.3k | } |
308 | 14.7k | break; |
309 | | |
310 | 14.7k | case 0xdc: /* FPU 0xdc */ |
311 | 598 | vcpu_must_have(fpu); |
312 | 598 | get_fpu(X86EMUL_FPU_fpu); |
313 | 597 | switch ( s->modrm ) |
314 | 597 | { |
315 | 1 | case 0xc0 ... 0xc7: /* fadd %st,%stN */ |
316 | 7 | case 0xc8 ... 0xcf: /* fmul %st,%stN */ |
317 | 10 | case 0xd0 ... 0xd7: /* fcom %stN,%st (alternative encoding) */ |
318 | 290 | case 0xd8 ... 0xdf: /* fcomp %stN,%st (alternative encoding) */ |
319 | 424 | case 0xe0 ... 0xe7: /* fsubr %st,%stN */ |
320 | 471 | case 0xe8 ... 0xef: /* fsub %st,%stN */ |
321 | 496 | case 0xf0 ... 0xf7: /* fdivr %st,%stN */ |
322 | 502 | case 0xf8 ... 0xff: /* fdiv %st,%stN */ |
323 | 502 | emulate_fpu_insn_stub(0xdc, s->modrm); |
324 | 502 | break; |
325 | 502 | default: |
326 | 328 | fpu_memsrc64: |
327 | 328 | ASSERT(s->ea.type == OP_MEM); |
328 | 328 | if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, &src->val, |
329 | 328 | 8, ctxt)) != X86EMUL_OKAY ) |
330 | 38 | goto done; |
331 | 290 | emulate_fpu_insn_memsrc(b, s->modrm_reg & 7, src->val); |
332 | 290 | break; |
333 | 597 | } |
334 | 792 | break; |
335 | | |
336 | 1.39k | case 0xdd: /* FPU 0xdd */ |
337 | 1.39k | vcpu_must_have(fpu); |
338 | 1.39k | get_fpu(X86EMUL_FPU_fpu); |
339 | 1.38k | switch ( s->modrm ) |
340 | 1.38k | { |
341 | 21 | case 0xc0 ... 0xc7: /* ffree %stN */ |
342 | 23 | case 0xc8 ... 0xcf: /* fxch %stN (alternative encoding) */ |
343 | 36 | case 0xd0 ... 0xd7: /* fst %stN */ |
344 | 305 | case 0xd8 ... 0xdf: /* fstp %stN */ |
345 | 311 | case 0xe0 ... 0xe7: /* fucom %stN */ |
346 | 315 | case 0xe8 ... 0xef: /* fucomp %stN */ |
347 | 315 | emulate_fpu_insn_stub(0xdd, s->modrm); |
348 | 315 | break; |
349 | 1.07k | default: |
350 | 1.07k | generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD); |
351 | 1.07k | switch ( s->modrm_reg & 7 ) |
352 | 1.07k | { |
353 | 50 | case 0: /* fld m64fp */; |
354 | 50 | goto fpu_memsrc64; |
355 | 205 | case 1: /* fisttp m64i */ |
356 | 205 | vcpu_must_have(sse3); |
357 | | /* fall through */ |
358 | 392 | case 2: /* fst m64fp */ |
359 | 470 | case 3: /* fstp m64fp */ |
360 | 760 | fpu_memdst64: |
361 | 760 | *dst = s->ea; |
362 | 760 | dst->bytes = 8; |
363 | 760 | emulate_fpu_insn_memdst(b, s->modrm_reg & 7, dst->val); |
364 | 760 | break; |
365 | 760 | case 4: /* frstor */ |
366 | | /* Raise #MF now if there are pending unmasked exceptions. */ |
367 | 2 | emulate_fpu_insn_stub(0xd9, 0xd0 /* fnop */); |
368 | | /* fall through */ |
369 | 3 | case 6: /* fnsave */ |
370 | 3 | fail_if(!ops->blk); |
371 | 0 | s->blk = s->modrm_reg & 2 ? blk_fst : blk_fld; |
372 | | /* |
373 | | * REX is meaningless for these insns by this point - (ab)use |
374 | | * the field to communicate real vs protected mode to ->blk(). |
375 | | */ |
376 | 0 | s->rex_prefix = in_protmode(ctxt, ops); |
377 | 0 | if ( (rc = ops->blk(s->ea.mem.seg, s->ea.mem.off, NULL, |
378 | 0 | s->op_bytes > 2 ? sizeof(struct x87_env32) + 80 |
379 | 0 | : sizeof(struct x87_env16) + 80, |
380 | 0 | ®s->eflags, |
381 | 0 | s, ctxt)) != X86EMUL_OKAY ) |
382 | 0 | goto done; |
383 | 0 | s->fpu_ctrl = true; |
384 | 0 | break; |
385 | 549 | case 7: /* fnstsw m2byte */ |
386 | 549 | s->fpu_ctrl = true; |
387 | 549 | goto fpu_memdst16; |
388 | 1 | default: |
389 | 1 | generate_exception(X86_EXC_UD); |
390 | 1.07k | } |
391 | | /* |
392 | | * Control instructions can't raise FPU exceptions, so we need |
393 | | * to consider suppressing writes only for non-control ones. |
394 | | */ |
395 | 760 | if ( dst->type == OP_MEM && !s->fpu_ctrl && !fpu_check_write() ) |
396 | 0 | dst->type = OP_NONE; |
397 | 760 | break; |
398 | 1.38k | } |
399 | 1.07k | break; |
400 | | |
401 | 14.1k | case 0xde: /* FPU 0xde */ |
402 | 14.1k | vcpu_must_have(fpu); |
403 | 14.1k | get_fpu(X86EMUL_FPU_fpu); |
404 | 14.1k | switch ( s->modrm ) |
405 | 14.1k | { |
406 | 2.04k | case 0xc0 ... 0xc7: /* faddp %stN */ |
407 | 3.97k | case 0xc8 ... 0xcf: /* fmulp %stN */ |
408 | 6.11k | case 0xd0 ... 0xd7: /* fcomp %stN (alternative encoding) */ |
409 | 6.33k | case 0xd9: /* fcompp */ |
410 | 8.83k | case 0xe0 ... 0xe7: /* fsubrp %stN */ |
411 | 10.4k | case 0xe8 ... 0xef: /* fsubp %stN */ |
412 | 12.1k | case 0xf0 ... 0xf7: /* fdivrp %stN */ |
413 | 13.7k | case 0xf8 ... 0xff: /* fdivp %stN */ |
414 | 13.7k | emulate_fpu_insn_stub(0xde, s->modrm); |
415 | 13.7k | break; |
416 | 13.7k | default: |
417 | 397 | generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD); |
418 | 397 | emulate_fpu_insn_memsrc(b, s->modrm_reg & 7, src->val); |
419 | 395 | break; |
420 | 14.1k | } |
421 | 14.1k | break; |
422 | | |
423 | 14.7k | case 0xdf: /* FPU 0xdf */ |
424 | 14.7k | vcpu_must_have(fpu); |
425 | 14.7k | get_fpu(X86EMUL_FPU_fpu); |
426 | 14.7k | switch ( s->modrm ) |
427 | 14.7k | { |
428 | 197 | case 0xe0: |
429 | | /* fnstsw %ax */ |
430 | 197 | s->fpu_ctrl = true; |
431 | 197 | dst->bytes = 2; |
432 | 197 | dst->type = OP_REG; |
433 | 197 | dst->reg = (void *)®s->ax; |
434 | 197 | emulate_fpu_insn_memdst(b, s->modrm_reg & 7, dst->val); |
435 | 197 | break; |
436 | 1.75k | case 0xe8 ... 0xef: /* fucomip %stN */ |
437 | 3.84k | case 0xf0 ... 0xf7: /* fcomip %stN */ |
438 | 3.84k | vcpu_must_have(cmov); |
439 | 3.84k | emulate_fpu_insn_stub_eflags(0xdf, s->modrm); |
440 | 3.84k | break; |
441 | 3.84k | case 0xc0 ... 0xc7: /* ffreep %stN */ |
442 | 4.06k | case 0xc8 ... 0xcf: /* fxch %stN (alternative encoding) */ |
443 | 6.49k | case 0xd0 ... 0xd7: /* fstp %stN (alternative encoding) */ |
444 | 8.31k | case 0xd8 ... 0xdf: /* fstp %stN (alternative encoding) */ |
445 | 8.31k | emulate_fpu_insn_stub(0xdf, s->modrm); |
446 | 8.31k | break; |
447 | 8.31k | default: |
448 | 2.34k | generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD); |
449 | 2.34k | switch ( s->modrm_reg & 7 ) |
450 | 2.34k | { |
451 | 475 | case 0: /* fild m16i */ |
452 | 475 | goto fpu_memsrc16; |
453 | 334 | case 1: /* fisttp m16i */ |
454 | 334 | vcpu_must_have(sse3); |
455 | | /* fall through */ |
456 | 605 | case 2: /* fist m16i */ |
457 | 820 | case 3: /* fistp m16i */ |
458 | 820 | goto fpu_memdst16; |
459 | 266 | case 4: /* fbld m80dec */ |
460 | 266 | goto fpu_memsrc80; |
461 | 183 | case 5: /* fild m64i */ |
462 | 183 | dst->type = OP_NONE; |
463 | 183 | goto fpu_memsrc64; |
464 | 308 | case 6: /* fbstp packed bcd */ |
465 | 308 | goto fpu_memdst80; |
466 | 290 | case 7: /* fistp m64i */ |
467 | 290 | goto fpu_memdst64; |
468 | 2.34k | } |
469 | 0 | ASSERT_UNREACHABLE(); |
470 | 0 | return X86EMUL_UNHANDLEABLE; |
471 | 14.7k | } |
472 | 12.3k | break; |
473 | | |
474 | 12.3k | default: |
475 | 0 | ASSERT_UNREACHABLE(); |
476 | 0 | return X86EMUL_UNHANDLEABLE; |
477 | 66.8k | } |
478 | | |
479 | 66.6k | rc = X86EMUL_OKAY; |
480 | | |
481 | 66.8k | done: |
482 | 66.8k | put_stub(stub); |
483 | 66.8k | return rc; |
484 | | |
485 | | #ifdef __XEN__ |
486 | | emulation_stub_failure: |
487 | | return X86EMUL_stub_failure; |
488 | | #endif |
489 | 66.6k | } |