VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h@ 47565

Last change on this file since 47565 was 47565, checked in by vboxsync, 11 years ago

logging corrections

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 185.3 KB
Line 
1/* $Id: IEMAllCImpl.cpp.h 47565 2013-08-06 17:17:05Z vboxsync $ */
2/** @file
3 * IEM - Instruction Implementation in C/C++ (code include).
4 */
5
6/*
7 * Copyright (C) 2011-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @name Misc Helpers
20 * @{
21 */
22
23
24/**
25 * Worker function for iemHlpCheckPortIOPermission, don't call directly.
26 *
27 * @returns Strict VBox status code.
28 *
29 * @param pIemCpu The IEM per CPU data.
30 * @param pCtx The register context.
31 * @param u16Port The port number.
32 * @param cbOperand The operand size.
33 */
34static VBOXSTRICTRC iemHlpCheckPortIOPermissionBitmap(PIEMCPU pIemCpu, PCCPUMCTX pCtx, uint16_t u16Port, uint8_t cbOperand)
35{
36 /* The TSS bits we're interested in are the same on 386 and AMD64. */
37 AssertCompile(AMD64_SEL_TYPE_SYS_TSS_BUSY == X86_SEL_TYPE_SYS_386_TSS_BUSY);
38 AssertCompile(AMD64_SEL_TYPE_SYS_TSS_AVAIL == X86_SEL_TYPE_SYS_386_TSS_AVAIL);
39 AssertCompileMembersAtSameOffset(X86TSS32, offIoBitmap, X86TSS64, offIoBitmap);
40 AssertCompile(sizeof(X86TSS32) == sizeof(X86TSS64));
41
42 /*
43 * Check the TSS type, 16-bit TSSes doesn't have any I/O permission bitmap.
44 */
45 Assert(!pCtx->tr.Attr.n.u1DescType);
46 if (RT_UNLIKELY( pCtx->tr.Attr.n.u4Type != AMD64_SEL_TYPE_SYS_TSS_BUSY
47 && pCtx->tr.Attr.n.u4Type != AMD64_SEL_TYPE_SYS_TSS_AVAIL))
48 {
49 Log(("iemHlpCheckPortIOPermissionBitmap: Port=%#x cb=%d - TSS type %#x (attr=%#x) has no I/O bitmap -> #GP(0)\n",
50 u16Port, cbOperand, pCtx->tr.Attr.n.u4Type, pCtx->tr.Attr.u));
51 return iemRaiseGeneralProtectionFault0(pIemCpu);
52 }
53
54 /*
55 * Read the bitmap offset (may #PF).
56 */
57 uint16_t offBitmap;
58 VBOXSTRICTRC rcStrict = iemMemFetchSysU16(pIemCpu, &offBitmap, UINT8_MAX,
59 pCtx->tr.u64Base + RT_OFFSETOF(X86TSS64, offIoBitmap));
60 if (rcStrict != VINF_SUCCESS)
61 {
62 Log(("iemHlpCheckPortIOPermissionBitmap: Error reading offIoBitmap (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
63 return rcStrict;
64 }
65
66 /*
67 * The bit range from u16Port to (u16Port + cbOperand - 1), however intel
68 * describes the CPU actually reading two bytes regardless of whether the
69 * bit range crosses a byte boundrary. Thus the + 1 in the test below.
70 */
71 uint32_t offFirstBit = (uint32_t)u16Port / 8 + offBitmap;
72 /** @todo check if real CPUs ensures that offBitmap has a minimum value of
73 * for instance sizeof(X86TSS32). */
74 if (offFirstBit + 1 > pCtx->tr.u32Limit) /* the limit is inclusive */
75 {
76 Log(("iemHlpCheckPortIOPermissionBitmap: offFirstBit=%#x + 1 is beyond u32Limit=%#x -> #GP(0)\n",
77 offFirstBit, pCtx->tr.u32Limit));
78 return iemRaiseGeneralProtectionFault0(pIemCpu);
79 }
80
81 /*
82 * Read the necessary bits.
83 */
84 /** @todo Test the assertion in the intel manual that the CPU reads two
85 * bytes. The question is how this works wrt to #PF and #GP on the
86 * 2nd byte when it's not required. */
87 uint16_t bmBytes = UINT16_MAX;
88 rcStrict = iemMemFetchSysU16(pIemCpu, &bmBytes, UINT8_MAX, pCtx->tr.u64Base + offFirstBit);
89 if (rcStrict != VINF_SUCCESS)
90 {
91 Log(("iemHlpCheckPortIOPermissionBitmap: Error reading I/O bitmap @%#x (%Rrc)\n", offFirstBit, VBOXSTRICTRC_VAL(rcStrict)));
92 return rcStrict;
93 }
94
95 /*
96 * Perform the check.
97 */
98 uint16_t fPortMask = (1 << cbOperand) - 1;
99 bmBytes >>= (u16Port & 7);
100 if (bmBytes & fPortMask)
101 {
102 Log(("iemHlpCheckPortIOPermissionBitmap: u16Port=%#x LB %u - access denied (bm=%#x mask=%#x) -> #GP(0)\n",
103 u16Port, cbOperand, bmBytes, fPortMask));
104 return iemRaiseGeneralProtectionFault0(pIemCpu);
105 }
106
107 return VINF_SUCCESS;
108}
109
110
111/**
112 * Checks if we are allowed to access the given I/O port, raising the
113 * appropriate exceptions if we aren't (or if the I/O bitmap is not
114 * accessible).
115 *
116 * @returns Strict VBox status code.
117 *
118 * @param pIemCpu The IEM per CPU data.
119 * @param pCtx The register context.
120 * @param u16Port The port number.
121 * @param cbOperand The operand size.
122 */
123DECLINLINE(VBOXSTRICTRC) iemHlpCheckPortIOPermission(PIEMCPU pIemCpu, PCCPUMCTX pCtx, uint16_t u16Port, uint8_t cbOperand)
124{
125 X86EFLAGS Efl;
126 Efl.u = IEMMISC_GET_EFL(pIemCpu, pCtx);
127 if ( (pCtx->cr0 & X86_CR0_PE)
128 && ( pIemCpu->uCpl > Efl.Bits.u2IOPL
129 || Efl.Bits.u1VM) )
130 return iemHlpCheckPortIOPermissionBitmap(pIemCpu, pCtx, u16Port, cbOperand);
131 return VINF_SUCCESS;
132}
133
134
135#if 0
136/**
137 * Calculates the parity bit.
138 *
139 * @returns true if the bit is set, false if not.
140 * @param u8Result The least significant byte of the result.
141 */
142static bool iemHlpCalcParityFlag(uint8_t u8Result)
143{
144 /*
145 * Parity is set if the number of bits in the least significant byte of
146 * the result is even.
147 */
148 uint8_t cBits;
149 cBits = u8Result & 1; /* 0 */
150 u8Result >>= 1;
151 cBits += u8Result & 1;
152 u8Result >>= 1;
153 cBits += u8Result & 1;
154 u8Result >>= 1;
155 cBits += u8Result & 1;
156 u8Result >>= 1;
157 cBits += u8Result & 1; /* 4 */
158 u8Result >>= 1;
159 cBits += u8Result & 1;
160 u8Result >>= 1;
161 cBits += u8Result & 1;
162 u8Result >>= 1;
163 cBits += u8Result & 1;
164 return !(cBits & 1);
165}
166#endif /* not used */
167
168
169/**
170 * Updates the specified flags according to a 8-bit result.
171 *
172 * @param pIemCpu The IEM state of the calling EMT.
173 * @param u8Result The result to set the flags according to.
174 * @param fToUpdate The flags to update.
175 * @param fUndefined The flags that are specified as undefined.
176 */
177static void iemHlpUpdateArithEFlagsU8(PIEMCPU pIemCpu, uint8_t u8Result, uint32_t fToUpdate, uint32_t fUndefined)
178{
179 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
180
181 uint32_t fEFlags = pCtx->eflags.u;
182 iemAImpl_test_u8(&u8Result, u8Result, &fEFlags);
183 pCtx->eflags.u &= ~(fToUpdate | fUndefined);
184 pCtx->eflags.u |= (fToUpdate | fUndefined) & fEFlags;
185}
186
187
188/**
189 * Loads a NULL data selector into a selector register, both the hidden and
190 * visible parts, in protected mode.
191 *
192 * @param pSReg Pointer to the segment register.
193 * @param uRpl The RPL.
194 */
195static void iemHlpLoadNullDataSelectorProt(PCPUMSELREG pSReg, RTSEL uRpl)
196{
197 /** @todo Testcase: write a testcase checking what happends when loading a NULL
198 * data selector in protected mode. */
199 pSReg->Sel = uRpl;
200 pSReg->ValidSel = uRpl;
201 pSReg->fFlags = CPUMSELREG_FLAGS_VALID;
202 pSReg->u64Base = 0;
203 pSReg->u32Limit = 0;
204 pSReg->Attr.u = X86DESCATTR_UNUSABLE;
205}
206
207
208/**
209 * Helper used by iret.
210 *
211 * @param uCpl The new CPL.
212 * @param pSReg Pointer to the segment register.
213 */
214static void iemHlpAdjustSelectorForNewCpl(PIEMCPU pIemCpu, uint8_t uCpl, PCPUMSELREG pSReg)
215{
216#ifdef VBOX_WITH_RAW_MODE_NOT_R0
217 if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(IEMCPU_TO_VMCPU(pIemCpu), pSReg))
218 CPUMGuestLazyLoadHiddenSelectorReg(IEMCPU_TO_VMCPU(pIemCpu), pSReg);
219#else
220 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(IEMCPU_TO_VMCPU(pIemCpu), pSReg));
221#endif
222
223 if ( uCpl > pSReg->Attr.n.u2Dpl
224 && pSReg->Attr.n.u1DescType /* code or data, not system */
225 && (pSReg->Attr.n.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
226 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF)) /* not conforming code */
227 iemHlpLoadNullDataSelectorProt(pSReg, 0);
228}
229
230
231/**
232 * Indicates that we have modified the FPU state.
233 *
234 * @param pIemCpu The IEM state of the calling EMT.
235 */
236DECLINLINE(void) iemHlpUsedFpu(PIEMCPU pIemCpu)
237{
238 CPUMSetChangedFlags(IEMCPU_TO_VMCPU(pIemCpu), CPUM_CHANGED_FPU_REM);
239}
240
241/** @} */
242
243/** @name C Implementations
244 * @{
245 */
246
247/**
248 * Implements a 16-bit popa.
249 */
250IEM_CIMPL_DEF_0(iemCImpl_popa_16)
251{
252 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
253 RTGCPTR GCPtrStart = iemRegGetEffRsp(pIemCpu, pCtx);
254 RTGCPTR GCPtrLast = GCPtrStart + 15;
255 VBOXSTRICTRC rcStrict;
256
257 /*
258 * The docs are a bit hard to comprehend here, but it looks like we wrap
259 * around in real mode as long as none of the individual "popa" crosses the
260 * end of the stack segment. In protected mode we check the whole access
261 * in one go. For efficiency, only do the word-by-word thing if we're in
262 * danger of wrapping around.
263 */
264 /** @todo do popa boundary / wrap-around checks. */
265 if (RT_UNLIKELY( IEM_IS_REAL_OR_V86_MODE(pIemCpu)
266 && (pCtx->cs.u32Limit < GCPtrLast)) ) /* ASSUMES 64-bit RTGCPTR */
267 {
268 /* word-by-word */
269 RTUINT64U TmpRsp;
270 TmpRsp.u = pCtx->rsp;
271 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->di, &TmpRsp);
272 if (rcStrict == VINF_SUCCESS)
273 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->si, &TmpRsp);
274 if (rcStrict == VINF_SUCCESS)
275 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->bp, &TmpRsp);
276 if (rcStrict == VINF_SUCCESS)
277 {
278 iemRegAddToRspEx(pIemCpu, pCtx, &TmpRsp, 2); /* sp */
279 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->bx, &TmpRsp);
280 }
281 if (rcStrict == VINF_SUCCESS)
282 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->dx, &TmpRsp);
283 if (rcStrict == VINF_SUCCESS)
284 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->cx, &TmpRsp);
285 if (rcStrict == VINF_SUCCESS)
286 rcStrict = iemMemStackPopU16Ex(pIemCpu, &pCtx->ax, &TmpRsp);
287 if (rcStrict == VINF_SUCCESS)
288 {
289 pCtx->rsp = TmpRsp.u;
290 iemRegAddToRip(pIemCpu, cbInstr);
291 }
292 }
293 else
294 {
295 uint16_t const *pa16Mem = NULL;
296 rcStrict = iemMemMap(pIemCpu, (void **)&pa16Mem, 16, X86_SREG_SS, GCPtrStart, IEM_ACCESS_STACK_R);
297 if (rcStrict == VINF_SUCCESS)
298 {
299 pCtx->di = pa16Mem[7 - X86_GREG_xDI];
300 pCtx->si = pa16Mem[7 - X86_GREG_xSI];
301 pCtx->bp = pa16Mem[7 - X86_GREG_xBP];
302 /* skip sp */
303 pCtx->bx = pa16Mem[7 - X86_GREG_xBX];
304 pCtx->dx = pa16Mem[7 - X86_GREG_xDX];
305 pCtx->cx = pa16Mem[7 - X86_GREG_xCX];
306 pCtx->ax = pa16Mem[7 - X86_GREG_xAX];
307 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)pa16Mem, IEM_ACCESS_STACK_R);
308 if (rcStrict == VINF_SUCCESS)
309 {
310 iemRegAddToRsp(pIemCpu, pCtx, 16);
311 iemRegAddToRip(pIemCpu, cbInstr);
312 }
313 }
314 }
315 return rcStrict;
316}
317
318
319/**
320 * Implements a 32-bit popa.
321 */
322IEM_CIMPL_DEF_0(iemCImpl_popa_32)
323{
324 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
325 RTGCPTR GCPtrStart = iemRegGetEffRsp(pIemCpu, pCtx);
326 RTGCPTR GCPtrLast = GCPtrStart + 31;
327 VBOXSTRICTRC rcStrict;
328
329 /*
330 * The docs are a bit hard to comprehend here, but it looks like we wrap
331 * around in real mode as long as none of the individual "popa" crosses the
332 * end of the stack segment. In protected mode we check the whole access
333 * in one go. For efficiency, only do the word-by-word thing if we're in
334 * danger of wrapping around.
335 */
336 /** @todo do popa boundary / wrap-around checks. */
337 if (RT_UNLIKELY( IEM_IS_REAL_OR_V86_MODE(pIemCpu)
338 && (pCtx->cs.u32Limit < GCPtrLast)) ) /* ASSUMES 64-bit RTGCPTR */
339 {
340 /* word-by-word */
341 RTUINT64U TmpRsp;
342 TmpRsp.u = pCtx->rsp;
343 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->edi, &TmpRsp);
344 if (rcStrict == VINF_SUCCESS)
345 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->esi, &TmpRsp);
346 if (rcStrict == VINF_SUCCESS)
347 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->ebp, &TmpRsp);
348 if (rcStrict == VINF_SUCCESS)
349 {
350 iemRegAddToRspEx(pIemCpu, pCtx, &TmpRsp, 2); /* sp */
351 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->ebx, &TmpRsp);
352 }
353 if (rcStrict == VINF_SUCCESS)
354 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->edx, &TmpRsp);
355 if (rcStrict == VINF_SUCCESS)
356 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->ecx, &TmpRsp);
357 if (rcStrict == VINF_SUCCESS)
358 rcStrict = iemMemStackPopU32Ex(pIemCpu, &pCtx->eax, &TmpRsp);
359 if (rcStrict == VINF_SUCCESS)
360 {
361#if 1 /** @todo what actually happens with the high bits when we're in 16-bit mode? */
362 pCtx->rdi &= UINT32_MAX;
363 pCtx->rsi &= UINT32_MAX;
364 pCtx->rbp &= UINT32_MAX;
365 pCtx->rbx &= UINT32_MAX;
366 pCtx->rdx &= UINT32_MAX;
367 pCtx->rcx &= UINT32_MAX;
368 pCtx->rax &= UINT32_MAX;
369#endif
370 pCtx->rsp = TmpRsp.u;
371 iemRegAddToRip(pIemCpu, cbInstr);
372 }
373 }
374 else
375 {
376 uint32_t const *pa32Mem;
377 rcStrict = iemMemMap(pIemCpu, (void **)&pa32Mem, 32, X86_SREG_SS, GCPtrStart, IEM_ACCESS_STACK_R);
378 if (rcStrict == VINF_SUCCESS)
379 {
380 pCtx->rdi = pa32Mem[7 - X86_GREG_xDI];
381 pCtx->rsi = pa32Mem[7 - X86_GREG_xSI];
382 pCtx->rbp = pa32Mem[7 - X86_GREG_xBP];
383 /* skip esp */
384 pCtx->rbx = pa32Mem[7 - X86_GREG_xBX];
385 pCtx->rdx = pa32Mem[7 - X86_GREG_xDX];
386 pCtx->rcx = pa32Mem[7 - X86_GREG_xCX];
387 pCtx->rax = pa32Mem[7 - X86_GREG_xAX];
388 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)pa32Mem, IEM_ACCESS_STACK_R);
389 if (rcStrict == VINF_SUCCESS)
390 {
391 iemRegAddToRsp(pIemCpu, pCtx, 32);
392 iemRegAddToRip(pIemCpu, cbInstr);
393 }
394 }
395 }
396 return rcStrict;
397}
398
399
400/**
401 * Implements a 16-bit pusha.
402 */
403IEM_CIMPL_DEF_0(iemCImpl_pusha_16)
404{
405 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
406 RTGCPTR GCPtrTop = iemRegGetEffRsp(pIemCpu, pCtx);
407 RTGCPTR GCPtrBottom = GCPtrTop - 15;
408 VBOXSTRICTRC rcStrict;
409
410 /*
411 * The docs are a bit hard to comprehend here, but it looks like we wrap
412 * around in real mode as long as none of the individual "pushd" crosses the
413 * end of the stack segment. In protected mode we check the whole access
414 * in one go. For efficiency, only do the word-by-word thing if we're in
415 * danger of wrapping around.
416 */
417 /** @todo do pusha boundary / wrap-around checks. */
418 if (RT_UNLIKELY( GCPtrBottom > GCPtrTop
419 && IEM_IS_REAL_OR_V86_MODE(pIemCpu) ) )
420 {
421 /* word-by-word */
422 RTUINT64U TmpRsp;
423 TmpRsp.u = pCtx->rsp;
424 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->ax, &TmpRsp);
425 if (rcStrict == VINF_SUCCESS)
426 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->cx, &TmpRsp);
427 if (rcStrict == VINF_SUCCESS)
428 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->dx, &TmpRsp);
429 if (rcStrict == VINF_SUCCESS)
430 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->bx, &TmpRsp);
431 if (rcStrict == VINF_SUCCESS)
432 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->sp, &TmpRsp);
433 if (rcStrict == VINF_SUCCESS)
434 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->bp, &TmpRsp);
435 if (rcStrict == VINF_SUCCESS)
436 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->si, &TmpRsp);
437 if (rcStrict == VINF_SUCCESS)
438 rcStrict = iemMemStackPushU16Ex(pIemCpu, pCtx->di, &TmpRsp);
439 if (rcStrict == VINF_SUCCESS)
440 {
441 pCtx->rsp = TmpRsp.u;
442 iemRegAddToRip(pIemCpu, cbInstr);
443 }
444 }
445 else
446 {
447 GCPtrBottom--;
448 uint16_t *pa16Mem = NULL;
449 rcStrict = iemMemMap(pIemCpu, (void **)&pa16Mem, 16, X86_SREG_SS, GCPtrBottom, IEM_ACCESS_STACK_W);
450 if (rcStrict == VINF_SUCCESS)
451 {
452 pa16Mem[7 - X86_GREG_xDI] = pCtx->di;
453 pa16Mem[7 - X86_GREG_xSI] = pCtx->si;
454 pa16Mem[7 - X86_GREG_xBP] = pCtx->bp;
455 pa16Mem[7 - X86_GREG_xSP] = pCtx->sp;
456 pa16Mem[7 - X86_GREG_xBX] = pCtx->bx;
457 pa16Mem[7 - X86_GREG_xDX] = pCtx->dx;
458 pa16Mem[7 - X86_GREG_xCX] = pCtx->cx;
459 pa16Mem[7 - X86_GREG_xAX] = pCtx->ax;
460 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)pa16Mem, IEM_ACCESS_STACK_W);
461 if (rcStrict == VINF_SUCCESS)
462 {
463 iemRegSubFromRsp(pIemCpu, pCtx, 16);
464 iemRegAddToRip(pIemCpu, cbInstr);
465 }
466 }
467 }
468 return rcStrict;
469}
470
471
472/**
473 * Implements a 32-bit pusha.
474 */
475IEM_CIMPL_DEF_0(iemCImpl_pusha_32)
476{
477 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
478 RTGCPTR GCPtrTop = iemRegGetEffRsp(pIemCpu, pCtx);
479 RTGCPTR GCPtrBottom = GCPtrTop - 31;
480 VBOXSTRICTRC rcStrict;
481
482 /*
483 * The docs are a bit hard to comprehend here, but it looks like we wrap
484 * around in real mode as long as none of the individual "pusha" crosses the
485 * end of the stack segment. In protected mode we check the whole access
486 * in one go. For efficiency, only do the word-by-word thing if we're in
487 * danger of wrapping around.
488 */
489 /** @todo do pusha boundary / wrap-around checks. */
490 if (RT_UNLIKELY( GCPtrBottom > GCPtrTop
491 && IEM_IS_REAL_OR_V86_MODE(pIemCpu) ) )
492 {
493 /* word-by-word */
494 RTUINT64U TmpRsp;
495 TmpRsp.u = pCtx->rsp;
496 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->eax, &TmpRsp);
497 if (rcStrict == VINF_SUCCESS)
498 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->ecx, &TmpRsp);
499 if (rcStrict == VINF_SUCCESS)
500 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->edx, &TmpRsp);
501 if (rcStrict == VINF_SUCCESS)
502 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->ebx, &TmpRsp);
503 if (rcStrict == VINF_SUCCESS)
504 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->esp, &TmpRsp);
505 if (rcStrict == VINF_SUCCESS)
506 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->ebp, &TmpRsp);
507 if (rcStrict == VINF_SUCCESS)
508 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->esi, &TmpRsp);
509 if (rcStrict == VINF_SUCCESS)
510 rcStrict = iemMemStackPushU32Ex(pIemCpu, pCtx->edi, &TmpRsp);
511 if (rcStrict == VINF_SUCCESS)
512 {
513 pCtx->rsp = TmpRsp.u;
514 iemRegAddToRip(pIemCpu, cbInstr);
515 }
516 }
517 else
518 {
519 GCPtrBottom--;
520 uint32_t *pa32Mem;
521 rcStrict = iemMemMap(pIemCpu, (void **)&pa32Mem, 32, X86_SREG_SS, GCPtrBottom, IEM_ACCESS_STACK_W);
522 if (rcStrict == VINF_SUCCESS)
523 {
524 pa32Mem[7 - X86_GREG_xDI] = pCtx->edi;
525 pa32Mem[7 - X86_GREG_xSI] = pCtx->esi;
526 pa32Mem[7 - X86_GREG_xBP] = pCtx->ebp;
527 pa32Mem[7 - X86_GREG_xSP] = pCtx->esp;
528 pa32Mem[7 - X86_GREG_xBX] = pCtx->ebx;
529 pa32Mem[7 - X86_GREG_xDX] = pCtx->edx;
530 pa32Mem[7 - X86_GREG_xCX] = pCtx->ecx;
531 pa32Mem[7 - X86_GREG_xAX] = pCtx->eax;
532 rcStrict = iemMemCommitAndUnmap(pIemCpu, pa32Mem, IEM_ACCESS_STACK_W);
533 if (rcStrict == VINF_SUCCESS)
534 {
535 iemRegSubFromRsp(pIemCpu, pCtx, 32);
536 iemRegAddToRip(pIemCpu, cbInstr);
537 }
538 }
539 }
540 return rcStrict;
541}
542
543
544/**
545 * Implements pushf.
546 *
547 *
548 * @param enmEffOpSize The effective operand size.
549 */
550IEM_CIMPL_DEF_1(iemCImpl_pushf, IEMMODE, enmEffOpSize)
551{
552 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
553
554 /*
555 * If we're in V8086 mode some care is required (which is why we're in
556 * doing this in a C implementation).
557 */
558 uint32_t fEfl = IEMMISC_GET_EFL(pIemCpu, pCtx);
559 if ( (fEfl & X86_EFL_VM)
560 && X86_EFL_GET_IOPL(fEfl) != 3 )
561 {
562 Assert(pCtx->cr0 & X86_CR0_PE);
563 if ( enmEffOpSize != IEMMODE_16BIT
564 || !(pCtx->cr4 & X86_CR4_VME))
565 return iemRaiseGeneralProtectionFault0(pIemCpu);
566 fEfl &= ~X86_EFL_IF; /* (RF and VM are out of range) */
567 fEfl |= (fEfl & X86_EFL_VIF) >> (19 - 9);
568 return iemMemStackPushU16(pIemCpu, (uint16_t)fEfl);
569 }
570
571 /*
572 * Ok, clear RF and VM and push the flags.
573 */
574 fEfl &= ~(X86_EFL_RF | X86_EFL_VM);
575
576 VBOXSTRICTRC rcStrict;
577 switch (enmEffOpSize)
578 {
579 case IEMMODE_16BIT:
580 rcStrict = iemMemStackPushU16(pIemCpu, (uint16_t)fEfl);
581 break;
582 case IEMMODE_32BIT:
583 rcStrict = iemMemStackPushU32(pIemCpu, fEfl);
584 break;
585 case IEMMODE_64BIT:
586 rcStrict = iemMemStackPushU64(pIemCpu, fEfl);
587 break;
588 IEM_NOT_REACHED_DEFAULT_CASE_RET();
589 }
590 if (rcStrict != VINF_SUCCESS)
591 return rcStrict;
592
593 iemRegAddToRip(pIemCpu, cbInstr);
594 return VINF_SUCCESS;
595}
596
597
598/**
599 * Implements popf.
600 *
601 * @param enmEffOpSize The effective operand size.
602 */
603IEM_CIMPL_DEF_1(iemCImpl_popf, IEMMODE, enmEffOpSize)
604{
605 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
606 PVMCPU pVCpu = IEMCPU_TO_VMCPU(pIemCpu);
607 uint32_t const fEflOld = IEMMISC_GET_EFL(pIemCpu, pCtx);
608 VBOXSTRICTRC rcStrict;
609 uint32_t fEflNew;
610
611 /*
612 * V8086 is special as usual.
613 */
614 if (fEflOld & X86_EFL_VM)
615 {
616 /*
617 * Almost anything goes if IOPL is 3.
618 */
619 if (X86_EFL_GET_IOPL(fEflOld) == 3)
620 {
621 switch (enmEffOpSize)
622 {
623 case IEMMODE_16BIT:
624 {
625 uint16_t u16Value;
626 rcStrict = iemMemStackPopU16(pIemCpu, &u16Value);
627 if (rcStrict != VINF_SUCCESS)
628 return rcStrict;
629 fEflNew = u16Value | (fEflOld & UINT32_C(0xffff0000));
630 break;
631 }
632 case IEMMODE_32BIT:
633 rcStrict = iemMemStackPopU32(pIemCpu, &fEflNew);
634 if (rcStrict != VINF_SUCCESS)
635 return rcStrict;
636 break;
637 IEM_NOT_REACHED_DEFAULT_CASE_RET();
638 }
639
640 fEflNew &= X86_EFL_POPF_BITS & ~(X86_EFL_IOPL);
641 fEflNew |= ~(X86_EFL_POPF_BITS & ~(X86_EFL_IOPL)) & fEflOld;
642 }
643 /*
644 * Interrupt flag virtualization with CR4.VME=1.
645 */
646 else if ( enmEffOpSize == IEMMODE_16BIT
647 && (pCtx->cr4 & X86_CR4_VME) )
648 {
649 uint16_t u16Value;
650 RTUINT64U TmpRsp;
651 TmpRsp.u = pCtx->rsp;
652 rcStrict = iemMemStackPopU16Ex(pIemCpu, &u16Value, &TmpRsp);
653 if (rcStrict != VINF_SUCCESS)
654 return rcStrict;
655
656 /** @todo Is the popf VME #GP(0) delivered after updating RSP+RIP
657 * or before? */
658 if ( ( (u16Value & X86_EFL_IF)
659 && (fEflOld & X86_EFL_VIP))
660 || (u16Value & X86_EFL_TF) )
661 return iemRaiseGeneralProtectionFault0(pIemCpu);
662
663 fEflNew = u16Value | (fEflOld & UINT32_C(0xffff0000) & ~X86_EFL_VIF);
664 fEflNew |= (fEflNew & X86_EFL_IF) << (19 - 9);
665 fEflNew &= X86_EFL_POPF_BITS & ~(X86_EFL_IOPL | X86_EFL_IF);
666 fEflNew |= ~(X86_EFL_POPF_BITS & ~(X86_EFL_IOPL | X86_EFL_IF)) & fEflOld;
667
668 pCtx->rsp = TmpRsp.u;
669 }
670 else
671 return iemRaiseGeneralProtectionFault0(pIemCpu);
672
673 }
674 /*
675 * Not in V8086 mode.
676 */
677 else
678 {
679 /* Pop the flags. */
680 switch (enmEffOpSize)
681 {
682 case IEMMODE_16BIT:
683 {
684 uint16_t u16Value;
685 rcStrict = iemMemStackPopU16(pIemCpu, &u16Value);
686 if (rcStrict != VINF_SUCCESS)
687 return rcStrict;
688 fEflNew = u16Value | (fEflOld & UINT32_C(0xffff0000));
689 break;
690 }
691 case IEMMODE_32BIT:
692 rcStrict = iemMemStackPopU32(pIemCpu, &fEflNew);
693 if (rcStrict != VINF_SUCCESS)
694 return rcStrict;
695 break;
696 case IEMMODE_64BIT:
697 {
698 uint64_t u64Value;
699 rcStrict = iemMemStackPopU64(pIemCpu, &u64Value);
700 if (rcStrict != VINF_SUCCESS)
701 return rcStrict;
702 fEflNew = u64Value; /** @todo testcase: Check exactly what happens if high bits are set. */
703 break;
704 }
705 IEM_NOT_REACHED_DEFAULT_CASE_RET();
706 }
707
708 /* Merge them with the current flags. */
709 if ( (fEflNew & (X86_EFL_IOPL | X86_EFL_IF)) == (fEflOld & (X86_EFL_IOPL | X86_EFL_IF))
710 || pIemCpu->uCpl == 0)
711 {
712 fEflNew &= X86_EFL_POPF_BITS;
713 fEflNew |= ~X86_EFL_POPF_BITS & fEflOld;
714 }
715 else if (pIemCpu->uCpl <= X86_EFL_GET_IOPL(fEflOld))
716 {
717 fEflNew &= X86_EFL_POPF_BITS & ~(X86_EFL_IOPL);
718 fEflNew |= ~(X86_EFL_POPF_BITS & ~(X86_EFL_IOPL)) & fEflOld;
719 }
720 else
721 {
722 fEflNew &= X86_EFL_POPF_BITS & ~(X86_EFL_IOPL | X86_EFL_IF);
723 fEflNew |= ~(X86_EFL_POPF_BITS & ~(X86_EFL_IOPL | X86_EFL_IF)) & fEflOld;
724 }
725 }
726
727 /*
728 * Commit the flags.
729 */
730 Assert(fEflNew & RT_BIT_32(1));
731 IEMMISC_SET_EFL(pIemCpu, pCtx, fEflNew);
732 iemRegAddToRip(pIemCpu, cbInstr);
733
734 return VINF_SUCCESS;
735}
736
737
738/**
739 * Implements an indirect call.
740 *
741 * @param uNewPC The new program counter (RIP) value (loaded from the
742 * operand).
743 * @param enmEffOpSize The effective operand size.
744 */
745IEM_CIMPL_DEF_1(iemCImpl_call_16, uint16_t, uNewPC)
746{
747 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
748 uint16_t uOldPC = pCtx->ip + cbInstr;
749 if (uNewPC > pCtx->cs.u32Limit)
750 return iemRaiseGeneralProtectionFault0(pIemCpu);
751
752 VBOXSTRICTRC rcStrict = iemMemStackPushU16(pIemCpu, uOldPC);
753 if (rcStrict != VINF_SUCCESS)
754 return rcStrict;
755
756 pCtx->rip = uNewPC;
757 return VINF_SUCCESS;
758
759}
760
761
762/**
763 * Implements a 16-bit relative call.
764 *
765 * @param offDisp The displacment offset.
766 */
767IEM_CIMPL_DEF_1(iemCImpl_call_rel_16, int16_t, offDisp)
768{
769 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
770 uint16_t uOldPC = pCtx->ip + cbInstr;
771 uint16_t uNewPC = uOldPC + offDisp;
772 if (uNewPC > pCtx->cs.u32Limit)
773 return iemRaiseGeneralProtectionFault0(pIemCpu);
774
775 VBOXSTRICTRC rcStrict = iemMemStackPushU16(pIemCpu, uOldPC);
776 if (rcStrict != VINF_SUCCESS)
777 return rcStrict;
778
779 pCtx->rip = uNewPC;
780 return VINF_SUCCESS;
781}
782
783
784/**
785 * Implements a 32-bit indirect call.
786 *
787 * @param uNewPC The new program counter (RIP) value (loaded from the
788 * operand).
789 * @param enmEffOpSize The effective operand size.
790 */
791IEM_CIMPL_DEF_1(iemCImpl_call_32, uint32_t, uNewPC)
792{
793 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
794 uint32_t uOldPC = pCtx->eip + cbInstr;
795 if (uNewPC > pCtx->cs.u32Limit)
796 return iemRaiseGeneralProtectionFault0(pIemCpu);
797
798 VBOXSTRICTRC rcStrict = iemMemStackPushU32(pIemCpu, uOldPC);
799 if (rcStrict != VINF_SUCCESS)
800 return rcStrict;
801
802 pCtx->rip = uNewPC;
803 return VINF_SUCCESS;
804
805}
806
807
808/**
809 * Implements a 32-bit relative call.
810 *
811 * @param offDisp The displacment offset.
812 */
813IEM_CIMPL_DEF_1(iemCImpl_call_rel_32, int32_t, offDisp)
814{
815 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
816 uint32_t uOldPC = pCtx->eip + cbInstr;
817 uint32_t uNewPC = uOldPC + offDisp;
818 if (uNewPC > pCtx->cs.u32Limit)
819 return iemRaiseGeneralProtectionFault0(pIemCpu);
820
821 VBOXSTRICTRC rcStrict = iemMemStackPushU32(pIemCpu, uOldPC);
822 if (rcStrict != VINF_SUCCESS)
823 return rcStrict;
824
825 pCtx->rip = uNewPC;
826 return VINF_SUCCESS;
827}
828
829
830/**
831 * Implements a 64-bit indirect call.
832 *
833 * @param uNewPC The new program counter (RIP) value (loaded from the
834 * operand).
835 * @param enmEffOpSize The effective operand size.
836 */
837IEM_CIMPL_DEF_1(iemCImpl_call_64, uint64_t, uNewPC)
838{
839 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
840 uint64_t uOldPC = pCtx->rip + cbInstr;
841 if (!IEM_IS_CANONICAL(uNewPC))
842 return iemRaiseGeneralProtectionFault0(pIemCpu);
843
844 VBOXSTRICTRC rcStrict = iemMemStackPushU64(pIemCpu, uOldPC);
845 if (rcStrict != VINF_SUCCESS)
846 return rcStrict;
847
848 pCtx->rip = uNewPC;
849 return VINF_SUCCESS;
850
851}
852
853
854/**
855 * Implements a 64-bit relative call.
856 *
857 * @param offDisp The displacment offset.
858 */
859IEM_CIMPL_DEF_1(iemCImpl_call_rel_64, int64_t, offDisp)
860{
861 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
862 uint64_t uOldPC = pCtx->rip + cbInstr;
863 uint64_t uNewPC = uOldPC + offDisp;
864 if (!IEM_IS_CANONICAL(uNewPC))
865 return iemRaiseNotCanonical(pIemCpu);
866
867 VBOXSTRICTRC rcStrict = iemMemStackPushU64(pIemCpu, uOldPC);
868 if (rcStrict != VINF_SUCCESS)
869 return rcStrict;
870
871 pCtx->rip = uNewPC;
872 return VINF_SUCCESS;
873}
874
875
876/**
877 * Implements far jumps and calls thru task segments (TSS).
878 *
879 * @param uSel The selector.
880 * @param enmBranch The kind of branching we're performing.
881 * @param enmEffOpSize The effective operand size.
882 * @param pDesc The descriptor corrsponding to @a uSel. The type is
883 * call gate.
884 */
885IEM_CIMPL_DEF_4(iemCImpl_BranchTaskSegment, uint16_t, uSel, IEMBRANCH, enmBranch, IEMMODE, enmEffOpSize, PIEMSELDESC, pDesc)
886{
887 /* Call various functions to do the work. */
888 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
889}
890
891
892/**
893 * Implements far jumps and calls thru task gates.
894 *
895 * @param uSel The selector.
896 * @param enmBranch The kind of branching we're performing.
897 * @param enmEffOpSize The effective operand size.
898 * @param pDesc The descriptor corrsponding to @a uSel. The type is
899 * call gate.
900 */
901IEM_CIMPL_DEF_4(iemCImpl_BranchTaskGate, uint16_t, uSel, IEMBRANCH, enmBranch, IEMMODE, enmEffOpSize, PIEMSELDESC, pDesc)
902{
903 /* Call various functions to do the work. */
904 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
905}
906
907
908/**
909 * Implements far jumps and calls thru call gates.
910 *
911 * @param uSel The selector.
912 * @param enmBranch The kind of branching we're performing.
913 * @param enmEffOpSize The effective operand size.
914 * @param pDesc The descriptor corrsponding to @a uSel. The type is
915 * call gate.
916 */
917IEM_CIMPL_DEF_4(iemCImpl_BranchCallGate, uint16_t, uSel, IEMBRANCH, enmBranch, IEMMODE, enmEffOpSize, PIEMSELDESC, pDesc)
918{
919 /* Call various functions to do the work. */
920 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
921}
922
923
924/**
925 * Implements far jumps and calls thru system selectors.
926 *
927 * @param uSel The selector.
928 * @param enmBranch The kind of branching we're performing.
929 * @param enmEffOpSize The effective operand size.
930 * @param pDesc The descriptor corrsponding to @a uSel.
931 */
932IEM_CIMPL_DEF_4(iemCImpl_BranchSysSel, uint16_t, uSel, IEMBRANCH, enmBranch, IEMMODE, enmEffOpSize, PIEMSELDESC, pDesc)
933{
934 Assert(enmBranch == IEMBRANCH_JUMP || enmBranch == IEMBRANCH_CALL);
935 Assert((uSel & X86_SEL_MASK_OFF_RPL));
936
937 if (IEM_IS_LONG_MODE(pIemCpu))
938 switch (pDesc->Legacy.Gen.u4Type)
939 {
940 case AMD64_SEL_TYPE_SYS_CALL_GATE:
941 return IEM_CIMPL_CALL_4(iemCImpl_BranchCallGate, uSel, enmBranch, enmEffOpSize, pDesc);
942
943 default:
944 case AMD64_SEL_TYPE_SYS_LDT:
945 case AMD64_SEL_TYPE_SYS_TSS_BUSY:
946 case AMD64_SEL_TYPE_SYS_TSS_AVAIL:
947 case AMD64_SEL_TYPE_SYS_TRAP_GATE:
948 case AMD64_SEL_TYPE_SYS_INT_GATE:
949 Log(("branch %04x -> wrong sys selector (64-bit): %d\n", uSel, pDesc->Legacy.Gen.u4Type));
950 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
951
952 }
953
954 switch (pDesc->Legacy.Gen.u4Type)
955 {
956 case X86_SEL_TYPE_SYS_286_CALL_GATE:
957 case X86_SEL_TYPE_SYS_386_CALL_GATE:
958 return IEM_CIMPL_CALL_4(iemCImpl_BranchCallGate, uSel, enmBranch, enmEffOpSize, pDesc);
959
960 case X86_SEL_TYPE_SYS_TASK_GATE:
961 return IEM_CIMPL_CALL_4(iemCImpl_BranchTaskGate, uSel, enmBranch, enmEffOpSize, pDesc);
962
963 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
964 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
965 return IEM_CIMPL_CALL_4(iemCImpl_BranchTaskSegment, uSel, enmBranch, enmEffOpSize, pDesc);
966
967 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
968 Log(("branch %04x -> busy 286 TSS\n", uSel));
969 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
970
971 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
972 Log(("branch %04x -> busy 386 TSS\n", uSel));
973 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
974
975 default:
976 case X86_SEL_TYPE_SYS_LDT:
977 case X86_SEL_TYPE_SYS_286_INT_GATE:
978 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
979 case X86_SEL_TYPE_SYS_386_INT_GATE:
980 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
981 Log(("branch %04x -> wrong sys selector: %d\n", uSel, pDesc->Legacy.Gen.u4Type));
982 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
983 }
984}
985
986
987/**
988 * Implements far jumps.
989 *
990 * @param uSel The selector.
991 * @param offSeg The segment offset.
992 * @param enmEffOpSize The effective operand size.
993 */
994IEM_CIMPL_DEF_3(iemCImpl_FarJmp, uint16_t, uSel, uint64_t, offSeg, IEMMODE, enmEffOpSize)
995{
996 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
997 NOREF(cbInstr);
998 Assert(offSeg <= UINT32_MAX);
999
1000 /*
1001 * Real mode and V8086 mode are easy. The only snag seems to be that
1002 * CS.limit doesn't change and the limit check is done against the current
1003 * limit.
1004 */
1005 if ( pIemCpu->enmCpuMode == IEMMODE_16BIT
1006 && IEM_IS_REAL_OR_V86_MODE(pIemCpu))
1007 {
1008 if (offSeg > pCtx->cs.u32Limit)
1009 return iemRaiseGeneralProtectionFault0(pIemCpu);
1010
1011 if (enmEffOpSize == IEMMODE_16BIT) /** @todo WRONG, must pass this. */
1012 pCtx->rip = offSeg;
1013 else
1014 pCtx->rip = offSeg & UINT16_MAX;
1015 pCtx->cs.Sel = uSel;
1016 pCtx->cs.ValidSel = uSel;
1017 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1018 pCtx->cs.u64Base = (uint32_t)uSel << 4;
1019 return VINF_SUCCESS;
1020 }
1021
1022 /*
1023 * Protected mode. Need to parse the specified descriptor...
1024 */
1025 if (!(uSel & X86_SEL_MASK_OFF_RPL))
1026 {
1027 Log(("jmpf %04x:%08RX64 -> invalid selector, #GP(0)\n", uSel, offSeg));
1028 return iemRaiseGeneralProtectionFault0(pIemCpu);
1029 }
1030
1031 /* Fetch the descriptor. */
1032 IEMSELDESC Desc;
1033 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pIemCpu, &Desc, uSel, X86_XCPT_GP);
1034 if (rcStrict != VINF_SUCCESS)
1035 return rcStrict;
1036
1037 /* Is it there? */
1038 if (!Desc.Legacy.Gen.u1Present) /** @todo this is probably checked too early. Testcase! */
1039 {
1040 Log(("jmpf %04x:%08RX64 -> segment not present\n", uSel, offSeg));
1041 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uSel);
1042 }
1043
1044 /*
1045 * Deal with it according to its type. We do the standard code selectors
1046 * here and dispatch the system selectors to worker functions.
1047 */
1048 if (!Desc.Legacy.Gen.u1DescType)
1049 return IEM_CIMPL_CALL_4(iemCImpl_BranchSysSel, uSel, IEMBRANCH_JUMP, enmEffOpSize, &Desc);
1050
1051 /* Only code segments. */
1052 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
1053 {
1054 Log(("jmpf %04x:%08RX64 -> not a code selector (u4Type=%#x).\n", uSel, offSeg, Desc.Legacy.Gen.u4Type));
1055 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1056 }
1057
1058 /* L vs D. */
1059 if ( Desc.Legacy.Gen.u1Long
1060 && Desc.Legacy.Gen.u1DefBig
1061 && IEM_IS_LONG_MODE(pIemCpu))
1062 {
1063 Log(("jmpf %04x:%08RX64 -> both L and D are set.\n", uSel, offSeg));
1064 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1065 }
1066
1067 /* DPL/RPL/CPL check, where conforming segments makes a difference. */
1068 if (Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
1069 {
1070 if (pIemCpu->uCpl < Desc.Legacy.Gen.u2Dpl)
1071 {
1072 Log(("jmpf %04x:%08RX64 -> DPL violation (conforming); DPL=%d CPL=%u\n",
1073 uSel, offSeg, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
1074 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1075 }
1076 }
1077 else
1078 {
1079 if (pIemCpu->uCpl != Desc.Legacy.Gen.u2Dpl)
1080 {
1081 Log(("jmpf %04x:%08RX64 -> CPL != DPL; DPL=%d CPL=%u\n", uSel, offSeg, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
1082 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1083 }
1084 if ((uSel & X86_SEL_RPL) > pIemCpu->uCpl)
1085 {
1086 Log(("jmpf %04x:%08RX64 -> RPL > DPL; RPL=%d CPL=%u\n", uSel, offSeg, (uSel & X86_SEL_RPL), pIemCpu->uCpl));
1087 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1088 }
1089 }
1090
1091 /* Chop the high bits if 16-bit (Intel says so). */
1092 if (enmEffOpSize == IEMMODE_16BIT)
1093 offSeg &= UINT16_MAX;
1094
1095 /* Limit check. (Should alternatively check for non-canonical addresses
1096 here, but that is ruled out by offSeg being 32-bit, right?) */
1097 uint64_t u64Base;
1098 uint32_t cbLimit = X86DESC_LIMIT_G(&Desc.Legacy);
1099 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
1100 u64Base = 0;
1101 else
1102 {
1103 if (offSeg > cbLimit)
1104 {
1105 Log(("jmpf %04x:%08RX64 -> out of bounds (%#x)\n", uSel, offSeg, cbLimit));
1106 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1107 }
1108 u64Base = X86DESC_BASE(&Desc.Legacy);
1109 }
1110
1111 /*
1112 * Ok, everything checked out fine. Now set the accessed bit before
1113 * committing the result into CS, CSHID and RIP.
1114 */
1115 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1116 {
1117 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uSel);
1118 if (rcStrict != VINF_SUCCESS)
1119 return rcStrict;
1120 /** @todo check what VT-x and AMD-V does. */
1121 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1122 }
1123
1124 /* commit */
1125 pCtx->rip = offSeg;
1126 pCtx->cs.Sel = uSel & X86_SEL_MASK_OFF_RPL;
1127 pCtx->cs.Sel |= pIemCpu->uCpl; /** @todo is this right for conforming segs? or in general? */
1128 pCtx->cs.ValidSel = pCtx->cs.Sel;
1129 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1130 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
1131 pCtx->cs.u32Limit = cbLimit;
1132 pCtx->cs.u64Base = u64Base;
1133 /** @todo check if the hidden bits are loaded correctly for 64-bit
1134 * mode. */
1135 return VINF_SUCCESS;
1136}
1137
1138
1139/**
1140 * Implements far calls.
1141 *
1142 * This very similar to iemCImpl_FarJmp.
1143 *
1144 * @param uSel The selector.
1145 * @param offSeg The segment offset.
1146 * @param enmEffOpSize The operand size (in case we need it).
1147 */
1148IEM_CIMPL_DEF_3(iemCImpl_callf, uint16_t, uSel, uint64_t, offSeg, IEMMODE, enmEffOpSize)
1149{
1150 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
1151 VBOXSTRICTRC rcStrict;
1152 uint64_t uNewRsp;
1153 RTPTRUNION uPtrRet;
1154
1155 /*
1156 * Real mode and V8086 mode are easy. The only snag seems to be that
1157 * CS.limit doesn't change and the limit check is done against the current
1158 * limit.
1159 */
1160 if ( pIemCpu->enmCpuMode == IEMMODE_16BIT
1161 && IEM_IS_REAL_OR_V86_MODE(pIemCpu))
1162 {
1163 Assert(enmEffOpSize == IEMMODE_16BIT || enmEffOpSize == IEMMODE_32BIT);
1164
1165 /* Check stack first - may #SS(0). */
1166 rcStrict = iemMemStackPushBeginSpecial(pIemCpu, enmEffOpSize == IEMMODE_32BIT ? 6 : 4,
1167 &uPtrRet.pv, &uNewRsp);
1168 if (rcStrict != VINF_SUCCESS)
1169 return rcStrict;
1170
1171 /* Check the target address range. */
1172 if (offSeg > UINT32_MAX)
1173 return iemRaiseGeneralProtectionFault0(pIemCpu);
1174
1175 /* Everything is fine, push the return address. */
1176 if (enmEffOpSize == IEMMODE_16BIT)
1177 {
1178 uPtrRet.pu16[0] = pCtx->ip + cbInstr;
1179 uPtrRet.pu16[1] = pCtx->cs.Sel;
1180 }
1181 else
1182 {
1183 uPtrRet.pu32[0] = pCtx->eip + cbInstr;
1184 uPtrRet.pu16[3] = pCtx->cs.Sel;
1185 }
1186 rcStrict = iemMemStackPushCommitSpecial(pIemCpu, uPtrRet.pv, uNewRsp);
1187 if (rcStrict != VINF_SUCCESS)
1188 return rcStrict;
1189
1190 /* Branch. */
1191 pCtx->rip = offSeg;
1192 pCtx->cs.Sel = uSel;
1193 pCtx->cs.ValidSel = uSel;
1194 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1195 pCtx->cs.u64Base = (uint32_t)uSel << 4;
1196 return VINF_SUCCESS;
1197 }
1198
1199 /*
1200 * Protected mode. Need to parse the specified descriptor...
1201 */
1202 if (!(uSel & X86_SEL_MASK_OFF_RPL))
1203 {
1204 Log(("callf %04x:%08RX64 -> invalid selector, #GP(0)\n", uSel, offSeg));
1205 return iemRaiseGeneralProtectionFault0(pIemCpu);
1206 }
1207
1208 /* Fetch the descriptor. */
1209 IEMSELDESC Desc;
1210 rcStrict = iemMemFetchSelDesc(pIemCpu, &Desc, uSel, X86_XCPT_GP);
1211 if (rcStrict != VINF_SUCCESS)
1212 return rcStrict;
1213
1214 /*
1215 * Deal with it according to its type. We do the standard code selectors
1216 * here and dispatch the system selectors to worker functions.
1217 */
1218 if (!Desc.Legacy.Gen.u1DescType)
1219 return IEM_CIMPL_CALL_4(iemCImpl_BranchSysSel, uSel, IEMBRANCH_CALL, enmEffOpSize, &Desc);
1220
1221 /* Only code segments. */
1222 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
1223 {
1224 Log(("callf %04x:%08RX64 -> not a code selector (u4Type=%#x).\n", uSel, offSeg, Desc.Legacy.Gen.u4Type));
1225 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1226 }
1227
1228 /* L vs D. */
1229 if ( Desc.Legacy.Gen.u1Long
1230 && Desc.Legacy.Gen.u1DefBig
1231 && IEM_IS_LONG_MODE(pIemCpu))
1232 {
1233 Log(("callf %04x:%08RX64 -> both L and D are set.\n", uSel, offSeg));
1234 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1235 }
1236
1237 /* DPL/RPL/CPL check, where conforming segments makes a difference. */
1238 if (Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
1239 {
1240 if (pIemCpu->uCpl < Desc.Legacy.Gen.u2Dpl)
1241 {
1242 Log(("callf %04x:%08RX64 -> DPL violation (conforming); DPL=%d CPL=%u\n",
1243 uSel, offSeg, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
1244 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1245 }
1246 }
1247 else
1248 {
1249 if (pIemCpu->uCpl != Desc.Legacy.Gen.u2Dpl)
1250 {
1251 Log(("callf %04x:%08RX64 -> CPL != DPL; DPL=%d CPL=%u\n", uSel, offSeg, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
1252 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1253 }
1254 if ((uSel & X86_SEL_RPL) > pIemCpu->uCpl)
1255 {
1256 Log(("callf %04x:%08RX64 -> RPL > DPL; RPL=%d CPL=%u\n", uSel, offSeg, (uSel & X86_SEL_RPL), pIemCpu->uCpl));
1257 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1258 }
1259 }
1260
1261 /* Is it there? */
1262 if (!Desc.Legacy.Gen.u1Present)
1263 {
1264 Log(("callf %04x:%08RX64 -> segment not present\n", uSel, offSeg));
1265 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uSel);
1266 }
1267
1268 /* Check stack first - may #SS(0). */
1269 /** @todo check how operand prefix affects pushing of CS! Does callf 16:32 in
1270 * 16-bit code cause a two or four byte CS to be pushed? */
1271 rcStrict = iemMemStackPushBeginSpecial(pIemCpu,
1272 enmEffOpSize == IEMMODE_64BIT ? 8+8
1273 : enmEffOpSize == IEMMODE_32BIT ? 4+4 : 2+2,
1274 &uPtrRet.pv, &uNewRsp);
1275 if (rcStrict != VINF_SUCCESS)
1276 return rcStrict;
1277
1278 /* Chop the high bits if 16-bit (Intel says so). */
1279 if (enmEffOpSize == IEMMODE_16BIT)
1280 offSeg &= UINT16_MAX;
1281
1282 /* Limit / canonical check. */
1283 uint64_t u64Base;
1284 uint32_t cbLimit = X86DESC_LIMIT_G(&Desc.Legacy);
1285 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
1286 {
1287 if (!IEM_IS_CANONICAL(offSeg))
1288 {
1289 Log(("callf %04x:%016RX64 - not canonical -> #GP\n", uSel, offSeg));
1290 return iemRaiseNotCanonical(pIemCpu);
1291 }
1292 u64Base = 0;
1293 }
1294 else
1295 {
1296 if (offSeg > cbLimit)
1297 {
1298 Log(("callf %04x:%08RX64 -> out of bounds (%#x)\n", uSel, offSeg, cbLimit));
1299 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
1300 }
1301 u64Base = X86DESC_BASE(&Desc.Legacy);
1302 }
1303
1304 /*
1305 * Now set the accessed bit before
1306 * writing the return address to the stack and committing the result into
1307 * CS, CSHID and RIP.
1308 */
1309 /** @todo Testcase: Need to check WHEN exactly the accessed bit is set. */
1310 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1311 {
1312 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uSel);
1313 if (rcStrict != VINF_SUCCESS)
1314 return rcStrict;
1315 /** @todo check what VT-x and AMD-V does. */
1316 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1317 }
1318
1319 /* stack */
1320 if (enmEffOpSize == IEMMODE_16BIT)
1321 {
1322 uPtrRet.pu16[0] = pCtx->ip + cbInstr;
1323 uPtrRet.pu16[1] = pCtx->cs.Sel;
1324 }
1325 else if (enmEffOpSize == IEMMODE_32BIT)
1326 {
1327 uPtrRet.pu32[0] = pCtx->eip + cbInstr;
1328 uPtrRet.pu32[1] = pCtx->cs.Sel; /** @todo Testcase: What is written to the high word when callf is pushing CS? */
1329 }
1330 else
1331 {
1332 uPtrRet.pu64[0] = pCtx->rip + cbInstr;
1333 uPtrRet.pu64[1] = pCtx->cs.Sel; /** @todo Testcase: What is written to the high words when callf is pushing CS? */
1334 }
1335 rcStrict = iemMemStackPushCommitSpecial(pIemCpu, uPtrRet.pv, uNewRsp);
1336 if (rcStrict != VINF_SUCCESS)
1337 return rcStrict;
1338
1339 /* commit */
1340 pCtx->rip = offSeg;
1341 pCtx->cs.Sel = uSel & X86_SEL_MASK_OFF_RPL;
1342 pCtx->cs.Sel |= pIemCpu->uCpl;
1343 pCtx->cs.ValidSel = pCtx->cs.Sel;
1344 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1345 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
1346 pCtx->cs.u32Limit = cbLimit;
1347 pCtx->cs.u64Base = u64Base;
1348 /** @todo check if the hidden bits are loaded correctly for 64-bit
1349 * mode. */
1350 return VINF_SUCCESS;
1351}
1352
1353
1354/**
1355 * Implements retf.
1356 *
1357 * @param enmEffOpSize The effective operand size.
1358 * @param cbPop The amount of arguments to pop from the stack
1359 * (bytes).
1360 */
1361IEM_CIMPL_DEF_2(iemCImpl_retf, IEMMODE, enmEffOpSize, uint16_t, cbPop)
1362{
1363 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
1364 VBOXSTRICTRC rcStrict;
1365 RTCPTRUNION uPtrFrame;
1366 uint64_t uNewRsp;
1367 uint64_t uNewRip;
1368 uint16_t uNewCs;
1369 NOREF(cbInstr);
1370
1371 /*
1372 * Read the stack values first.
1373 */
1374 uint32_t cbRetPtr = enmEffOpSize == IEMMODE_16BIT ? 2+2
1375 : enmEffOpSize == IEMMODE_32BIT ? 4+4 : 8+8;
1376 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, cbRetPtr, &uPtrFrame.pv, &uNewRsp);
1377 if (rcStrict != VINF_SUCCESS)
1378 return rcStrict;
1379 if (enmEffOpSize == IEMMODE_16BIT)
1380 {
1381 uNewRip = uPtrFrame.pu16[0];
1382 uNewCs = uPtrFrame.pu16[1];
1383 }
1384 else if (enmEffOpSize == IEMMODE_32BIT)
1385 {
1386 uNewRip = uPtrFrame.pu32[0];
1387 uNewCs = uPtrFrame.pu16[2];
1388 }
1389 else
1390 {
1391 uNewRip = uPtrFrame.pu64[0];
1392 uNewCs = uPtrFrame.pu16[4];
1393 }
1394
1395 /*
1396 * Real mode and V8086 mode are easy.
1397 */
1398 if ( pIemCpu->enmCpuMode == IEMMODE_16BIT
1399 && IEM_IS_REAL_OR_V86_MODE(pIemCpu))
1400 {
1401 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
1402 /** @todo check how this is supposed to work if sp=0xfffe. */
1403
1404 /* Check the limit of the new EIP. */
1405 /** @todo Intel pseudo code only does the limit check for 16-bit
1406 * operands, AMD does not make any distinction. What is right? */
1407 if (uNewRip > pCtx->cs.u32Limit)
1408 return iemRaiseSelectorBounds(pIemCpu, X86_SREG_CS, IEM_ACCESS_INSTRUCTION);
1409
1410 /* commit the operation. */
1411 rcStrict = iemMemStackPopCommitSpecial(pIemCpu, uPtrFrame.pv, uNewRsp);
1412 if (rcStrict != VINF_SUCCESS)
1413 return rcStrict;
1414 pCtx->rip = uNewRip;
1415 pCtx->cs.Sel = uNewCs;
1416 pCtx->cs.ValidSel = uNewCs;
1417 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1418 pCtx->cs.u64Base = (uint32_t)uNewCs << 4;
1419 /** @todo do we load attribs and limit as well? */
1420 if (cbPop)
1421 iemRegAddToRsp(pIemCpu, pCtx, cbPop);
1422 return VINF_SUCCESS;
1423 }
1424
1425 /*
1426 * Protected mode is complicated, of course.
1427 */
1428 if (!(uNewCs & X86_SEL_MASK_OFF_RPL))
1429 {
1430 Log(("retf %04x:%08RX64 -> invalid selector, #GP(0)\n", uNewCs, uNewRip));
1431 return iemRaiseGeneralProtectionFault0(pIemCpu);
1432 }
1433
1434 /* Fetch the descriptor. */
1435 IEMSELDESC DescCs;
1436 rcStrict = iemMemFetchSelDesc(pIemCpu, &DescCs, uNewCs, X86_XCPT_GP);
1437 if (rcStrict != VINF_SUCCESS)
1438 return rcStrict;
1439
1440 /* Can only return to a code selector. */
1441 if ( !DescCs.Legacy.Gen.u1DescType
1442 || !(DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE) )
1443 {
1444 Log(("retf %04x:%08RX64 -> not a code selector (u1DescType=%u u4Type=%#x).\n",
1445 uNewCs, uNewRip, DescCs.Legacy.Gen.u1DescType, DescCs.Legacy.Gen.u4Type));
1446 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1447 }
1448
1449 /* L vs D. */
1450 if ( DescCs.Legacy.Gen.u1Long /** @todo Testcase: far return to a selector with both L and D set. */
1451 && DescCs.Legacy.Gen.u1DefBig
1452 && IEM_IS_LONG_MODE(pIemCpu))
1453 {
1454 Log(("retf %04x:%08RX64 -> both L & D set.\n", uNewCs, uNewRip));
1455 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1456 }
1457
1458 /* DPL/RPL/CPL checks. */
1459 if ((uNewCs & X86_SEL_RPL) < pIemCpu->uCpl)
1460 {
1461 Log(("retf %04x:%08RX64 -> RPL < CPL(%d).\n", uNewCs, uNewRip, pIemCpu->uCpl));
1462 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1463 }
1464
1465 if (DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
1466 {
1467 if ((uNewCs & X86_SEL_RPL) < DescCs.Legacy.Gen.u2Dpl)
1468 {
1469 Log(("retf %04x:%08RX64 -> DPL violation (conforming); DPL=%u RPL=%u\n",
1470 uNewCs, uNewRip, DescCs.Legacy.Gen.u2Dpl, (uNewCs & X86_SEL_RPL)));
1471 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1472 }
1473 }
1474 else
1475 {
1476 if ((uNewCs & X86_SEL_RPL) != DescCs.Legacy.Gen.u2Dpl)
1477 {
1478 Log(("retf %04x:%08RX64 -> RPL != DPL; DPL=%u RPL=%u\n",
1479 uNewCs, uNewRip, DescCs.Legacy.Gen.u2Dpl, (uNewCs & X86_SEL_RPL)));
1480 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1481 }
1482 }
1483
1484 /* Is it there? */
1485 if (!DescCs.Legacy.Gen.u1Present)
1486 {
1487 Log(("retf %04x:%08RX64 -> segment not present\n", uNewCs, uNewRip));
1488 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uNewCs);
1489 }
1490
1491 /*
1492 * Return to outer privilege? (We'll typically have entered via a call gate.)
1493 */
1494 if ((uNewCs & X86_SEL_RPL) != pIemCpu->uCpl)
1495 {
1496 /* Read the return pointer, it comes before the parameters. */
1497 RTCPTRUNION uPtrStack;
1498 rcStrict = iemMemStackPopContinueSpecial(pIemCpu, cbPop + cbRetPtr, &uPtrStack.pv, &uNewRsp);
1499 if (rcStrict != VINF_SUCCESS)
1500 return rcStrict;
1501 uint16_t uNewOuterSs;
1502 uint64_t uNewOuterRsp;
1503 if (enmEffOpSize == IEMMODE_16BIT)
1504 {
1505 uNewOuterRsp = uPtrFrame.pu16[0];
1506 uNewOuterSs = uPtrFrame.pu16[1];
1507 }
1508 else if (enmEffOpSize == IEMMODE_32BIT)
1509 {
1510 uNewOuterRsp = uPtrFrame.pu32[0];
1511 uNewOuterSs = uPtrFrame.pu16[2];
1512 }
1513 else
1514 {
1515 uNewOuterRsp = uPtrFrame.pu64[0];
1516 uNewOuterSs = uPtrFrame.pu16[4];
1517 }
1518
1519 /* Check for NULL stack selector (invalid in ring-3 and non-long mode)
1520 and read the selector. */
1521 IEMSELDESC DescSs;
1522 if (!(uNewOuterSs & X86_SEL_MASK_OFF_RPL))
1523 {
1524 if ( !DescCs.Legacy.Gen.u1Long
1525 || (uNewOuterSs & X86_SEL_RPL) == 3)
1526 {
1527 Log(("retf %04x:%08RX64 %04x:%08RX64 -> invalid stack selector, #GP\n",
1528 uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp));
1529 return iemRaiseGeneralProtectionFault0(pIemCpu);
1530 }
1531 /** @todo Testcase: Return far to ring-1 or ring-2 with SS=0. */
1532 iemMemFakeStackSelDesc(&DescSs, (uNewOuterSs & X86_SEL_RPL));
1533 }
1534 else
1535 {
1536 /* Fetch the descriptor for the new stack segment. */
1537 rcStrict = iemMemFetchSelDesc(pIemCpu, &DescSs, uNewOuterSs, X86_XCPT_GP);
1538 if (rcStrict != VINF_SUCCESS)
1539 return rcStrict;
1540 }
1541
1542 /* Check that RPL of stack and code selectors match. */
1543 if ((uNewCs & X86_SEL_RPL) != (uNewOuterSs & X86_SEL_RPL))
1544 {
1545 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS.RPL != CS.RPL -> #GP(SS)\n", uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp));
1546 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewOuterSs);
1547 }
1548
1549 /* Must be a writable data segment. */
1550 if ( !DescSs.Legacy.Gen.u1DescType
1551 || (DescSs.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE)
1552 || !(DescSs.Legacy.Gen.u4Type & X86_SEL_TYPE_WRITE) )
1553 {
1554 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS not a writable data segment (u1DescType=%u u4Type=%#x) -> #GP(SS).\n",
1555 uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp, DescSs.Legacy.Gen.u1DescType, DescSs.Legacy.Gen.u4Type));
1556 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewOuterSs);
1557 }
1558
1559 /* L vs D. (Not mentioned by intel.) */
1560 if ( DescSs.Legacy.Gen.u1Long /** @todo Testcase: far return to a stack selector with both L and D set. */
1561 && DescSs.Legacy.Gen.u1DefBig
1562 && IEM_IS_LONG_MODE(pIemCpu))
1563 {
1564 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS has both L & D set -> #GP(SS).\n",
1565 uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp, DescSs.Legacy.Gen.u1DescType, DescSs.Legacy.Gen.u4Type));
1566 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewOuterSs);
1567 }
1568
1569 /* DPL/RPL/CPL checks. */
1570 if (DescSs.Legacy.Gen.u2Dpl != (uNewCs & X86_SEL_RPL))
1571 {
1572 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS.DPL(%u) != CS.RPL (%u) -> #GP(SS).\n",
1573 uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp, DescSs.Legacy.Gen.u2Dpl, uNewCs & X86_SEL_RPL));
1574 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewOuterSs);
1575 }
1576
1577 /* Is it there? */
1578 if (!DescSs.Legacy.Gen.u1Present)
1579 {
1580 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS not present -> #NP(SS).\n", uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp));
1581 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uNewCs);
1582 }
1583
1584 /* Calc SS limit.*/
1585 uint32_t cbLimitSs = X86DESC_LIMIT_G(&DescSs.Legacy);
1586
1587 /* Is RIP canonical or within CS.limit? */
1588 uint64_t u64Base;
1589 uint32_t cbLimitCs = X86DESC_LIMIT_G(&DescCs.Legacy);
1590
1591 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
1592 {
1593 if (!IEM_IS_CANONICAL(uNewRip))
1594 {
1595 Log(("retf %04x:%08RX64 %04x:%08RX64 - not canonical -> #GP.\n", uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp));
1596 return iemRaiseNotCanonical(pIemCpu);
1597 }
1598 u64Base = 0;
1599 }
1600 else
1601 {
1602 if (uNewRip > cbLimitCs)
1603 {
1604 Log(("retf %04x:%08RX64 %04x:%08RX64 - out of bounds (%#x)-> #GP(CS).\n",
1605 uNewCs, uNewRip, uNewOuterSs, uNewOuterRsp, cbLimitCs));
1606 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1607 }
1608 u64Base = X86DESC_BASE(&DescCs.Legacy);
1609 }
1610
1611 /*
1612 * Now set the accessed bit before
1613 * writing the return address to the stack and committing the result into
1614 * CS, CSHID and RIP.
1615 */
1616 /** @todo Testcase: Need to check WHEN exactly the CS accessed bit is set. */
1617 if (!(DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1618 {
1619 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewCs);
1620 if (rcStrict != VINF_SUCCESS)
1621 return rcStrict;
1622 /** @todo check what VT-x and AMD-V does. */
1623 DescCs.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1624 }
1625 /** @todo Testcase: Need to check WHEN exactly the SS accessed bit is set. */
1626 if (!(DescSs.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1627 {
1628 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewOuterSs);
1629 if (rcStrict != VINF_SUCCESS)
1630 return rcStrict;
1631 /** @todo check what VT-x and AMD-V does. */
1632 DescSs.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1633 }
1634
1635 /* commit */
1636 rcStrict = iemMemStackPopCommitSpecial(pIemCpu, uPtrFrame.pv, uNewRsp);
1637 if (rcStrict != VINF_SUCCESS)
1638 return rcStrict;
1639 if (enmEffOpSize == IEMMODE_16BIT)
1640 pCtx->rip = uNewRip & UINT16_MAX; /** @todo Testcase: When exactly does this occur? With call it happens prior to the limit check according to Intel... */
1641 else
1642 pCtx->rip = uNewRip;
1643 pCtx->cs.Sel = uNewCs;
1644 pCtx->cs.ValidSel = uNewCs;
1645 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1646 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCs.Legacy);
1647 pCtx->cs.u32Limit = cbLimitCs;
1648 pCtx->cs.u64Base = u64Base;
1649 pCtx->rsp = uNewRsp;
1650 pCtx->ss.Sel = uNewOuterSs;
1651 pCtx->ss.ValidSel = uNewOuterSs;
1652 pCtx->ss.fFlags = CPUMSELREG_FLAGS_VALID;
1653 pCtx->ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSs.Legacy);
1654 pCtx->ss.u32Limit = cbLimitSs;
1655 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
1656 pCtx->ss.u64Base = 0;
1657 else
1658 pCtx->ss.u64Base = X86DESC_BASE(&DescSs.Legacy);
1659
1660 pIemCpu->uCpl = (uNewCs & X86_SEL_RPL);
1661 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->ds);
1662 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->es);
1663 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->fs);
1664 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->gs);
1665
1666 /** @todo check if the hidden bits are loaded correctly for 64-bit
1667 * mode. */
1668
1669 if (cbPop)
1670 iemRegAddToRsp(pIemCpu, pCtx, cbPop);
1671
1672 /* Done! */
1673 }
1674 /*
1675 * Return to the same privilege level
1676 */
1677 else
1678 {
1679 /* Limit / canonical check. */
1680 uint64_t u64Base;
1681 uint32_t cbLimitCs = X86DESC_LIMIT_G(&DescCs.Legacy);
1682
1683 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
1684 {
1685 if (!IEM_IS_CANONICAL(uNewRip))
1686 {
1687 Log(("retf %04x:%08RX64 - not canonical -> #GP\n", uNewCs, uNewRip));
1688 return iemRaiseNotCanonical(pIemCpu);
1689 }
1690 u64Base = 0;
1691 }
1692 else
1693 {
1694 if (uNewRip > cbLimitCs)
1695 {
1696 Log(("retf %04x:%08RX64 -> out of bounds (%#x)\n", uNewCs, uNewRip, cbLimitCs));
1697 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
1698 }
1699 u64Base = X86DESC_BASE(&DescCs.Legacy);
1700 }
1701
1702 /*
1703 * Now set the accessed bit before
1704 * writing the return address to the stack and committing the result into
1705 * CS, CSHID and RIP.
1706 */
1707 /** @todo Testcase: Need to check WHEN exactly the accessed bit is set. */
1708 if (!(DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1709 {
1710 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewCs);
1711 if (rcStrict != VINF_SUCCESS)
1712 return rcStrict;
1713 /** @todo check what VT-x and AMD-V does. */
1714 DescCs.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1715 }
1716
1717 /* commit */
1718 rcStrict = iemMemStackPopCommitSpecial(pIemCpu, uPtrFrame.pv, uNewRsp);
1719 if (rcStrict != VINF_SUCCESS)
1720 return rcStrict;
1721 if (enmEffOpSize == IEMMODE_16BIT)
1722 pCtx->rip = uNewRip & UINT16_MAX; /** @todo Testcase: When exactly does this occur? With call it happens prior to the limit check according to Intel... */
1723 else
1724 pCtx->rip = uNewRip;
1725 pCtx->cs.Sel = uNewCs;
1726 pCtx->cs.ValidSel = uNewCs;
1727 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
1728 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCs.Legacy);
1729 pCtx->cs.u32Limit = cbLimitCs;
1730 pCtx->cs.u64Base = u64Base;
1731 /** @todo check if the hidden bits are loaded correctly for 64-bit
1732 * mode. */
1733 if (cbPop)
1734 iemRegAddToRsp(pIemCpu, pCtx, cbPop);
1735 }
1736 return VINF_SUCCESS;
1737}
1738
1739
1740/**
1741 * Implements retn.
1742 *
1743 * We're doing this in C because of the \#GP that might be raised if the popped
1744 * program counter is out of bounds.
1745 *
1746 * @param enmEffOpSize The effective operand size.
1747 * @param cbPop The amount of arguments to pop from the stack
1748 * (bytes).
1749 */
1750IEM_CIMPL_DEF_2(iemCImpl_retn, IEMMODE, enmEffOpSize, uint16_t, cbPop)
1751{
1752 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
1753 NOREF(cbInstr);
1754
1755 /* Fetch the RSP from the stack. */
1756 VBOXSTRICTRC rcStrict;
1757 RTUINT64U NewRip;
1758 RTUINT64U NewRsp;
1759 NewRsp.u = pCtx->rsp;
1760 switch (enmEffOpSize)
1761 {
1762 case IEMMODE_16BIT:
1763 NewRip.u = 0;
1764 rcStrict = iemMemStackPopU16Ex(pIemCpu, &NewRip.Words.w0, &NewRsp);
1765 break;
1766 case IEMMODE_32BIT:
1767 NewRip.u = 0;
1768 rcStrict = iemMemStackPopU32Ex(pIemCpu, &NewRip.DWords.dw0, &NewRsp);
1769 break;
1770 case IEMMODE_64BIT:
1771 rcStrict = iemMemStackPopU64Ex(pIemCpu, &NewRip.u, &NewRsp);
1772 break;
1773 IEM_NOT_REACHED_DEFAULT_CASE_RET();
1774 }
1775 if (rcStrict != VINF_SUCCESS)
1776 return rcStrict;
1777
1778 /* Check the new RSP before loading it. */
1779 /** @todo Should test this as the intel+amd pseudo code doesn't mention half
1780 * of it. The canonical test is performed here and for call. */
1781 if (enmEffOpSize != IEMMODE_64BIT)
1782 {
1783 if (NewRip.DWords.dw0 > pCtx->cs.u32Limit)
1784 {
1785 Log(("retn newrip=%llx - out of bounds (%x) -> #GP\n", NewRip.u, pCtx->cs.u32Limit));
1786 return iemRaiseSelectorBounds(pIemCpu, X86_SREG_CS, IEM_ACCESS_INSTRUCTION);
1787 }
1788 }
1789 else
1790 {
1791 if (!IEM_IS_CANONICAL(NewRip.u))
1792 {
1793 Log(("retn newrip=%llx - not canonical -> #GP\n", NewRip.u));
1794 return iemRaiseNotCanonical(pIemCpu);
1795 }
1796 }
1797
1798 /* Commit it. */
1799 pCtx->rip = NewRip.u;
1800 pCtx->rsp = NewRsp.u;
1801 if (cbPop)
1802 iemRegAddToRsp(pIemCpu, pCtx, cbPop);
1803
1804 return VINF_SUCCESS;
1805}
1806
1807
1808/**
1809 * Implements enter.
1810 *
1811 * We're doing this in C because the instruction is insane, even for the
1812 * u8NestingLevel=0 case dealing with the stack is tedious.
1813 *
1814 * @param enmEffOpSize The effective operand size.
1815 */
1816IEM_CIMPL_DEF_3(iemCImpl_enter, IEMMODE, enmEffOpSize, uint16_t, cbFrame, uint8_t, cParameters)
1817{
1818 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
1819
1820 /* Push RBP, saving the old value in TmpRbp. */
1821 RTUINT64U NewRsp; NewRsp.u = pCtx->rsp;
1822 RTUINT64U TmpRbp; TmpRbp.u = pCtx->rbp;
1823 RTUINT64U NewRbp;
1824 VBOXSTRICTRC rcStrict;
1825 if (enmEffOpSize == IEMMODE_64BIT)
1826 {
1827 rcStrict = iemMemStackPushU64Ex(pIemCpu, TmpRbp.u, &NewRsp);
1828 NewRbp = NewRsp;
1829 }
1830 else if (pCtx->ss.Attr.n.u1DefBig)
1831 {
1832 rcStrict = iemMemStackPushU32Ex(pIemCpu, TmpRbp.DWords.dw0, &NewRsp);
1833 NewRbp = NewRsp;
1834 }
1835 else
1836 {
1837 rcStrict = iemMemStackPushU16Ex(pIemCpu, TmpRbp.Words.w0, &NewRsp);
1838 NewRbp = TmpRbp;
1839 NewRbp.Words.w0 = NewRsp.Words.w0;
1840 }
1841 if (rcStrict != VINF_SUCCESS)
1842 return rcStrict;
1843
1844 /* Copy the parameters (aka nesting levels by Intel). */
1845 cParameters &= 0x1f;
1846 if (cParameters > 0)
1847 {
1848 switch (enmEffOpSize)
1849 {
1850 case IEMMODE_16BIT:
1851 if (pCtx->ss.Attr.n.u1DefBig)
1852 TmpRbp.DWords.dw0 -= 2;
1853 else
1854 TmpRbp.Words.w0 -= 2;
1855 do
1856 {
1857 uint16_t u16Tmp;
1858 rcStrict = iemMemStackPopU16Ex(pIemCpu, &u16Tmp, &TmpRbp);
1859 if (rcStrict != VINF_SUCCESS)
1860 break;
1861 rcStrict = iemMemStackPushU16Ex(pIemCpu, u16Tmp, &NewRsp);
1862 } while (--cParameters > 0 && rcStrict == VINF_SUCCESS);
1863 break;
1864
1865 case IEMMODE_32BIT:
1866 if (pCtx->ss.Attr.n.u1DefBig)
1867 TmpRbp.DWords.dw0 -= 4;
1868 else
1869 TmpRbp.Words.w0 -= 4;
1870 do
1871 {
1872 uint32_t u32Tmp;
1873 rcStrict = iemMemStackPopU32Ex(pIemCpu, &u32Tmp, &TmpRbp);
1874 if (rcStrict != VINF_SUCCESS)
1875 break;
1876 rcStrict = iemMemStackPushU32Ex(pIemCpu, u32Tmp, &NewRsp);
1877 } while (--cParameters > 0 && rcStrict == VINF_SUCCESS);
1878 break;
1879
1880 case IEMMODE_64BIT:
1881 TmpRbp.u -= 8;
1882 do
1883 {
1884 uint64_t u64Tmp;
1885 rcStrict = iemMemStackPopU64Ex(pIemCpu, &u64Tmp, &TmpRbp);
1886 if (rcStrict != VINF_SUCCESS)
1887 break;
1888 rcStrict = iemMemStackPushU64Ex(pIemCpu, u64Tmp, &NewRsp);
1889 } while (--cParameters > 0 && rcStrict == VINF_SUCCESS);
1890 break;
1891
1892 IEM_NOT_REACHED_DEFAULT_CASE_RET();
1893 }
1894 if (rcStrict != VINF_SUCCESS)
1895 return VINF_SUCCESS;
1896
1897 /* Push the new RBP */
1898 if (enmEffOpSize == IEMMODE_64BIT)
1899 rcStrict = iemMemStackPushU64Ex(pIemCpu, NewRbp.u, &NewRsp);
1900 else if (pCtx->ss.Attr.n.u1DefBig)
1901 rcStrict = iemMemStackPushU32Ex(pIemCpu, NewRbp.DWords.dw0, &NewRsp);
1902 else
1903 rcStrict = iemMemStackPushU16Ex(pIemCpu, NewRbp.Words.w0, &NewRsp);
1904 if (rcStrict != VINF_SUCCESS)
1905 return rcStrict;
1906
1907 }
1908
1909 /* Recalc RSP. */
1910 iemRegSubFromRspEx(pIemCpu, pCtx, &NewRsp, cbFrame);
1911
1912 /** @todo Should probe write access at the new RSP according to AMD. */
1913
1914 /* Commit it. */
1915 pCtx->rbp = NewRbp.u;
1916 pCtx->rsp = NewRsp.u;
1917 iemRegAddToRip(pIemCpu, cbInstr);
1918
1919 return VINF_SUCCESS;
1920}
1921
1922
1923
1924/**
1925 * Implements leave.
1926 *
1927 * We're doing this in C because messing with the stack registers is annoying
1928 * since they depends on SS attributes.
1929 *
1930 * @param enmEffOpSize The effective operand size.
1931 */
1932IEM_CIMPL_DEF_1(iemCImpl_leave, IEMMODE, enmEffOpSize)
1933{
1934 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
1935
1936 /* Calculate the intermediate RSP from RBP and the stack attributes. */
1937 RTUINT64U NewRsp;
1938 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
1939 NewRsp.u = pCtx->rbp;
1940 else if (pCtx->ss.Attr.n.u1DefBig)
1941 NewRsp.u = pCtx->ebp;
1942 else
1943 {
1944 /** @todo Check that LEAVE actually preserve the high EBP bits. */
1945 NewRsp.u = pCtx->rsp;
1946 NewRsp.Words.w0 = pCtx->bp;
1947 }
1948
1949 /* Pop RBP according to the operand size. */
1950 VBOXSTRICTRC rcStrict;
1951 RTUINT64U NewRbp;
1952 switch (enmEffOpSize)
1953 {
1954 case IEMMODE_16BIT:
1955 NewRbp.u = pCtx->rbp;
1956 rcStrict = iemMemStackPopU16Ex(pIemCpu, &NewRbp.Words.w0, &NewRsp);
1957 break;
1958 case IEMMODE_32BIT:
1959 NewRbp.u = 0;
1960 rcStrict = iemMemStackPopU32Ex(pIemCpu, &NewRbp.DWords.dw0, &NewRsp);
1961 break;
1962 case IEMMODE_64BIT:
1963 rcStrict = iemMemStackPopU64Ex(pIemCpu, &NewRbp.u, &NewRsp);
1964 break;
1965 IEM_NOT_REACHED_DEFAULT_CASE_RET();
1966 }
1967 if (rcStrict != VINF_SUCCESS)
1968 return rcStrict;
1969
1970
1971 /* Commit it. */
1972 pCtx->rbp = NewRbp.u;
1973 pCtx->rsp = NewRsp.u;
1974 iemRegAddToRip(pIemCpu, cbInstr);
1975
1976 return VINF_SUCCESS;
1977}
1978
1979
1980/**
1981 * Implements int3 and int XX.
1982 *
1983 * @param u8Int The interrupt vector number.
1984 * @param fIsBpInstr Is it the breakpoint instruction.
1985 */
1986IEM_CIMPL_DEF_2(iemCImpl_int, uint8_t, u8Int, bool, fIsBpInstr)
1987{
1988 Assert(pIemCpu->cXcptRecursions == 0);
1989 return iemRaiseXcptOrInt(pIemCpu,
1990 cbInstr,
1991 u8Int,
1992 (fIsBpInstr ? IEM_XCPT_FLAGS_BP_INSTR : 0) | IEM_XCPT_FLAGS_T_SOFT_INT,
1993 0,
1994 0);
1995}
1996
1997
1998/**
1999 * Implements iret for real mode and V8086 mode.
2000 *
2001 * @param enmEffOpSize The effective operand size.
2002 */
2003IEM_CIMPL_DEF_1(iemCImpl_iret_real_v8086, IEMMODE, enmEffOpSize)
2004{
2005 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
2006 PVMCPU pVCpu = IEMCPU_TO_VMCPU(pIemCpu);
2007 X86EFLAGS Efl;
2008 Efl.u = IEMMISC_GET_EFL(pIemCpu, pCtx);
2009 NOREF(cbInstr);
2010
2011 /*
2012 * iret throws an exception if VME isn't enabled.
2013 */
2014 if ( Efl.Bits.u1VM
2015 && Efl.Bits.u2IOPL != 3
2016 && !(pCtx->cr4 & X86_CR4_VME))
2017 return iemRaiseGeneralProtectionFault0(pIemCpu);
2018
2019 /*
2020 * Do the stack bits, but don't commit RSP before everything checks
2021 * out right.
2022 */
2023 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
2024 VBOXSTRICTRC rcStrict;
2025 RTCPTRUNION uFrame;
2026 uint16_t uNewCs;
2027 uint32_t uNewEip;
2028 uint32_t uNewFlags;
2029 uint64_t uNewRsp;
2030 if (enmEffOpSize == IEMMODE_32BIT)
2031 {
2032 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 12, &uFrame.pv, &uNewRsp);
2033 if (rcStrict != VINF_SUCCESS)
2034 return rcStrict;
2035 uNewEip = uFrame.pu32[0];
2036 if (uNewEip > UINT16_MAX)
2037 return iemRaiseGeneralProtectionFault0(pIemCpu);
2038
2039 uNewCs = (uint16_t)uFrame.pu32[1];
2040 uNewFlags = uFrame.pu32[2];
2041 uNewFlags &= X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2042 | X86_EFL_TF | X86_EFL_IF | X86_EFL_DF | X86_EFL_OF | X86_EFL_IOPL | X86_EFL_NT
2043 | X86_EFL_RF /*| X86_EFL_VM*/ | X86_EFL_AC /*|X86_EFL_VIF*/ /*|X86_EFL_VIP*/
2044 | X86_EFL_ID;
2045 uNewFlags |= Efl.u & (X86_EFL_VM | X86_EFL_VIF | X86_EFL_VIP | X86_EFL_1);
2046 }
2047 else
2048 {
2049 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 6, &uFrame.pv, &uNewRsp);
2050 if (rcStrict != VINF_SUCCESS)
2051 return rcStrict;
2052 uNewEip = uFrame.pu16[0];
2053 uNewCs = uFrame.pu16[1];
2054 uNewFlags = uFrame.pu16[2];
2055 uNewFlags &= X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2056 | X86_EFL_TF | X86_EFL_IF | X86_EFL_DF | X86_EFL_OF | X86_EFL_IOPL | X86_EFL_NT;
2057 uNewFlags |= Efl.u & (UINT32_C(0xffff0000) | X86_EFL_1);
2058 /** @todo The intel pseudo code does not indicate what happens to
2059 * reserved flags. We just ignore them. */
2060 }
2061 /** @todo Check how this is supposed to work if sp=0xfffe. */
2062
2063 /*
2064 * Check the limit of the new EIP.
2065 */
2066 /** @todo Only the AMD pseudo code check the limit here, what's
2067 * right? */
2068 if (uNewEip > pCtx->cs.u32Limit)
2069 return iemRaiseSelectorBounds(pIemCpu, X86_SREG_CS, IEM_ACCESS_INSTRUCTION);
2070
2071 /*
2072 * V8086 checks and flag adjustments
2073 */
2074 if (Efl.Bits.u1VM)
2075 {
2076 if (Efl.Bits.u2IOPL == 3)
2077 {
2078 /* Preserve IOPL and clear RF. */
2079 uNewFlags &= ~(X86_EFL_IOPL | X86_EFL_RF);
2080 uNewFlags |= Efl.u & (X86_EFL_IOPL);
2081 }
2082 else if ( enmEffOpSize == IEMMODE_16BIT
2083 && ( !(uNewFlags & X86_EFL_IF)
2084 || !Efl.Bits.u1VIP )
2085 && !(uNewFlags & X86_EFL_TF) )
2086 {
2087 /* Move IF to VIF, clear RF and preserve IF and IOPL.*/
2088 uNewFlags &= ~X86_EFL_VIF;
2089 uNewFlags |= (uNewFlags & X86_EFL_IF) << (19 - 9);
2090 uNewFlags &= ~(X86_EFL_IF | X86_EFL_IOPL | X86_EFL_RF);
2091 uNewFlags |= Efl.u & (X86_EFL_IF | X86_EFL_IOPL);
2092 }
2093 else
2094 return iemRaiseGeneralProtectionFault0(pIemCpu);
2095 }
2096
2097 /*
2098 * Commit the operation.
2099 */
2100 rcStrict = iemMemStackPopCommitSpecial(pIemCpu, uFrame.pv, uNewRsp);
2101 if (rcStrict != VINF_SUCCESS)
2102 return rcStrict;
2103 pCtx->rip = uNewEip;
2104 pCtx->cs.Sel = uNewCs;
2105 pCtx->cs.ValidSel = uNewCs;
2106 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
2107 pCtx->cs.u64Base = (uint32_t)uNewCs << 4;
2108 /** @todo do we load attribs and limit as well? */
2109 Assert(uNewFlags & X86_EFL_1);
2110 IEMMISC_SET_EFL(pIemCpu, pCtx, uNewFlags);
2111
2112 return VINF_SUCCESS;
2113}
2114
2115
2116/**
2117 * Loads a segment register when entering V8086 mode.
2118 *
2119 * @param pSReg The segment register.
2120 * @param uSeg The segment to load.
2121 */
2122static void iemCImplCommonV8086LoadSeg(PCPUMSELREG pSReg, uint16_t uSeg)
2123{
2124 pSReg->Sel = uSeg;
2125 pSReg->ValidSel = uSeg;
2126 pSReg->fFlags = CPUMSELREG_FLAGS_VALID;
2127 pSReg->u64Base = (uint32_t)uSeg << 4;
2128 pSReg->u32Limit = 0xffff;
2129 pSReg->Attr.u = X86_SEL_TYPE_RW_ACC | RT_BIT(4) /*!sys*/ | RT_BIT(7) /*P*/ | (3 /*DPL*/ << 5); /* VT-x wants 0xf3 */
2130 /** @todo Testcase: Check if VT-x really needs this and what it does itself when
2131 * IRET'ing to V8086. */
2132}
2133
2134
2135/**
2136 * Implements iret for protected mode returning to V8086 mode.
2137 *
2138 * @param pCtx Pointer to the CPU context.
2139 * @param uNewEip The new EIP.
2140 * @param uNewCs The new CS.
2141 * @param uNewFlags The new EFLAGS.
2142 * @param uNewRsp The RSP after the initial IRET frame.
2143 *
2144 * @note This can only be a 32-bit iret du to the X86_EFL_VM position.
2145 */
2146IEM_CIMPL_DEF_5(iemCImpl_iret_prot_v8086, PCPUMCTX, pCtx, uint32_t, uNewEip, uint16_t, uNewCs,
2147 uint32_t, uNewFlags, uint64_t, uNewRsp)
2148{
2149#if 0
2150 if (!LogIs6Enabled())
2151 {
2152 RTLogGroupSettings(NULL, "iem.eo.l6.l2");
2153 RTLogFlags(NULL, "enabled");
2154 return VERR_IEM_RESTART_INSTRUCTION;
2155 }
2156#endif
2157
2158 /*
2159 * Pop the V8086 specific frame bits off the stack.
2160 */
2161 VBOXSTRICTRC rcStrict;
2162 RTCPTRUNION uFrame;
2163 rcStrict = iemMemStackPopContinueSpecial(pIemCpu, 24, &uFrame.pv, &uNewRsp);
2164 if (rcStrict != VINF_SUCCESS)
2165 return rcStrict;
2166 uint32_t uNewEsp = uFrame.pu32[0];
2167 uint16_t uNewSs = uFrame.pu32[1];
2168 uint16_t uNewEs = uFrame.pu32[2];
2169 uint16_t uNewDs = uFrame.pu32[3];
2170 uint16_t uNewFs = uFrame.pu32[4];
2171 uint16_t uNewGs = uFrame.pu32[5];
2172 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uFrame.pv, IEM_ACCESS_STACK_R); /* don't use iemMemStackPopCommitSpecial here. */
2173 if (rcStrict != VINF_SUCCESS)
2174 return rcStrict;
2175
2176 /*
2177 * Commit the operation.
2178 */
2179 iemCImplCommonV8086LoadSeg(&pCtx->cs, uNewCs);
2180 iemCImplCommonV8086LoadSeg(&pCtx->ss, uNewSs);
2181 iemCImplCommonV8086LoadSeg(&pCtx->es, uNewEs);
2182 iemCImplCommonV8086LoadSeg(&pCtx->ds, uNewDs);
2183 iemCImplCommonV8086LoadSeg(&pCtx->fs, uNewFs);
2184 iemCImplCommonV8086LoadSeg(&pCtx->gs, uNewGs);
2185 pCtx->rip = uNewEip;
2186 pCtx->rsp = uNewEsp;
2187 uNewFlags &= X86_EFL_LIVE_MASK;
2188 uNewFlags |= X86_EFL_RA1_MASK;
2189 IEMMISC_SET_EFL(pIemCpu, pCtx, uNewFlags);
2190 pIemCpu->uCpl = 3;
2191
2192 return VINF_SUCCESS;
2193}
2194
2195
2196/**
2197 * Implements iret for protected mode returning via a nested task.
2198 *
2199 * @param enmEffOpSize The effective operand size.
2200 */
2201IEM_CIMPL_DEF_1(iemCImpl_iret_prot_NestedTask, IEMMODE, enmEffOpSize)
2202{
2203 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
2204}
2205
2206
2207/**
2208 * Implements iret for protected mode
2209 *
2210 * @param enmEffOpSize The effective operand size.
2211 */
2212IEM_CIMPL_DEF_1(iemCImpl_iret_prot, IEMMODE, enmEffOpSize)
2213{
2214 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
2215 NOREF(cbInstr);
2216
2217 /*
2218 * Nested task return.
2219 */
2220 if (pCtx->eflags.Bits.u1NT)
2221 return IEM_CIMPL_CALL_1(iemCImpl_iret_prot_NestedTask, enmEffOpSize);
2222
2223 /*
2224 * Normal return.
2225 *
2226 * Do the stack bits, but don't commit RSP before everything checks
2227 * out right.
2228 */
2229 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
2230 VBOXSTRICTRC rcStrict;
2231 RTCPTRUNION uFrame;
2232 uint16_t uNewCs;
2233 uint32_t uNewEip;
2234 uint32_t uNewFlags;
2235 uint64_t uNewRsp;
2236 if (enmEffOpSize == IEMMODE_32BIT)
2237 {
2238 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 12, &uFrame.pv, &uNewRsp);
2239 if (rcStrict != VINF_SUCCESS)
2240 return rcStrict;
2241 uNewEip = uFrame.pu32[0];
2242 uNewCs = (uint16_t)uFrame.pu32[1];
2243 uNewFlags = uFrame.pu32[2];
2244 }
2245 else
2246 {
2247 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 6, &uFrame.pv, &uNewRsp);
2248 if (rcStrict != VINF_SUCCESS)
2249 return rcStrict;
2250 uNewEip = uFrame.pu16[0];
2251 uNewCs = uFrame.pu16[1];
2252 uNewFlags = uFrame.pu16[2];
2253 }
2254 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uFrame.pv, IEM_ACCESS_STACK_R); /* don't use iemMemStackPopCommitSpecial here. */
2255 if (rcStrict != VINF_SUCCESS)
2256 return rcStrict;
2257
2258 /*
2259 * We're hopefully not returning to V8086 mode...
2260 */
2261 if ( (uNewFlags & X86_EFL_VM)
2262 && pIemCpu->uCpl == 0)
2263 {
2264 Assert(enmEffOpSize == IEMMODE_32BIT);
2265 return IEM_CIMPL_CALL_5(iemCImpl_iret_prot_v8086, pCtx, uNewEip, uNewCs, uNewFlags, uNewRsp);
2266 }
2267
2268 /*
2269 * Protected mode.
2270 */
2271 /* Read the CS descriptor. */
2272 if (!(uNewCs & X86_SEL_MASK_OFF_RPL))
2273 {
2274 Log(("iret %04x:%08x -> invalid CS selector, #GP(0)\n", uNewCs, uNewEip));
2275 return iemRaiseGeneralProtectionFault0(pIemCpu);
2276 }
2277
2278 IEMSELDESC DescCS;
2279 rcStrict = iemMemFetchSelDesc(pIemCpu, &DescCS, uNewCs, X86_XCPT_GP);
2280 if (rcStrict != VINF_SUCCESS)
2281 {
2282 Log(("iret %04x:%08x - rcStrict=%Rrc when fetching CS\n", uNewCs, uNewEip, VBOXSTRICTRC_VAL(rcStrict)));
2283 return rcStrict;
2284 }
2285
2286 /* Must be a code descriptor. */
2287 if (!DescCS.Legacy.Gen.u1DescType)
2288 {
2289 Log(("iret %04x:%08x - CS is system segment (%#x) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u4Type));
2290 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2291 }
2292 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
2293 {
2294 Log(("iret %04x:%08x - not code segment (%#x) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u4Type));
2295 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2296 }
2297
2298 /* Privilege checks. */
2299 if ((uNewCs & X86_SEL_RPL) < pIemCpu->uCpl)
2300 {
2301 Log(("iret %04x:%08x - RPL < CPL (%d) -> #GP\n", uNewCs, uNewEip, pIemCpu->uCpl));
2302 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2303 }
2304 if ( (DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
2305 && (uNewCs & X86_SEL_RPL) < DescCS.Legacy.Gen.u2Dpl)
2306 {
2307 Log(("iret %04x:%08x - RPL < DPL (%d) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u2Dpl));
2308 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2309 }
2310
2311 /* Present? */
2312 if (!DescCS.Legacy.Gen.u1Present)
2313 {
2314 Log(("iret %04x:%08x - CS not present -> #NP\n", uNewCs, uNewEip));
2315 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uNewCs);
2316 }
2317
2318 uint32_t cbLimitCS = X86DESC_LIMIT_G(&DescCS.Legacy);
2319
2320 /*
2321 * Return to outer level?
2322 */
2323 if ((uNewCs & X86_SEL_RPL) != pIemCpu->uCpl)
2324 {
2325 uint16_t uNewSS;
2326 uint32_t uNewESP;
2327 if (enmEffOpSize == IEMMODE_32BIT)
2328 {
2329 rcStrict = iemMemStackPopContinueSpecial(pIemCpu, 8, &uFrame.pv, &uNewRsp);
2330 if (rcStrict != VINF_SUCCESS)
2331 return rcStrict;
2332 uNewESP = uFrame.pu32[0];
2333 uNewSS = (uint16_t)uFrame.pu32[1];
2334 }
2335 else
2336 {
2337 rcStrict = iemMemStackPopContinueSpecial(pIemCpu, 8, &uFrame.pv, &uNewRsp);
2338 if (rcStrict != VINF_SUCCESS)
2339 return rcStrict;
2340 uNewESP = uFrame.pu16[0];
2341 uNewSS = uFrame.pu16[1];
2342 }
2343 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uFrame.pv, IEM_ACCESS_STACK_R);
2344 if (rcStrict != VINF_SUCCESS)
2345 return rcStrict;
2346
2347 /* Read the SS descriptor. */
2348 if (!(uNewSS & X86_SEL_MASK_OFF_RPL))
2349 {
2350 Log(("iret %04x:%08x/%04x:%08x -> invalid SS selector, #GP(0)\n", uNewCs, uNewEip, uNewSS, uNewESP));
2351 return iemRaiseGeneralProtectionFault0(pIemCpu);
2352 }
2353
2354 IEMSELDESC DescSS;
2355 rcStrict = iemMemFetchSelDesc(pIemCpu, &DescSS, uNewSS, X86_XCPT_GP); /** @todo Correct exception? */
2356 if (rcStrict != VINF_SUCCESS)
2357 {
2358 Log(("iret %04x:%08x/%04x:%08x - %Rrc when fetching SS\n",
2359 uNewCs, uNewEip, uNewSS, uNewESP, VBOXSTRICTRC_VAL(rcStrict)));
2360 return rcStrict;
2361 }
2362
2363 /* Privilege checks. */
2364 if ((uNewSS & X86_SEL_RPL) != (uNewCs & X86_SEL_RPL))
2365 {
2366 Log(("iret %04x:%08x/%04x:%08x -> SS.RPL != CS.RPL -> #GP\n", uNewCs, uNewEip, uNewSS, uNewESP));
2367 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
2368 }
2369 if (DescSS.Legacy.Gen.u2Dpl != (uNewCs & X86_SEL_RPL))
2370 {
2371 Log(("iret %04x:%08x/%04x:%08x -> SS.DPL (%d) != CS.RPL -> #GP\n",
2372 uNewCs, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u2Dpl));
2373 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
2374 }
2375
2376 /* Must be a writeable data segment descriptor. */
2377 if (!DescSS.Legacy.Gen.u1DescType)
2378 {
2379 Log(("iret %04x:%08x/%04x:%08x -> SS is system segment (%#x) -> #GP\n",
2380 uNewCs, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u4Type));
2381 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
2382 }
2383 if ((DescSS.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE)
2384 {
2385 Log(("iret %04x:%08x/%04x:%08x - not writable data segment (%#x) -> #GP\n",
2386 uNewCs, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u4Type));
2387 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
2388 }
2389
2390 /* Present? */
2391 if (!DescSS.Legacy.Gen.u1Present)
2392 {
2393 Log(("iret %04x:%08x/%04x:%08x -> SS not present -> #SS\n", uNewCs, uNewEip, uNewSS, uNewESP));
2394 return iemRaiseStackSelectorNotPresentBySelector(pIemCpu, uNewSS);
2395 }
2396
2397 uint32_t cbLimitSs = X86DESC_LIMIT_G(&DescSS.Legacy);
2398
2399 /* Check EIP. */
2400 if (uNewEip > cbLimitCS)
2401 {
2402 Log(("iret %04x:%08x/%04x:%08x -> EIP is out of bounds (%#x) -> #GP(0)\n",
2403 uNewCs, uNewEip, uNewSS, uNewESP, cbLimitCS));
2404 return iemRaiseSelectorBoundsBySelector(pIemCpu, uNewCs);
2405 }
2406
2407 /*
2408 * Commit the changes, marking CS and SS accessed first since
2409 * that may fail.
2410 */
2411 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2412 {
2413 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewCs);
2414 if (rcStrict != VINF_SUCCESS)
2415 return rcStrict;
2416 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2417 }
2418 if (!(DescSS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2419 {
2420 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewSS);
2421 if (rcStrict != VINF_SUCCESS)
2422 return rcStrict;
2423 DescSS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2424 }
2425
2426 pCtx->rip = uNewEip;
2427 pCtx->cs.Sel = uNewCs;
2428 pCtx->cs.ValidSel = uNewCs;
2429 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
2430 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
2431 pCtx->cs.u32Limit = cbLimitCS;
2432 pCtx->cs.u64Base = X86DESC_BASE(&DescCS.Legacy);
2433 pCtx->rsp = uNewESP;
2434 pCtx->ss.Sel = uNewSS;
2435 pCtx->ss.ValidSel = uNewSS;
2436 pCtx->ss.fFlags = CPUMSELREG_FLAGS_VALID;
2437 pCtx->ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSS.Legacy);
2438 pCtx->ss.u32Limit = cbLimitSs;
2439 pCtx->ss.u64Base = X86DESC_BASE(&DescSS.Legacy);
2440
2441 uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2442 | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
2443 if (enmEffOpSize != IEMMODE_16BIT)
2444 fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
2445 if (pIemCpu->uCpl == 0)
2446 fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is 0 */
2447 else if (pIemCpu->uCpl <= pCtx->eflags.Bits.u2IOPL)
2448 fEFlagsMask |= X86_EFL_IF;
2449 uint32_t fEFlagsNew = IEMMISC_GET_EFL(pIemCpu, pCtx);
2450 fEFlagsNew &= ~fEFlagsMask;
2451 fEFlagsNew |= uNewFlags & fEFlagsMask;
2452 IEMMISC_SET_EFL(pIemCpu, pCtx, fEFlagsNew);
2453
2454 pIemCpu->uCpl = uNewCs & X86_SEL_RPL;
2455 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->ds);
2456 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->es);
2457 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->fs);
2458 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCs & X86_SEL_RPL, &pCtx->gs);
2459
2460 /* Done! */
2461
2462 }
2463 /*
2464 * Return to the same level.
2465 */
2466 else
2467 {
2468 /* Check EIP. */
2469 if (uNewEip > cbLimitCS)
2470 {
2471 Log(("iret %04x:%08x - EIP is out of bounds (%#x) -> #GP(0)\n", uNewCs, uNewEip, cbLimitCS));
2472 return iemRaiseSelectorBoundsBySelector(pIemCpu, uNewCs);
2473 }
2474
2475 /*
2476 * Commit the changes, marking CS first since it may fail.
2477 */
2478 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2479 {
2480 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewCs);
2481 if (rcStrict != VINF_SUCCESS)
2482 return rcStrict;
2483 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2484 }
2485
2486 pCtx->rip = uNewEip;
2487 pCtx->cs.Sel = uNewCs;
2488 pCtx->cs.ValidSel = uNewCs;
2489 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
2490 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
2491 pCtx->cs.u32Limit = cbLimitCS;
2492 pCtx->cs.u64Base = X86DESC_BASE(&DescCS.Legacy);
2493 pCtx->rsp = uNewRsp;
2494
2495 X86EFLAGS NewEfl;
2496 NewEfl.u = IEMMISC_GET_EFL(pIemCpu, pCtx);
2497 uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2498 | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
2499 if (enmEffOpSize != IEMMODE_16BIT)
2500 fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
2501 if (pIemCpu->uCpl == 0)
2502 fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is 0 */
2503 else if (pIemCpu->uCpl <= NewEfl.Bits.u2IOPL)
2504 fEFlagsMask |= X86_EFL_IF;
2505 NewEfl.u &= ~fEFlagsMask;
2506 NewEfl.u |= fEFlagsMask & uNewFlags;
2507 IEMMISC_SET_EFL(pIemCpu, pCtx, NewEfl.u);
2508 /* Done! */
2509 }
2510 return VINF_SUCCESS;
2511}
2512
2513
2514/**
2515 * Implements iret for long mode
2516 *
2517 * @param enmEffOpSize The effective operand size.
2518 */
2519IEM_CIMPL_DEF_1(iemCImpl_iret_long, IEMMODE, enmEffOpSize)
2520{
2521 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
2522 NOREF(cbInstr);
2523
2524 /*
2525 * Nested task return is not supported in long mode.
2526 */
2527 if (pCtx->eflags.Bits.u1NT)
2528 {
2529 Log(("iretq with NT=1 (eflags=%#x) -> #GP(0)\n", pCtx->eflags.u));
2530 return iemRaiseGeneralProtectionFault0(pIemCpu);
2531 }
2532
2533 /*
2534 * Normal return.
2535 *
2536 * Do the stack bits, but don't commit RSP before everything checks
2537 * out right.
2538 */
2539 VBOXSTRICTRC rcStrict;
2540 RTCPTRUNION uFrame;
2541 uint64_t uNewRip;
2542 uint16_t uNewCs;
2543 uint16_t uNewSs;
2544 uint32_t uNewFlags;
2545 uint64_t uNewRsp;
2546 if (enmEffOpSize == IEMMODE_64BIT)
2547 {
2548 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 5*8, &uFrame.pv, &uNewRsp);
2549 if (rcStrict != VINF_SUCCESS)
2550 return rcStrict;
2551 uNewRip = uFrame.pu64[0];
2552 uNewCs = (uint16_t)uFrame.pu64[1];
2553 uNewFlags = (uint32_t)uFrame.pu64[2];
2554 uNewRsp = uFrame.pu64[3];
2555 uNewSs = (uint16_t)uFrame.pu64[4];
2556 }
2557 else if (enmEffOpSize == IEMMODE_32BIT)
2558 {
2559 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 5*4, &uFrame.pv, &uNewRsp);
2560 if (rcStrict != VINF_SUCCESS)
2561 return rcStrict;
2562 uNewRip = uFrame.pu32[0];
2563 uNewCs = (uint16_t)uFrame.pu32[1];
2564 uNewFlags = uFrame.pu32[2];
2565 uNewRsp = uFrame.pu32[3];
2566 uNewSs = (uint16_t)uFrame.pu32[4];
2567 }
2568 else
2569 {
2570 Assert(enmEffOpSize == IEMMODE_16BIT);
2571 rcStrict = iemMemStackPopBeginSpecial(pIemCpu, 5*2, &uFrame.pv, &uNewRsp);
2572 if (rcStrict != VINF_SUCCESS)
2573 return rcStrict;
2574 uNewRip = uFrame.pu16[0];
2575 uNewCs = uFrame.pu16[1];
2576 uNewFlags = uFrame.pu16[2];
2577 uNewRsp = uFrame.pu16[3];
2578 uNewSs = uFrame.pu16[4];
2579 }
2580 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uFrame.pv, IEM_ACCESS_STACK_R); /* don't use iemMemStackPopCommitSpecial here. */
2581 if (rcStrict != VINF_SUCCESS)
2582 return rcStrict;
2583 Log2(("iretq stack: cs:rip=%04x:%016RX16 rflags=%016RX16 ss:rsp=%04x:%016RX16\n",
2584 uNewCs, uNewRip, uNewFlags, uNewSs, uNewRsp));
2585
2586 /*
2587 * Check stuff.
2588 */
2589 /* Read the CS descriptor. */
2590 if (!(uNewCs & X86_SEL_MASK_OFF_RPL))
2591 {
2592 Log(("iret %04x:%016RX64/%04x:%016RX64 -> invalid CS selector, #GP(0)\n", uNewCs, uNewRip, uNewSs, uNewRsp));
2593 return iemRaiseGeneralProtectionFault0(pIemCpu);
2594 }
2595
2596 IEMSELDESC DescCS;
2597 rcStrict = iemMemFetchSelDesc(pIemCpu, &DescCS, uNewCs, X86_XCPT_GP);
2598 if (rcStrict != VINF_SUCCESS)
2599 {
2600 Log(("iret %04x:%016RX64/%04x:%016RX64 - rcStrict=%Rrc when fetching CS\n",
2601 uNewCs, uNewRip, uNewSs, uNewRsp, VBOXSTRICTRC_VAL(rcStrict)));
2602 return rcStrict;
2603 }
2604
2605 /* Must be a code descriptor. */
2606 if ( !DescCS.Legacy.Gen.u1DescType
2607 || !(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
2608 {
2609 Log(("iret %04x:%016RX64/%04x:%016RX64 - CS is not a code segment T=%u T=%#xu -> #GP\n",
2610 uNewCs, uNewRip, uNewSs, uNewRsp, DescCS.Legacy.Gen.u1DescType, DescCS.Legacy.Gen.u4Type));
2611 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2612 }
2613
2614 /* Privilege checks. */
2615 uint8_t const uNewCpl = uNewCs & X86_SEL_RPL;
2616 if ((uNewCs & X86_SEL_RPL) < pIemCpu->uCpl)
2617 {
2618 Log(("iret %04x:%016RX64/%04x:%016RX64 - RPL < CPL (%d) -> #GP\n", uNewCs, uNewRip, uNewSs, uNewRsp, pIemCpu->uCpl));
2619 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2620 }
2621 if ( (DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
2622 && (uNewCs & X86_SEL_RPL) < DescCS.Legacy.Gen.u2Dpl)
2623 {
2624 Log(("iret %04x:%016RX64/%04x:%016RX64 - RPL < DPL (%d) -> #GP\n",
2625 uNewCs, uNewRip, uNewSs, uNewRsp, DescCS.Legacy.Gen.u2Dpl));
2626 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewCs);
2627 }
2628
2629 /* Present? */
2630 if (!DescCS.Legacy.Gen.u1Present)
2631 {
2632 Log(("iret %04x:%016RX64/%04x:%016RX64 - CS not present -> #NP\n", uNewCs, uNewRip, uNewSs, uNewRsp));
2633 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uNewCs);
2634 }
2635
2636 uint32_t cbLimitCS = X86DESC_LIMIT_G(&DescCS.Legacy);
2637
2638 /* Read the SS descriptor. */
2639 IEMSELDESC DescSS;
2640 if (!(uNewSs & X86_SEL_MASK_OFF_RPL))
2641 {
2642 if ( !DescCS.Legacy.Gen.u1Long
2643 || DescCS.Legacy.Gen.u1DefBig /** @todo exactly how does iret (and others) behave with u1Long=1 and u1DefBig=1? \#GP(sel)? */
2644 || uNewCpl > 2) /** @todo verify SS=0 impossible for ring-3. */
2645 {
2646 Log(("iret %04x:%016RX64/%04x:%016RX64 -> invalid SS selector, #GP(0)\n", uNewCs, uNewRip, uNewSs, uNewRsp));
2647 return iemRaiseGeneralProtectionFault0(pIemCpu);
2648 }
2649 DescSS.Legacy.u = 0;
2650 }
2651 else
2652 {
2653 rcStrict = iemMemFetchSelDesc(pIemCpu, &DescSS, uNewSs, X86_XCPT_GP); /** @todo Correct exception? */
2654 if (rcStrict != VINF_SUCCESS)
2655 {
2656 Log(("iret %04x:%016RX64/%04x:%016RX64 - %Rrc when fetching SS\n",
2657 uNewCs, uNewRip, uNewSs, uNewRsp, VBOXSTRICTRC_VAL(rcStrict)));
2658 return rcStrict;
2659 }
2660 }
2661
2662 /* Privilege checks. */
2663 if ((uNewSs & X86_SEL_RPL) != (uNewCs & X86_SEL_RPL))
2664 {
2665 Log(("iret %04x:%016RX64/%04x:%016RX64 -> SS.RPL != CS.RPL -> #GP\n", uNewCs, uNewRip, uNewSs, uNewRsp));
2666 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSs);
2667 }
2668
2669 uint32_t cbLimitSs;
2670 if (!(uNewSs & X86_SEL_MASK_OFF_RPL))
2671 cbLimitSs = UINT32_MAX;
2672 else
2673 {
2674 if (DescSS.Legacy.Gen.u2Dpl != (uNewCs & X86_SEL_RPL))
2675 {
2676 Log(("iret %04x:%016RX64/%04x:%016RX64 -> SS.DPL (%d) != CS.RPL -> #GP\n",
2677 uNewCs, uNewRip, uNewSs, uNewRsp, DescSS.Legacy.Gen.u2Dpl));
2678 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSs);
2679 }
2680
2681 /* Must be a writeable data segment descriptor. */
2682 if (!DescSS.Legacy.Gen.u1DescType)
2683 {
2684 Log(("iret %04x:%016RX64/%04x:%016RX64 -> SS is system segment (%#x) -> #GP\n",
2685 uNewCs, uNewRip, uNewSs, uNewRsp, DescSS.Legacy.Gen.u4Type));
2686 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSs);
2687 }
2688 if ((DescSS.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE)
2689 {
2690 Log(("iret %04x:%016RX64/%04x:%016RX64 - not writable data segment (%#x) -> #GP\n",
2691 uNewCs, uNewRip, uNewSs, uNewRsp, DescSS.Legacy.Gen.u4Type));
2692 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSs);
2693 }
2694
2695 /* Present? */
2696 if (!DescSS.Legacy.Gen.u1Present)
2697 {
2698 Log(("iret %04x:%016RX64/%04x:%016RX64 -> SS not present -> #SS\n", uNewCs, uNewRip, uNewSs, uNewRsp));
2699 return iemRaiseStackSelectorNotPresentBySelector(pIemCpu, uNewSs);
2700 }
2701 cbLimitSs = X86DESC_LIMIT_G(&DescSS.Legacy);
2702 }
2703
2704 /* Check EIP. */
2705 if (DescCS.Legacy.Gen.u1Long)
2706 {
2707 if (!IEM_IS_CANONICAL(uNewRip))
2708 {
2709 Log(("iret %04x:%016RX64/%04x:%016RX64 -> RIP is not canonical -> #GP(0)\n",
2710 uNewCs, uNewRip, uNewSs, uNewRsp));
2711 return iemRaiseSelectorBoundsBySelector(pIemCpu, uNewCs);
2712 }
2713 }
2714 else
2715 {
2716 if (uNewRip > cbLimitCS)
2717 {
2718 Log(("iret %04x:%016RX64/%04x:%016RX64 -> EIP is out of bounds (%#x) -> #GP(0)\n",
2719 uNewCs, uNewRip, uNewSs, uNewRsp, cbLimitCS));
2720 return iemRaiseSelectorBoundsBySelector(pIemCpu, uNewCs);
2721 }
2722 }
2723
2724 /*
2725 * Commit the changes, marking CS and SS accessed first since
2726 * that may fail.
2727 */
2728 /** @todo where exactly are these actually marked accessed by a real CPU? */
2729 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2730 {
2731 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewCs);
2732 if (rcStrict != VINF_SUCCESS)
2733 return rcStrict;
2734 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2735 }
2736 if (!(DescSS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2737 {
2738 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewSs);
2739 if (rcStrict != VINF_SUCCESS)
2740 return rcStrict;
2741 DescSS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2742 }
2743
2744 pCtx->rip = uNewRip;
2745 pCtx->cs.Sel = uNewCs;
2746 pCtx->cs.ValidSel = uNewCs;
2747 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
2748 pCtx->cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
2749 pCtx->cs.u32Limit = cbLimitCS;
2750 pCtx->cs.u64Base = X86DESC_BASE(&DescCS.Legacy);
2751 pCtx->rsp = uNewRsp;
2752 pCtx->ss.Sel = uNewSs;
2753 pCtx->ss.ValidSel = uNewSs;
2754 if (!(uNewSs & X86_SEL_MASK_OFF_RPL))
2755 {
2756 pCtx->ss.fFlags = CPUMSELREG_FLAGS_VALID;
2757 pCtx->ss.Attr.u = X86DESCATTR_UNUSABLE | (uNewCpl << X86DESCATTR_DPL_SHIFT);
2758 pCtx->ss.u32Limit = UINT32_MAX;
2759 pCtx->ss.u64Base = 0;
2760 Log2(("iretq new SS: NULL\n"));
2761 }
2762 else
2763 {
2764 pCtx->ss.fFlags = CPUMSELREG_FLAGS_VALID;
2765 pCtx->ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSS.Legacy);
2766 pCtx->ss.u32Limit = cbLimitSs;
2767 pCtx->ss.u64Base = X86DESC_BASE(&DescSS.Legacy);
2768 Log2(("iretq new SS: base=%#RX64 lim=%#x attr=%#x\n", pCtx->ss.u64Base, pCtx->ss.u32Limit, pCtx->ss.Attr.u));
2769 }
2770
2771 uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2772 | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
2773 if (enmEffOpSize != IEMMODE_16BIT)
2774 fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
2775 if (pIemCpu->uCpl == 0)
2776 fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is ignored */
2777 else if (pIemCpu->uCpl <= pCtx->eflags.Bits.u2IOPL)
2778 fEFlagsMask |= X86_EFL_IF;
2779 uint32_t fEFlagsNew = IEMMISC_GET_EFL(pIemCpu, pCtx);
2780 fEFlagsNew &= ~fEFlagsMask;
2781 fEFlagsNew |= uNewFlags & fEFlagsMask;
2782 IEMMISC_SET_EFL(pIemCpu, pCtx, fEFlagsNew);
2783
2784 if (pIemCpu->uCpl != uNewCpl)
2785 {
2786 pIemCpu->uCpl = uNewCpl;
2787 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCpl, &pCtx->ds);
2788 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCpl, &pCtx->es);
2789 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCpl, &pCtx->fs);
2790 iemHlpAdjustSelectorForNewCpl(pIemCpu, uNewCpl, &pCtx->gs);
2791 }
2792
2793 return VINF_SUCCESS;
2794}
2795
2796
2797/**
2798 * Implements iret.
2799 *
2800 * @param enmEffOpSize The effective operand size.
2801 */
2802IEM_CIMPL_DEF_1(iemCImpl_iret, IEMMODE, enmEffOpSize)
2803{
2804 /*
2805 * Call a mode specific worker.
2806 */
2807 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
2808 return IEM_CIMPL_CALL_1(iemCImpl_iret_real_v8086, enmEffOpSize);
2809 if (IEM_IS_LONG_MODE(pIemCpu))
2810 return IEM_CIMPL_CALL_1(iemCImpl_iret_long, enmEffOpSize);
2811
2812 return IEM_CIMPL_CALL_1(iemCImpl_iret_prot, enmEffOpSize);
2813}
2814
2815
2816/**
2817 * Implements SYSCALL (AMD and Intel64).
2818 *
2819 * @param enmEffOpSize The effective operand size.
2820 */
2821IEM_CIMPL_DEF_0(iemCImpl_syscall)
2822{
2823 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
2824
2825 /*
2826 * Check preconditions.
2827 *
2828 * Note that CPUs described in the documentation may load a few odd values
2829 * into CS and SS than we allow here. This has yet to be checked on real
2830 * hardware.
2831 */
2832 if (!(pCtx->msrEFER & MSR_K6_EFER_SCE))
2833 {
2834 Log(("syscall: Not enabled in EFER -> #UD\n"));
2835 return iemRaiseUndefinedOpcode(pIemCpu);
2836 }
2837 if (!(pCtx->cr0 & X86_CR0_PE))
2838 {
2839 Log(("syscall: Protected mode is required -> #GP(0)\n"));
2840 return iemRaiseGeneralProtectionFault0(pIemCpu);
2841 }
2842 if (IEM_IS_GUEST_CPU_INTEL(pIemCpu) && !CPUMIsGuestInLongModeEx(pCtx))
2843 {
2844 Log(("syscall: Only available in long mode on intel -> #UD\n"));
2845 return iemRaiseUndefinedOpcode(pIemCpu);
2846 }
2847
2848 /** @todo verify RPL ignoring and CS=0xfff8 (i.e. SS == 0). */
2849 /** @todo what about LDT selectors? Shouldn't matter, really. */
2850 uint16_t uNewCs = (pCtx->msrSTAR >> MSR_K6_STAR_SYSCALL_CS_SS_SHIFT) & X86_SEL_MASK_OFF_RPL;
2851 uint16_t uNewSs = uNewCs + 8;
2852 if (uNewCs == 0 || uNewSs == 0)
2853 {
2854 Log(("syscall: msrSTAR.CS = 0 or SS = 0 -> #GP(0)\n"));
2855 return iemRaiseGeneralProtectionFault0(pIemCpu);
2856 }
2857
2858 /* Long mode and legacy mode differs. */
2859 if (CPUMIsGuestInLongModeEx(pCtx))
2860 {
2861 uint64_t uNewRip = pIemCpu->enmCpuMode == IEMMODE_64BIT ? pCtx->msrLSTAR : pCtx-> msrCSTAR;
2862
2863 /* This test isn't in the docs, but I'm not trusting the guys writing
2864 the MSRs to have validated the values as canonical like they should. */
2865 if (!IEM_IS_CANONICAL(uNewRip))
2866 {
2867 Log(("syscall: Only available in long mode on intel -> #UD\n"));
2868 return iemRaiseUndefinedOpcode(pIemCpu);
2869 }
2870
2871 /*
2872 * Commit it.
2873 */
2874 Log(("syscall: %04x:%016RX64 [efl=%#llx] -> %04x:%016RX64\n", pCtx->cs, pCtx->rip, pCtx->rflags.u, uNewCs, uNewRip));
2875 pCtx->rcx = pCtx->rip + cbInstr;
2876 pCtx->rip = uNewRip;
2877
2878 pCtx->rflags.u &= ~X86_EFL_RF;
2879 pCtx->r11 = pCtx->rflags.u;
2880 pCtx->rflags.u &= ~pCtx->msrSFMASK;
2881 pCtx->rflags.u |= X86_EFL_1;
2882
2883 pCtx->cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_L | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC;
2884 pCtx->ss.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_L | X86DESCATTR_DT | X86_SEL_TYPE_RW_ACC;
2885 }
2886 else
2887 {
2888 /*
2889 * Commit it.
2890 */
2891 Log(("syscall: %04x:%08RX32 [efl=%#x] -> %04x:%08RX32\n",
2892 pCtx->cs, pCtx->eip, pCtx->eflags.u, uNewCs, (uint32_t)(pCtx->msrSTAR & MSR_K6_STAR_SYSCALL_EIP_MASK)));
2893 pCtx->rcx = pCtx->eip + cbInstr;
2894 pCtx->rip = pCtx->msrSTAR & MSR_K6_STAR_SYSCALL_EIP_MASK;
2895 pCtx->rflags.u &= ~(X86_EFL_VM | X86_EFL_IF | X86_EFL_RF);
2896
2897 pCtx->cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC;
2898 pCtx->ss.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_RW_ACC;
2899 }
2900 pCtx->cs.Sel = uNewCs;
2901 pCtx->cs.ValidSel = uNewCs;
2902 pCtx->cs.u64Base = 0;
2903 pCtx->cs.u32Limit = UINT32_MAX;
2904 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
2905
2906 pCtx->ss.Sel = uNewSs;
2907 pCtx->ss.ValidSel = uNewSs;
2908 pCtx->ss.u64Base = 0;
2909 pCtx->ss.u32Limit = UINT32_MAX;
2910 pCtx->ss.fFlags = CPUMSELREG_FLAGS_VALID;
2911
2912 return VINF_SUCCESS;
2913}
2914
2915
2916/**
2917 * Implements SYSRET (AMD and Intel64).
2918 */
2919IEM_CIMPL_DEF_0(iemCImpl_sysret)
2920
2921{
2922 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
2923
2924 /*
2925 * Check preconditions.
2926 *
2927 * Note that CPUs described in the documentation may load a few odd values
2928 * into CS and SS than we allow here. This has yet to be checked on real
2929 * hardware.
2930 */
2931 if (!(pCtx->msrEFER & MSR_K6_EFER_SCE))
2932 {
2933 Log(("sysret: Not enabled in EFER -> #UD\n"));
2934 return iemRaiseUndefinedOpcode(pIemCpu);
2935 }
2936 if (IEM_IS_GUEST_CPU_INTEL(pIemCpu) && !CPUMIsGuestInLongModeEx(pCtx))
2937 {
2938 Log(("sysret: Only available in long mode on intel -> #UD\n"));
2939 return iemRaiseUndefinedOpcode(pIemCpu);
2940 }
2941 if (!(pCtx->cr0 & X86_CR0_PE))
2942 {
2943 Log(("sysret: Protected mode is required -> #GP(0)\n"));
2944 return iemRaiseGeneralProtectionFault0(pIemCpu);
2945 }
2946 if (pIemCpu->uCpl != 0)
2947 {
2948 Log(("sysret: CPL must be 0 not %u -> #GP(0)\n", pIemCpu->uCpl));
2949 return iemRaiseGeneralProtectionFault0(pIemCpu);
2950 }
2951
2952 /** @todo Does SYSRET verify CS != 0 and SS != 0? Neither is valid in ring-3. */
2953 uint16_t uNewCs = (pCtx->msrSTAR >> MSR_K6_STAR_SYSRET_CS_SS_SHIFT) & X86_SEL_MASK_OFF_RPL;
2954 uint16_t uNewSs = uNewCs + 8;
2955 if (pIemCpu->enmEffOpSize == IEMMODE_64BIT)
2956 uNewCs += 16;
2957 if (uNewCs == 0 || uNewSs == 0)
2958 {
2959 Log(("sysret: msrSTAR.CS = 0 or SS = 0 -> #GP(0)\n"));
2960 return iemRaiseGeneralProtectionFault0(pIemCpu);
2961 }
2962
2963 /*
2964 * Commit it.
2965 */
2966 if (CPUMIsGuestInLongModeEx(pCtx))
2967 {
2968 if (pIemCpu->enmEffOpSize == IEMMODE_64BIT)
2969 {
2970 Log(("sysret: %04x:%016RX64 [efl=%#llx] -> %04x:%016RX64 [r11=%#llx]\n",
2971 pCtx->cs, pCtx->rip, pCtx->rflags.u, uNewCs, pCtx->rcx, pCtx->r11));
2972 /* Note! We disregard intel manual regarding the RCX cananonical
2973 check, ask intel+xen why AMD doesn't do it. */
2974 pCtx->rip = pCtx->rcx;
2975 pCtx->cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_L | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC
2976 | (3 << X86DESCATTR_DPL_SHIFT);
2977 }
2978 else
2979 {
2980 Log(("sysret: %04x:%016RX64 [efl=%#llx] -> %04x:%08RX32 [r11=%#llx]\n",
2981 pCtx->cs, pCtx->rip, pCtx->rflags.u, uNewCs, pCtx->ecx, pCtx->r11));
2982 pCtx->rip = pCtx->ecx;
2983 pCtx->cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC
2984 | (3 << X86DESCATTR_DPL_SHIFT);
2985 }
2986 /** @todo testcase: See what kind of flags we can make SYSRET restore and
2987 * what it really ignores. RF and VM are hinted at being zero, by AMD. */
2988 pCtx->rflags.u = pCtx->r11 & (X86_EFL_POPF_BITS | X86_EFL_VIF | X86_EFL_VIP);
2989 pCtx->rflags.u |= X86_EFL_1;
2990 }
2991 else
2992 {
2993 Log(("sysret: %04x:%08RX32 [efl=%#x] -> %04x:%08RX32\n", pCtx->cs, pCtx->eip, pCtx->eflags.u, uNewCs, pCtx->ecx));
2994 pCtx->rip = pCtx->rcx;
2995 pCtx->rflags.u |= X86_EFL_IF;
2996 pCtx->cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC
2997 | (3 << X86DESCATTR_DPL_SHIFT);
2998 }
2999 pCtx->cs.Sel = uNewCs | 3;
3000 pCtx->cs.ValidSel = uNewCs | 3;
3001 pCtx->cs.u64Base = 0;
3002 pCtx->cs.u32Limit = UINT32_MAX;
3003 pCtx->cs.fFlags = CPUMSELREG_FLAGS_VALID;
3004
3005 pCtx->ss.Sel = uNewSs | 3;
3006 pCtx->ss.ValidSel = uNewSs | 3;
3007 pCtx->ss.fFlags = CPUMSELREG_FLAGS_VALID;
3008 /* The SS hidden bits remains unchanged says AMD. To that I say "Yeah, right!". */
3009 pCtx->ss.Attr.u |= (3 << X86DESCATTR_DPL_SHIFT);
3010 /** @todo Testcase: verify that SS.u1Long and SS.u1DefBig are left unchanged
3011 * on sysret. */
3012
3013 return VINF_SUCCESS;
3014}
3015
3016
3017/**
3018 * Common worker for 'pop SReg', 'mov SReg, GReg' and 'lXs GReg, reg/mem'.
3019 *
3020 * @param iSegReg The segment register number (valid).
3021 * @param uSel The new selector value.
3022 */
3023IEM_CIMPL_DEF_2(iemCImpl_LoadSReg, uint8_t, iSegReg, uint16_t, uSel)
3024{
3025 /*PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);*/
3026 uint16_t *pSel = iemSRegRef(pIemCpu, iSegReg);
3027 PCPUMSELREGHID pHid = iemSRegGetHid(pIemCpu, iSegReg);
3028
3029 Assert(iSegReg <= X86_SREG_GS && iSegReg != X86_SREG_CS);
3030
3031 /*
3032 * Real mode and V8086 mode are easy.
3033 */
3034 if ( pIemCpu->enmCpuMode == IEMMODE_16BIT
3035 && IEM_IS_REAL_OR_V86_MODE(pIemCpu))
3036 {
3037 *pSel = uSel;
3038 pHid->u64Base = (uint32_t)uSel << 4;
3039 pHid->ValidSel = uSel;
3040 pHid->fFlags = CPUMSELREG_FLAGS_VALID;
3041#if 0 /* AMD Volume 2, chapter 4.1 - "real mode segmentation" - states that limit and attributes are untouched. */
3042 /** @todo Does the CPU actually load limits and attributes in the
3043 * real/V8086 mode segment load case? It doesn't for CS in far
3044 * jumps... Affects unreal mode. */
3045 pHid->u32Limit = 0xffff;
3046 pHid->Attr.u = 0;
3047 pHid->Attr.n.u1Present = 1;
3048 pHid->Attr.n.u1DescType = 1;
3049 pHid->Attr.n.u4Type = iSegReg != X86_SREG_CS
3050 ? X86_SEL_TYPE_RW
3051 : X86_SEL_TYPE_READ | X86_SEL_TYPE_CODE;
3052#endif
3053 CPUMSetChangedFlags(IEMCPU_TO_VMCPU(pIemCpu), CPUM_CHANGED_HIDDEN_SEL_REGS);
3054 iemRegAddToRip(pIemCpu, cbInstr);
3055 return VINF_SUCCESS;
3056 }
3057
3058 /*
3059 * Protected mode.
3060 *
3061 * Check if it's a null segment selector value first, that's OK for DS, ES,
3062 * FS and GS. If not null, then we have to load and parse the descriptor.
3063 */
3064 if (!(uSel & X86_SEL_MASK_OFF_RPL))
3065 {
3066 Assert(iSegReg != X86_SREG_CS); /** @todo testcase for \#UD on MOV CS, ax! */
3067 if (iSegReg == X86_SREG_SS)
3068 {
3069 /* In 64-bit kernel mode, the stack can be 0 because of the way
3070 interrupts are dispatched. AMD seems to have a slighly more
3071 relaxed relationship to SS.RPL than intel does. */
3072 /** @todo We cannot 'mov ss, 3' in 64-bit kernel mode, can we? There is a testcase (bs-cpu-xcpt-1), but double check this! */
3073 if ( pIemCpu->enmCpuMode != IEMMODE_64BIT
3074 || pIemCpu->uCpl > 2
3075 || ( uSel != pIemCpu->uCpl
3076 && !IEM_IS_GUEST_CPU_AMD(pIemCpu)) )
3077 {
3078 Log(("load sreg %#x -> invalid stack selector, #GP(0)\n", uSel));
3079 return iemRaiseGeneralProtectionFault0(pIemCpu);
3080 }
3081 }
3082
3083 *pSel = uSel; /* Not RPL, remember :-) */
3084 iemHlpLoadNullDataSelectorProt(pHid, uSel);
3085 if (iSegReg == X86_SREG_SS)
3086 pHid->Attr.u |= pIemCpu->uCpl << X86DESCATTR_DPL_SHIFT;
3087
3088 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(IEMCPU_TO_VMCPU(pIemCpu), pHid));
3089 CPUMSetChangedFlags(IEMCPU_TO_VMCPU(pIemCpu), CPUM_CHANGED_HIDDEN_SEL_REGS);
3090
3091 iemRegAddToRip(pIemCpu, cbInstr);
3092 return VINF_SUCCESS;
3093 }
3094
3095 /* Fetch the descriptor. */
3096 IEMSELDESC Desc;
3097 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pIemCpu, &Desc, uSel, X86_XCPT_GP); /** @todo Correct exception? */
3098 if (rcStrict != VINF_SUCCESS)
3099 return rcStrict;
3100
3101 /* Check GPs first. */
3102 if (!Desc.Legacy.Gen.u1DescType)
3103 {
3104 Log(("load sreg %d - system selector (%#x) -> #GP\n", iSegReg, uSel, Desc.Legacy.Gen.u4Type));
3105 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3106 }
3107 if (iSegReg == X86_SREG_SS) /* SS gets different treatment */
3108 {
3109 if ( (Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE)
3110 || !(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_WRITE) )
3111 {
3112 Log(("load sreg SS, %#x - code or read only (%#x) -> #GP\n", uSel, Desc.Legacy.Gen.u4Type));
3113 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3114 }
3115 if ((uSel & X86_SEL_RPL) != pIemCpu->uCpl)
3116 {
3117 Log(("load sreg SS, %#x - RPL and CPL (%d) differs -> #GP\n", uSel, pIemCpu->uCpl));
3118 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3119 }
3120 if (Desc.Legacy.Gen.u2Dpl != pIemCpu->uCpl)
3121 {
3122 Log(("load sreg SS, %#x - DPL (%d) and CPL (%d) differs -> #GP\n", uSel, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
3123 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3124 }
3125 }
3126 else
3127 {
3128 if ((Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_READ)) == X86_SEL_TYPE_CODE)
3129 {
3130 Log(("load sreg%u, %#x - execute only segment -> #GP\n", iSegReg, uSel));
3131 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3132 }
3133 if ( (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
3134 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
3135 {
3136#if 0 /* this is what intel says. */
3137 if ( (uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl
3138 && pIemCpu->uCpl > Desc.Legacy.Gen.u2Dpl)
3139 {
3140 Log(("load sreg%u, %#x - both RPL (%d) and CPL (%d) are greater than DPL (%d) -> #GP\n",
3141 iSegReg, uSel, (uSel & X86_SEL_RPL), pIemCpu->uCpl, Desc.Legacy.Gen.u2Dpl));
3142 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3143 }
3144#else /* this is what makes more sense. */
3145 if ((unsigned)(uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl)
3146 {
3147 Log(("load sreg%u, %#x - RPL (%d) is greater than DPL (%d) -> #GP\n",
3148 iSegReg, uSel, (uSel & X86_SEL_RPL), Desc.Legacy.Gen.u2Dpl));
3149 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3150 }
3151 if (pIemCpu->uCpl > Desc.Legacy.Gen.u2Dpl)
3152 {
3153 Log(("load sreg%u, %#x - CPL (%d) is greater than DPL (%d) -> #GP\n",
3154 iSegReg, uSel, pIemCpu->uCpl, Desc.Legacy.Gen.u2Dpl));
3155 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uSel);
3156 }
3157#endif
3158 }
3159 }
3160
3161 /* Is it there? */
3162 if (!Desc.Legacy.Gen.u1Present)
3163 {
3164 Log(("load sreg%d,%#x - segment not present -> #NP\n", iSegReg, uSel));
3165 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uSel);
3166 }
3167
3168 /* The base and limit. */
3169 uint32_t cbLimit = X86DESC_LIMIT_G(&Desc.Legacy);
3170 uint64_t u64Base;
3171 if ( pIemCpu->enmCpuMode == IEMMODE_64BIT
3172 && iSegReg < X86_SREG_FS)
3173 u64Base = 0;
3174 else
3175 u64Base = X86DESC_BASE(&Desc.Legacy);
3176
3177 /*
3178 * Ok, everything checked out fine. Now set the accessed bit before
3179 * committing the result into the registers.
3180 */
3181 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
3182 {
3183 rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uSel);
3184 if (rcStrict != VINF_SUCCESS)
3185 return rcStrict;
3186 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
3187 }
3188
3189 /* commit */
3190 *pSel = uSel;
3191 pHid->Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
3192 pHid->u32Limit = cbLimit;
3193 pHid->u64Base = u64Base;
3194 pHid->ValidSel = uSel;
3195 pHid->fFlags = CPUMSELREG_FLAGS_VALID;
3196
3197 /** @todo check if the hidden bits are loaded correctly for 64-bit
3198 * mode. */
3199 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(IEMCPU_TO_VMCPU(pIemCpu), pHid));
3200
3201 CPUMSetChangedFlags(IEMCPU_TO_VMCPU(pIemCpu), CPUM_CHANGED_HIDDEN_SEL_REGS);
3202 iemRegAddToRip(pIemCpu, cbInstr);
3203 return VINF_SUCCESS;
3204}
3205
3206
3207/**
3208 * Implements 'mov SReg, r/m'.
3209 *
3210 * @param iSegReg The segment register number (valid).
3211 * @param uSel The new selector value.
3212 */
3213IEM_CIMPL_DEF_2(iemCImpl_load_SReg, uint8_t, iSegReg, uint16_t, uSel)
3214{
3215 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_2(iemCImpl_LoadSReg, iSegReg, uSel);
3216 if (rcStrict == VINF_SUCCESS)
3217 {
3218 if (iSegReg == X86_SREG_SS)
3219 {
3220 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3221 EMSetInhibitInterruptsPC(IEMCPU_TO_VMCPU(pIemCpu), pCtx->rip);
3222 }
3223 }
3224 return rcStrict;
3225}
3226
3227
3228/**
3229 * Implements 'pop SReg'.
3230 *
3231 * @param iSegReg The segment register number (valid).
3232 * @param enmEffOpSize The efficient operand size (valid).
3233 */
3234IEM_CIMPL_DEF_2(iemCImpl_pop_Sreg, uint8_t, iSegReg, IEMMODE, enmEffOpSize)
3235{
3236 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3237 VBOXSTRICTRC rcStrict;
3238
3239 /*
3240 * Read the selector off the stack and join paths with mov ss, reg.
3241 */
3242 RTUINT64U TmpRsp;
3243 TmpRsp.u = pCtx->rsp;
3244 switch (enmEffOpSize)
3245 {
3246 case IEMMODE_16BIT:
3247 {
3248 uint16_t uSel;
3249 rcStrict = iemMemStackPopU16Ex(pIemCpu, &uSel, &TmpRsp);
3250 if (rcStrict == VINF_SUCCESS)
3251 rcStrict = IEM_CIMPL_CALL_2(iemCImpl_LoadSReg, iSegReg, uSel);
3252 break;
3253 }
3254
3255 case IEMMODE_32BIT:
3256 {
3257 uint32_t u32Value;
3258 rcStrict = iemMemStackPopU32Ex(pIemCpu, &u32Value, &TmpRsp);
3259 if (rcStrict == VINF_SUCCESS)
3260 rcStrict = IEM_CIMPL_CALL_2(iemCImpl_LoadSReg, iSegReg, (uint16_t)u32Value);
3261 break;
3262 }
3263
3264 case IEMMODE_64BIT:
3265 {
3266 uint64_t u64Value;
3267 rcStrict = iemMemStackPopU64Ex(pIemCpu, &u64Value, &TmpRsp);
3268 if (rcStrict == VINF_SUCCESS)
3269 rcStrict = IEM_CIMPL_CALL_2(iemCImpl_LoadSReg, iSegReg, (uint16_t)u64Value);
3270 break;
3271 }
3272 IEM_NOT_REACHED_DEFAULT_CASE_RET();
3273 }
3274
3275 /*
3276 * Commit the stack on success.
3277 */
3278 if (rcStrict == VINF_SUCCESS)
3279 {
3280 pCtx->rsp = TmpRsp.u;
3281 if (iSegReg == X86_SREG_SS)
3282 EMSetInhibitInterruptsPC(IEMCPU_TO_VMCPU(pIemCpu), pCtx->rip);
3283 }
3284 return rcStrict;
3285}
3286
3287
3288/**
3289 * Implements lgs, lfs, les, lds & lss.
3290 */
3291IEM_CIMPL_DEF_5(iemCImpl_load_SReg_Greg,
3292 uint16_t, uSel,
3293 uint64_t, offSeg,
3294 uint8_t, iSegReg,
3295 uint8_t, iGReg,
3296 IEMMODE, enmEffOpSize)
3297{
3298 /*PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);*/
3299 VBOXSTRICTRC rcStrict;
3300
3301 /*
3302 * Use iemCImpl_LoadSReg to do the tricky segment register loading.
3303 */
3304 /** @todo verify and test that mov, pop and lXs works the segment
3305 * register loading in the exact same way. */
3306 rcStrict = IEM_CIMPL_CALL_2(iemCImpl_LoadSReg, iSegReg, uSel);
3307 if (rcStrict == VINF_SUCCESS)
3308 {
3309 switch (enmEffOpSize)
3310 {
3311 case IEMMODE_16BIT:
3312 *(uint16_t *)iemGRegRef(pIemCpu, iGReg) = offSeg;
3313 break;
3314 case IEMMODE_32BIT:
3315 *(uint64_t *)iemGRegRef(pIemCpu, iGReg) = offSeg;
3316 break;
3317 case IEMMODE_64BIT:
3318 *(uint64_t *)iemGRegRef(pIemCpu, iGReg) = offSeg;
3319 break;
3320 IEM_NOT_REACHED_DEFAULT_CASE_RET();
3321 }
3322 }
3323
3324 return rcStrict;
3325}
3326
3327
3328/**
3329 * Implements verr (fWrite = false) and verw (fWrite = true).
3330 */
3331IEM_CIMPL_DEF_2(iemCImpl_VerX, uint16_t, uSel, bool, fWrite)
3332{
3333 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3334 Assert(!IEM_IS_REAL_OR_V86_MODE(pIemCpu));
3335
3336 /** @todo figure whether the accessed bit is set or not. */
3337
3338 bool fAccessible = true;
3339 if (!(uSel & X86_SEL_MASK_OFF_RPL))
3340 fAccessible = false; /** @todo test this on 64-bit. */
3341 else
3342 {
3343 /* Fetch the descriptor. */
3344 RTGCPTR GCPtrBase;
3345 if (uSel & X86_SEL_LDT)
3346 {
3347 if ( !pCtx->ldtr.Attr.n.u1Present
3348 || (uSel | X86_SEL_RPL_LDT) > pCtx->ldtr.u32Limit )
3349 fAccessible = false;
3350 GCPtrBase = pCtx->ldtr.u64Base;
3351 }
3352 else
3353 {
3354 if ((uSel | X86_SEL_RPL_LDT) > pCtx->gdtr.cbGdt)
3355 fAccessible = false;
3356 GCPtrBase = pCtx->gdtr.pGdt;
3357 }
3358 if (fAccessible)
3359 {
3360 IEMSELDESC Desc;
3361 VBOXSTRICTRC rcStrict = iemMemFetchSysU64(pIemCpu, &Desc.Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
3362 if (rcStrict != VINF_SUCCESS)
3363 return rcStrict;
3364
3365 /* Check the descriptor, order doesn't matter much here. */
3366 if ( !Desc.Legacy.Gen.u1DescType
3367 || !Desc.Legacy.Gen.u1Present)
3368 fAccessible = false;
3369 else
3370 {
3371 if ( fWrite
3372 ? (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE
3373 : (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_READ)) == X86_SEL_TYPE_CODE)
3374 fAccessible = false;
3375
3376 /** @todo testcase for the conforming behavior. */
3377 if ( (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
3378 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
3379 {
3380 if ((unsigned)(uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl)
3381 fAccessible = false;
3382 else if (pIemCpu->uCpl > Desc.Legacy.Gen.u2Dpl)
3383 fAccessible = false;
3384 }
3385 }
3386 }
3387 }
3388
3389 /* commit */
3390 pIemCpu->CTX_SUFF(pCtx)->eflags.Bits.u1ZF = fAccessible;
3391
3392 iemRegAddToRip(pIemCpu, cbInstr);
3393 return VINF_SUCCESS;
3394}
3395
3396
3397/**
3398 * Implements lgdt.
3399 *
3400 * @param iEffSeg The segment of the new gdtr contents
3401 * @param GCPtrEffSrc The address of the new gdtr contents.
3402 * @param enmEffOpSize The effective operand size.
3403 */
3404IEM_CIMPL_DEF_3(iemCImpl_lgdt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc, IEMMODE, enmEffOpSize)
3405{
3406 if (pIemCpu->uCpl != 0)
3407 return iemRaiseGeneralProtectionFault0(pIemCpu);
3408 Assert(!pIemCpu->CTX_SUFF(pCtx)->eflags.Bits.u1VM);
3409
3410 /*
3411 * Fetch the limit and base address.
3412 */
3413 uint16_t cbLimit;
3414 RTGCPTR GCPtrBase;
3415 VBOXSTRICTRC rcStrict = iemMemFetchDataXdtr(pIemCpu, &cbLimit, &GCPtrBase, iEffSeg, GCPtrEffSrc, enmEffOpSize);
3416 if (rcStrict == VINF_SUCCESS)
3417 {
3418 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3419 rcStrict = CPUMSetGuestGDTR(IEMCPU_TO_VMCPU(pIemCpu), GCPtrBase, cbLimit);
3420 else
3421 {
3422 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3423 pCtx->gdtr.cbGdt = cbLimit;
3424 pCtx->gdtr.pGdt = GCPtrBase;
3425 }
3426 if (rcStrict == VINF_SUCCESS)
3427 iemRegAddToRip(pIemCpu, cbInstr);
3428 }
3429 return rcStrict;
3430}
3431
3432
3433/**
3434 * Implements sgdt.
3435 *
3436 * @param iEffSeg The segment where to store the gdtr content.
3437 * @param GCPtrEffDst The address where to store the gdtr content.
3438 * @param enmEffOpSize The effective operand size.
3439 */
3440IEM_CIMPL_DEF_3(iemCImpl_sgdt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, IEMMODE, enmEffOpSize)
3441{
3442 /*
3443 * Join paths with sidt.
3444 * Note! No CPL or V8086 checks here, it's a really sad story, ask Intel if
3445 * you really must know.
3446 */
3447 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3448 VBOXSTRICTRC rcStrict = iemMemStoreDataXdtr(pIemCpu, pCtx->gdtr.cbGdt, pCtx->gdtr.pGdt, iEffSeg, GCPtrEffDst, enmEffOpSize);
3449 if (rcStrict == VINF_SUCCESS)
3450 iemRegAddToRip(pIemCpu, cbInstr);
3451 return rcStrict;
3452}
3453
3454
3455/**
3456 * Implements lidt.
3457 *
3458 * @param iEffSeg The segment of the new idtr contents
3459 * @param GCPtrEffSrc The address of the new idtr contents.
3460 * @param enmEffOpSize The effective operand size.
3461 */
3462IEM_CIMPL_DEF_3(iemCImpl_lidt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc, IEMMODE, enmEffOpSize)
3463{
3464 if (pIemCpu->uCpl != 0)
3465 return iemRaiseGeneralProtectionFault0(pIemCpu);
3466 Assert(!pIemCpu->CTX_SUFF(pCtx)->eflags.Bits.u1VM);
3467
3468 /*
3469 * Fetch the limit and base address.
3470 */
3471 uint16_t cbLimit;
3472 RTGCPTR GCPtrBase;
3473 VBOXSTRICTRC rcStrict = iemMemFetchDataXdtr(pIemCpu, &cbLimit, &GCPtrBase, iEffSeg, GCPtrEffSrc, enmEffOpSize);
3474 if (rcStrict == VINF_SUCCESS)
3475 {
3476 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3477 CPUMSetGuestIDTR(IEMCPU_TO_VMCPU(pIemCpu), GCPtrBase, cbLimit);
3478 else
3479 {
3480 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3481 pCtx->idtr.cbIdt = cbLimit;
3482 pCtx->idtr.pIdt = GCPtrBase;
3483 }
3484 iemRegAddToRip(pIemCpu, cbInstr);
3485 }
3486 return rcStrict;
3487}
3488
3489
3490/**
3491 * Implements sidt.
3492 *
3493 * @param iEffSeg The segment where to store the idtr content.
3494 * @param GCPtrEffDst The address where to store the idtr content.
3495 * @param enmEffOpSize The effective operand size.
3496 */
3497IEM_CIMPL_DEF_3(iemCImpl_sidt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, IEMMODE, enmEffOpSize)
3498{
3499 /*
3500 * Join paths with sgdt.
3501 * Note! No CPL or V8086 checks here, it's a really sad story, ask Intel if
3502 * you really must know.
3503 */
3504 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3505 VBOXSTRICTRC rcStrict = iemMemStoreDataXdtr(pIemCpu, pCtx->idtr.cbIdt, pCtx->idtr.pIdt, iEffSeg, GCPtrEffDst, enmEffOpSize);
3506 if (rcStrict == VINF_SUCCESS)
3507 iemRegAddToRip(pIemCpu, cbInstr);
3508 return rcStrict;
3509}
3510
3511
3512/**
3513 * Implements lldt.
3514 *
3515 * @param uNewLdt The new LDT selector value.
3516 */
3517IEM_CIMPL_DEF_1(iemCImpl_lldt, uint16_t, uNewLdt)
3518{
3519 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3520
3521 /*
3522 * Check preconditions.
3523 */
3524 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
3525 {
3526 Log(("lldt %04x - real or v8086 mode -> #GP(0)\n", uNewLdt));
3527 return iemRaiseUndefinedOpcode(pIemCpu);
3528 }
3529 if (pIemCpu->uCpl != 0)
3530 {
3531 Log(("lldt %04x - CPL is %d -> #GP(0)\n", uNewLdt, pIemCpu->uCpl));
3532 return iemRaiseGeneralProtectionFault0(pIemCpu);
3533 }
3534 if (uNewLdt & X86_SEL_LDT)
3535 {
3536 Log(("lldt %04x - LDT selector -> #GP\n", uNewLdt));
3537 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewLdt);
3538 }
3539
3540 /*
3541 * Now, loading a NULL selector is easy.
3542 */
3543 if (!(uNewLdt & X86_SEL_MASK_OFF_RPL))
3544 {
3545 Log(("lldt %04x: Loading NULL selector.\n", uNewLdt));
3546 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3547 CPUMSetGuestLDTR(IEMCPU_TO_VMCPU(pIemCpu), uNewLdt);
3548 else
3549 pCtx->ldtr.Sel = uNewLdt;
3550 pCtx->ldtr.ValidSel = uNewLdt;
3551 pCtx->ldtr.fFlags = CPUMSELREG_FLAGS_VALID;
3552 pCtx->ldtr.Attr.u = X86DESCATTR_UNUSABLE;
3553 if (!IEM_IS_GUEST_CPU_AMD(pIemCpu) || !IEM_VERIFICATION_ENABLED(pIemCpu)) /* See bs-cpu-hidden-regs-1 on AMD. */
3554 {
3555 pCtx->ldtr.u64Base = 0;
3556 pCtx->ldtr.u32Limit = 0;
3557 }
3558
3559 iemRegAddToRip(pIemCpu, cbInstr);
3560 return VINF_SUCCESS;
3561 }
3562
3563 /*
3564 * Read the descriptor.
3565 */
3566 IEMSELDESC Desc;
3567 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pIemCpu, &Desc, uNewLdt, X86_XCPT_GP); /** @todo Correct exception? */
3568 if (rcStrict != VINF_SUCCESS)
3569 return rcStrict;
3570
3571 /* Check GPs first. */
3572 if (Desc.Legacy.Gen.u1DescType)
3573 {
3574 Log(("lldt %#x - not system selector (type %x) -> #GP\n", uNewLdt, Desc.Legacy.Gen.u4Type));
3575 return iemRaiseGeneralProtectionFault(pIemCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
3576 }
3577 if (Desc.Legacy.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
3578 {
3579 Log(("lldt %#x - not LDT selector (type %x) -> #GP\n", uNewLdt, Desc.Legacy.Gen.u4Type));
3580 return iemRaiseGeneralProtectionFault(pIemCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
3581 }
3582 uint64_t u64Base;
3583 if (!IEM_IS_LONG_MODE(pIemCpu))
3584 u64Base = X86DESC_BASE(&Desc.Legacy);
3585 else
3586 {
3587 if (Desc.Long.Gen.u5Zeros)
3588 {
3589 Log(("lldt %#x - u5Zeros=%#x -> #GP\n", uNewLdt, Desc.Long.Gen.u5Zeros));
3590 return iemRaiseGeneralProtectionFault(pIemCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
3591 }
3592
3593 u64Base = X86DESC64_BASE(&Desc.Long);
3594 if (!IEM_IS_CANONICAL(u64Base))
3595 {
3596 Log(("lldt %#x - non-canonical base address %#llx -> #GP\n", uNewLdt, u64Base));
3597 return iemRaiseGeneralProtectionFault(pIemCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
3598 }
3599 }
3600
3601 /* NP */
3602 if (!Desc.Legacy.Gen.u1Present)
3603 {
3604 Log(("lldt %#x - segment not present -> #NP\n", uNewLdt));
3605 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uNewLdt);
3606 }
3607
3608 /*
3609 * It checks out alright, update the registers.
3610 */
3611/** @todo check if the actual value is loaded or if the RPL is dropped */
3612 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3613 CPUMSetGuestLDTR(IEMCPU_TO_VMCPU(pIemCpu), uNewLdt & X86_SEL_MASK_OFF_RPL);
3614 else
3615 pCtx->ldtr.Sel = uNewLdt & X86_SEL_MASK_OFF_RPL;
3616 pCtx->ldtr.ValidSel = uNewLdt & X86_SEL_MASK_OFF_RPL;
3617 pCtx->ldtr.fFlags = CPUMSELREG_FLAGS_VALID;
3618 pCtx->ldtr.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
3619 pCtx->ldtr.u32Limit = X86DESC_LIMIT_G(&Desc.Legacy);
3620 pCtx->ldtr.u64Base = u64Base;
3621
3622 iemRegAddToRip(pIemCpu, cbInstr);
3623 return VINF_SUCCESS;
3624}
3625
3626
3627/**
3628 * Implements lldt.
3629 *
3630 * @param uNewLdt The new LDT selector value.
3631 */
3632IEM_CIMPL_DEF_1(iemCImpl_ltr, uint16_t, uNewTr)
3633{
3634 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3635
3636 /*
3637 * Check preconditions.
3638 */
3639 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
3640 {
3641 Log(("ltr %04x - real or v8086 mode -> #GP(0)\n", uNewTr));
3642 return iemRaiseUndefinedOpcode(pIemCpu);
3643 }
3644 if (pIemCpu->uCpl != 0)
3645 {
3646 Log(("ltr %04x - CPL is %d -> #GP(0)\n", uNewTr, pIemCpu->uCpl));
3647 return iemRaiseGeneralProtectionFault0(pIemCpu);
3648 }
3649 if (uNewTr & X86_SEL_LDT)
3650 {
3651 Log(("ltr %04x - LDT selector -> #GP\n", uNewTr));
3652 return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewTr);
3653 }
3654 if (!(uNewTr & X86_SEL_MASK_OFF_RPL))
3655 {
3656 Log(("ltr %04x - NULL selector -> #GP(0)\n", uNewTr));
3657 return iemRaiseGeneralProtectionFault0(pIemCpu);
3658 }
3659
3660 /*
3661 * Read the descriptor.
3662 */
3663 IEMSELDESC Desc;
3664 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pIemCpu, &Desc, uNewTr, X86_XCPT_GP); /** @todo Correct exception? */
3665 if (rcStrict != VINF_SUCCESS)
3666 return rcStrict;
3667
3668 /* Check GPs first. */
3669 if (Desc.Legacy.Gen.u1DescType)
3670 {
3671 Log(("ltr %#x - not system selector (type %x) -> #GP\n", uNewTr, Desc.Legacy.Gen.u4Type));
3672 return iemRaiseGeneralProtectionFault(pIemCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
3673 }
3674 if ( Desc.Legacy.Gen.u4Type != X86_SEL_TYPE_SYS_386_TSS_AVAIL /* same as AMD64_SEL_TYPE_SYS_TSS_AVAIL */
3675 && ( Desc.Legacy.Gen.u4Type != X86_SEL_TYPE_SYS_286_TSS_AVAIL
3676 || IEM_IS_LONG_MODE(pIemCpu)) )
3677 {
3678 Log(("ltr %#x - not an available TSS selector (type %x) -> #GP\n", uNewTr, Desc.Legacy.Gen.u4Type));
3679 return iemRaiseGeneralProtectionFault(pIemCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
3680 }
3681 uint64_t u64Base;
3682 if (!IEM_IS_LONG_MODE(pIemCpu))
3683 u64Base = X86DESC_BASE(&Desc.Legacy);
3684 else
3685 {
3686 if (Desc.Long.Gen.u5Zeros)
3687 {
3688 Log(("ltr %#x - u5Zeros=%#x -> #GP\n", uNewTr, Desc.Long.Gen.u5Zeros));
3689 return iemRaiseGeneralProtectionFault(pIemCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
3690 }
3691
3692 u64Base = X86DESC64_BASE(&Desc.Long);
3693 if (!IEM_IS_CANONICAL(u64Base))
3694 {
3695 Log(("ltr %#x - non-canonical base address %#llx -> #GP\n", uNewTr, u64Base));
3696 return iemRaiseGeneralProtectionFault(pIemCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
3697 }
3698 }
3699
3700 /* NP */
3701 if (!Desc.Legacy.Gen.u1Present)
3702 {
3703 Log(("ltr %#x - segment not present -> #NP\n", uNewTr));
3704 return iemRaiseSelectorNotPresentBySelector(pIemCpu, uNewTr);
3705 }
3706
3707 /*
3708 * Set it busy.
3709 * Note! Intel says this should lock down the whole descriptor, but we'll
3710 * restrict our selves to 32-bit for now due to lack of inline
3711 * assembly and such.
3712 */
3713 void *pvDesc;
3714 rcStrict = iemMemMap(pIemCpu, &pvDesc, 8, UINT8_MAX, pCtx->gdtr.pGdt + (uNewTr & X86_SEL_MASK_OFF_RPL), IEM_ACCESS_DATA_RW);
3715 if (rcStrict != VINF_SUCCESS)
3716 return rcStrict;
3717 switch ((uintptr_t)pvDesc & 3)
3718 {
3719 case 0: ASMAtomicBitSet(pvDesc, 40 + 1); break;
3720 case 1: ASMAtomicBitSet((uint8_t *)pvDesc + 3, 40 + 1 - 24); break;
3721 case 2: ASMAtomicBitSet((uint8_t *)pvDesc + 2, 40 + 1 - 16); break;
3722 case 3: ASMAtomicBitSet((uint8_t *)pvDesc + 1, 40 + 1 - 8); break;
3723 }
3724 rcStrict = iemMemCommitAndUnmap(pIemCpu, pvDesc, IEM_ACCESS_DATA_RW);
3725 if (rcStrict != VINF_SUCCESS)
3726 return rcStrict;
3727 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_SYS_TSS_BUSY_MASK;
3728
3729 /*
3730 * It checks out alright, update the registers.
3731 */
3732/** @todo check if the actual value is loaded or if the RPL is dropped */
3733 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3734 CPUMSetGuestTR(IEMCPU_TO_VMCPU(pIemCpu), uNewTr & X86_SEL_MASK_OFF_RPL);
3735 else
3736 pCtx->tr.Sel = uNewTr & X86_SEL_MASK_OFF_RPL;
3737 pCtx->tr.ValidSel = uNewTr & X86_SEL_MASK_OFF_RPL;
3738 pCtx->tr.fFlags = CPUMSELREG_FLAGS_VALID;
3739 pCtx->tr.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
3740 pCtx->tr.u32Limit = X86DESC_LIMIT_G(&Desc.Legacy);
3741 pCtx->tr.u64Base = u64Base;
3742
3743 iemRegAddToRip(pIemCpu, cbInstr);
3744 return VINF_SUCCESS;
3745}
3746
3747
3748/**
3749 * Implements mov GReg,CRx.
3750 *
3751 * @param iGReg The general register to store the CRx value in.
3752 * @param iCrReg The CRx register to read (valid).
3753 */
3754IEM_CIMPL_DEF_2(iemCImpl_mov_Rd_Cd, uint8_t, iGReg, uint8_t, iCrReg)
3755{
3756 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3757 if (pIemCpu->uCpl != 0)
3758 return iemRaiseGeneralProtectionFault0(pIemCpu);
3759 Assert(!pCtx->eflags.Bits.u1VM);
3760
3761 /* read it */
3762 uint64_t crX;
3763 switch (iCrReg)
3764 {
3765 case 0: crX = pCtx->cr0; break;
3766 case 2: crX = pCtx->cr2; break;
3767 case 3: crX = pCtx->cr3; break;
3768 case 4: crX = pCtx->cr4; break;
3769 case 8:
3770 {
3771 uint8_t uTpr;
3772 int rc = PDMApicGetTPR(IEMCPU_TO_VMCPU(pIemCpu), &uTpr, NULL, NULL);
3773 if (RT_SUCCESS(rc))
3774 crX = uTpr >> 4;
3775 else
3776 crX = 0;
3777 break;
3778 }
3779 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
3780 }
3781
3782 /* store it */
3783 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
3784 *(uint64_t *)iemGRegRef(pIemCpu, iGReg) = crX;
3785 else
3786 *(uint64_t *)iemGRegRef(pIemCpu, iGReg) = (uint32_t)crX;
3787
3788 iemRegAddToRip(pIemCpu, cbInstr);
3789 return VINF_SUCCESS;
3790}
3791
3792
3793/**
3794 * Used to implemented 'mov CRx,GReg' and 'lmsw r/m16'.
3795 *
3796 * @param iCrReg The CRx register to write (valid).
3797 * @param uNewCrX The new value.
3798 */
3799IEM_CIMPL_DEF_2(iemCImpl_load_CrX, uint8_t, iCrReg, uint64_t, uNewCrX)
3800{
3801 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
3802 PVMCPU pVCpu = IEMCPU_TO_VMCPU(pIemCpu);
3803 VBOXSTRICTRC rcStrict;
3804 int rc;
3805
3806 /*
3807 * Try store it.
3808 * Unfortunately, CPUM only does a tiny bit of the work.
3809 */
3810 switch (iCrReg)
3811 {
3812 case 0:
3813 {
3814 /*
3815 * Perform checks.
3816 */
3817 uint64_t const uOldCrX = pCtx->cr0;
3818 uNewCrX |= X86_CR0_ET; /* hardcoded */
3819
3820 /* Check for reserved bits. */
3821 uint32_t const fValid = X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS
3822 | X86_CR0_ET | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM
3823 | X86_CR0_NW | X86_CR0_CD | X86_CR0_PG;
3824 if (uNewCrX & ~(uint64_t)fValid)
3825 {
3826 Log(("Trying to set reserved CR0 bits: NewCR0=%#llx InvalidBits=%#llx\n", uNewCrX, uNewCrX & ~(uint64_t)fValid));
3827 return iemRaiseGeneralProtectionFault0(pIemCpu);
3828 }
3829
3830 /* Check for invalid combinations. */
3831 if ( (uNewCrX & X86_CR0_PG)
3832 && !(uNewCrX & X86_CR0_PE) )
3833 {
3834 Log(("Trying to set CR0.PG without CR0.PE\n"));
3835 return iemRaiseGeneralProtectionFault0(pIemCpu);
3836 }
3837
3838 if ( !(uNewCrX & X86_CR0_CD)
3839 && (uNewCrX & X86_CR0_NW) )
3840 {
3841 Log(("Trying to clear CR0.CD while leaving CR0.NW set\n"));
3842 return iemRaiseGeneralProtectionFault0(pIemCpu);
3843 }
3844
3845 /* Long mode consistency checks. */
3846 if ( (uNewCrX & X86_CR0_PG)
3847 && !(uOldCrX & X86_CR0_PG)
3848 && (pCtx->msrEFER & MSR_K6_EFER_LME) )
3849 {
3850 if (!(pCtx->cr4 & X86_CR4_PAE))
3851 {
3852 Log(("Trying to enabled long mode paging without CR4.PAE set\n"));
3853 return iemRaiseGeneralProtectionFault0(pIemCpu);
3854 }
3855 if (pCtx->cs.Attr.n.u1Long)
3856 {
3857 Log(("Trying to enabled long mode paging with a long CS descriptor loaded.\n"));
3858 return iemRaiseGeneralProtectionFault0(pIemCpu);
3859 }
3860 }
3861
3862 /** @todo check reserved PDPTR bits as AMD states. */
3863
3864 /*
3865 * Change CR0.
3866 */
3867 if (!IEM_VERIFICATION_ENABLED(pIemCpu))
3868 CPUMSetGuestCR0(pVCpu, uNewCrX);
3869 else
3870 pCtx->cr0 = uNewCrX;
3871 Assert(pCtx->cr0 == uNewCrX);
3872
3873 /*
3874 * Change EFER.LMA if entering or leaving long mode.
3875 */
3876 if ( (uNewCrX & X86_CR0_PG) != (uOldCrX & X86_CR0_PG)
3877 && (pCtx->msrEFER & MSR_K6_EFER_LME) )
3878 {
3879 uint64_t NewEFER = pCtx->msrEFER;
3880 if (uNewCrX & X86_CR0_PG)
3881 NewEFER |= MSR_K6_EFER_LMA;
3882 else
3883 NewEFER &= ~MSR_K6_EFER_LMA;
3884
3885 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3886 CPUMSetGuestEFER(pVCpu, NewEFER);
3887 else
3888 pCtx->msrEFER = NewEFER;
3889 Assert(pCtx->msrEFER == NewEFER);
3890 }
3891
3892 /*
3893 * Inform PGM.
3894 */
3895 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3896 {
3897 if ( (uNewCrX & (X86_CR0_PG | X86_CR0_WP | X86_CR0_PE))
3898 != (uOldCrX & (X86_CR0_PG | X86_CR0_WP | X86_CR0_PE)) )
3899 {
3900 rc = PGMFlushTLB(pVCpu, pCtx->cr3, true /* global */);
3901 AssertRCReturn(rc, rc);
3902 /* ignore informational status codes */
3903 }
3904 rcStrict = PGMChangeMode(pVCpu, pCtx->cr0, pCtx->cr4, pCtx->msrEFER);
3905 }
3906 else
3907 rcStrict = VINF_SUCCESS;
3908
3909#ifdef IN_RC
3910 /* Return to ring-3 for rescheduling if WP or AM changes. */
3911 if ( rcStrict == VINF_SUCCESS
3912 && ( (uNewCrX & (X86_CR0_WP | X86_CR0_AM))
3913 != (uOldCrX & (X86_CR0_WP | X86_CR0_AM))) )
3914 rcStrict = VINF_EM_RESCHEDULE;
3915#endif
3916 break;
3917 }
3918
3919 /*
3920 * CR2 can be changed without any restrictions.
3921 */
3922 case 2:
3923 pCtx->cr2 = uNewCrX;
3924 rcStrict = VINF_SUCCESS;
3925 break;
3926
3927 /*
3928 * CR3 is relatively simple, although AMD and Intel have different
3929 * accounts of how setting reserved bits are handled. We take intel's
3930 * word for the lower bits and AMD's for the high bits (63:52).
3931 */
3932 /** @todo Testcase: Setting reserved bits in CR3, especially before
3933 * enabling paging. */
3934 case 3:
3935 {
3936 /* check / mask the value. */
3937 if (uNewCrX & UINT64_C(0xfff0000000000000))
3938 {
3939 Log(("Trying to load CR3 with invalid high bits set: %#llx\n", uNewCrX));
3940 return iemRaiseGeneralProtectionFault0(pIemCpu);
3941 }
3942
3943 uint64_t fValid;
3944 if ( (pCtx->cr4 & X86_CR4_PAE)
3945 && (pCtx->msrEFER & MSR_K6_EFER_LME))
3946 fValid = UINT64_C(0x000ffffffffff014);
3947 else if (pCtx->cr4 & X86_CR4_PAE)
3948 fValid = UINT64_C(0xfffffff4);
3949 else
3950 fValid = UINT64_C(0xfffff014);
3951 if (uNewCrX & ~fValid)
3952 {
3953 Log(("Automatically clearing reserved bits in CR3 load: NewCR3=%#llx ClearedBits=%#llx\n",
3954 uNewCrX, uNewCrX & ~fValid));
3955 uNewCrX &= fValid;
3956 }
3957
3958 /** @todo If we're in PAE mode we should check the PDPTRs for
3959 * invalid bits. */
3960
3961 /* Make the change. */
3962 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3963 {
3964 rc = CPUMSetGuestCR3(pVCpu, uNewCrX);
3965 AssertRCSuccessReturn(rc, rc);
3966 }
3967 else
3968 pCtx->cr3 = uNewCrX;
3969
3970 /* Inform PGM. */
3971 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
3972 {
3973 if (pCtx->cr0 & X86_CR0_PG)
3974 {
3975 rc = PGMFlushTLB(pVCpu, pCtx->cr3, !(pCtx->cr4 & X86_CR4_PGE));
3976 AssertRCReturn(rc, rc);
3977 /* ignore informational status codes */
3978 }
3979 }
3980 rcStrict = VINF_SUCCESS;
3981 break;
3982 }
3983
3984 /*
3985 * CR4 is a bit more tedious as there are bits which cannot be cleared
3986 * under some circumstances and such.
3987 */
3988 case 4:
3989 {
3990 uint64_t const uOldCrX = pCtx->cr4;
3991
3992 /* reserved bits */
3993 uint32_t fValid = X86_CR4_VME | X86_CR4_PVI
3994 | X86_CR4_TSD | X86_CR4_DE
3995 | X86_CR4_PSE | X86_CR4_PAE
3996 | X86_CR4_MCE | X86_CR4_PGE
3997 | X86_CR4_PCE | X86_CR4_OSFSXR
3998 | X86_CR4_OSXMMEEXCPT;
3999 //if (xxx)
4000 // fValid |= X86_CR4_VMXE;
4001 //if (xxx)
4002 // fValid |= X86_CR4_OSXSAVE;
4003 if (uNewCrX & ~(uint64_t)fValid)
4004 {
4005 Log(("Trying to set reserved CR4 bits: NewCR4=%#llx InvalidBits=%#llx\n", uNewCrX, uNewCrX & ~(uint64_t)fValid));
4006 return iemRaiseGeneralProtectionFault0(pIemCpu);
4007 }
4008
4009 /* long mode checks. */
4010 if ( (uOldCrX & X86_CR4_PAE)
4011 && !(uNewCrX & X86_CR4_PAE)
4012 && CPUMIsGuestInLongModeEx(pCtx) )
4013 {
4014 Log(("Trying to set clear CR4.PAE while long mode is active\n"));
4015 return iemRaiseGeneralProtectionFault0(pIemCpu);
4016 }
4017
4018
4019 /*
4020 * Change it.
4021 */
4022 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
4023 {
4024 rc = CPUMSetGuestCR4(pVCpu, uNewCrX);
4025 AssertRCSuccessReturn(rc, rc);
4026 }
4027 else
4028 pCtx->cr4 = uNewCrX;
4029 Assert(pCtx->cr4 == uNewCrX);
4030
4031 /*
4032 * Notify SELM and PGM.
4033 */
4034 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
4035 {
4036 /* SELM - VME may change things wrt to the TSS shadowing. */
4037 if ((uNewCrX ^ uOldCrX) & X86_CR4_VME)
4038 {
4039 Log(("iemCImpl_load_CrX: VME %d -> %d => Setting VMCPU_FF_SELM_SYNC_TSS\n",
4040 RT_BOOL(uOldCrX & X86_CR4_VME), RT_BOOL(uNewCrX & X86_CR4_VME) ));
4041#ifdef VBOX_WITH_RAW_MODE
4042 if (!HMIsEnabled(IEMCPU_TO_VM(pIemCpu)))
4043 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
4044#endif
4045 }
4046
4047 /* PGM - flushing and mode. */
4048 if ((uNewCrX ^ uOldCrX) & (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_PGE))
4049 {
4050 rc = PGMFlushTLB(pVCpu, pCtx->cr3, true /* global */);
4051 AssertRCReturn(rc, rc);
4052 /* ignore informational status codes */
4053 }
4054 rcStrict = PGMChangeMode(pVCpu, pCtx->cr0, pCtx->cr4, pCtx->msrEFER);
4055 }
4056 else
4057 rcStrict = VINF_SUCCESS;
4058 break;
4059 }
4060
4061 /*
4062 * CR8 maps to the APIC TPR.
4063 */
4064 case 8:
4065 if (uNewCrX & ~(uint64_t)0xf)
4066 {
4067 Log(("Trying to set reserved CR8 bits (%#RX64)\n", uNewCrX));
4068 return iemRaiseGeneralProtectionFault0(pIemCpu);
4069 }
4070
4071 if (!IEM_FULL_VERIFICATION_ENABLED(pIemCpu))
4072 PDMApicSetTPR(IEMCPU_TO_VMCPU(pIemCpu), (uint8_t)uNewCrX << 4);
4073 rcStrict = VINF_SUCCESS;
4074 break;
4075
4076 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
4077 }
4078
4079 /*
4080 * Advance the RIP on success.
4081 */
4082 if (RT_SUCCESS(rcStrict))
4083 {
4084 if (rcStrict != VINF_SUCCESS)
4085 rcStrict = iemSetPassUpStatus(pIemCpu, rcStrict);
4086 iemRegAddToRip(pIemCpu, cbInstr);
4087 }
4088
4089 return rcStrict;
4090}
4091
4092
4093/**
4094 * Implements mov CRx,GReg.
4095 *
4096 * @param iCrReg The CRx register to write (valid).
4097 * @param iGReg The general register to load the DRx value from.
4098 */
4099IEM_CIMPL_DEF_2(iemCImpl_mov_Cd_Rd, uint8_t, iCrReg, uint8_t, iGReg)
4100{
4101 if (pIemCpu->uCpl != 0)
4102 return iemRaiseGeneralProtectionFault0(pIemCpu);
4103 Assert(!pIemCpu->CTX_SUFF(pCtx)->eflags.Bits.u1VM);
4104
4105 /*
4106 * Read the new value from the source register and call common worker.
4107 */
4108 uint64_t uNewCrX;
4109 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
4110 uNewCrX = iemGRegFetchU64(pIemCpu, iGReg);
4111 else
4112 uNewCrX = iemGRegFetchU32(pIemCpu, iGReg);
4113 return IEM_CIMPL_CALL_2(iemCImpl_load_CrX, iCrReg, uNewCrX);
4114}
4115
4116
4117/**
4118 * Implements 'LMSW r/m16'
4119 *
4120 * @param u16NewMsw The new value.
4121 */
4122IEM_CIMPL_DEF_1(iemCImpl_lmsw, uint16_t, u16NewMsw)
4123{
4124 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4125
4126 if (pIemCpu->uCpl != 0)
4127 return iemRaiseGeneralProtectionFault0(pIemCpu);
4128 Assert(!pCtx->eflags.Bits.u1VM);
4129
4130 /*
4131 * Compose the new CR0 value and call common worker.
4132 */
4133 uint64_t uNewCr0 = pCtx->cr0 & ~(X86_CR0_MP | X86_CR0_EM | X86_CR0_TS);
4134 uNewCr0 |= u16NewMsw & (X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS);
4135 return IEM_CIMPL_CALL_2(iemCImpl_load_CrX, /*cr*/ 0, uNewCr0);
4136}
4137
4138
4139/**
4140 * Implements 'CLTS'.
4141 */
4142IEM_CIMPL_DEF_0(iemCImpl_clts)
4143{
4144 if (pIemCpu->uCpl != 0)
4145 return iemRaiseGeneralProtectionFault0(pIemCpu);
4146
4147 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4148 uint64_t uNewCr0 = pCtx->cr0;
4149 uNewCr0 &= ~X86_CR0_TS;
4150 return IEM_CIMPL_CALL_2(iemCImpl_load_CrX, /*cr*/ 0, uNewCr0);
4151}
4152
4153
4154/**
4155 * Implements mov GReg,DRx.
4156 *
4157 * @param iGReg The general register to store the DRx value in.
4158 * @param iDrReg The DRx register to read (0-7).
4159 */
4160IEM_CIMPL_DEF_2(iemCImpl_mov_Rd_Dd, uint8_t, iGReg, uint8_t, iDrReg)
4161{
4162 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4163
4164 /*
4165 * Check preconditions.
4166 */
4167
4168 /* Raise GPs. */
4169 if (pIemCpu->uCpl != 0)
4170 return iemRaiseGeneralProtectionFault0(pIemCpu);
4171 Assert(!pCtx->eflags.Bits.u1VM);
4172
4173 if ( (iDrReg == 4 || iDrReg == 5)
4174 && (pCtx->cr4 & X86_CR4_DE) )
4175 {
4176 Log(("mov r%u,dr%u: CR4.DE=1 -> #GP(0)\n", iGReg, iDrReg));
4177 return iemRaiseGeneralProtectionFault0(pIemCpu);
4178 }
4179
4180 /* Raise #DB if general access detect is enabled. */
4181 if (pCtx->dr[7] & X86_DR7_GD)
4182 {
4183 Log(("mov r%u,dr%u: DR7.GD=1 -> #DB\n", iGReg, iDrReg));
4184 return iemRaiseDebugException(pIemCpu);
4185 }
4186
4187 /*
4188 * Read the debug register and store it in the specified general register.
4189 */
4190 uint64_t drX;
4191 switch (iDrReg)
4192 {
4193 case 0: drX = pCtx->dr[0]; break;
4194 case 1: drX = pCtx->dr[1]; break;
4195 case 2: drX = pCtx->dr[2]; break;
4196 case 3: drX = pCtx->dr[3]; break;
4197 case 6:
4198 case 4:
4199 drX = pCtx->dr[6];
4200 drX |= X86_DR6_RA1_MASK;
4201 drX &= ~X86_DR6_RAZ_MASK;
4202 break;
4203 case 7:
4204 case 5:
4205 drX = pCtx->dr[7];
4206 drX |=X86_DR7_RA1_MASK;
4207 drX &= ~X86_DR7_RAZ_MASK;
4208 break;
4209 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
4210 }
4211
4212 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
4213 *(uint64_t *)iemGRegRef(pIemCpu, iGReg) = drX;
4214 else
4215 *(uint64_t *)iemGRegRef(pIemCpu, iGReg) = (uint32_t)drX;
4216
4217 iemRegAddToRip(pIemCpu, cbInstr);
4218 return VINF_SUCCESS;
4219}
4220
4221
4222/**
4223 * Implements mov DRx,GReg.
4224 *
4225 * @param iDrReg The DRx register to write (valid).
4226 * @param iGReg The general register to load the DRx value from.
4227 */
4228IEM_CIMPL_DEF_2(iemCImpl_mov_Dd_Rd, uint8_t, iDrReg, uint8_t, iGReg)
4229{
4230 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4231
4232 /*
4233 * Check preconditions.
4234 */
4235 if (pIemCpu->uCpl != 0)
4236 return iemRaiseGeneralProtectionFault0(pIemCpu);
4237 Assert(!pCtx->eflags.Bits.u1VM);
4238
4239 if (iDrReg == 4 || iDrReg == 5)
4240 {
4241 if (pCtx->cr4 & X86_CR4_DE)
4242 {
4243 Log(("mov dr%u,r%u: CR4.DE=1 -> #GP(0)\n", iDrReg, iGReg));
4244 return iemRaiseGeneralProtectionFault0(pIemCpu);
4245 }
4246 iDrReg += 2;
4247 }
4248
4249 /* Raise #DB if general access detect is enabled. */
4250 /** @todo is \#DB/DR7.GD raised before any reserved high bits in DR7/DR6
4251 * \#GP? */
4252 if (pCtx->dr[7] & X86_DR7_GD)
4253 {
4254 Log(("mov dr%u,r%u: DR7.GD=1 -> #DB\n", iDrReg, iGReg));
4255 return iemRaiseDebugException(pIemCpu);
4256 }
4257
4258 /*
4259 * Read the new value from the source register.
4260 */
4261 uint64_t uNewDrX;
4262 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
4263 uNewDrX = iemGRegFetchU64(pIemCpu, iGReg);
4264 else
4265 uNewDrX = iemGRegFetchU32(pIemCpu, iGReg);
4266
4267 /*
4268 * Adjust it.
4269 */
4270 switch (iDrReg)
4271 {
4272 case 0:
4273 case 1:
4274 case 2:
4275 case 3:
4276 /* nothing to adjust */
4277 break;
4278
4279 case 6:
4280 if (uNewDrX & X86_DR6_MBZ_MASK)
4281 {
4282 Log(("mov dr%u,%#llx: DR6 high bits are not zero -> #GP(0)\n", iDrReg, uNewDrX));
4283 return iemRaiseGeneralProtectionFault0(pIemCpu);
4284 }
4285 uNewDrX |= X86_DR6_RA1_MASK;
4286 uNewDrX &= ~X86_DR6_RAZ_MASK;
4287 break;
4288
4289 case 7:
4290 if (uNewDrX & X86_DR7_MBZ_MASK)
4291 {
4292 Log(("mov dr%u,%#llx: DR7 high bits are not zero -> #GP(0)\n", iDrReg, uNewDrX));
4293 return iemRaiseGeneralProtectionFault0(pIemCpu);
4294 }
4295 uNewDrX |= X86_DR7_RA1_MASK;
4296 uNewDrX &= ~X86_DR7_RAZ_MASK;
4297 break;
4298
4299 IEM_NOT_REACHED_DEFAULT_CASE_RET();
4300 }
4301
4302 /*
4303 * Do the actual setting.
4304 */
4305 if (!IEM_VERIFICATION_ENABLED(pIemCpu))
4306 {
4307 int rc = CPUMSetGuestDRx(IEMCPU_TO_VMCPU(pIemCpu), iDrReg, uNewDrX);
4308 AssertRCSuccessReturn(rc, RT_SUCCESS_NP(rc) ? VERR_INTERNAL_ERROR : rc);
4309 }
4310 else
4311 pCtx->dr[iDrReg] = uNewDrX;
4312
4313 iemRegAddToRip(pIemCpu, cbInstr);
4314 return VINF_SUCCESS;
4315}
4316
4317
4318/**
4319 * Implements 'INVLPG m'.
4320 *
4321 * @param GCPtrPage The effective address of the page to invalidate.
4322 * @remarks Updates the RIP.
4323 */
4324IEM_CIMPL_DEF_1(iemCImpl_invlpg, uint8_t, GCPtrPage)
4325{
4326 /* ring-0 only. */
4327 if (pIemCpu->uCpl != 0)
4328 return iemRaiseGeneralProtectionFault0(pIemCpu);
4329 Assert(!pIemCpu->CTX_SUFF(pCtx)->eflags.Bits.u1VM);
4330
4331 int rc = PGMInvalidatePage(IEMCPU_TO_VMCPU(pIemCpu), GCPtrPage);
4332 iemRegAddToRip(pIemCpu, cbInstr);
4333
4334 if (rc == VINF_SUCCESS)
4335 return VINF_SUCCESS;
4336 if (rc == VINF_PGM_SYNC_CR3)
4337 return iemSetPassUpStatus(pIemCpu, rc);
4338
4339 AssertMsg(rc == VINF_EM_RAW_EMULATE_INSTR || RT_FAILURE_NP(rc), ("%Rrc\n", rc));
4340 Log(("PGMInvalidatePage(%RGv) -> %Rrc\n", rc));
4341 return rc;
4342}
4343
4344
4345/**
4346 * Implements RDTSC.
4347 */
4348IEM_CIMPL_DEF_0(iemCImpl_rdtsc)
4349{
4350 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4351
4352 /*
4353 * Check preconditions.
4354 */
4355 if (!IEM_IS_INTEL_CPUID_FEATURE_PRESENT_EDX(X86_CPUID_FEATURE_EDX_TSC))
4356 return iemRaiseUndefinedOpcode(pIemCpu);
4357
4358 if ( (pCtx->cr4 & X86_CR4_TSD)
4359 && pIemCpu->uCpl != 0)
4360 {
4361 Log(("rdtsc: CR4.TSD and CPL=%u -> #GP(0)\n", pIemCpu->uCpl));
4362 return iemRaiseGeneralProtectionFault0(pIemCpu);
4363 }
4364
4365 /*
4366 * Do the job.
4367 */
4368 uint64_t uTicks = TMCpuTickGet(IEMCPU_TO_VMCPU(pIemCpu));
4369 pCtx->rax = (uint32_t)uTicks;
4370 pCtx->rdx = uTicks >> 32;
4371#ifdef IEM_VERIFICATION_MODE_FULL
4372 pIemCpu->fIgnoreRaxRdx = true;
4373#endif
4374
4375 iemRegAddToRip(pIemCpu, cbInstr);
4376 return VINF_SUCCESS;
4377}
4378
4379
4380/**
4381 * Implements RDMSR.
4382 */
4383IEM_CIMPL_DEF_0(iemCImpl_rdmsr)
4384{
4385 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4386
4387 /*
4388 * Check preconditions.
4389 */
4390 if (!IEM_IS_INTEL_CPUID_FEATURE_PRESENT_EDX(X86_CPUID_FEATURE_EDX_MSR))
4391 return iemRaiseUndefinedOpcode(pIemCpu);
4392 if (pIemCpu->uCpl != 0)
4393 return iemRaiseGeneralProtectionFault0(pIemCpu);
4394
4395 /*
4396 * Do the job.
4397 */
4398 RTUINT64U uValue;
4399 int rc = CPUMQueryGuestMsr(IEMCPU_TO_VMCPU(pIemCpu), pCtx->ecx, &uValue.u);
4400 if (rc != VINF_SUCCESS)
4401 {
4402 Log(("IEM: rdmsr(%#x) -> GP(0)\n", pCtx->ecx));
4403 AssertMsgReturn(rc == VERR_CPUM_RAISE_GP_0, ("%Rrc\n", rc), VERR_IPE_UNEXPECTED_STATUS);
4404 return iemRaiseGeneralProtectionFault0(pIemCpu);
4405 }
4406
4407 pCtx->rax = uValue.s.Lo;
4408 pCtx->rdx = uValue.s.Hi;
4409
4410 iemRegAddToRip(pIemCpu, cbInstr);
4411 return VINF_SUCCESS;
4412}
4413
4414
4415/**
4416 * Implements WRMSR.
4417 */
4418IEM_CIMPL_DEF_0(iemCImpl_wrmsr)
4419{
4420 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4421
4422 /*
4423 * Check preconditions.
4424 */
4425 if (!IEM_IS_INTEL_CPUID_FEATURE_PRESENT_EDX(X86_CPUID_FEATURE_EDX_MSR))
4426 return iemRaiseUndefinedOpcode(pIemCpu);
4427 if (pIemCpu->uCpl != 0)
4428 return iemRaiseGeneralProtectionFault0(pIemCpu);
4429
4430 /*
4431 * Do the job.
4432 */
4433 RTUINT64U uValue;
4434 uValue.s.Lo = pCtx->eax;
4435 uValue.s.Hi = pCtx->edx;
4436
4437 int rc;
4438 if (!IEM_VERIFICATION_ENABLED(pIemCpu))
4439 rc = CPUMSetGuestMsr(IEMCPU_TO_VMCPU(pIemCpu), pCtx->ecx, uValue.u);
4440 else
4441 {
4442 CPUMCTX CtxTmp = *pCtx;
4443 rc = CPUMSetGuestMsr(IEMCPU_TO_VMCPU(pIemCpu), pCtx->ecx, uValue.u);
4444 PCPUMCTX pCtx2 = CPUMQueryGuestCtxPtr(IEMCPU_TO_VMCPU(pIemCpu));
4445 *pCtx = *pCtx2;
4446 *pCtx2 = CtxTmp;
4447 }
4448 if (rc != VINF_SUCCESS)
4449 {
4450 Log(("IEM: wrmsr(%#x,%#x`%08x) -> GP(0)\n", pCtx->ecx, uValue.s.Hi, uValue.s.Lo));
4451 AssertMsgReturn(rc == VERR_CPUM_RAISE_GP_0, ("%Rrc\n", rc), VERR_IPE_UNEXPECTED_STATUS);
4452 return iemRaiseGeneralProtectionFault0(pIemCpu);
4453 }
4454
4455 iemRegAddToRip(pIemCpu, cbInstr);
4456 return VINF_SUCCESS;
4457}
4458
4459
4460/**
4461 * Implements 'IN eAX, port'.
4462 *
4463 * @param u16Port The source port.
4464 * @param cbReg The register size.
4465 */
4466IEM_CIMPL_DEF_2(iemCImpl_in, uint16_t, u16Port, uint8_t, cbReg)
4467{
4468 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4469
4470 /*
4471 * CPL check
4472 */
4473 VBOXSTRICTRC rcStrict = iemHlpCheckPortIOPermission(pIemCpu, pCtx, u16Port, cbReg);
4474 if (rcStrict != VINF_SUCCESS)
4475 return rcStrict;
4476
4477 /*
4478 * Perform the I/O.
4479 */
4480 uint32_t u32Value;
4481 if (!IEM_VERIFICATION_ENABLED(pIemCpu))
4482 rcStrict = IOMIOPortRead(IEMCPU_TO_VM(pIemCpu), IEMCPU_TO_VMCPU(pIemCpu), u16Port, &u32Value, cbReg);
4483 else
4484 rcStrict = iemVerifyFakeIOPortRead(pIemCpu, u16Port, &u32Value, cbReg);
4485 if (IOM_SUCCESS(rcStrict))
4486 {
4487 switch (cbReg)
4488 {
4489 case 1: pCtx->al = (uint8_t)u32Value; break;
4490 case 2: pCtx->ax = (uint16_t)u32Value; break;
4491 case 4: pCtx->rax = u32Value; break;
4492 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
4493 }
4494 iemRegAddToRip(pIemCpu, cbInstr);
4495 pIemCpu->cPotentialExits++;
4496 if (rcStrict != VINF_SUCCESS)
4497 rcStrict = iemSetPassUpStatus(pIemCpu, rcStrict);
4498 }
4499
4500 return rcStrict;
4501}
4502
4503
4504/**
4505 * Implements 'IN eAX, DX'.
4506 *
4507 * @param cbReg The register size.
4508 */
4509IEM_CIMPL_DEF_1(iemCImpl_in_eAX_DX, uint8_t, cbReg)
4510{
4511 return IEM_CIMPL_CALL_2(iemCImpl_in, pIemCpu->CTX_SUFF(pCtx)->dx, cbReg);
4512}
4513
4514
4515/**
4516 * Implements 'OUT port, eAX'.
4517 *
4518 * @param u16Port The destination port.
4519 * @param cbReg The register size.
4520 */
4521IEM_CIMPL_DEF_2(iemCImpl_out, uint16_t, u16Port, uint8_t, cbReg)
4522{
4523 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4524
4525 /*
4526 * CPL check
4527 */
4528 VBOXSTRICTRC rcStrict = iemHlpCheckPortIOPermission(pIemCpu, pCtx, u16Port, cbReg);
4529 if (rcStrict != VINF_SUCCESS)
4530 return rcStrict;
4531
4532 /*
4533 * Perform the I/O.
4534 */
4535 uint32_t u32Value;
4536 switch (cbReg)
4537 {
4538 case 1: u32Value = pCtx->al; break;
4539 case 2: u32Value = pCtx->ax; break;
4540 case 4: u32Value = pCtx->eax; break;
4541 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
4542 }
4543 if (!IEM_VERIFICATION_ENABLED(pIemCpu))
4544 rcStrict = IOMIOPortWrite(IEMCPU_TO_VM(pIemCpu), IEMCPU_TO_VMCPU(pIemCpu), u16Port, u32Value, cbReg);
4545 else
4546 rcStrict = iemVerifyFakeIOPortWrite(pIemCpu, u16Port, u32Value, cbReg);
4547 if (IOM_SUCCESS(rcStrict))
4548 {
4549 iemRegAddToRip(pIemCpu, cbInstr);
4550 pIemCpu->cPotentialExits++;
4551 if (rcStrict != VINF_SUCCESS)
4552 rcStrict = iemSetPassUpStatus(pIemCpu, rcStrict);
4553 }
4554 return rcStrict;
4555}
4556
4557
4558/**
4559 * Implements 'OUT DX, eAX'.
4560 *
4561 * @param cbReg The register size.
4562 */
4563IEM_CIMPL_DEF_1(iemCImpl_out_DX_eAX, uint8_t, cbReg)
4564{
4565 return IEM_CIMPL_CALL_2(iemCImpl_out, pIemCpu->CTX_SUFF(pCtx)->dx, cbReg);
4566}
4567
4568
4569/**
4570 * Implements 'CLI'.
4571 */
4572IEM_CIMPL_DEF_0(iemCImpl_cli)
4573{
4574 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4575 PVMCPU pVCpu = IEMCPU_TO_VMCPU(pIemCpu);
4576 uint32_t fEfl = IEMMISC_GET_EFL(pIemCpu, pCtx);
4577 uint32_t const fEflOld = fEfl;
4578 if (pCtx->cr0 & X86_CR0_PE)
4579 {
4580 uint8_t const uIopl = X86_EFL_GET_IOPL(fEfl);
4581 if (!(fEfl & X86_EFL_VM))
4582 {
4583 if (pIemCpu->uCpl <= uIopl)
4584 fEfl &= ~X86_EFL_IF;
4585 else if ( pIemCpu->uCpl == 3
4586 && (pCtx->cr4 & X86_CR4_PVI) )
4587 fEfl &= ~X86_EFL_VIF;
4588 else
4589 return iemRaiseGeneralProtectionFault0(pIemCpu);
4590 }
4591 /* V8086 */
4592 else if (uIopl == 3)
4593 fEfl &= ~X86_EFL_IF;
4594 else if ( uIopl < 3
4595 && (pCtx->cr4 & X86_CR4_VME) )
4596 fEfl &= ~X86_EFL_VIF;
4597 else
4598 return iemRaiseGeneralProtectionFault0(pIemCpu);
4599 }
4600 /* real mode */
4601 else
4602 fEfl &= ~X86_EFL_IF;
4603
4604 /* Commit. */
4605 IEMMISC_SET_EFL(pIemCpu, pCtx, fEfl);
4606 iemRegAddToRip(pIemCpu, cbInstr);
4607 Log2(("CLI: %#x -> %#x\n", fEflOld, fEfl)); NOREF(fEflOld);
4608 return VINF_SUCCESS;
4609}
4610
4611
4612/**
4613 * Implements 'STI'.
4614 */
4615IEM_CIMPL_DEF_0(iemCImpl_sti)
4616{
4617 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4618 PVMCPU pVCpu = IEMCPU_TO_VMCPU(pIemCpu);
4619 uint32_t fEfl = IEMMISC_GET_EFL(pIemCpu, pCtx);
4620 uint32_t const fEflOld = fEfl;
4621
4622 if (pCtx->cr0 & X86_CR0_PE)
4623 {
4624 uint8_t const uIopl = X86_EFL_GET_IOPL(fEfl);
4625 if (!(fEfl & X86_EFL_VM))
4626 {
4627 if (pIemCpu->uCpl <= uIopl)
4628 fEfl |= X86_EFL_IF;
4629 else if ( pIemCpu->uCpl == 3
4630 && (pCtx->cr4 & X86_CR4_PVI)
4631 && !(fEfl & X86_EFL_VIP) )
4632 fEfl |= X86_EFL_VIF;
4633 else
4634 return iemRaiseGeneralProtectionFault0(pIemCpu);
4635 }
4636 /* V8086 */
4637 else if (uIopl == 3)
4638 fEfl |= X86_EFL_IF;
4639 else if ( uIopl < 3
4640 && (pCtx->cr4 & X86_CR4_VME)
4641 && !(fEfl & X86_EFL_VIP) )
4642 fEfl |= X86_EFL_VIF;
4643 else
4644 return iemRaiseGeneralProtectionFault0(pIemCpu);
4645 }
4646 /* real mode */
4647 else
4648 fEfl |= X86_EFL_IF;
4649
4650 /* Commit. */
4651 IEMMISC_SET_EFL(pIemCpu, pCtx, fEfl);
4652 iemRegAddToRip(pIemCpu, cbInstr);
4653 if ((!(fEflOld & X86_EFL_IF) && (fEfl & X86_EFL_IF)) || IEM_VERIFICATION_ENABLED(pIemCpu))
4654 EMSetInhibitInterruptsPC(IEMCPU_TO_VMCPU(pIemCpu), pCtx->rip);
4655 Log2(("STI: %#x -> %#x\n", fEflOld, fEfl));
4656 return VINF_SUCCESS;
4657}
4658
4659
4660/**
4661 * Implements 'HLT'.
4662 */
4663IEM_CIMPL_DEF_0(iemCImpl_hlt)
4664{
4665 if (pIemCpu->uCpl != 0)
4666 return iemRaiseGeneralProtectionFault0(pIemCpu);
4667 iemRegAddToRip(pIemCpu, cbInstr);
4668 return VINF_EM_HALT;
4669}
4670
4671
4672/**
4673 * Implements 'MONITOR'.
4674 */
4675IEM_CIMPL_DEF_1(iemCImpl_monitor, uint8_t, iEffSeg)
4676{
4677 /*
4678 * Permission checks.
4679 */
4680 if (pIemCpu->uCpl != 0)
4681 {
4682 Log2(("monitor: CPL != 0\n"));
4683 return iemRaiseUndefinedOpcode(pIemCpu); /** @todo MSR[0xC0010015].MonMwaitUserEn if we care. */
4684 }
4685 if (!IEM_IS_INTEL_CPUID_FEATURE_PRESENT_ECX(X86_CPUID_FEATURE_ECX_MONITOR))
4686 {
4687 Log2(("monitor: Not in CPUID\n"));
4688 return iemRaiseUndefinedOpcode(pIemCpu);
4689 }
4690
4691 /*
4692 * Gather the operands and validate them.
4693 */
4694 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4695 RTGCPTR GCPtrMem = pIemCpu->enmCpuMode == IEMMODE_64BIT ? pCtx->rax : pCtx->eax;
4696 uint32_t uEcx = pCtx->ecx;
4697 uint32_t uEdx = pCtx->edx;
4698/** @todo Test whether EAX or ECX is processed first, i.e. do we get \#PF or
4699 * \#GP first. */
4700 if (uEcx != 0)
4701 {
4702 Log2(("monitor rax=%RX64, ecx=%RX32, edx=%RX32; ECX != 0 -> #GP(0)\n", GCPtrMem, uEcx, uEdx));
4703 return iemRaiseGeneralProtectionFault0(pIemCpu);
4704 }
4705
4706 VBOXSTRICTRC rcStrict = iemMemApplySegment(pIemCpu, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, iEffSeg, 1, &GCPtrMem);
4707 if (rcStrict != VINF_SUCCESS)
4708 return rcStrict;
4709
4710 RTGCPHYS GCPhysMem;
4711 rcStrict = iemMemPageTranslateAndCheckAccess(pIemCpu, GCPtrMem, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, &GCPhysMem);
4712 if (rcStrict != VINF_SUCCESS)
4713 return rcStrict;
4714
4715 /*
4716 * Call EM to prepare the monitor/wait.
4717 */
4718 rcStrict = EMMonitorWaitPrepare(IEMCPU_TO_VMCPU(pIemCpu), pCtx->rax, pCtx->rcx, pCtx->rdx, GCPhysMem);
4719 Assert(rcStrict == VINF_SUCCESS);
4720
4721 iemRegAddToRip(pIemCpu, cbInstr);
4722 return rcStrict;
4723}
4724
4725
4726/**
4727 * Implements 'MWAIT'.
4728 */
4729IEM_CIMPL_DEF_0(iemCImpl_mwait)
4730{
4731 /*
4732 * Permission checks.
4733 */
4734 if (pIemCpu->uCpl != 0)
4735 {
4736 Log2(("mwait: CPL != 0\n"));
4737 /** @todo MSR[0xC0010015].MonMwaitUserEn if we care. (Remember to check
4738 * EFLAGS.VM then.) */
4739 return iemRaiseUndefinedOpcode(pIemCpu);
4740 }
4741 if (!IEM_IS_INTEL_CPUID_FEATURE_PRESENT_ECX(X86_CPUID_FEATURE_ECX_MONITOR))
4742 {
4743 Log2(("mwait: Not in CPUID\n"));
4744 return iemRaiseUndefinedOpcode(pIemCpu);
4745 }
4746
4747 /*
4748 * Gather the operands and validate them.
4749 */
4750 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4751 uint32_t uEax = pCtx->eax;
4752 uint32_t uEcx = pCtx->ecx;
4753 if (uEcx != 0)
4754 {
4755 /* Only supported extension is break on IRQ when IF=0. */
4756 if (uEcx > 1)
4757 {
4758 Log2(("mwait eax=%RX32, ecx=%RX32; ECX > 1 -> #GP(0)\n", uEax, uEcx));
4759 return iemRaiseGeneralProtectionFault0(pIemCpu);
4760 }
4761 uint32_t fMWaitFeatures = 0;
4762 uint32_t uIgnore = 0;
4763 CPUMGetGuestCpuId(IEMCPU_TO_VMCPU(pIemCpu), 5, &uIgnore, &uIgnore, &fMWaitFeatures, &uIgnore);
4764 if ( (fMWaitFeatures & (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
4765 != (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
4766 {
4767 Log2(("mwait eax=%RX32, ecx=%RX32; break-on-IRQ-IF=0 extension not enabled -> #GP(0)\n", uEax, uEcx));
4768 return iemRaiseGeneralProtectionFault0(pIemCpu);
4769 }
4770 }
4771
4772 /*
4773 * Call EM to prepare the monitor/wait.
4774 */
4775 VBOXSTRICTRC rcStrict = EMMonitorWaitPerform(IEMCPU_TO_VMCPU(pIemCpu), uEax, uEcx);
4776
4777 iemRegAddToRip(pIemCpu, cbInstr);
4778 return rcStrict;
4779}
4780
4781
4782/**
4783 * Implements 'SWAPGS'.
4784 */
4785IEM_CIMPL_DEF_0(iemCImpl_swapgs)
4786{
4787 Assert(pIemCpu->enmCpuMode == IEMMODE_64BIT); /* Caller checks this. */
4788
4789 /*
4790 * Permission checks.
4791 */
4792 if (pIemCpu->uCpl != 0)
4793 {
4794 Log2(("swapgs: CPL != 0\n"));
4795 return iemRaiseUndefinedOpcode(pIemCpu);
4796 }
4797
4798 /*
4799 * Do the job.
4800 */
4801 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4802 uint64_t uOtherGsBase = pCtx->msrKERNELGSBASE;
4803 pCtx->msrKERNELGSBASE = pCtx->gs.u64Base;
4804 pCtx->gs.u64Base = uOtherGsBase;
4805
4806 iemRegAddToRip(pIemCpu, cbInstr);
4807 return VINF_SUCCESS;
4808}
4809
4810
4811/**
4812 * Implements 'CPUID'.
4813 */
4814IEM_CIMPL_DEF_0(iemCImpl_cpuid)
4815{
4816 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4817
4818 CPUMGetGuestCpuId(IEMCPU_TO_VMCPU(pIemCpu), pCtx->eax, &pCtx->eax, &pCtx->ebx, &pCtx->ecx, &pCtx->edx);
4819 pCtx->rax &= UINT32_C(0xffffffff);
4820 pCtx->rbx &= UINT32_C(0xffffffff);
4821 pCtx->rcx &= UINT32_C(0xffffffff);
4822 pCtx->rdx &= UINT32_C(0xffffffff);
4823
4824 iemRegAddToRip(pIemCpu, cbInstr);
4825 return VINF_SUCCESS;
4826}
4827
4828
4829/**
4830 * Implements 'AAD'.
4831 *
4832 * @param enmEffOpSize The effective operand size.
4833 */
4834IEM_CIMPL_DEF_1(iemCImpl_aad, uint8_t, bImm)
4835{
4836 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4837
4838 uint16_t const ax = pCtx->ax;
4839 uint8_t const al = (uint8_t)ax + (uint8_t)(ax >> 8) * bImm;
4840 pCtx->ax = al;
4841 iemHlpUpdateArithEFlagsU8(pIemCpu, al,
4842 X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF,
4843 X86_EFL_OF | X86_EFL_AF | X86_EFL_CF);
4844
4845 iemRegAddToRip(pIemCpu, cbInstr);
4846 return VINF_SUCCESS;
4847}
4848
4849
4850/**
4851 * Implements 'AAM'.
4852 *
4853 * @param bImm The immediate operand. Cannot be 0.
4854 */
4855IEM_CIMPL_DEF_1(iemCImpl_aam, uint8_t, bImm)
4856{
4857 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4858 Assert(bImm != 0); /* #DE on 0 is handled in the decoder. */
4859
4860 uint16_t const ax = pCtx->ax;
4861 uint8_t const al = (uint8_t)ax % bImm;
4862 uint8_t const ah = (uint8_t)ax / bImm;
4863 pCtx->ax = (ah << 8) + al;
4864 iemHlpUpdateArithEFlagsU8(pIemCpu, al,
4865 X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF,
4866 X86_EFL_OF | X86_EFL_AF | X86_EFL_CF);
4867
4868 iemRegAddToRip(pIemCpu, cbInstr);
4869 return VINF_SUCCESS;
4870}
4871
4872
4873
4874
4875/*
4876 * Instantiate the various string operation combinations.
4877 */
4878#define OP_SIZE 8
4879#define ADDR_SIZE 16
4880#include "IEMAllCImplStrInstr.cpp.h"
4881#define OP_SIZE 8
4882#define ADDR_SIZE 32
4883#include "IEMAllCImplStrInstr.cpp.h"
4884#define OP_SIZE 8
4885#define ADDR_SIZE 64
4886#include "IEMAllCImplStrInstr.cpp.h"
4887
4888#define OP_SIZE 16
4889#define ADDR_SIZE 16
4890#include "IEMAllCImplStrInstr.cpp.h"
4891#define OP_SIZE 16
4892#define ADDR_SIZE 32
4893#include "IEMAllCImplStrInstr.cpp.h"
4894#define OP_SIZE 16
4895#define ADDR_SIZE 64
4896#include "IEMAllCImplStrInstr.cpp.h"
4897
4898#define OP_SIZE 32
4899#define ADDR_SIZE 16
4900#include "IEMAllCImplStrInstr.cpp.h"
4901#define OP_SIZE 32
4902#define ADDR_SIZE 32
4903#include "IEMAllCImplStrInstr.cpp.h"
4904#define OP_SIZE 32
4905#define ADDR_SIZE 64
4906#include "IEMAllCImplStrInstr.cpp.h"
4907
4908#define OP_SIZE 64
4909#define ADDR_SIZE 32
4910#include "IEMAllCImplStrInstr.cpp.h"
4911#define OP_SIZE 64
4912#define ADDR_SIZE 64
4913#include "IEMAllCImplStrInstr.cpp.h"
4914
4915
4916/**
4917 * Implements 'FINIT' and 'FNINIT'.
4918 *
4919 * @param fCheckXcpts Whether to check for umasked pending exceptions or
4920 * not.
4921 */
4922IEM_CIMPL_DEF_1(iemCImpl_finit, bool, fCheckXcpts)
4923{
4924 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4925
4926 if (pCtx->cr0 & (X86_CR0_EM | X86_CR0_TS))
4927 return iemRaiseDeviceNotAvailable(pIemCpu);
4928
4929 NOREF(fCheckXcpts); /** @todo trigger pending exceptions:
4930 if (fCheckXcpts && TODO )
4931 return iemRaiseMathFault(pIemCpu);
4932 */
4933
4934 if (iemFRegIsFxSaveFormat(pIemCpu))
4935 {
4936 pCtx->fpu.FCW = 0x37f;
4937 pCtx->fpu.FSW = 0;
4938 pCtx->fpu.FTW = 0x00; /* 0 - empty. */
4939 pCtx->fpu.FPUDP = 0;
4940 pCtx->fpu.DS = 0; //??
4941 pCtx->fpu.Rsrvd2= 0;
4942 pCtx->fpu.FPUIP = 0;
4943 pCtx->fpu.CS = 0; //??
4944 pCtx->fpu.Rsrvd1= 0;
4945 pCtx->fpu.FOP = 0;
4946 }
4947 else
4948 {
4949 PX86FPUSTATE pFpu = (PX86FPUSTATE)&pCtx->fpu;
4950 pFpu->FCW = 0x37f;
4951 pFpu->FSW = 0;
4952 pFpu->FTW = 0xffff; /* 11 - empty */
4953 pFpu->FPUOO = 0; //??
4954 pFpu->FPUOS = 0; //??
4955 pFpu->FPUIP = 0;
4956 pFpu->CS = 0; //??
4957 pFpu->FOP = 0;
4958 }
4959
4960 iemHlpUsedFpu(pIemCpu);
4961 iemRegAddToRip(pIemCpu, cbInstr);
4962 return VINF_SUCCESS;
4963}
4964
4965
4966/**
4967 * Implements 'FXSAVE'.
4968 *
4969 * @param iEffSeg The effective segment.
4970 * @param GCPtrEff The address of the image.
4971 * @param enmEffOpSize The operand size (only REX.W really matters).
4972 */
4973IEM_CIMPL_DEF_3(iemCImpl_fxsave, uint8_t, iEffSeg, RTGCPTR, GCPtrEff, IEMMODE, enmEffOpSize)
4974{
4975 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
4976
4977 /*
4978 * Raise exceptions.
4979 */
4980 if (pCtx->cr0 & X86_CR0_EM)
4981 return iemRaiseUndefinedOpcode(pIemCpu);
4982 if (pCtx->cr0 & (X86_CR0_TS | X86_CR0_EM))
4983 return iemRaiseDeviceNotAvailable(pIemCpu);
4984 if (GCPtrEff & 15)
4985 {
4986 /** @todo CPU/VM detection possible! \#AC might not be signal for
4987 * all/any misalignment sizes, intel says its an implementation detail. */
4988 if ( (pCtx->cr0 & X86_CR0_AM)
4989 && pCtx->eflags.Bits.u1AC
4990 && pIemCpu->uCpl == 3)
4991 return iemRaiseAlignmentCheckException(pIemCpu);
4992 return iemRaiseGeneralProtectionFault0(pIemCpu);
4993 }
4994 AssertReturn(iemFRegIsFxSaveFormat(pIemCpu), VERR_IEM_IPE_2);
4995
4996 /*
4997 * Access the memory.
4998 */
4999 void *pvMem512;
5000 VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, &pvMem512, 512, iEffSeg, GCPtrEff, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE);
5001 if (rcStrict != VINF_SUCCESS)
5002 return rcStrict;
5003 PX86FXSTATE pDst = (PX86FXSTATE)pvMem512;
5004
5005 /*
5006 * Store the registers.
5007 */
5008 /** @todo CPU/VM detection possible! If CR4.OSFXSR=0 MXCSR it's
5009 * implementation specific whether MXCSR and XMM0-XMM7 are saved. */
5010
5011 /* common for all formats */
5012 pDst->FCW = pCtx->fpu.FCW;
5013 pDst->FSW = pCtx->fpu.FSW;
5014 pDst->FTW = pCtx->fpu.FTW & UINT16_C(0xff);
5015 pDst->FOP = pCtx->fpu.FOP;
5016 pDst->MXCSR = pCtx->fpu.MXCSR;
5017 pDst->MXCSR_MASK = pCtx->fpu.MXCSR_MASK;
5018 for (uint32_t i = 0; i < RT_ELEMENTS(pDst->aRegs); i++)
5019 {
5020 /** @todo Testcase: What actually happens to the 6 reserved bytes? I'm clearing
5021 * them for now... */
5022 pDst->aRegs[i].au32[0] = pCtx->fpu.aRegs[i].au32[0];
5023 pDst->aRegs[i].au32[1] = pCtx->fpu.aRegs[i].au32[1];
5024 pDst->aRegs[i].au32[2] = pCtx->fpu.aRegs[i].au32[2] & UINT32_C(0xffff);
5025 pDst->aRegs[i].au32[3] = 0;
5026 }
5027
5028 /* FPU IP, CS, DP and DS. */
5029 /** @todo FPU IP, CS, DP and DS cannot be implemented correctly without extra
5030 * state information. :-/
5031 * Storing zeros now to prevent any potential leakage of host info. */
5032 pDst->FPUIP = 0;
5033 pDst->CS = 0;
5034 pDst->Rsrvd1 = 0;
5035 pDst->FPUDP = 0;
5036 pDst->DS = 0;
5037 pDst->Rsrvd2 = 0;
5038
5039 /* XMM registers. */
5040 if ( !(pCtx->msrEFER & MSR_K6_EFER_FFXSR)
5041 || pIemCpu->enmCpuMode != IEMMODE_64BIT
5042 || pIemCpu->uCpl != 0)
5043 {
5044 uint32_t cXmmRegs = enmEffOpSize == IEMMODE_64BIT ? 16 : 8;
5045 for (uint32_t i = 0; i < cXmmRegs; i++)
5046 pDst->aXMM[i] = pCtx->fpu.aXMM[i];
5047 /** @todo Testcase: What happens to the reserved XMM registers? Untouched,
5048 * right? */
5049 }
5050
5051 /*
5052 * Commit the memory.
5053 */
5054 rcStrict = iemMemCommitAndUnmap(pIemCpu, pvMem512, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE);
5055 if (rcStrict != VINF_SUCCESS)
5056 return rcStrict;
5057
5058 iemRegAddToRip(pIemCpu, cbInstr);
5059 return VINF_SUCCESS;
5060}
5061
5062
5063/**
5064 * Implements 'FXRSTOR'.
5065 *
5066 * @param GCPtrEff The address of the image.
5067 * @param enmEffOpSize The operand size (only REX.W really matters).
5068 */
5069IEM_CIMPL_DEF_3(iemCImpl_fxrstor, uint8_t, iEffSeg, RTGCPTR, GCPtrEff, IEMMODE, enmEffOpSize)
5070{
5071 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5072
5073 /*
5074 * Raise exceptions.
5075 */
5076 if (pCtx->cr0 & X86_CR0_EM)
5077 return iemRaiseUndefinedOpcode(pIemCpu);
5078 if (pCtx->cr0 & (X86_CR0_TS | X86_CR0_EM))
5079 return iemRaiseDeviceNotAvailable(pIemCpu);
5080 if (GCPtrEff & 15)
5081 {
5082 /** @todo CPU/VM detection possible! \#AC might not be signal for
5083 * all/any misalignment sizes, intel says its an implementation detail. */
5084 if ( (pCtx->cr0 & X86_CR0_AM)
5085 && pCtx->eflags.Bits.u1AC
5086 && pIemCpu->uCpl == 3)
5087 return iemRaiseAlignmentCheckException(pIemCpu);
5088 return iemRaiseGeneralProtectionFault0(pIemCpu);
5089 }
5090 AssertReturn(iemFRegIsFxSaveFormat(pIemCpu), VERR_IEM_IPE_2);
5091
5092 /*
5093 * Access the memory.
5094 */
5095 void *pvMem512;
5096 VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, &pvMem512, 512, iEffSeg, GCPtrEff, IEM_ACCESS_DATA_R);
5097 if (rcStrict != VINF_SUCCESS)
5098 return rcStrict;
5099 PCX86FXSTATE pSrc = (PCX86FXSTATE)pvMem512;
5100
5101 /*
5102 * Check the state for stuff which will GP(0).
5103 */
5104 uint32_t const fMXCSR = pSrc->MXCSR;
5105 uint32_t const fMXCSR_MASK = pCtx->fpu.MXCSR_MASK ? pCtx->fpu.MXCSR_MASK : UINT32_C(0xffbf);
5106 if (fMXCSR & ~fMXCSR_MASK)
5107 {
5108 Log(("fxrstor: MXCSR=%#x (MXCSR_MASK=%#x) -> #GP(0)\n", fMXCSR, fMXCSR_MASK));
5109 return iemRaiseGeneralProtectionFault0(pIemCpu);
5110 }
5111
5112 /*
5113 * Load the registers.
5114 */
5115 /** @todo CPU/VM detection possible! If CR4.OSFXSR=0 MXCSR it's
5116 * implementation specific whether MXCSR and XMM0-XMM7 are restored. */
5117
5118 /* common for all formats */
5119 pCtx->fpu.FCW = pSrc->FCW;
5120 pCtx->fpu.FSW = pSrc->FSW;
5121 pCtx->fpu.FTW = pSrc->FTW & UINT16_C(0xff);
5122 pCtx->fpu.FOP = pSrc->FOP;
5123 pCtx->fpu.MXCSR = fMXCSR;
5124 /* (MXCSR_MASK is read-only) */
5125 for (uint32_t i = 0; i < RT_ELEMENTS(pSrc->aRegs); i++)
5126 {
5127 pCtx->fpu.aRegs[i].au32[0] = pSrc->aRegs[i].au32[0];
5128 pCtx->fpu.aRegs[i].au32[1] = pSrc->aRegs[i].au32[1];
5129 pCtx->fpu.aRegs[i].au32[2] = pSrc->aRegs[i].au32[2] & UINT32_C(0xffff);
5130 pCtx->fpu.aRegs[i].au32[3] = 0;
5131 }
5132
5133 /* FPU IP, CS, DP and DS. */
5134 if (pIemCpu->enmCpuMode == IEMMODE_64BIT)
5135 {
5136 pCtx->fpu.FPUIP = pSrc->FPUIP;
5137 pCtx->fpu.CS = pSrc->CS;
5138 pCtx->fpu.Rsrvd1 = pSrc->Rsrvd1;
5139 pCtx->fpu.FPUDP = pSrc->FPUDP;
5140 pCtx->fpu.DS = pSrc->DS;
5141 pCtx->fpu.Rsrvd2 = pSrc->Rsrvd2;
5142 }
5143 else
5144 {
5145 pCtx->fpu.FPUIP = pSrc->FPUIP;
5146 pCtx->fpu.CS = pSrc->CS;
5147 pCtx->fpu.Rsrvd1 = 0;
5148 pCtx->fpu.FPUDP = pSrc->FPUDP;
5149 pCtx->fpu.DS = pSrc->DS;
5150 pCtx->fpu.Rsrvd2 = 0;
5151 }
5152
5153 /* XMM registers. */
5154 if ( !(pCtx->msrEFER & MSR_K6_EFER_FFXSR)
5155 || pIemCpu->enmCpuMode != IEMMODE_64BIT
5156 || pIemCpu->uCpl != 0)
5157 {
5158 uint32_t cXmmRegs = enmEffOpSize == IEMMODE_64BIT ? 16 : 8;
5159 for (uint32_t i = 0; i < cXmmRegs; i++)
5160 pCtx->fpu.aXMM[i] = pSrc->aXMM[i];
5161 }
5162
5163 /*
5164 * Commit the memory.
5165 */
5166 rcStrict = iemMemCommitAndUnmap(pIemCpu, pvMem512, IEM_ACCESS_DATA_R);
5167 if (rcStrict != VINF_SUCCESS)
5168 return rcStrict;
5169
5170 iemHlpUsedFpu(pIemCpu);
5171 iemRegAddToRip(pIemCpu, cbInstr);
5172 return VINF_SUCCESS;
5173}
5174
5175
5176/**
5177 * Commmon routine for fnstenv and fnsave.
5178 *
5179 * @param uPtr Where to store the state.
5180 * @param pCtx The CPU context.
5181 */
5182static void iemCImplCommonFpuStoreEnv(PIEMCPU pIemCpu, IEMMODE enmEffOpSize, RTPTRUNION uPtr, PCCPUMCTX pCtx)
5183{
5184 if (enmEffOpSize == IEMMODE_16BIT)
5185 {
5186 uPtr.pu16[0] = pCtx->fpu.FCW;
5187 uPtr.pu16[1] = pCtx->fpu.FSW;
5188 uPtr.pu16[2] = iemFpuCalcFullFtw(pCtx);
5189 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
5190 {
5191 /** @todo Testcase: How does this work when the FPUIP/CS was saved in
5192 * protected mode or long mode and we save it in real mode? And vice
5193 * versa? And with 32-bit operand size? I think CPU is storing the
5194 * effective address ((CS << 4) + IP) in the offset register and not
5195 * doing any address calculations here. */
5196 uPtr.pu16[3] = (uint16_t)pCtx->fpu.FPUIP;
5197 uPtr.pu16[4] = ((pCtx->fpu.FPUIP >> 4) & UINT16_C(0xf000)) | pCtx->fpu.FOP;
5198 uPtr.pu16[5] = (uint16_t)pCtx->fpu.FPUDP;
5199 uPtr.pu16[6] = (pCtx->fpu.FPUDP >> 4) & UINT16_C(0xf000);
5200 }
5201 else
5202 {
5203 uPtr.pu16[3] = pCtx->fpu.FPUIP;
5204 uPtr.pu16[4] = pCtx->fpu.CS;
5205 uPtr.pu16[5] = pCtx->fpu.FPUDP;
5206 uPtr.pu16[6] = pCtx->fpu.DS;
5207 }
5208 }
5209 else
5210 {
5211 /** @todo Testcase: what is stored in the "gray" areas? (figure 8-9 and 8-10) */
5212 uPtr.pu16[0*2] = pCtx->fpu.FCW;
5213 uPtr.pu16[1*2] = pCtx->fpu.FSW;
5214 uPtr.pu16[2*2] = iemFpuCalcFullFtw(pCtx);
5215 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
5216 {
5217 uPtr.pu16[3*2] = (uint16_t)pCtx->fpu.FPUIP;
5218 uPtr.pu32[4] = ((pCtx->fpu.FPUIP & UINT32_C(0xffff0000)) >> 4) | pCtx->fpu.FOP;
5219 uPtr.pu16[5*2] = (uint16_t)pCtx->fpu.FPUDP;
5220 uPtr.pu32[6] = (pCtx->fpu.FPUDP & UINT32_C(0xffff0000)) >> 4;
5221 }
5222 else
5223 {
5224 uPtr.pu32[3] = pCtx->fpu.FPUIP;
5225 uPtr.pu16[4*2] = pCtx->fpu.CS;
5226 uPtr.pu16[4*2+1]= pCtx->fpu.FOP;
5227 uPtr.pu32[5] = pCtx->fpu.FPUDP;
5228 uPtr.pu16[6*2] = pCtx->fpu.DS;
5229 }
5230 }
5231}
5232
5233
5234/**
5235 * Commmon routine for fldenv and frstor
5236 *
5237 * @param uPtr Where to store the state.
5238 * @param pCtx The CPU context.
5239 */
5240static void iemCImplCommonFpuRestoreEnv(PIEMCPU pIemCpu, IEMMODE enmEffOpSize, RTCPTRUNION uPtr, PCPUMCTX pCtx)
5241{
5242 if (enmEffOpSize == IEMMODE_16BIT)
5243 {
5244 pCtx->fpu.FCW = uPtr.pu16[0];
5245 pCtx->fpu.FSW = uPtr.pu16[1];
5246 pCtx->fpu.FTW = uPtr.pu16[2];
5247 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
5248 {
5249 pCtx->fpu.FPUIP = uPtr.pu16[3] | ((uint32_t)(uPtr.pu16[4] & UINT16_C(0xf000)) << 4);
5250 pCtx->fpu.FPUDP = uPtr.pu16[5] | ((uint32_t)(uPtr.pu16[6] & UINT16_C(0xf000)) << 4);
5251 pCtx->fpu.FOP = uPtr.pu16[4] & UINT16_C(0x07ff);
5252 pCtx->fpu.CS = 0;
5253 pCtx->fpu.Rsrvd1= 0;
5254 pCtx->fpu.DS = 0;
5255 pCtx->fpu.Rsrvd2= 0;
5256 }
5257 else
5258 {
5259 pCtx->fpu.FPUIP = uPtr.pu16[3];
5260 pCtx->fpu.CS = uPtr.pu16[4];
5261 pCtx->fpu.Rsrvd1= 0;
5262 pCtx->fpu.FPUDP = uPtr.pu16[5];
5263 pCtx->fpu.DS = uPtr.pu16[6];
5264 pCtx->fpu.Rsrvd2= 0;
5265 /** @todo Testcase: Is FOP cleared when doing 16-bit protected mode fldenv? */
5266 }
5267 }
5268 else
5269 {
5270 pCtx->fpu.FCW = uPtr.pu16[0*2];
5271 pCtx->fpu.FSW = uPtr.pu16[1*2];
5272 pCtx->fpu.FTW = uPtr.pu16[2*2];
5273 if (IEM_IS_REAL_OR_V86_MODE(pIemCpu))
5274 {
5275 pCtx->fpu.FPUIP = uPtr.pu16[3*2] | ((uPtr.pu32[4] & UINT32_C(0x0ffff000)) << 4);
5276 pCtx->fpu.FOP = uPtr.pu32[4] & UINT16_C(0x07ff);
5277 pCtx->fpu.FPUDP = uPtr.pu16[5*2] | ((uPtr.pu32[6] & UINT32_C(0x0ffff000)) << 4);
5278 pCtx->fpu.CS = 0;
5279 pCtx->fpu.Rsrvd1= 0;
5280 pCtx->fpu.DS = 0;
5281 pCtx->fpu.Rsrvd2= 0;
5282 }
5283 else
5284 {
5285 pCtx->fpu.FPUIP = uPtr.pu32[3];
5286 pCtx->fpu.CS = uPtr.pu16[4*2];
5287 pCtx->fpu.Rsrvd1= 0;
5288 pCtx->fpu.FOP = uPtr.pu16[4*2+1];
5289 pCtx->fpu.FPUDP = uPtr.pu32[5];
5290 pCtx->fpu.DS = uPtr.pu16[6*2];
5291 pCtx->fpu.Rsrvd2= 0;
5292 }
5293 }
5294
5295 /* Make adjustments. */
5296 pCtx->fpu.FTW = iemFpuCompressFtw(pCtx->fpu.FTW);
5297 pCtx->fpu.FCW &= ~X86_FCW_ZERO_MASK;
5298 iemFpuRecalcExceptionStatus(pCtx);
5299 /** @todo Testcase: Check if ES and/or B are automatically cleared if no
5300 * exceptions are pending after loading the saved state? */
5301}
5302
5303
5304/**
5305 * Implements 'FNSTENV'.
5306 *
5307 * @param enmEffOpSize The operand size (only REX.W really matters).
5308 * @param iEffSeg The effective segment register for @a GCPtrEff.
5309 * @param GCPtrEffDst The address of the image.
5310 */
5311IEM_CIMPL_DEF_3(iemCImpl_fnstenv, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5312{
5313 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5314 RTPTRUNION uPtr;
5315 VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, &uPtr.pv, enmEffOpSize == IEMMODE_16BIT ? 14 : 28,
5316 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE);
5317 if (rcStrict != VINF_SUCCESS)
5318 return rcStrict;
5319
5320 iemCImplCommonFpuStoreEnv(pIemCpu, enmEffOpSize, uPtr, pCtx);
5321
5322 rcStrict = iemMemCommitAndUnmap(pIemCpu, uPtr.pv, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE);
5323 if (rcStrict != VINF_SUCCESS)
5324 return rcStrict;
5325
5326 /* Note: C0, C1, C2 and C3 are documented as undefined, we leave them untouched! */
5327 iemRegAddToRip(pIemCpu, cbInstr);
5328 return VINF_SUCCESS;
5329}
5330
5331
5332/**
5333 * Implements 'FNSAVE'.
5334 *
5335 * @param GCPtrEffDst The address of the image.
5336 * @param enmEffOpSize The operand size.
5337 */
5338IEM_CIMPL_DEF_3(iemCImpl_fnsave, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5339{
5340 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5341 RTPTRUNION uPtr;
5342 VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, &uPtr.pv, enmEffOpSize == IEMMODE_16BIT ? 94 : 108,
5343 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE);
5344 if (rcStrict != VINF_SUCCESS)
5345 return rcStrict;
5346
5347 iemCImplCommonFpuStoreEnv(pIemCpu, enmEffOpSize, uPtr, pCtx);
5348 PRTFLOAT80U paRegs = (PRTFLOAT80U)(uPtr.pu8 + (enmEffOpSize == IEMMODE_16BIT ? 14 : 28));
5349 for (uint32_t i = 0; i < RT_ELEMENTS(pCtx->fpu.aRegs); i++)
5350 {
5351 paRegs[i].au32[0] = pCtx->fpu.aRegs[i].au32[0];
5352 paRegs[i].au32[1] = pCtx->fpu.aRegs[i].au32[1];
5353 paRegs[i].au16[4] = pCtx->fpu.aRegs[i].au16[4];
5354 }
5355
5356 rcStrict = iemMemCommitAndUnmap(pIemCpu, uPtr.pv, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE);
5357 if (rcStrict != VINF_SUCCESS)
5358 return rcStrict;
5359
5360 /*
5361 * Re-initialize the FPU.
5362 */
5363 pCtx->fpu.FCW = 0x37f;
5364 pCtx->fpu.FSW = 0;
5365 pCtx->fpu.FTW = 0x00; /* 0 - empty */
5366 pCtx->fpu.FPUDP = 0;
5367 pCtx->fpu.DS = 0;
5368 pCtx->fpu.Rsrvd2= 0;
5369 pCtx->fpu.FPUIP = 0;
5370 pCtx->fpu.CS = 0;
5371 pCtx->fpu.Rsrvd1= 0;
5372 pCtx->fpu.FOP = 0;
5373
5374 iemHlpUsedFpu(pIemCpu);
5375 iemRegAddToRip(pIemCpu, cbInstr);
5376 return VINF_SUCCESS;
5377}
5378
5379
5380
5381/**
5382 * Implements 'FLDENV'.
5383 *
5384 * @param enmEffOpSize The operand size (only REX.W really matters).
5385 * @param iEffSeg The effective segment register for @a GCPtrEff.
5386 * @param GCPtrEffSrc The address of the image.
5387 */
5388IEM_CIMPL_DEF_3(iemCImpl_fldenv, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
5389{
5390 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5391 RTCPTRUNION uPtr;
5392 VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, (void **)&uPtr.pv, enmEffOpSize == IEMMODE_16BIT ? 14 : 28,
5393 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R);
5394 if (rcStrict != VINF_SUCCESS)
5395 return rcStrict;
5396
5397 iemCImplCommonFpuRestoreEnv(pIemCpu, enmEffOpSize, uPtr, pCtx);
5398
5399 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uPtr.pv, IEM_ACCESS_DATA_R);
5400 if (rcStrict != VINF_SUCCESS)
5401 return rcStrict;
5402
5403 iemHlpUsedFpu(pIemCpu);
5404 iemRegAddToRip(pIemCpu, cbInstr);
5405 return VINF_SUCCESS;
5406}
5407
5408
5409/**
5410 * Implements 'FRSTOR'.
5411 *
5412 * @param GCPtrEffSrc The address of the image.
5413 * @param enmEffOpSize The operand size.
5414 */
5415IEM_CIMPL_DEF_3(iemCImpl_frstor, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
5416{
5417 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5418 RTCPTRUNION uPtr;
5419 VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, (void **)&uPtr.pv, enmEffOpSize == IEMMODE_16BIT ? 94 : 108,
5420 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R);
5421 if (rcStrict != VINF_SUCCESS)
5422 return rcStrict;
5423
5424 iemCImplCommonFpuRestoreEnv(pIemCpu, enmEffOpSize, uPtr, pCtx);
5425 PCRTFLOAT80U paRegs = (PCRTFLOAT80U)(uPtr.pu8 + (enmEffOpSize == IEMMODE_16BIT ? 14 : 28));
5426 for (uint32_t i = 0; i < RT_ELEMENTS(pCtx->fpu.aRegs); i++)
5427 {
5428 pCtx->fpu.aRegs[i].au32[0] = paRegs[i].au32[0];
5429 pCtx->fpu.aRegs[i].au32[1] = paRegs[i].au32[1];
5430 pCtx->fpu.aRegs[i].au32[2] = paRegs[i].au16[4];
5431 pCtx->fpu.aRegs[i].au32[3] = 0;
5432 }
5433
5434 rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uPtr.pv, IEM_ACCESS_DATA_R);
5435 if (rcStrict != VINF_SUCCESS)
5436 return rcStrict;
5437
5438 iemHlpUsedFpu(pIemCpu);
5439 iemRegAddToRip(pIemCpu, cbInstr);
5440 return VINF_SUCCESS;
5441}
5442
5443
5444/**
5445 * Implements 'FLDCW'.
5446 *
5447 * @param u16Fcw The new FCW.
5448 */
5449IEM_CIMPL_DEF_1(iemCImpl_fldcw, uint16_t, u16Fcw)
5450{
5451 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5452
5453 /** @todo Testcase: Check what happens when trying to load X86_FCW_PC_RSVD. */
5454 /** @todo Testcase: Try see what happens when trying to set undefined bits
5455 * (other than 6 and 7). Currently ignoring them. */
5456 /** @todo Testcase: Test that it raises and loweres the FPU exception bits
5457 * according to FSW. (This is was is currently implemented.) */
5458 pCtx->fpu.FCW = u16Fcw & ~X86_FCW_ZERO_MASK;
5459 iemFpuRecalcExceptionStatus(pCtx);
5460
5461 /* Note: C0, C1, C2 and C3 are documented as undefined, we leave them untouched! */
5462 iemHlpUsedFpu(pIemCpu);
5463 iemRegAddToRip(pIemCpu, cbInstr);
5464 return VINF_SUCCESS;
5465}
5466
5467
5468
5469/**
5470 * Implements the underflow case of fxch.
5471 *
5472 * @param iStReg The other stack register.
5473 */
5474IEM_CIMPL_DEF_1(iemCImpl_fxch_underflow, uint8_t, iStReg)
5475{
5476 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5477
5478 unsigned const iReg1 = X86_FSW_TOP_GET(pCtx->fpu.FSW);
5479 unsigned const iReg2 = (iReg1 + iStReg) & X86_FSW_TOP_SMASK;
5480 Assert(!(RT_BIT(iReg1) & pCtx->fpu.FTW) || !(RT_BIT(iReg2) & pCtx->fpu.FTW));
5481
5482 /** @todo Testcase: fxch underflow. Making assumptions that underflowed
5483 * registers are read as QNaN and then exchanged. This could be
5484 * wrong... */
5485 if (pCtx->fpu.FCW & X86_FCW_IM)
5486 {
5487 if (RT_BIT(iReg1) & pCtx->fpu.FTW)
5488 {
5489 if (RT_BIT(iReg2) & pCtx->fpu.FTW)
5490 iemFpuStoreQNan(&pCtx->fpu.aRegs[0].r80);
5491 else
5492 pCtx->fpu.aRegs[0].r80 = pCtx->fpu.aRegs[iStReg].r80;
5493 iemFpuStoreQNan(&pCtx->fpu.aRegs[iStReg].r80);
5494 }
5495 else
5496 {
5497 pCtx->fpu.aRegs[iStReg].r80 = pCtx->fpu.aRegs[0].r80;
5498 iemFpuStoreQNan(&pCtx->fpu.aRegs[0].r80);
5499 }
5500 pCtx->fpu.FSW &= ~X86_FSW_C_MASK;
5501 pCtx->fpu.FSW |= X86_FSW_C1 | X86_FSW_IE | X86_FSW_SF;
5502 }
5503 else
5504 {
5505 /* raise underflow exception, don't change anything. */
5506 pCtx->fpu.FSW &= ~(X86_FSW_TOP_MASK | X86_FSW_XCPT_MASK);
5507 pCtx->fpu.FSW |= X86_FSW_C1 | X86_FSW_IE | X86_FSW_SF | X86_FSW_ES | X86_FSW_B;
5508 }
5509
5510 iemFpuUpdateOpcodeAndIpWorker(pIemCpu, pCtx);
5511 iemHlpUsedFpu(pIemCpu);
5512 iemRegAddToRip(pIemCpu, cbInstr);
5513 return VINF_SUCCESS;
5514}
5515
5516
5517/**
5518 * Implements 'FCOMI', 'FCOMIP', 'FUCOMI', and 'FUCOMIP'.
5519 *
5520 * @param cToAdd 1 or 7.
5521 */
5522IEM_CIMPL_DEF_3(iemCImpl_fcomi_fucomi, uint8_t, iStReg, PFNIEMAIMPLFPUR80EFL, pfnAImpl, bool, fPop)
5523{
5524 PCPUMCTX pCtx = pIemCpu->CTX_SUFF(pCtx);
5525 Assert(iStReg < 8);
5526
5527 /*
5528 * Raise exceptions.
5529 */
5530 if (pCtx->cr0 & (X86_CR0_EM | X86_CR0_TS))
5531 return iemRaiseDeviceNotAvailable(pIemCpu);
5532 uint16_t u16Fsw = pCtx->fpu.FSW;
5533 if (u16Fsw & X86_FSW_ES)
5534 return iemRaiseMathFault(pIemCpu);
5535
5536 /*
5537 * Check if any of the register accesses causes #SF + #IA.
5538 */
5539 unsigned const iReg1 = X86_FSW_TOP_GET(u16Fsw);
5540 unsigned const iReg2 = (iReg1 + iStReg) & X86_FSW_TOP_SMASK;
5541 if ((pCtx->fpu.FTW & (RT_BIT(iReg1) | RT_BIT(iReg2))) == (RT_BIT(iReg1) | RT_BIT(iReg2)))
5542 {
5543 uint32_t u32Eflags = pfnAImpl(&pCtx->fpu, &u16Fsw, &pCtx->fpu.aRegs[0].r80, &pCtx->fpu.aRegs[iStReg].r80);
5544 pCtx->fpu.FSW &= ~X86_FSW_C1;
5545 pCtx->fpu.FSW |= u16Fsw & ~X86_FSW_TOP_MASK;
5546 if ( !(u16Fsw & X86_FSW_IE)
5547 || (pCtx->fpu.FCW & X86_FCW_IM) )
5548 {
5549 pCtx->eflags.u &= ~(X86_EFL_OF | X86_EFL_SF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF);
5550 pCtx->eflags.u |= pCtx->eflags.u & (X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF);
5551 }
5552 }
5553 else if (pCtx->fpu.FCW & X86_FCW_IM)
5554 {
5555 /* Masked underflow. */
5556 pCtx->fpu.FSW &= ~X86_FSW_C1;
5557 pCtx->fpu.FSW |= X86_FSW_IE | X86_FSW_SF;
5558 pCtx->eflags.u &= ~(X86_EFL_OF | X86_EFL_SF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF);
5559 pCtx->eflags.u |= X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF;
5560 }
5561 else
5562 {
5563 /* Raise underflow - don't touch EFLAGS or TOP. */
5564 pCtx->fpu.FSW &= ~X86_FSW_C1;
5565 pCtx->fpu.FSW |= X86_FSW_IE | X86_FSW_SF | X86_FSW_ES | X86_FSW_B;
5566 fPop = false;
5567 }
5568
5569 /*
5570 * Pop if necessary.
5571 */
5572 if (fPop)
5573 {
5574 pCtx->fpu.FTW &= ~RT_BIT(iReg1);
5575 pCtx->fpu.FSW &= X86_FSW_TOP_MASK;
5576 pCtx->fpu.FSW |= ((iReg1 + 7) & X86_FSW_TOP_SMASK) << X86_FSW_TOP_SHIFT;
5577 }
5578
5579 iemFpuUpdateOpcodeAndIpWorker(pIemCpu, pCtx);
5580 iemHlpUsedFpu(pIemCpu);
5581 iemRegAddToRip(pIemCpu, cbInstr);
5582 return VINF_SUCCESS;
5583}
5584
5585/** @} */
5586
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette