VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/target-x86/IEMAllCImpl-x86.cpp

Last change on this file was 108260, checked in by vboxsync, 2 months ago

VMM/IEM: Splitting up IEMInline.h. jiraref:VBP-1531

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 399.6 KB
Line 
1/* $Id: IEMAllCImpl-x86.cpp 108260 2025-02-17 15:24:14Z vboxsync $ */
2/** @file
3 * IEM - Instruction Implementation in C/C++, x86 target.
4 */
5
6/*
7 * Copyright (C) 2011-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_IEM
33#define VMCPU_INCL_CPUM_GST_CTX
34#define IEM_WITH_OPAQUE_DECODER_STATE
35#ifdef IN_RING0
36# define VBOX_VMM_TARGET_X86
37#endif
38#include <VBox/vmm/iem.h>
39#include <VBox/vmm/cpum.h>
40#include <VBox/vmm/pdmapic.h>
41#include <VBox/vmm/pgm.h>
42#include <VBox/vmm/iom.h>
43#include <VBox/vmm/em.h>
44#include <VBox/vmm/gcm.h>
45#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
46# include <VBox/vmm/hm_svm.h>
47#endif
48#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
49# include <VBox/vmm/hmvmxinline.h>
50#endif
51#ifndef VBOX_WITHOUT_CPUID_HOST_CALL
52# include <VBox/vmm/cpuidcall.h>
53#endif
54#include <VBox/vmm/tm.h>
55#include <VBox/vmm/dbgf.h>
56#include <VBox/vmm/dbgftrace.h>
57#include "IEMInternal.h"
58#include <VBox/vmm/vmcc.h>
59#include <VBox/log.h>
60#include <VBox/err.h>
61#include <VBox/param.h>
62#include <iprt/assert.h>
63#include <iprt/string.h>
64#include <iprt/x86.h>
65
66#include "IEMInline.h"
67#include "IEMInline-x86.h"
68#include "IEMInlineMem-x86.h"
69
70
71/*********************************************************************************************************************************
72* Defined Constants And Macros *
73*********************************************************************************************************************************/
74/**
75 * Flushes the prefetch buffer, light version.
76 * @todo The \#if conditions here must match the ones in iemOpcodeFlushLight().
77 */
78#ifndef IEM_WITH_CODE_TLB
79# define IEM_FLUSH_PREFETCH_LIGHT(a_pVCpu, a_cbInstr) iemOpcodeFlushLight(a_pVCpu, a_cbInstr)
80#else
81# define IEM_FLUSH_PREFETCH_LIGHT(a_pVCpu, a_cbInstr) do { } while (0)
82#endif
83
84/**
85 * Flushes the prefetch buffer, heavy version.
86 * @todo The \#if conditions here must match the ones in iemOpcodeFlushHeavy().
87 */
88#if !defined(IEM_WITH_CODE_TLB) || 1
89# define IEM_FLUSH_PREFETCH_HEAVY(a_pVCpu, a_cbInstr) iemOpcodeFlushHeavy(a_pVCpu, a_cbInstr)
90#else
91# define IEM_FLUSH_PREFETCH_HEAVY(a_pVCpu, a_cbInstr) do { } while (0)
92#endif
93
94
95/*********************************************************************************************************************************
96* Structures and Typedefs *
97*********************************************************************************************************************************/
98/**
99 * Branch types - iemCImpl_BranchTaskSegment(), iemCImpl_BranchTaskGate(),
100 * iemCImpl_BranchCallGate() and iemCImpl_BranchSysSel().
101 */
102typedef enum IEMBRANCH
103{
104 IEMBRANCH_JUMP = 1,
105 IEMBRANCH_CALL,
106 IEMBRANCH_TRAP,
107 IEMBRANCH_SOFTWARE_INT,
108 IEMBRANCH_HARDWARE_INT
109} IEMBRANCH;
110AssertCompileSize(IEMBRANCH, 4);
111
112
113
114
115/** @name Misc Helpers
116 * @{
117 */
118
119
120/**
121 * Worker function for iemHlpCheckPortIOPermission, don't call directly.
122 *
123 * @returns Strict VBox status code.
124 *
125 * @param pVCpu The cross context virtual CPU structure of the calling thread.
126 * @param u16Port The port number.
127 * @param cbOperand The operand size.
128 */
129static VBOXSTRICTRC iemHlpCheckPortIOPermissionBitmap(PVMCPUCC pVCpu, uint16_t u16Port, uint8_t cbOperand)
130{
131 /* The TSS bits we're interested in are the same on 386 and AMD64. */
132 AssertCompile(AMD64_SEL_TYPE_SYS_TSS_BUSY == X86_SEL_TYPE_SYS_386_TSS_BUSY);
133 AssertCompile(AMD64_SEL_TYPE_SYS_TSS_AVAIL == X86_SEL_TYPE_SYS_386_TSS_AVAIL);
134 AssertCompileMembersAtSameOffset(X86TSS32, offIoBitmap, X86TSS64, offIoBitmap);
135 AssertCompile(sizeof(X86TSS32) == sizeof(X86TSS64));
136
137 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_TR);
138
139 /*
140 * Check the TSS type, 16-bit TSSes doesn't have any I/O permission bitmap.
141 */
142 Assert(!pVCpu->cpum.GstCtx.tr.Attr.n.u1DescType);
143 if (RT_UNLIKELY( pVCpu->cpum.GstCtx.tr.Attr.n.u4Type != AMD64_SEL_TYPE_SYS_TSS_BUSY
144 && pVCpu->cpum.GstCtx.tr.Attr.n.u4Type != AMD64_SEL_TYPE_SYS_TSS_AVAIL))
145 {
146 Log(("iemHlpCheckPortIOPermissionBitmap: Port=%#x cb=%d - TSS type %#x (attr=%#x) has no I/O bitmap -> #GP(0)\n",
147 u16Port, cbOperand, pVCpu->cpum.GstCtx.tr.Attr.n.u4Type, pVCpu->cpum.GstCtx.tr.Attr.u));
148 return iemRaiseGeneralProtectionFault0(pVCpu);
149 }
150
151 /*
152 * Read the bitmap offset (may #PF).
153 */
154 uint16_t offBitmap;
155 VBOXSTRICTRC rcStrict = iemMemFetchSysU16(pVCpu, &offBitmap, UINT8_MAX,
156 pVCpu->cpum.GstCtx.tr.u64Base + RT_UOFFSETOF(X86TSS64, offIoBitmap));
157 if (rcStrict != VINF_SUCCESS)
158 {
159 Log(("iemHlpCheckPortIOPermissionBitmap: Error reading offIoBitmap (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
160 return rcStrict;
161 }
162
163 /*
164 * The bit range from u16Port to (u16Port + cbOperand - 1), however intel
165 * describes the CPU actually reading two bytes regardless of whether the
166 * bit range crosses a byte boundrary. Thus the + 1 in the test below.
167 */
168 uint32_t offFirstBit = (uint32_t)u16Port / 8 + offBitmap;
169 /** @todo check if real CPUs ensures that offBitmap has a minimum value of
170 * for instance sizeof(X86TSS32). */
171 if (offFirstBit + 1 > pVCpu->cpum.GstCtx.tr.u32Limit) /* the limit is inclusive */
172 {
173 Log(("iemHlpCheckPortIOPermissionBitmap: offFirstBit=%#x + 1 is beyond u32Limit=%#x -> #GP(0)\n",
174 offFirstBit, pVCpu->cpum.GstCtx.tr.u32Limit));
175 return iemRaiseGeneralProtectionFault0(pVCpu);
176 }
177
178 /*
179 * Read the necessary bits.
180 */
181 /** @todo Test the assertion in the intel manual that the CPU reads two
182 * bytes. The question is how this works wrt to \#PF and \#GP on the
183 * 2nd byte when it's not required. */
184 uint16_t bmBytes = UINT16_MAX;
185 rcStrict = iemMemFetchSysU16(pVCpu, &bmBytes, UINT8_MAX, pVCpu->cpum.GstCtx.tr.u64Base + offFirstBit);
186 if (rcStrict != VINF_SUCCESS)
187 {
188 Log(("iemHlpCheckPortIOPermissionBitmap: Error reading I/O bitmap @%#x (%Rrc)\n", offFirstBit, VBOXSTRICTRC_VAL(rcStrict)));
189 return rcStrict;
190 }
191
192 /*
193 * Perform the check.
194 */
195 uint16_t fPortMask = (1 << cbOperand) - 1;
196 bmBytes >>= (u16Port & 7);
197 if (bmBytes & fPortMask)
198 {
199 Log(("iemHlpCheckPortIOPermissionBitmap: u16Port=%#x LB %u - access denied (bm=%#x mask=%#x) -> #GP(0)\n",
200 u16Port, cbOperand, bmBytes, fPortMask));
201 return iemRaiseGeneralProtectionFault0(pVCpu);
202 }
203
204 return VINF_SUCCESS;
205}
206
207
208/**
209 * Checks if we are allowed to access the given I/O port, raising the
210 * appropriate exceptions if we aren't (or if the I/O bitmap is not
211 * accessible).
212 *
213 * @returns Strict VBox status code.
214 *
215 * @param pVCpu The cross context virtual CPU structure of the calling thread.
216 * @param u16Port The port number.
217 * @param cbOperand The operand size.
218 */
219DECLINLINE(VBOXSTRICTRC) iemHlpCheckPortIOPermission(PVMCPUCC pVCpu, uint16_t u16Port, uint8_t cbOperand)
220{
221 X86EFLAGS Efl;
222 Efl.u = IEMMISC_GET_EFL(pVCpu);
223 if ( (pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE)
224 && ( IEM_GET_CPL(pVCpu) > Efl.Bits.u2IOPL
225 || Efl.Bits.u1VM) )
226 return iemHlpCheckPortIOPermissionBitmap(pVCpu, u16Port, cbOperand);
227 return VINF_SUCCESS;
228}
229
230
231#if 0
232/**
233 * Calculates the parity bit.
234 *
235 * @returns true if the bit is set, false if not.
236 * @param u8Result The least significant byte of the result.
237 */
238static bool iemHlpCalcParityFlag(uint8_t u8Result)
239{
240 /*
241 * Parity is set if the number of bits in the least significant byte of
242 * the result is even.
243 */
244 uint8_t cBits;
245 cBits = u8Result & 1; /* 0 */
246 u8Result >>= 1;
247 cBits += u8Result & 1;
248 u8Result >>= 1;
249 cBits += u8Result & 1;
250 u8Result >>= 1;
251 cBits += u8Result & 1;
252 u8Result >>= 1;
253 cBits += u8Result & 1; /* 4 */
254 u8Result >>= 1;
255 cBits += u8Result & 1;
256 u8Result >>= 1;
257 cBits += u8Result & 1;
258 u8Result >>= 1;
259 cBits += u8Result & 1;
260 return !(cBits & 1);
261}
262#endif /* not used */
263
264
265/**
266 * Updates the specified flags according to a 8-bit result.
267 *
268 * @param pVCpu The cross context virtual CPU structure of the calling thread.
269 * @param u8Result The result to set the flags according to.
270 * @param fToUpdate The flags to update.
271 * @param fUndefined The flags that are specified as undefined.
272 */
273static void iemHlpUpdateArithEFlagsU8(PVMCPUCC pVCpu, uint8_t u8Result, uint32_t fToUpdate, uint32_t fUndefined)
274{
275 uint32_t fEFlags = iemAImpl_test_u8(pVCpu->cpum.GstCtx.eflags.u, &u8Result, u8Result);
276 pVCpu->cpum.GstCtx.eflags.u &= ~(fToUpdate | fUndefined);
277 pVCpu->cpum.GstCtx.eflags.u |= (fToUpdate | fUndefined) & fEFlags;
278}
279
280
281/**
282 * Updates the specified flags according to a 16-bit result.
283 *
284 * @param pVCpu The cross context virtual CPU structure of the calling thread.
285 * @param u16Result The result to set the flags according to.
286 * @param fToUpdate The flags to update.
287 * @param fUndefined The flags that are specified as undefined.
288 */
289static void iemHlpUpdateArithEFlagsU16(PVMCPUCC pVCpu, uint16_t u16Result, uint32_t fToUpdate, uint32_t fUndefined)
290{
291 uint32_t fEFlags = iemAImpl_test_u16(pVCpu->cpum.GstCtx.eflags.u, &u16Result, u16Result);
292 pVCpu->cpum.GstCtx.eflags.u &= ~(fToUpdate | fUndefined);
293 pVCpu->cpum.GstCtx.eflags.u |= (fToUpdate | fUndefined) & fEFlags;
294}
295
296
297/**
298 * Helper used by iret.
299 *
300 * @param pVCpu The cross context virtual CPU structure of the calling thread.
301 * @param uCpl The new CPL.
302 * @param pSReg Pointer to the segment register.
303 */
304static void iemHlpAdjustSelectorForNewCpl(PVMCPUCC pVCpu, uint8_t uCpl, PCPUMSELREG pSReg)
305{
306 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg));
307 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_SREG_MASK);
308
309 if ( uCpl > pSReg->Attr.n.u2Dpl
310 && pSReg->Attr.n.u1DescType /* code or data, not system */
311 && (pSReg->Attr.n.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
312 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF)) /* not conforming code */
313 iemHlpLoadNullDataSelectorProt(pVCpu, pSReg, 0);
314}
315
316
317/**
318 * Indicates that we have modified the FPU state.
319 *
320 * @param pVCpu The cross context virtual CPU structure of the calling thread.
321 */
322DECLINLINE(void) iemHlpUsedFpu(PVMCPUCC pVCpu)
323{
324 CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_FPU_REM);
325}
326
327/** @} */
328
329/** @name C Implementations
330 * @{
331 */
332
333
334/**
335 * Implements a pop [mem16].
336 */
337IEM_CIMPL_DEF_2(iemCImpl_pop_mem16, uint16_t, iEffSeg, RTGCPTR, GCPtrEffDst)
338{
339 uint16_t u16Value;
340 RTUINT64U TmpRsp;
341 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
342 VBOXSTRICTRC rcStrict = iemMemStackPopU16Ex(pVCpu, &u16Value, &TmpRsp);
343 if (rcStrict == VINF_SUCCESS)
344 {
345 rcStrict = iemMemStoreDataU16(pVCpu, iEffSeg, GCPtrEffDst, u16Value);
346 if (rcStrict == VINF_SUCCESS)
347 {
348 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
349 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
350 }
351 }
352 return rcStrict;
353
354}
355
356
357/**
358 * Implements a pop [mem32].
359 */
360IEM_CIMPL_DEF_2(iemCImpl_pop_mem32, uint16_t, iEffSeg, RTGCPTR, GCPtrEffDst)
361{
362 uint32_t u32Value;
363 RTUINT64U TmpRsp;
364 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
365 VBOXSTRICTRC rcStrict = iemMemStackPopU32Ex(pVCpu, &u32Value, &TmpRsp);
366 if (rcStrict == VINF_SUCCESS)
367 {
368 rcStrict = iemMemStoreDataU32(pVCpu, iEffSeg, GCPtrEffDst, u32Value);
369 if (rcStrict == VINF_SUCCESS)
370 {
371 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
372 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
373 }
374 }
375 return rcStrict;
376
377}
378
379
380/**
381 * Implements a pop [mem64].
382 */
383IEM_CIMPL_DEF_2(iemCImpl_pop_mem64, uint16_t, iEffSeg, RTGCPTR, GCPtrEffDst)
384{
385 uint64_t u64Value;
386 RTUINT64U TmpRsp;
387 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
388 VBOXSTRICTRC rcStrict = iemMemStackPopU64Ex(pVCpu, &u64Value, &TmpRsp);
389 if (rcStrict == VINF_SUCCESS)
390 {
391 rcStrict = iemMemStoreDataU64(pVCpu, iEffSeg, GCPtrEffDst, u64Value);
392 if (rcStrict == VINF_SUCCESS)
393 {
394 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
395 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
396 }
397 }
398 return rcStrict;
399
400}
401
402
403/**
404 * Implements a 16-bit popa.
405 */
406IEM_CIMPL_DEF_0(iemCImpl_popa_16)
407{
408 RTGCPTR GCPtrStart = iemRegGetEffRsp(pVCpu);
409 RTGCPTR GCPtrLast = GCPtrStart + 15;
410 VBOXSTRICTRC rcStrict;
411
412 /*
413 * The docs are a bit hard to comprehend here, but it looks like we wrap
414 * around in real mode as long as none of the individual "popa" crosses the
415 * end of the stack segment. In protected mode we check the whole access
416 * in one go. For efficiency, only do the word-by-word thing if we're in
417 * danger of wrapping around.
418 */
419 /** @todo do popa boundary / wrap-around checks. */
420 if (RT_UNLIKELY( IEM_IS_REAL_OR_V86_MODE(pVCpu)
421 && (pVCpu->cpum.GstCtx.cs.u32Limit < GCPtrLast)) ) /* ASSUMES 64-bit RTGCPTR */
422 {
423 /* word-by-word */
424 RTUINT64U TmpRsp;
425 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
426 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.di, &TmpRsp);
427 if (rcStrict == VINF_SUCCESS)
428 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.si, &TmpRsp);
429 if (rcStrict == VINF_SUCCESS)
430 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.bp, &TmpRsp);
431 if (rcStrict == VINF_SUCCESS)
432 {
433 iemRegAddToRspEx(pVCpu, &TmpRsp, 2); /* sp */
434 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.bx, &TmpRsp);
435 }
436 if (rcStrict == VINF_SUCCESS)
437 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.dx, &TmpRsp);
438 if (rcStrict == VINF_SUCCESS)
439 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.cx, &TmpRsp);
440 if (rcStrict == VINF_SUCCESS)
441 rcStrict = iemMemStackPopU16Ex(pVCpu, &pVCpu->cpum.GstCtx.ax, &TmpRsp);
442 if (rcStrict == VINF_SUCCESS)
443 {
444 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
445 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
446 }
447 }
448 else
449 {
450 uint8_t bUnmapInfo;
451 uint16_t const *pau16Mem = NULL;
452 rcStrict = iemMemMap(pVCpu, (void **)&pau16Mem, &bUnmapInfo, 16, X86_SREG_SS, GCPtrStart,
453 IEM_ACCESS_STACK_R, sizeof(*pau16Mem) - 1);
454 if (rcStrict == VINF_SUCCESS)
455 {
456 pVCpu->cpum.GstCtx.di = pau16Mem[7 - X86_GREG_xDI];
457 pVCpu->cpum.GstCtx.si = pau16Mem[7 - X86_GREG_xSI];
458 pVCpu->cpum.GstCtx.bp = pau16Mem[7 - X86_GREG_xBP];
459 /* skip sp */
460 pVCpu->cpum.GstCtx.bx = pau16Mem[7 - X86_GREG_xBX];
461 pVCpu->cpum.GstCtx.dx = pau16Mem[7 - X86_GREG_xDX];
462 pVCpu->cpum.GstCtx.cx = pau16Mem[7 - X86_GREG_xCX];
463 pVCpu->cpum.GstCtx.ax = pau16Mem[7 - X86_GREG_xAX];
464 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
465 if (rcStrict == VINF_SUCCESS)
466 {
467 iemRegAddToRsp(pVCpu, 16);
468 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
469 }
470 }
471 }
472 return rcStrict;
473}
474
475
476/**
477 * Implements a 32-bit popa.
478 */
479IEM_CIMPL_DEF_0(iemCImpl_popa_32)
480{
481 RTGCPTR GCPtrStart = iemRegGetEffRsp(pVCpu);
482 RTGCPTR GCPtrLast = GCPtrStart + 31;
483 VBOXSTRICTRC rcStrict;
484
485 /*
486 * The docs are a bit hard to comprehend here, but it looks like we wrap
487 * around in real mode as long as none of the individual "popa" crosses the
488 * end of the stack segment. In protected mode we check the whole access
489 * in one go. For efficiency, only do the word-by-word thing if we're in
490 * danger of wrapping around.
491 */
492 /** @todo do popa boundary / wrap-around checks. */
493 if (RT_UNLIKELY( IEM_IS_REAL_OR_V86_MODE(pVCpu)
494 && (pVCpu->cpum.GstCtx.cs.u32Limit < GCPtrLast)) ) /* ASSUMES 64-bit RTGCPTR */
495 {
496 /* word-by-word */
497 RTUINT64U TmpRsp;
498 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
499 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.edi, &TmpRsp);
500 if (rcStrict == VINF_SUCCESS)
501 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.esi, &TmpRsp);
502 if (rcStrict == VINF_SUCCESS)
503 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.ebp, &TmpRsp);
504 if (rcStrict == VINF_SUCCESS)
505 {
506 iemRegAddToRspEx(pVCpu, &TmpRsp, 2); /* sp */
507 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.ebx, &TmpRsp);
508 }
509 if (rcStrict == VINF_SUCCESS)
510 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.edx, &TmpRsp);
511 if (rcStrict == VINF_SUCCESS)
512 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.ecx, &TmpRsp);
513 if (rcStrict == VINF_SUCCESS)
514 rcStrict = iemMemStackPopU32Ex(pVCpu, &pVCpu->cpum.GstCtx.eax, &TmpRsp);
515 if (rcStrict == VINF_SUCCESS)
516 {
517#if 1 /** @todo what actually happens with the high bits when we're in 16-bit mode? */
518 pVCpu->cpum.GstCtx.rdi &= UINT32_MAX;
519 pVCpu->cpum.GstCtx.rsi &= UINT32_MAX;
520 pVCpu->cpum.GstCtx.rbp &= UINT32_MAX;
521 pVCpu->cpum.GstCtx.rbx &= UINT32_MAX;
522 pVCpu->cpum.GstCtx.rdx &= UINT32_MAX;
523 pVCpu->cpum.GstCtx.rcx &= UINT32_MAX;
524 pVCpu->cpum.GstCtx.rax &= UINT32_MAX;
525#endif
526 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
527 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
528 }
529 }
530 else
531 {
532 uint8_t bUnmapInfo;
533 uint32_t const *pau32Mem;
534 rcStrict = iemMemMap(pVCpu, (void **)&pau32Mem, &bUnmapInfo, 32, X86_SREG_SS, GCPtrStart,
535 IEM_ACCESS_STACK_R, sizeof(*pau32Mem) - 1);
536 if (rcStrict == VINF_SUCCESS)
537 {
538 pVCpu->cpum.GstCtx.rdi = pau32Mem[7 - X86_GREG_xDI];
539 pVCpu->cpum.GstCtx.rsi = pau32Mem[7 - X86_GREG_xSI];
540 pVCpu->cpum.GstCtx.rbp = pau32Mem[7 - X86_GREG_xBP];
541 /* skip esp */
542 pVCpu->cpum.GstCtx.rbx = pau32Mem[7 - X86_GREG_xBX];
543 pVCpu->cpum.GstCtx.rdx = pau32Mem[7 - X86_GREG_xDX];
544 pVCpu->cpum.GstCtx.rcx = pau32Mem[7 - X86_GREG_xCX];
545 pVCpu->cpum.GstCtx.rax = pau32Mem[7 - X86_GREG_xAX];
546 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
547 if (rcStrict == VINF_SUCCESS)
548 {
549 iemRegAddToRsp(pVCpu, 32);
550 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
551 }
552 }
553 }
554 return rcStrict;
555}
556
557
558/**
559 * Implements a 16-bit pusha.
560 */
561IEM_CIMPL_DEF_0(iemCImpl_pusha_16)
562{
563 RTGCPTR GCPtrTop = iemRegGetEffRsp(pVCpu);
564 RTGCPTR GCPtrBottom = GCPtrTop - 15;
565 VBOXSTRICTRC rcStrict;
566
567 /*
568 * The docs are a bit hard to comprehend here, but it looks like we wrap
569 * around in real mode as long as none of the individual "pushd" crosses the
570 * end of the stack segment. In protected mode we check the whole access
571 * in one go. For efficiency, only do the word-by-word thing if we're in
572 * danger of wrapping around.
573 */
574 /** @todo do pusha boundary / wrap-around checks. */
575 if (RT_UNLIKELY( GCPtrBottom > GCPtrTop
576 && IEM_IS_REAL_OR_V86_MODE(pVCpu) ) )
577 {
578 /* word-by-word */
579 RTUINT64U TmpRsp;
580 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
581 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.ax, &TmpRsp);
582 if (rcStrict == VINF_SUCCESS)
583 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.cx, &TmpRsp);
584 if (rcStrict == VINF_SUCCESS)
585 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.dx, &TmpRsp);
586 if (rcStrict == VINF_SUCCESS)
587 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.bx, &TmpRsp);
588 if (rcStrict == VINF_SUCCESS)
589 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.sp, &TmpRsp);
590 if (rcStrict == VINF_SUCCESS)
591 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.bp, &TmpRsp);
592 if (rcStrict == VINF_SUCCESS)
593 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.si, &TmpRsp);
594 if (rcStrict == VINF_SUCCESS)
595 rcStrict = iemMemStackPushU16Ex(pVCpu, pVCpu->cpum.GstCtx.di, &TmpRsp);
596 if (rcStrict == VINF_SUCCESS)
597 {
598 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
599 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
600 }
601 }
602 else
603 {
604 GCPtrBottom--;
605 uint8_t bUnmapInfo;
606 uint16_t *pau16Mem = NULL;
607 rcStrict = iemMemMap(pVCpu, (void **)&pau16Mem, &bUnmapInfo, 16, X86_SREG_SS, GCPtrBottom,
608 IEM_ACCESS_STACK_W, sizeof(*pau16Mem) - 1);
609 if (rcStrict == VINF_SUCCESS)
610 {
611 pau16Mem[7 - X86_GREG_xDI] = pVCpu->cpum.GstCtx.di;
612 pau16Mem[7 - X86_GREG_xSI] = pVCpu->cpum.GstCtx.si;
613 pau16Mem[7 - X86_GREG_xBP] = pVCpu->cpum.GstCtx.bp;
614 pau16Mem[7 - X86_GREG_xSP] = pVCpu->cpum.GstCtx.sp;
615 pau16Mem[7 - X86_GREG_xBX] = pVCpu->cpum.GstCtx.bx;
616 pau16Mem[7 - X86_GREG_xDX] = pVCpu->cpum.GstCtx.dx;
617 pau16Mem[7 - X86_GREG_xCX] = pVCpu->cpum.GstCtx.cx;
618 pau16Mem[7 - X86_GREG_xAX] = pVCpu->cpum.GstCtx.ax;
619 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
620 if (rcStrict == VINF_SUCCESS)
621 {
622 iemRegSubFromRsp(pVCpu, 16);
623 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
624 }
625 }
626 }
627 return rcStrict;
628}
629
630
631/**
632 * Implements a 32-bit pusha.
633 */
634IEM_CIMPL_DEF_0(iemCImpl_pusha_32)
635{
636 RTGCPTR GCPtrTop = iemRegGetEffRsp(pVCpu);
637 RTGCPTR GCPtrBottom = GCPtrTop - 31;
638 VBOXSTRICTRC rcStrict;
639
640 /*
641 * The docs are a bit hard to comprehend here, but it looks like we wrap
642 * around in real mode as long as none of the individual "pusha" crosses the
643 * end of the stack segment. In protected mode we check the whole access
644 * in one go. For efficiency, only do the word-by-word thing if we're in
645 * danger of wrapping around.
646 */
647 /** @todo do pusha boundary / wrap-around checks. */
648 if (RT_UNLIKELY( GCPtrBottom > GCPtrTop
649 && IEM_IS_REAL_OR_V86_MODE(pVCpu) ) )
650 {
651 /* word-by-word */
652 RTUINT64U TmpRsp;
653 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
654 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.eax, &TmpRsp);
655 if (rcStrict == VINF_SUCCESS)
656 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.ecx, &TmpRsp);
657 if (rcStrict == VINF_SUCCESS)
658 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.edx, &TmpRsp);
659 if (rcStrict == VINF_SUCCESS)
660 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.ebx, &TmpRsp);
661 if (rcStrict == VINF_SUCCESS)
662 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.esp, &TmpRsp);
663 if (rcStrict == VINF_SUCCESS)
664 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.ebp, &TmpRsp);
665 if (rcStrict == VINF_SUCCESS)
666 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.esi, &TmpRsp);
667 if (rcStrict == VINF_SUCCESS)
668 rcStrict = iemMemStackPushU32Ex(pVCpu, pVCpu->cpum.GstCtx.edi, &TmpRsp);
669 if (rcStrict == VINF_SUCCESS)
670 {
671 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
672 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
673 }
674 }
675 else
676 {
677 GCPtrBottom--;
678 uint8_t bUnmapInfo;
679 uint32_t *pau32Mem;
680 rcStrict = iemMemMap(pVCpu, (void **)&pau32Mem, &bUnmapInfo, 32, X86_SREG_SS, GCPtrBottom,
681 IEM_ACCESS_STACK_W, sizeof(*pau32Mem) - 1);
682 if (rcStrict == VINF_SUCCESS)
683 {
684 pau32Mem[7 - X86_GREG_xDI] = pVCpu->cpum.GstCtx.edi;
685 pau32Mem[7 - X86_GREG_xSI] = pVCpu->cpum.GstCtx.esi;
686 pau32Mem[7 - X86_GREG_xBP] = pVCpu->cpum.GstCtx.ebp;
687 pau32Mem[7 - X86_GREG_xSP] = pVCpu->cpum.GstCtx.esp;
688 pau32Mem[7 - X86_GREG_xBX] = pVCpu->cpum.GstCtx.ebx;
689 pau32Mem[7 - X86_GREG_xDX] = pVCpu->cpum.GstCtx.edx;
690 pau32Mem[7 - X86_GREG_xCX] = pVCpu->cpum.GstCtx.ecx;
691 pau32Mem[7 - X86_GREG_xAX] = pVCpu->cpum.GstCtx.eax;
692 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
693 if (rcStrict == VINF_SUCCESS)
694 {
695 iemRegSubFromRsp(pVCpu, 32);
696 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
697 }
698 }
699 }
700 return rcStrict;
701}
702
703
704/**
705 * Implements pushf.
706 *
707 *
708 * @param enmEffOpSize The effective operand size.
709 */
710IEM_CIMPL_DEF_1(iemCImpl_pushf, IEMMODE, enmEffOpSize)
711{
712 VBOXSTRICTRC rcStrict;
713
714 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_PUSHF))
715 { /* probable */ }
716 else
717 {
718 Log2(("pushf: Guest intercept -> #VMEXIT\n"));
719 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
720 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_PUSHF, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
721 }
722
723 /*
724 * If we're in V8086 mode some care is required (which is why we're in
725 * doing this in a C implementation).
726 */
727 uint32_t fEfl = IEMMISC_GET_EFL(pVCpu);
728 if ( (fEfl & X86_EFL_VM)
729 && X86_EFL_GET_IOPL(fEfl) != 3 )
730 {
731 Assert(pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE);
732 if ( enmEffOpSize != IEMMODE_16BIT
733 || !(pVCpu->cpum.GstCtx.cr4 & X86_CR4_VME))
734 return iemRaiseGeneralProtectionFault0(pVCpu);
735 fEfl &= ~X86_EFL_IF; /* (RF and VM are out of range) */
736 fEfl |= (fEfl & X86_EFL_VIF) >> (19 - 9);
737 rcStrict = iemMemStackPushU16(pVCpu, (uint16_t)fEfl);
738 }
739 else
740 {
741
742 /*
743 * Ok, clear RF and VM, adjust for ancient CPUs, and push the flags.
744 */
745 fEfl &= ~(X86_EFL_RF | X86_EFL_VM);
746
747 switch (enmEffOpSize)
748 {
749 case IEMMODE_16BIT:
750 AssertCompile(IEMTARGETCPU_8086 <= IEMTARGETCPU_186 && IEMTARGETCPU_V20 <= IEMTARGETCPU_186 && IEMTARGETCPU_286 > IEMTARGETCPU_186);
751 if (IEM_GET_TARGET_CPU(pVCpu) <= IEMTARGETCPU_186)
752 fEfl |= UINT16_C(0xf000);
753 rcStrict = iemMemStackPushU16(pVCpu, (uint16_t)fEfl);
754 break;
755 case IEMMODE_32BIT:
756 rcStrict = iemMemStackPushU32(pVCpu, fEfl);
757 break;
758 case IEMMODE_64BIT:
759 rcStrict = iemMemStackPushU64(pVCpu, fEfl);
760 break;
761 IEM_NOT_REACHED_DEFAULT_CASE_RET();
762 }
763 }
764
765 if (rcStrict == VINF_SUCCESS)
766 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
767 return rcStrict;
768}
769
770
771/**
772 * Implements popf.
773 *
774 * @param enmEffOpSize The effective operand size.
775 */
776IEM_CIMPL_DEF_1(iemCImpl_popf, IEMMODE, enmEffOpSize)
777{
778 uint32_t const fEflOld = IEMMISC_GET_EFL(pVCpu);
779 VBOXSTRICTRC rcStrict;
780 uint32_t fEflNew;
781
782 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_POPF))
783 { /* probable */ }
784 else
785 {
786 Log2(("popf: Guest intercept -> #VMEXIT\n"));
787 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
788 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_POPF, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
789 }
790
791 /*
792 * V8086 is special as usual.
793 */
794 if (fEflOld & X86_EFL_VM)
795 {
796 /*
797 * Almost anything goes if IOPL is 3.
798 */
799 if (X86_EFL_GET_IOPL(fEflOld) == 3)
800 {
801 switch (enmEffOpSize)
802 {
803 case IEMMODE_16BIT:
804 {
805 uint16_t u16Value;
806 rcStrict = iemMemStackPopU16(pVCpu, &u16Value);
807 if (rcStrict != VINF_SUCCESS)
808 return rcStrict;
809 fEflNew = u16Value | (fEflOld & UINT32_C(0xffff0000));
810 break;
811 }
812 case IEMMODE_32BIT:
813 rcStrict = iemMemStackPopU32(pVCpu, &fEflNew);
814 if (rcStrict != VINF_SUCCESS)
815 return rcStrict;
816 break;
817 IEM_NOT_REACHED_DEFAULT_CASE_RET();
818 }
819
820 const uint32_t fPopfBits = pVCpu->CTX_SUFF(pVM)->cpum.ro.GuestFeatures.enmMicroarch != kCpumMicroarch_Intel_80386
821 ? X86_EFL_POPF_BITS : X86_EFL_POPF_BITS_386;
822 fEflNew &= fPopfBits & ~(X86_EFL_IOPL);
823 fEflNew |= ~(fPopfBits & ~(X86_EFL_IOPL)) & fEflOld;
824 }
825 /*
826 * Interrupt flag virtualization with CR4.VME=1.
827 */
828 else if ( enmEffOpSize == IEMMODE_16BIT
829 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_VME) )
830 {
831 uint16_t u16Value;
832 RTUINT64U TmpRsp;
833 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
834 rcStrict = iemMemStackPopU16Ex(pVCpu, &u16Value, &TmpRsp);
835 if (rcStrict != VINF_SUCCESS)
836 return rcStrict;
837
838 if ( ( (u16Value & X86_EFL_IF)
839 && (fEflOld & X86_EFL_VIP))
840 || (u16Value & X86_EFL_TF) )
841 return iemRaiseGeneralProtectionFault0(pVCpu);
842
843 fEflNew = X86_EFL_RA1_MASK
844 | (u16Value & ~(X86_EFL_IF | X86_EFL_IOPL | X86_EFL_RAZ_MASK))
845 | (fEflOld & (UINT32_C(0xffff0000) | X86_EFL_IF | X86_EFL_IOPL) & ~(X86_EFL_VIF | X86_EFL_RF))
846 | ((uint32_t)(u16Value & X86_EFL_IF) << (X86_EFL_VIF_BIT - X86_EFL_IF_BIT));
847
848 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
849 }
850 else
851 return iemRaiseGeneralProtectionFault0(pVCpu);
852
853 }
854 /*
855 * Not in V8086 mode.
856 */
857 else
858 {
859 /* Pop the flags. */
860 switch (enmEffOpSize)
861 {
862 case IEMMODE_16BIT:
863 {
864 uint16_t u16Value;
865 rcStrict = iemMemStackPopU16(pVCpu, &u16Value);
866 if (rcStrict != VINF_SUCCESS)
867 return rcStrict;
868 fEflNew = u16Value | (fEflOld & UINT32_C(0xffff0000));
869
870 /*
871 * Ancient CPU adjustments:
872 * - 8086, 80186, V20/30:
873 * Fixed bits 15:12 bits are not kept correctly internally, mostly for
874 * practical reasons (masking below). We add them when pushing flags.
875 * - 80286:
876 * The NT and IOPL flags cannot be popped from real mode and are
877 * therefore always zero (since a 286 can never exit from PM and
878 * their initial value is zero). This changed on a 386 and can
879 * therefore be used to detect 286 or 386 CPU in real mode.
880 */
881 if ( IEM_GET_TARGET_CPU(pVCpu) == IEMTARGETCPU_286
882 && !(pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE) )
883 fEflNew &= ~(X86_EFL_NT | X86_EFL_IOPL);
884 break;
885 }
886 case IEMMODE_32BIT:
887 rcStrict = iemMemStackPopU32(pVCpu, &fEflNew);
888 if (rcStrict != VINF_SUCCESS)
889 return rcStrict;
890 break;
891 case IEMMODE_64BIT:
892 {
893 uint64_t u64Value;
894 rcStrict = iemMemStackPopU64(pVCpu, &u64Value);
895 if (rcStrict != VINF_SUCCESS)
896 return rcStrict;
897 fEflNew = u64Value; /** @todo testcase: Check exactly what happens if high bits are set. */
898 break;
899 }
900 IEM_NOT_REACHED_DEFAULT_CASE_RET();
901 }
902
903 /* Merge them with the current flags. */
904 const uint32_t fPopfBits = pVCpu->CTX_SUFF(pVM)->cpum.ro.GuestFeatures.enmMicroarch != kCpumMicroarch_Intel_80386
905 ? X86_EFL_POPF_BITS : X86_EFL_POPF_BITS_386;
906 if ( (fEflNew & (X86_EFL_IOPL | X86_EFL_IF)) == (fEflOld & (X86_EFL_IOPL | X86_EFL_IF))
907 || IEM_GET_CPL(pVCpu) == 0)
908 {
909 fEflNew &= fPopfBits;
910 fEflNew |= ~fPopfBits & fEflOld;
911 }
912 else if (IEM_GET_CPL(pVCpu) <= X86_EFL_GET_IOPL(fEflOld))
913 {
914 fEflNew &= fPopfBits & ~(X86_EFL_IOPL);
915 fEflNew |= ~(fPopfBits & ~(X86_EFL_IOPL)) & fEflOld;
916 }
917 else
918 {
919 fEflNew &= fPopfBits & ~(X86_EFL_IOPL | X86_EFL_IF);
920 fEflNew |= ~(fPopfBits & ~(X86_EFL_IOPL | X86_EFL_IF)) & fEflOld;
921 }
922 }
923
924 /*
925 * Commit the flags.
926 */
927 Assert(fEflNew & RT_BIT_32(1));
928 IEMMISC_SET_EFL(pVCpu, fEflNew);
929 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~IEM_F_X86_AC) | iemCalcExecAcFlag(pVCpu);
930 return iemRegAddToRipAndFinishingClearingRfEx(pVCpu, cbInstr, fEflOld);
931}
932
933
934/**
935 * Implements far jumps and calls thru task segments (TSS).
936 *
937 * @returns VBox strict status code.
938 * @param pVCpu The cross context virtual CPU structure of the
939 * calling thread.
940 * @param cbInstr The current instruction length.
941 * @param uSel The selector.
942 * @param enmBranch The kind of branching we're performing.
943 * @param enmEffOpSize The effective operand size.
944 * @param pDesc The descriptor corresponding to @a uSel. The type is
945 * task gate.
946 */
947static VBOXSTRICTRC iemCImpl_BranchTaskSegment(PVMCPUCC pVCpu, uint8_t cbInstr, uint16_t uSel, IEMBRANCH enmBranch,
948 IEMMODE enmEffOpSize, PIEMSELDESC pDesc)
949{
950#ifndef IEM_IMPLEMENTS_TASKSWITCH
951 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
952#else
953 Assert(enmBranch == IEMBRANCH_JUMP || enmBranch == IEMBRANCH_CALL);
954 Assert( pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_TSS_AVAIL
955 || pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_TSS_AVAIL);
956 RT_NOREF_PV(enmEffOpSize);
957 IEM_CTX_ASSERT(pVCpu, IEM_CPUMCTX_EXTRN_XCPT_MASK);
958
959 if ( pDesc->Legacy.Gate.u2Dpl < IEM_GET_CPL(pVCpu)
960 || pDesc->Legacy.Gate.u2Dpl < (uSel & X86_SEL_RPL))
961 {
962 Log(("BranchTaskSegment invalid priv. uSel=%04x TSS DPL=%d CPL=%u Sel RPL=%u -> #GP\n", uSel, pDesc->Legacy.Gate.u2Dpl,
963 IEM_GET_CPL(pVCpu), (uSel & X86_SEL_RPL)));
964 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel & X86_SEL_MASK_OFF_RPL);
965 }
966
967 /** @todo This is checked earlier for far jumps (see iemCImpl_FarJmp) but not
968 * far calls (see iemCImpl_callf). Most likely in both cases it should be
969 * checked here, need testcases. */
970 if (!pDesc->Legacy.Gen.u1Present)
971 {
972 Log(("BranchTaskSegment TSS not present uSel=%04x -> #NP\n", uSel));
973 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSel & X86_SEL_MASK_OFF_RPL);
974 }
975
976 uint32_t uNextEip = pVCpu->cpum.GstCtx.eip + cbInstr;
977 return iemTaskSwitch(pVCpu, enmBranch == IEMBRANCH_JUMP ? IEMTASKSWITCH_JUMP : IEMTASKSWITCH_CALL,
978 uNextEip, 0 /* fFlags */, 0 /* uErr */, 0 /* uCr2 */, uSel, pDesc);
979#endif
980}
981
982
983/**
984 * Implements far jumps and calls thru task gates.
985 *
986 * @returns VBox strict status code.
987 * @param pVCpu The cross context virtual CPU structure of the
988 * calling thread.
989 * @param cbInstr The current instruction length.
990 * @param uSel The selector.
991 * @param enmBranch The kind of branching we're performing.
992 * @param enmEffOpSize The effective operand size.
993 * @param pDesc The descriptor corresponding to @a uSel. The type is
994 * task gate.
995 */
996static VBOXSTRICTRC iemCImpl_BranchTaskGate(PVMCPUCC pVCpu, uint8_t cbInstr, uint16_t uSel, IEMBRANCH enmBranch,
997 IEMMODE enmEffOpSize, PIEMSELDESC pDesc)
998{
999#ifndef IEM_IMPLEMENTS_TASKSWITCH
1000 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
1001#else
1002 Assert(enmBranch == IEMBRANCH_JUMP || enmBranch == IEMBRANCH_CALL);
1003 RT_NOREF_PV(enmEffOpSize);
1004 IEM_CTX_ASSERT(pVCpu, IEM_CPUMCTX_EXTRN_XCPT_MASK);
1005
1006 if ( pDesc->Legacy.Gate.u2Dpl < IEM_GET_CPL(pVCpu)
1007 || pDesc->Legacy.Gate.u2Dpl < (uSel & X86_SEL_RPL))
1008 {
1009 Log(("BranchTaskGate invalid priv. uSel=%04x TSS DPL=%d CPL=%u Sel RPL=%u -> #GP\n", uSel, pDesc->Legacy.Gate.u2Dpl,
1010 IEM_GET_CPL(pVCpu), (uSel & X86_SEL_RPL)));
1011 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel & X86_SEL_MASK_OFF_RPL);
1012 }
1013
1014 /** @todo This is checked earlier for far jumps (see iemCImpl_FarJmp) but not
1015 * far calls (see iemCImpl_callf). Most likely in both cases it should be
1016 * checked here, need testcases. */
1017 if (!pDesc->Legacy.Gen.u1Present)
1018 {
1019 Log(("BranchTaskSegment segment not present uSel=%04x -> #NP\n", uSel));
1020 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSel & X86_SEL_MASK_OFF_RPL);
1021 }
1022
1023 /*
1024 * Fetch the new TSS descriptor from the GDT.
1025 */
1026 RTSEL uSelTss = pDesc->Legacy.Gate.u16Sel;
1027 if (uSelTss & X86_SEL_LDT)
1028 {
1029 Log(("BranchTaskGate TSS is in LDT. uSel=%04x uSelTss=%04x -> #GP\n", uSel, uSelTss));
1030 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel & X86_SEL_MASK_OFF_RPL);
1031 }
1032
1033 IEMSELDESC TssDesc;
1034 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pVCpu, &TssDesc, uSelTss, X86_XCPT_GP);
1035 if (rcStrict != VINF_SUCCESS)
1036 return rcStrict;
1037
1038 if (TssDesc.Legacy.Gate.u4Type & X86_SEL_TYPE_SYS_TSS_BUSY_MASK)
1039 {
1040 Log(("BranchTaskGate TSS is busy. uSel=%04x uSelTss=%04x DescType=%#x -> #GP\n", uSel, uSelTss,
1041 TssDesc.Legacy.Gate.u4Type));
1042 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel & X86_SEL_MASK_OFF_RPL);
1043 }
1044
1045 if (!TssDesc.Legacy.Gate.u1Present)
1046 {
1047 Log(("BranchTaskGate TSS is not present. uSel=%04x uSelTss=%04x -> #NP\n", uSel, uSelTss));
1048 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSelTss & X86_SEL_MASK_OFF_RPL);
1049 }
1050
1051 uint32_t uNextEip = pVCpu->cpum.GstCtx.eip + cbInstr;
1052 return iemTaskSwitch(pVCpu, enmBranch == IEMBRANCH_JUMP ? IEMTASKSWITCH_JUMP : IEMTASKSWITCH_CALL,
1053 uNextEip, 0 /* fFlags */, 0 /* uErr */, 0 /* uCr2 */, uSelTss, &TssDesc);
1054#endif
1055}
1056
1057
1058/**
1059 * Implements far jumps and calls thru call gates.
1060 *
1061 * @returns VBox strict status code.
1062 * @param pVCpu The cross context virtual CPU structure of the
1063 * calling thread.
1064 * @param cbInstr The current instruction length.
1065 * @param uSel The selector.
1066 * @param enmBranch The kind of branching we're performing.
1067 * @param enmEffOpSize The effective operand size.
1068 * @param pDesc The descriptor corresponding to @a uSel. The type is
1069 * call gate.
1070 */
1071static VBOXSTRICTRC iemCImpl_BranchCallGate(PVMCPUCC pVCpu, uint8_t cbInstr, uint16_t uSel, IEMBRANCH enmBranch,
1072 IEMMODE enmEffOpSize, PIEMSELDESC pDesc)
1073{
1074#define IEM_IMPLEMENTS_CALLGATE
1075#ifndef IEM_IMPLEMENTS_CALLGATE
1076 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
1077#else
1078 RT_NOREF_PV(enmEffOpSize);
1079 IEM_CTX_ASSERT(pVCpu, IEM_CPUMCTX_EXTRN_XCPT_MASK);
1080
1081 /* NB: Far jumps can only do intra-privilege transfers. Far calls support
1082 * inter-privilege calls and are much more complex.
1083 *
1084 * NB: 64-bit call gate has the same type as a 32-bit call gate! If
1085 * EFER.LMA=1, the gate must be 64-bit. Conversely if EFER.LMA=0, the gate
1086 * must be 16-bit or 32-bit.
1087 */
1088 /** @todo effective operand size is probably irrelevant here, only the
1089 * call gate bitness matters??
1090 */
1091 VBOXSTRICTRC rcStrict;
1092 RTPTRUNION uPtrRet;
1093 uint64_t uNewRsp;
1094 uint64_t uNewRip;
1095 uint64_t u64Base;
1096 uint32_t cbLimit;
1097 RTSEL uNewCS;
1098 IEMSELDESC DescCS;
1099
1100 AssertCompile(X86_SEL_TYPE_SYS_386_CALL_GATE == AMD64_SEL_TYPE_SYS_CALL_GATE);
1101 Assert(enmBranch == IEMBRANCH_JUMP || enmBranch == IEMBRANCH_CALL);
1102 Assert( pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_CALL_GATE
1103 || pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE);
1104
1105 /* Determine the new instruction pointer from the gate descriptor. */
1106 uNewRip = pDesc->Legacy.Gate.u16OffsetLow
1107 | ((uint32_t)pDesc->Legacy.Gate.u16OffsetHigh << 16)
1108 | ((uint64_t)pDesc->Long.Gate.u32OffsetTop << 32);
1109
1110 /* Perform DPL checks on the gate descriptor. */
1111 if ( pDesc->Legacy.Gate.u2Dpl < IEM_GET_CPL(pVCpu)
1112 || pDesc->Legacy.Gate.u2Dpl < (uSel & X86_SEL_RPL))
1113 {
1114 Log(("BranchCallGate invalid priv. uSel=%04x Gate DPL=%d CPL=%u Sel RPL=%u -> #GP\n", uSel, pDesc->Legacy.Gate.u2Dpl,
1115 IEM_GET_CPL(pVCpu), (uSel & X86_SEL_RPL)));
1116 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1117 }
1118
1119 /** @todo does this catch NULL selectors, too? */
1120 if (!pDesc->Legacy.Gen.u1Present)
1121 {
1122 Log(("BranchCallGate Gate not present uSel=%04x -> #NP\n", uSel));
1123 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSel);
1124 }
1125
1126 /*
1127 * Fetch the target CS descriptor from the GDT or LDT.
1128 */
1129 uNewCS = pDesc->Legacy.Gate.u16Sel;
1130 rcStrict = iemMemFetchSelDesc(pVCpu, &DescCS, uNewCS, X86_XCPT_GP);
1131 if (rcStrict != VINF_SUCCESS)
1132 return rcStrict;
1133
1134 /* Target CS must be a code selector. */
1135 if ( !DescCS.Legacy.Gen.u1DescType
1136 || !(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE) )
1137 {
1138 Log(("BranchCallGate %04x:%08RX64 -> not a code selector (u1DescType=%u u4Type=%#x).\n",
1139 uNewCS, uNewRip, DescCS.Legacy.Gen.u1DescType, DescCS.Legacy.Gen.u4Type));
1140 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCS);
1141 }
1142
1143 /* Privilege checks on target CS. */
1144 if (enmBranch == IEMBRANCH_JUMP)
1145 {
1146 if (DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
1147 {
1148 if (DescCS.Legacy.Gen.u2Dpl > IEM_GET_CPL(pVCpu))
1149 {
1150 Log(("BranchCallGate jump (conforming) bad DPL uNewCS=%04x Gate DPL=%d CPL=%u -> #GP\n",
1151 uNewCS, DescCS.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
1152 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCS);
1153 }
1154 }
1155 else
1156 {
1157 if (DescCS.Legacy.Gen.u2Dpl != IEM_GET_CPL(pVCpu))
1158 {
1159 Log(("BranchCallGate jump (non-conforming) bad DPL uNewCS=%04x Gate DPL=%d CPL=%u -> #GP\n",
1160 uNewCS, DescCS.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
1161 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCS);
1162 }
1163 }
1164 }
1165 else
1166 {
1167 Assert(enmBranch == IEMBRANCH_CALL);
1168 if (DescCS.Legacy.Gen.u2Dpl > IEM_GET_CPL(pVCpu))
1169 {
1170 Log(("BranchCallGate call invalid priv. uNewCS=%04x Gate DPL=%d CPL=%u -> #GP\n",
1171 uNewCS, DescCS.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
1172 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCS & X86_SEL_MASK_OFF_RPL);
1173 }
1174 }
1175
1176 /* Additional long mode checks. */
1177 if (IEM_IS_LONG_MODE(pVCpu))
1178 {
1179 if (!DescCS.Legacy.Gen.u1Long)
1180 {
1181 Log(("BranchCallGate uNewCS %04x -> not a 64-bit code segment.\n", uNewCS));
1182 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCS);
1183 }
1184
1185 /* L vs D. */
1186 if ( DescCS.Legacy.Gen.u1Long
1187 && DescCS.Legacy.Gen.u1DefBig)
1188 {
1189 Log(("BranchCallGate uNewCS %04x -> both L and D are set.\n", uNewCS));
1190 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCS);
1191 }
1192 }
1193
1194 if (!DescCS.Legacy.Gate.u1Present)
1195 {
1196 Log(("BranchCallGate target CS is not present. uSel=%04x uNewCS=%04x -> #NP(CS)\n", uSel, uNewCS));
1197 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewCS);
1198 }
1199
1200 if (enmBranch == IEMBRANCH_JUMP)
1201 {
1202 /** @todo This is very similar to regular far jumps; merge! */
1203 /* Jumps are fairly simple... */
1204
1205 /* Chop the high bits off if 16-bit gate (Intel says so). */
1206 if (pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_CALL_GATE)
1207 uNewRip = (uint16_t)uNewRip;
1208
1209 /* Limit check for non-long segments. */
1210 cbLimit = X86DESC_LIMIT_G(&DescCS.Legacy);
1211 if (DescCS.Legacy.Gen.u1Long)
1212 u64Base = 0;
1213 else
1214 {
1215 if (uNewRip > cbLimit)
1216 {
1217 Log(("BranchCallGate jump %04x:%08RX64 -> out of bounds (%#x) -> #GP(0)\n", uNewCS, uNewRip, cbLimit));
1218 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, 0);
1219 }
1220 u64Base = X86DESC_BASE(&DescCS.Legacy);
1221 }
1222
1223 /* Canonical address check. */
1224 if (!IEM_IS_CANONICAL(uNewRip))
1225 {
1226 Log(("BranchCallGate jump %04x:%016RX64 - not canonical -> #GP\n", uNewCS, uNewRip));
1227 return iemRaiseNotCanonical(pVCpu);
1228 }
1229
1230 /*
1231 * Ok, everything checked out fine. Now set the accessed bit before
1232 * committing the result into CS, CSHID and RIP.
1233 */
1234 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1235 {
1236 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCS);
1237 if (rcStrict != VINF_SUCCESS)
1238 return rcStrict;
1239 /** @todo check what VT-x and AMD-V does. */
1240 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1241 }
1242
1243 /* commit */
1244 pVCpu->cpum.GstCtx.rip = uNewRip;
1245 pVCpu->cpum.GstCtx.cs.Sel = uNewCS & X86_SEL_MASK_OFF_RPL;
1246 pVCpu->cpum.GstCtx.cs.Sel |= IEM_GET_CPL(pVCpu); /** @todo is this right for conforming segs? or in general? */
1247 pVCpu->cpum.GstCtx.cs.ValidSel = pVCpu->cpum.GstCtx.cs.Sel;
1248 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
1249 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
1250 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimit;
1251 pVCpu->cpum.GstCtx.cs.u64Base = u64Base;
1252 }
1253 else
1254 {
1255 Assert(enmBranch == IEMBRANCH_CALL);
1256 /* Calls are much more complicated. */
1257
1258 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF) && (DescCS.Legacy.Gen.u2Dpl < IEM_GET_CPL(pVCpu)))
1259 {
1260 /* More privilege. This is the fun part. */
1261 Assert(!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)); /* Filtered out above. */
1262
1263 /*
1264 * Determine new SS:rSP from the TSS.
1265 */
1266 Assert(!pVCpu->cpum.GstCtx.tr.Attr.n.u1DescType);
1267
1268 /* Figure out where the new stack pointer is stored in the TSS. */
1269 uint8_t const uNewCSDpl = DescCS.Legacy.Gen.u2Dpl;
1270 uint16_t offNewStack; /* Offset of new stack in TSS. */
1271 uint16_t cbNewStack; /* Number of bytes the stack information takes up in TSS. */
1272 if (!IEM_IS_LONG_MODE(pVCpu))
1273 {
1274 if (pVCpu->cpum.GstCtx.tr.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY)
1275 {
1276 offNewStack = RT_UOFFSETOF(X86TSS32, esp0) + uNewCSDpl * 8;
1277 cbNewStack = RT_SIZEOFMEMB(X86TSS32, esp0) + RT_SIZEOFMEMB(X86TSS32, ss0);
1278 }
1279 else
1280 {
1281 Assert(pVCpu->cpum.GstCtx.tr.Attr.n.u4Type == X86_SEL_TYPE_SYS_286_TSS_BUSY);
1282 offNewStack = RT_UOFFSETOF(X86TSS16, sp0) + uNewCSDpl * 4;
1283 cbNewStack = RT_SIZEOFMEMB(X86TSS16, sp0) + RT_SIZEOFMEMB(X86TSS16, ss0);
1284 }
1285 }
1286 else
1287 {
1288 Assert(pVCpu->cpum.GstCtx.tr.Attr.n.u4Type == AMD64_SEL_TYPE_SYS_TSS_BUSY);
1289 offNewStack = RT_UOFFSETOF(X86TSS64, rsp0) + uNewCSDpl * RT_SIZEOFMEMB(X86TSS64, rsp0);
1290 cbNewStack = RT_SIZEOFMEMB(X86TSS64, rsp0);
1291 }
1292
1293 /* Check against TSS limit. */
1294 if ((uint16_t)(offNewStack + cbNewStack - 1) > pVCpu->cpum.GstCtx.tr.u32Limit)
1295 {
1296 Log(("BranchCallGate inner stack past TSS limit - %u > %u -> #TS(TSS)\n", offNewStack + cbNewStack - 1, pVCpu->cpum.GstCtx.tr.u32Limit));
1297 return iemRaiseTaskSwitchFaultBySelector(pVCpu, pVCpu->cpum.GstCtx.tr.Sel);
1298 }
1299
1300 uint8_t bUnmapInfo;
1301 RTPTRUNION uPtrTss;
1302 RTGCPTR GCPtrTss = pVCpu->cpum.GstCtx.tr.u64Base + offNewStack;
1303 rcStrict = iemMemMap(pVCpu, &uPtrTss.pv, &bUnmapInfo, cbNewStack, UINT8_MAX, GCPtrTss, IEM_ACCESS_SYS_R, 0);
1304 if (rcStrict != VINF_SUCCESS)
1305 {
1306 Log(("BranchCallGate: TSS mapping failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1307 return rcStrict;
1308 }
1309
1310 RTSEL uNewSS;
1311 if (!IEM_IS_LONG_MODE(pVCpu))
1312 {
1313 if (pVCpu->cpum.GstCtx.tr.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY)
1314 {
1315 uNewRsp = uPtrTss.pu32[0];
1316 uNewSS = uPtrTss.pu16[2];
1317 }
1318 else
1319 {
1320 Assert(pVCpu->cpum.GstCtx.tr.Attr.n.u4Type == X86_SEL_TYPE_SYS_286_TSS_BUSY);
1321 uNewRsp = uPtrTss.pu16[0];
1322 uNewSS = uPtrTss.pu16[1];
1323 }
1324 }
1325 else
1326 {
1327 Assert(pVCpu->cpum.GstCtx.tr.Attr.n.u4Type == AMD64_SEL_TYPE_SYS_TSS_BUSY);
1328 /* SS will be a NULL selector, but that's valid. */
1329 uNewRsp = uPtrTss.pu64[0];
1330 uNewSS = uNewCSDpl;
1331 }
1332
1333 /* Done with the TSS now. */
1334 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
1335 if (rcStrict != VINF_SUCCESS)
1336 {
1337 Log(("BranchCallGate: TSS unmapping failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1338 return rcStrict;
1339 }
1340
1341 /* Only used outside of long mode. */
1342 uint8_t const cbWords = pDesc->Legacy.Gate.u5ParmCount;
1343
1344 /* If EFER.LMA is 0, there's extra work to do. */
1345 IEMSELDESC DescSS;
1346 if (!IEM_IS_LONG_MODE(pVCpu))
1347 {
1348 if ((uNewSS & X86_SEL_MASK_OFF_RPL) == 0)
1349 {
1350 Log(("BranchCallGate new SS NULL -> #TS(NewSS)\n"));
1351 return iemRaiseTaskSwitchFaultBySelector(pVCpu, uNewSS);
1352 }
1353
1354 /* Grab the new SS descriptor. */
1355 rcStrict = iemMemFetchSelDesc(pVCpu, &DescSS, uNewSS, X86_XCPT_SS);
1356 if (rcStrict != VINF_SUCCESS)
1357 return rcStrict;
1358
1359 /* Ensure that CS.DPL == SS.RPL == SS.DPL. */
1360 if ( (DescCS.Legacy.Gen.u2Dpl != (uNewSS & X86_SEL_RPL))
1361 || (DescCS.Legacy.Gen.u2Dpl != DescSS.Legacy.Gen.u2Dpl))
1362 {
1363 Log(("BranchCallGate call bad RPL/DPL uNewSS=%04x SS DPL=%d CS DPL=%u -> #TS(NewSS)\n",
1364 uNewSS, DescCS.Legacy.Gen.u2Dpl, DescCS.Legacy.Gen.u2Dpl));
1365 return iemRaiseTaskSwitchFaultBySelector(pVCpu, uNewSS);
1366 }
1367
1368 /* Ensure new SS is a writable data segment. */
1369 if ((DescSS.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE)
1370 {
1371 Log(("BranchCallGate call new SS -> not a writable data selector (u4Type=%#x)\n", DescSS.Legacy.Gen.u4Type));
1372 return iemRaiseTaskSwitchFaultBySelector(pVCpu, uNewSS);
1373 }
1374
1375 if (!DescSS.Legacy.Gen.u1Present)
1376 {
1377 Log(("BranchCallGate New stack not present uSel=%04x -> #SS(NewSS)\n", uNewSS));
1378 return iemRaiseStackSelectorNotPresentBySelector(pVCpu, uNewSS);
1379 }
1380 if (pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE)
1381 cbNewStack = (uint16_t)sizeof(uint32_t) * (4 + cbWords);
1382 else
1383 cbNewStack = (uint16_t)sizeof(uint16_t) * (4 + cbWords);
1384 }
1385 else
1386 {
1387 /* Just grab the new (NULL) SS descriptor. */
1388 /** @todo testcase: Check whether the zero GDT entry is actually loaded here
1389 * like we do... */
1390 rcStrict = iemMemFetchSelDesc(pVCpu, &DescSS, uNewSS, X86_XCPT_SS);
1391 if (rcStrict != VINF_SUCCESS)
1392 return rcStrict;
1393
1394 cbNewStack = sizeof(uint64_t) * 4;
1395 }
1396
1397 /** @todo According to Intel, new stack is checked for enough space first,
1398 * then switched. According to AMD, the stack is switched first and
1399 * then pushes might fault!
1400 * NB: OS/2 Warp 3/4 actively relies on the fact that possible
1401 * incoming stack \#PF happens before actual stack switch. AMD is
1402 * either lying or implicitly assumes that new state is committed
1403 * only if and when an instruction doesn't fault.
1404 */
1405
1406 /** @todo According to AMD, CS is loaded first, then SS.
1407 * According to Intel, it's the other way around!?
1408 */
1409
1410 /** @todo Intel and AMD disagree on when exactly the CPL changes! */
1411
1412 /* Set the accessed bit before committing new SS. */
1413 if (!(DescSS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1414 {
1415 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewSS);
1416 if (rcStrict != VINF_SUCCESS)
1417 return rcStrict;
1418 DescSS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1419 }
1420
1421 /* Remember the old SS:rSP and their linear address. */
1422 RTSEL const uOldSS = pVCpu->cpum.GstCtx.ss.Sel;
1423 uint64_t const uOldRsp = pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig ? pVCpu->cpum.GstCtx.rsp : pVCpu->cpum.GstCtx.sp;
1424
1425 RTGCPTR const GCPtrParmWds = pVCpu->cpum.GstCtx.ss.u64Base + uOldRsp;
1426
1427 /* HACK ALERT! Probe if the write to the new stack will succeed. May #SS(NewSS)
1428 or #PF, the former is not implemented in this workaround. */
1429 /** @todo Proper fix callgate target stack exceptions. */
1430 /** @todo testcase: Cover callgates with partially or fully inaccessible
1431 * target stacks. */
1432 void *pvNewFrame;
1433 RTGCPTR GCPtrNewStack = X86DESC_BASE(&DescSS.Legacy) + uNewRsp - cbNewStack;
1434 rcStrict = iemMemMap(pVCpu, &pvNewFrame, &bUnmapInfo, cbNewStack, UINT8_MAX, GCPtrNewStack, IEM_ACCESS_SYS_RW, 0);
1435 if (rcStrict != VINF_SUCCESS)
1436 {
1437 Log(("BranchCallGate: Incoming stack (%04x:%08RX64) not accessible, rc=%Rrc\n", uNewSS, uNewRsp, VBOXSTRICTRC_VAL(rcStrict)));
1438 return rcStrict;
1439 }
1440 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
1441 if (rcStrict != VINF_SUCCESS)
1442 {
1443 Log(("BranchCallGate: New stack probe unmapping failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1444 return rcStrict;
1445 }
1446
1447 /* Commit new SS:rSP. */
1448 pVCpu->cpum.GstCtx.ss.Sel = uNewSS;
1449 pVCpu->cpum.GstCtx.ss.ValidSel = uNewSS;
1450 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSS.Legacy);
1451 pVCpu->cpum.GstCtx.ss.u32Limit = X86DESC_LIMIT_G(&DescSS.Legacy);
1452 pVCpu->cpum.GstCtx.ss.u64Base = X86DESC_BASE(&DescSS.Legacy);
1453 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
1454 pVCpu->cpum.GstCtx.rsp = uNewRsp;
1455 IEM_SET_CPL(pVCpu, uNewCSDpl); /** @todo Are the parameter words accessed using the new CPL or the old CPL? */
1456 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pVCpu->cpum.GstCtx.ss));
1457 CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_HIDDEN_SEL_REGS);
1458
1459 /* At this point the stack access must not fail because new state was already committed. */
1460 /** @todo this can still fail due to SS.LIMIT not check. */
1461 uint8_t bUnmapInfoRet;
1462 rcStrict = iemMemStackPushBeginSpecial(pVCpu, cbNewStack,
1463 IEM_IS_LONG_MODE(pVCpu) ? 7
1464 : pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE ? 3 : 1,
1465 &uPtrRet.pv, &bUnmapInfoRet, &uNewRsp);
1466 AssertMsgReturn(rcStrict == VINF_SUCCESS, ("BranchCallGate: New stack mapping failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)),
1467 VERR_INTERNAL_ERROR_5);
1468
1469 if (!IEM_IS_LONG_MODE(pVCpu))
1470 {
1471 if (pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE)
1472 {
1473 if (cbWords)
1474 {
1475 /* Map the relevant chunk of the old stack. */
1476 RTPTRUNION uPtrParmWds;
1477 rcStrict = iemMemMap(pVCpu, &uPtrParmWds.pv, &bUnmapInfo, cbWords * 4, UINT8_MAX, GCPtrParmWds,
1478 IEM_ACCESS_DATA_R, 0 /** @todo Can uNewCSDpl == 3? Then we need alignment mask here! */);
1479 if (rcStrict != VINF_SUCCESS)
1480 {
1481 Log(("BranchCallGate: Old stack mapping (32-bit) failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1482 return rcStrict;
1483 }
1484
1485 /* Copy the parameter (d)words. */
1486 for (int i = 0; i < cbWords; ++i)
1487 uPtrRet.pu32[2 + i] = uPtrParmWds.pu32[i];
1488
1489 /* Unmap the old stack. */
1490 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
1491 if (rcStrict != VINF_SUCCESS)
1492 {
1493 Log(("BranchCallGate: Old stack unmapping (32-bit) failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1494 return rcStrict;
1495 }
1496 }
1497
1498 /* Push the old CS:rIP. */
1499 uPtrRet.pu32[0] = pVCpu->cpum.GstCtx.eip + cbInstr;
1500 uPtrRet.pu32[1] = pVCpu->cpum.GstCtx.cs.Sel; /** @todo Testcase: What is written to the high word when pushing CS? */
1501
1502 /* Push the old SS:rSP. */
1503 uPtrRet.pu32[2 + cbWords + 0] = uOldRsp;
1504 uPtrRet.pu32[2 + cbWords + 1] = uOldSS;
1505 }
1506 else
1507 {
1508 Assert(pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_CALL_GATE);
1509
1510 if (cbWords)
1511 {
1512 /* Map the relevant chunk of the old stack. */
1513 RTPTRUNION uPtrParmWds;
1514 rcStrict = iemMemMap(pVCpu, &uPtrParmWds.pv, &bUnmapInfo, cbWords * 2, UINT8_MAX, GCPtrParmWds,
1515 IEM_ACCESS_DATA_R, 0 /** @todo Can uNewCSDpl == 3? Then we need alignment mask here! */);
1516 if (rcStrict != VINF_SUCCESS)
1517 {
1518 Log(("BranchCallGate: Old stack mapping (16-bit) failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1519 return rcStrict;
1520 }
1521
1522 /* Copy the parameter words. */
1523 for (int i = 0; i < cbWords; ++i)
1524 uPtrRet.pu16[2 + i] = uPtrParmWds.pu16[i];
1525
1526 /* Unmap the old stack. */
1527 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
1528 if (rcStrict != VINF_SUCCESS)
1529 {
1530 Log(("BranchCallGate: Old stack unmapping (32-bit) failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1531 return rcStrict;
1532 }
1533 }
1534
1535 /* Push the old CS:rIP. */
1536 uPtrRet.pu16[0] = pVCpu->cpum.GstCtx.ip + cbInstr;
1537 uPtrRet.pu16[1] = pVCpu->cpum.GstCtx.cs.Sel;
1538
1539 /* Push the old SS:rSP. */
1540 uPtrRet.pu16[2 + cbWords + 0] = uOldRsp;
1541 uPtrRet.pu16[2 + cbWords + 1] = uOldSS;
1542 }
1543 }
1544 else
1545 {
1546 Assert(pDesc->Legacy.Gate.u4Type == AMD64_SEL_TYPE_SYS_CALL_GATE);
1547
1548 /* For 64-bit gates, no parameters are copied. Just push old SS:rSP and CS:rIP. */
1549 uPtrRet.pu64[0] = pVCpu->cpum.GstCtx.rip + cbInstr;
1550 uPtrRet.pu64[1] = pVCpu->cpum.GstCtx.cs.Sel; /** @todo Testcase: What is written to the high words when pushing CS? */
1551 uPtrRet.pu64[2] = uOldRsp;
1552 uPtrRet.pu64[3] = uOldSS; /** @todo Testcase: What is written to the high words when pushing SS? */
1553 }
1554
1555 rcStrict = iemMemStackPushCommitSpecial(pVCpu, bUnmapInfoRet, uNewRsp);
1556 if (rcStrict != VINF_SUCCESS)
1557 {
1558 Log(("BranchCallGate: New stack unmapping failed (%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict)));
1559 return rcStrict;
1560 }
1561
1562 /* Chop the high bits off if 16-bit gate (Intel says so). */
1563 if (pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_CALL_GATE)
1564 uNewRip = (uint16_t)uNewRip;
1565
1566 /* Limit / canonical check. */
1567 cbLimit = X86DESC_LIMIT_G(&DescCS.Legacy);
1568 if (!IEM_IS_LONG_MODE(pVCpu))
1569 {
1570 if (uNewRip > cbLimit)
1571 {
1572 Log(("BranchCallGate %04x:%08RX64 -> out of bounds (%#x)\n", uNewCS, uNewRip, cbLimit));
1573 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, 0);
1574 }
1575 u64Base = X86DESC_BASE(&DescCS.Legacy);
1576 }
1577 else
1578 {
1579 Assert(pDesc->Legacy.Gate.u4Type == AMD64_SEL_TYPE_SYS_CALL_GATE);
1580 if (!IEM_IS_CANONICAL(uNewRip))
1581 {
1582 Log(("BranchCallGate call %04x:%016RX64 - not canonical -> #GP\n", uNewCS, uNewRip));
1583 return iemRaiseNotCanonical(pVCpu);
1584 }
1585 u64Base = 0;
1586 }
1587
1588 /*
1589 * Now set the accessed bit before
1590 * writing the return address to the stack and committing the result into
1591 * CS, CSHID and RIP.
1592 */
1593 /** @todo Testcase: Need to check WHEN exactly the accessed bit is set. */
1594 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1595 {
1596 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCS);
1597 if (rcStrict != VINF_SUCCESS)
1598 return rcStrict;
1599 /** @todo check what VT-x and AMD-V does. */
1600 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1601 }
1602
1603 /* Commit new CS:rIP. */
1604 pVCpu->cpum.GstCtx.rip = uNewRip;
1605 pVCpu->cpum.GstCtx.cs.Sel = uNewCS & X86_SEL_MASK_OFF_RPL;
1606 pVCpu->cpum.GstCtx.cs.Sel |= IEM_GET_CPL(pVCpu);
1607 pVCpu->cpum.GstCtx.cs.ValidSel = pVCpu->cpum.GstCtx.cs.Sel;
1608 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
1609 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
1610 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimit;
1611 pVCpu->cpum.GstCtx.cs.u64Base = u64Base;
1612 }
1613 else
1614 {
1615 /* Same privilege. */
1616 /** @todo This is very similar to regular far calls; merge! */
1617
1618 /* Check stack first - may #SS(0). */
1619 /** @todo check how gate size affects pushing of CS! Does callf 16:32 in
1620 * 16-bit code cause a two or four byte CS to be pushed? */
1621 uint8_t bUnmapInfoRet;
1622 rcStrict = iemMemStackPushBeginSpecial(pVCpu,
1623 IEM_IS_LONG_MODE(pVCpu) ? 8+8
1624 : pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE ? 4+4 : 2+2,
1625 IEM_IS_LONG_MODE(pVCpu) ? 7
1626 : pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE ? 3 : 2,
1627 &uPtrRet.pv, &bUnmapInfoRet, &uNewRsp);
1628 if (rcStrict != VINF_SUCCESS)
1629 return rcStrict;
1630
1631 /* Chop the high bits off if 16-bit gate (Intel says so). */
1632 if (pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_CALL_GATE)
1633 uNewRip = (uint16_t)uNewRip;
1634
1635 /* Limit / canonical check. */
1636 cbLimit = X86DESC_LIMIT_G(&DescCS.Legacy);
1637 if (!IEM_IS_LONG_MODE(pVCpu))
1638 {
1639 if (uNewRip > cbLimit)
1640 {
1641 Log(("BranchCallGate %04x:%08RX64 -> out of bounds (%#x)\n", uNewCS, uNewRip, cbLimit));
1642 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, 0);
1643 }
1644 u64Base = X86DESC_BASE(&DescCS.Legacy);
1645 }
1646 else
1647 {
1648 if (!IEM_IS_CANONICAL(uNewRip))
1649 {
1650 Log(("BranchCallGate call %04x:%016RX64 - not canonical -> #GP\n", uNewCS, uNewRip));
1651 return iemRaiseNotCanonical(pVCpu);
1652 }
1653 u64Base = 0;
1654 }
1655
1656 /*
1657 * Now set the accessed bit before
1658 * writing the return address to the stack and committing the result into
1659 * CS, CSHID and RIP.
1660 */
1661 /** @todo Testcase: Need to check WHEN exactly the accessed bit is set. */
1662 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1663 {
1664 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCS);
1665 if (rcStrict != VINF_SUCCESS)
1666 return rcStrict;
1667 /** @todo check what VT-x and AMD-V does. */
1668 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1669 }
1670
1671 /* stack */
1672 if (!IEM_IS_LONG_MODE(pVCpu))
1673 {
1674 if (pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_386_CALL_GATE)
1675 {
1676 uPtrRet.pu32[0] = pVCpu->cpum.GstCtx.eip + cbInstr;
1677 uPtrRet.pu32[1] = pVCpu->cpum.GstCtx.cs.Sel; /** @todo Testcase: What is written to the high word when pushing CS? */
1678 }
1679 else
1680 {
1681 Assert(pDesc->Legacy.Gate.u4Type == X86_SEL_TYPE_SYS_286_CALL_GATE);
1682 uPtrRet.pu16[0] = pVCpu->cpum.GstCtx.ip + cbInstr;
1683 uPtrRet.pu16[1] = pVCpu->cpum.GstCtx.cs.Sel;
1684 }
1685 }
1686 else
1687 {
1688 Assert(pDesc->Legacy.Gate.u4Type == AMD64_SEL_TYPE_SYS_CALL_GATE);
1689 uPtrRet.pu64[0] = pVCpu->cpum.GstCtx.rip + cbInstr;
1690 uPtrRet.pu64[1] = pVCpu->cpum.GstCtx.cs.Sel; /** @todo Testcase: What is written to the high words when pushing CS? */
1691 }
1692
1693 rcStrict = iemMemStackPushCommitSpecial(pVCpu, bUnmapInfoRet, uNewRsp);
1694 if (rcStrict != VINF_SUCCESS)
1695 return rcStrict;
1696
1697 /* commit */
1698 pVCpu->cpum.GstCtx.rip = uNewRip;
1699 pVCpu->cpum.GstCtx.cs.Sel = uNewCS & X86_SEL_MASK_OFF_RPL;
1700 pVCpu->cpum.GstCtx.cs.Sel |= IEM_GET_CPL(pVCpu);
1701 pVCpu->cpum.GstCtx.cs.ValidSel = pVCpu->cpum.GstCtx.cs.Sel;
1702 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
1703 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
1704 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimit;
1705 pVCpu->cpum.GstCtx.cs.u64Base = u64Base;
1706 }
1707 }
1708 pVCpu->cpum.GstCtx.eflags.Bits.u1RF = 0;
1709
1710 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
1711
1712/** @todo single stepping */
1713
1714 /* Flush the prefetch buffer. */
1715 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
1716 return VINF_SUCCESS;
1717#endif /* IEM_IMPLEMENTS_CALLGATE */
1718}
1719
1720
1721/**
1722 * Implements far jumps and calls thru system selectors.
1723 *
1724 * @returns VBox strict status code.
1725 * @param pVCpu The cross context virtual CPU structure of the
1726 * calling thread.
1727 * @param cbInstr The current instruction length.
1728 * @param uSel The selector.
1729 * @param enmBranch The kind of branching we're performing.
1730 * @param enmEffOpSize The effective operand size.
1731 * @param pDesc The descriptor corresponding to @a uSel.
1732 */
1733static VBOXSTRICTRC iemCImpl_BranchSysSel(PVMCPUCC pVCpu, uint8_t cbInstr, uint16_t uSel, IEMBRANCH enmBranch,
1734 IEMMODE enmEffOpSize, PIEMSELDESC pDesc)
1735{
1736 Assert(enmBranch == IEMBRANCH_JUMP || enmBranch == IEMBRANCH_CALL);
1737 Assert((uSel & X86_SEL_MASK_OFF_RPL));
1738 IEM_CTX_IMPORT_RET(pVCpu, IEM_CPUMCTX_EXTRN_XCPT_MASK);
1739
1740 if (IEM_IS_LONG_MODE(pVCpu))
1741 switch (pDesc->Legacy.Gen.u4Type)
1742 {
1743 case AMD64_SEL_TYPE_SYS_CALL_GATE:
1744 return iemCImpl_BranchCallGate(pVCpu, cbInstr, uSel, enmBranch, enmEffOpSize, pDesc);
1745
1746 default:
1747 case AMD64_SEL_TYPE_SYS_LDT:
1748 case AMD64_SEL_TYPE_SYS_TSS_BUSY:
1749 case AMD64_SEL_TYPE_SYS_TSS_AVAIL:
1750 case AMD64_SEL_TYPE_SYS_TRAP_GATE:
1751 case AMD64_SEL_TYPE_SYS_INT_GATE:
1752 Log(("branch %04x -> wrong sys selector (64-bit): %d\n", uSel, pDesc->Legacy.Gen.u4Type));
1753 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1754 }
1755
1756 switch (pDesc->Legacy.Gen.u4Type)
1757 {
1758 case X86_SEL_TYPE_SYS_286_CALL_GATE:
1759 case X86_SEL_TYPE_SYS_386_CALL_GATE:
1760 return iemCImpl_BranchCallGate(pVCpu, cbInstr, uSel, enmBranch, enmEffOpSize, pDesc);
1761
1762 case X86_SEL_TYPE_SYS_TASK_GATE:
1763 return iemCImpl_BranchTaskGate(pVCpu, cbInstr, uSel, enmBranch, enmEffOpSize, pDesc);
1764
1765 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
1766 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
1767 return iemCImpl_BranchTaskSegment(pVCpu, cbInstr, uSel, enmBranch, enmEffOpSize, pDesc);
1768
1769 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
1770 Log(("branch %04x -> busy 286 TSS\n", uSel));
1771 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1772
1773 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
1774 Log(("branch %04x -> busy 386 TSS\n", uSel));
1775 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1776
1777 default:
1778 case X86_SEL_TYPE_SYS_LDT:
1779 case X86_SEL_TYPE_SYS_286_INT_GATE:
1780 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
1781 case X86_SEL_TYPE_SYS_386_INT_GATE:
1782 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
1783 Log(("branch %04x -> wrong sys selector: %d\n", uSel, pDesc->Legacy.Gen.u4Type));
1784 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1785 }
1786}
1787
1788
1789/**
1790 * Implements far jumps.
1791 *
1792 * @param uSel The selector.
1793 * @param offSeg The segment offset.
1794 * @param enmEffOpSize The effective operand size.
1795 */
1796IEM_CIMPL_DEF_3(iemCImpl_FarJmp, uint16_t, uSel, uint64_t, offSeg, IEMMODE, enmEffOpSize)
1797{
1798 NOREF(cbInstr);
1799 Assert(offSeg <= UINT32_MAX || (!IEM_IS_GUEST_CPU_AMD(pVCpu) && IEM_IS_64BIT_CODE(pVCpu)));
1800
1801 /*
1802 * Real mode and V8086 mode are easy. The only snag seems to be that
1803 * CS.limit doesn't change and the limit check is done against the current
1804 * limit.
1805 */
1806 /** @todo Robert Collins claims (The Segment Descriptor Cache, DDJ August
1807 * 1998) that up to and including the Intel 486, far control
1808 * transfers in real mode set default CS attributes (0x93) and also
1809 * set a 64K segment limit. Starting with the Pentium, the
1810 * attributes and limit are left alone but the access rights are
1811 * ignored. We only implement the Pentium+ behavior.
1812 * */
1813 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
1814 {
1815 Assert(enmEffOpSize == IEMMODE_16BIT || enmEffOpSize == IEMMODE_32BIT);
1816 if (offSeg > pVCpu->cpum.GstCtx.cs.u32Limit)
1817 {
1818 Log(("iemCImpl_FarJmp: 16-bit limit\n"));
1819 return iemRaiseGeneralProtectionFault0(pVCpu);
1820 }
1821
1822 if (enmEffOpSize == IEMMODE_16BIT) /** @todo WRONG, must pass this. */
1823 pVCpu->cpum.GstCtx.rip = offSeg;
1824 else
1825 pVCpu->cpum.GstCtx.rip = offSeg & UINT16_MAX;
1826 pVCpu->cpum.GstCtx.cs.Sel = uSel;
1827 pVCpu->cpum.GstCtx.cs.ValidSel = uSel;
1828 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
1829 pVCpu->cpum.GstCtx.cs.u64Base = (uint32_t)uSel << 4;
1830
1831 /* Update the FLAT 32-bit mode flag, if we're in 32-bit unreal mode (unlikely): */
1832 if (RT_LIKELY(!IEM_IS_32BIT_CODE(pVCpu)))
1833 { /* likely */ }
1834 else if (uSel != 0)
1835 pVCpu->iem.s.fExec &= ~IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK;
1836 else
1837 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK)
1838 | iemCalc32BitFlatIndicator(pVCpu);
1839
1840 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
1841 }
1842
1843 /*
1844 * Protected mode. Need to parse the specified descriptor...
1845 */
1846 if (!(uSel & X86_SEL_MASK_OFF_RPL))
1847 {
1848 Log(("jmpf %04x:%08RX64 -> invalid selector, #GP(0)\n", uSel, offSeg));
1849 return iemRaiseGeneralProtectionFault0(pVCpu);
1850 }
1851
1852 /* Fetch the descriptor. */
1853 IEMSELDESC Desc;
1854 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pVCpu, &Desc, uSel, X86_XCPT_GP);
1855 if (rcStrict != VINF_SUCCESS)
1856 return rcStrict;
1857
1858 /* Is it there? */
1859 if (!Desc.Legacy.Gen.u1Present) /** @todo this is probably checked too early. Testcase! */
1860 {
1861 Log(("jmpf %04x:%08RX64 -> segment not present\n", uSel, offSeg));
1862 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSel);
1863 }
1864
1865 /*
1866 * Deal with it according to its type. We do the standard code selectors
1867 * here and dispatch the system selectors to worker functions.
1868 */
1869 if (!Desc.Legacy.Gen.u1DescType)
1870 return iemCImpl_BranchSysSel(pVCpu, cbInstr, uSel, IEMBRANCH_JUMP, enmEffOpSize, &Desc);
1871
1872 /* Only code segments. */
1873 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
1874 {
1875 Log(("jmpf %04x:%08RX64 -> not a code selector (u4Type=%#x).\n", uSel, offSeg, Desc.Legacy.Gen.u4Type));
1876 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1877 }
1878
1879 /* L vs D. */
1880 if ( Desc.Legacy.Gen.u1Long
1881 && Desc.Legacy.Gen.u1DefBig
1882 && IEM_IS_LONG_MODE(pVCpu))
1883 {
1884 Log(("jmpf %04x:%08RX64 -> both L and D are set.\n", uSel, offSeg));
1885 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1886 }
1887
1888 /* DPL/RPL/CPL check, where conforming segments makes a difference. */
1889 if (Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
1890 {
1891 if (IEM_GET_CPL(pVCpu) < Desc.Legacy.Gen.u2Dpl)
1892 {
1893 Log(("jmpf %04x:%08RX64 -> DPL violation (conforming); DPL=%d CPL=%u\n",
1894 uSel, offSeg, Desc.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
1895 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1896 }
1897 }
1898 else
1899 {
1900 if (IEM_GET_CPL(pVCpu) != Desc.Legacy.Gen.u2Dpl)
1901 {
1902 Log(("jmpf %04x:%08RX64 -> CPL != DPL; DPL=%d CPL=%u\n", uSel, offSeg, Desc.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
1903 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1904 }
1905 if ((uSel & X86_SEL_RPL) > IEM_GET_CPL(pVCpu))
1906 {
1907 Log(("jmpf %04x:%08RX64 -> RPL > DPL; RPL=%d CPL=%u\n", uSel, offSeg, (uSel & X86_SEL_RPL), IEM_GET_CPL(pVCpu)));
1908 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1909 }
1910 }
1911
1912 /* Chop the high bits if 16-bit (Intel says so). */
1913 if (enmEffOpSize == IEMMODE_16BIT)
1914 offSeg &= UINT16_MAX;
1915
1916 /* Limit check and get the base. */
1917 uint64_t u64Base;
1918 uint32_t cbLimit = X86DESC_LIMIT_G(&Desc.Legacy);
1919 if ( !Desc.Legacy.Gen.u1Long
1920 || !IEM_IS_LONG_MODE(pVCpu))
1921 {
1922 if (RT_LIKELY(offSeg <= cbLimit))
1923 u64Base = X86DESC_BASE(&Desc.Legacy);
1924 else
1925 {
1926 Log(("jmpf %04x:%08RX64 -> out of bounds (%#x)\n", uSel, offSeg, cbLimit));
1927 /** @todo Intel says this is \#GP(0)! */
1928 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
1929 }
1930 }
1931 else
1932 u64Base = 0;
1933
1934 /*
1935 * Ok, everything checked out fine. Now set the accessed bit before
1936 * committing the result into CS, CSHID and RIP.
1937 */
1938 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1939 {
1940 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uSel);
1941 if (rcStrict != VINF_SUCCESS)
1942 return rcStrict;
1943 /** @todo check what VT-x and AMD-V does. */
1944 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1945 }
1946
1947 /* commit */
1948 pVCpu->cpum.GstCtx.rip = offSeg;
1949 pVCpu->cpum.GstCtx.cs.Sel = uSel & X86_SEL_MASK_OFF_RPL;
1950 pVCpu->cpum.GstCtx.cs.Sel |= IEM_GET_CPL(pVCpu); /** @todo is this right for conforming segs? or in general? */
1951 pVCpu->cpum.GstCtx.cs.ValidSel = pVCpu->cpum.GstCtx.cs.Sel;
1952 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
1953 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
1954 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimit;
1955 pVCpu->cpum.GstCtx.cs.u64Base = u64Base;
1956
1957 /** @todo check if the hidden bits are loaded correctly for 64-bit
1958 * mode. */
1959
1960 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
1961
1962 /* Flush the prefetch buffer. */
1963 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
1964
1965 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
1966}
1967
1968
1969/**
1970 * Implements far calls.
1971 *
1972 * This very similar to iemCImpl_FarJmp.
1973 *
1974 * @param uSel The selector.
1975 * @param offSeg The segment offset.
1976 * @param enmEffOpSize The operand size (in case we need it).
1977 */
1978IEM_CIMPL_DEF_3(iemCImpl_callf, uint16_t, uSel, uint64_t, offSeg, IEMMODE, enmEffOpSize)
1979{
1980 VBOXSTRICTRC rcStrict;
1981 uint64_t uNewRsp;
1982 RTPTRUNION uPtrRet;
1983 uint8_t bUnmapInfo;
1984
1985 /*
1986 * Real mode and V8086 mode are easy. The only snag seems to be that
1987 * CS.limit doesn't change and the limit check is done against the current
1988 * limit.
1989 */
1990 /** @todo See comment for similar code in iemCImpl_FarJmp */
1991 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
1992 {
1993 Assert(enmEffOpSize == IEMMODE_16BIT || enmEffOpSize == IEMMODE_32BIT);
1994
1995 /* Check stack first - may #SS(0). */
1996 rcStrict = iemMemStackPushBeginSpecial(pVCpu, enmEffOpSize == IEMMODE_32BIT ? 4+4 : 2+2,
1997 enmEffOpSize == IEMMODE_32BIT ? 3 : 1,
1998 &uPtrRet.pv, &bUnmapInfo, &uNewRsp);
1999 if (rcStrict != VINF_SUCCESS)
2000 return rcStrict;
2001
2002 /* Check the target address range. */
2003/** @todo this must be wrong! Write unreal mode tests! */
2004 if (offSeg > UINT32_MAX)
2005 return iemRaiseGeneralProtectionFault0(pVCpu);
2006
2007 /* Everything is fine, push the return address. */
2008 if (enmEffOpSize == IEMMODE_16BIT)
2009 {
2010 uPtrRet.pu16[0] = pVCpu->cpum.GstCtx.ip + cbInstr;
2011 uPtrRet.pu16[1] = pVCpu->cpum.GstCtx.cs.Sel;
2012 }
2013 else
2014 {
2015 uPtrRet.pu32[0] = pVCpu->cpum.GstCtx.eip + cbInstr;
2016 uPtrRet.pu16[2] = pVCpu->cpum.GstCtx.cs.Sel;
2017 }
2018 rcStrict = iemMemStackPushCommitSpecial(pVCpu, bUnmapInfo, uNewRsp);
2019 if (rcStrict != VINF_SUCCESS)
2020 return rcStrict;
2021
2022 /* Branch. */
2023 pVCpu->cpum.GstCtx.rip = offSeg;
2024 pVCpu->cpum.GstCtx.cs.Sel = uSel;
2025 pVCpu->cpum.GstCtx.cs.ValidSel = uSel;
2026 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
2027 pVCpu->cpum.GstCtx.cs.u64Base = (uint32_t)uSel << 4;
2028
2029 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
2030 }
2031
2032 /*
2033 * Protected mode. Need to parse the specified descriptor...
2034 */
2035 if (!(uSel & X86_SEL_MASK_OFF_RPL))
2036 {
2037 Log(("callf %04x:%08RX64 -> invalid selector, #GP(0)\n", uSel, offSeg));
2038 return iemRaiseGeneralProtectionFault0(pVCpu);
2039 }
2040
2041 /* Fetch the descriptor. */
2042 IEMSELDESC Desc;
2043 rcStrict = iemMemFetchSelDesc(pVCpu, &Desc, uSel, X86_XCPT_GP);
2044 if (rcStrict != VINF_SUCCESS)
2045 return rcStrict;
2046
2047 /*
2048 * Deal with it according to its type. We do the standard code selectors
2049 * here and dispatch the system selectors to worker functions.
2050 */
2051 if (!Desc.Legacy.Gen.u1DescType)
2052 return iemCImpl_BranchSysSel(pVCpu, cbInstr, uSel, IEMBRANCH_CALL, enmEffOpSize, &Desc);
2053
2054 /* Only code segments. */
2055 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
2056 {
2057 Log(("callf %04x:%08RX64 -> not a code selector (u4Type=%#x).\n", uSel, offSeg, Desc.Legacy.Gen.u4Type));
2058 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
2059 }
2060
2061 /* L vs D. */
2062 if ( Desc.Legacy.Gen.u1Long
2063 && Desc.Legacy.Gen.u1DefBig
2064 && IEM_IS_LONG_MODE(pVCpu))
2065 {
2066 Log(("callf %04x:%08RX64 -> both L and D are set.\n", uSel, offSeg));
2067 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
2068 }
2069
2070 /* DPL/RPL/CPL check, where conforming segments makes a difference. */
2071 if (Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
2072 {
2073 if (IEM_GET_CPL(pVCpu) < Desc.Legacy.Gen.u2Dpl)
2074 {
2075 Log(("callf %04x:%08RX64 -> DPL violation (conforming); DPL=%d CPL=%u\n",
2076 uSel, offSeg, Desc.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
2077 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
2078 }
2079 }
2080 else
2081 {
2082 if (IEM_GET_CPL(pVCpu) != Desc.Legacy.Gen.u2Dpl)
2083 {
2084 Log(("callf %04x:%08RX64 -> CPL != DPL; DPL=%d CPL=%u\n", uSel, offSeg, Desc.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
2085 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
2086 }
2087 if ((uSel & X86_SEL_RPL) > IEM_GET_CPL(pVCpu))
2088 {
2089 Log(("callf %04x:%08RX64 -> RPL > DPL; RPL=%d CPL=%u\n", uSel, offSeg, (uSel & X86_SEL_RPL), IEM_GET_CPL(pVCpu)));
2090 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
2091 }
2092 }
2093
2094 /* Is it there? */
2095 if (!Desc.Legacy.Gen.u1Present)
2096 {
2097 Log(("callf %04x:%08RX64 -> segment not present\n", uSel, offSeg));
2098 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSel);
2099 }
2100
2101 /* Check stack first - may #SS(0). */
2102 /** @todo check how operand prefix affects pushing of CS! Does callf 16:32 in
2103 * 16-bit code cause a two or four byte CS to be pushed? */
2104 rcStrict = iemMemStackPushBeginSpecial(pVCpu,
2105 enmEffOpSize == IEMMODE_64BIT ? 8+8 : enmEffOpSize == IEMMODE_32BIT ? 4+4 : 2+2,
2106 enmEffOpSize == IEMMODE_64BIT ? 7 : enmEffOpSize == IEMMODE_32BIT ? 3 : 1,
2107 &uPtrRet.pv, &bUnmapInfo, &uNewRsp);
2108 if (rcStrict != VINF_SUCCESS)
2109 return rcStrict;
2110
2111 /* Chop the high bits if 16-bit (Intel says so). */
2112 if (enmEffOpSize == IEMMODE_16BIT)
2113 offSeg &= UINT16_MAX;
2114
2115 /* Limit / canonical check. */
2116 uint64_t u64Base;
2117 uint32_t cbLimit = X86DESC_LIMIT_G(&Desc.Legacy);
2118 if ( !Desc.Legacy.Gen.u1Long
2119 || !IEM_IS_LONG_MODE(pVCpu))
2120 {
2121 if (RT_LIKELY(offSeg <= cbLimit))
2122 u64Base = X86DESC_BASE(&Desc.Legacy);
2123 else
2124 {
2125 Log(("callf %04x:%08RX64 -> out of bounds (%#x)\n", uSel, offSeg, cbLimit));
2126 /** @todo Intel says this is \#GP(0)! */
2127 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
2128 }
2129 }
2130 else if (IEM_IS_CANONICAL(offSeg))
2131 u64Base = 0;
2132 else
2133 {
2134 Log(("callf %04x:%016RX64 - not canonical -> #GP\n", uSel, offSeg));
2135 return iemRaiseNotCanonical(pVCpu);
2136 }
2137
2138 /*
2139 * Now set the accessed bit before
2140 * writing the return address to the stack and committing the result into
2141 * CS, CSHID and RIP.
2142 */
2143 /** @todo Testcase: Need to check WHEN exactly the accessed bit is set. */
2144 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2145 {
2146 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uSel);
2147 if (rcStrict != VINF_SUCCESS)
2148 return rcStrict;
2149 /** @todo check what VT-x and AMD-V does. */
2150 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2151 }
2152
2153 /* stack */
2154 if (enmEffOpSize == IEMMODE_16BIT)
2155 {
2156 uPtrRet.pu16[0] = pVCpu->cpum.GstCtx.ip + cbInstr;
2157 uPtrRet.pu16[1] = pVCpu->cpum.GstCtx.cs.Sel;
2158 }
2159 else if (enmEffOpSize == IEMMODE_32BIT)
2160 {
2161 uPtrRet.pu32[0] = pVCpu->cpum.GstCtx.eip + cbInstr;
2162 uPtrRet.pu32[1] = pVCpu->cpum.GstCtx.cs.Sel; /** @todo Testcase: What is written to the high word when callf is pushing CS? */
2163 }
2164 else
2165 {
2166 uPtrRet.pu64[0] = pVCpu->cpum.GstCtx.rip + cbInstr;
2167 uPtrRet.pu64[1] = pVCpu->cpum.GstCtx.cs.Sel; /** @todo Testcase: What is written to the high words when callf is pushing CS? */
2168 }
2169 rcStrict = iemMemStackPushCommitSpecial(pVCpu, bUnmapInfo, uNewRsp);
2170 if (rcStrict != VINF_SUCCESS)
2171 return rcStrict;
2172
2173 /* commit */
2174 pVCpu->cpum.GstCtx.rip = offSeg;
2175 pVCpu->cpum.GstCtx.cs.Sel = uSel & X86_SEL_MASK_OFF_RPL;
2176 pVCpu->cpum.GstCtx.cs.Sel |= IEM_GET_CPL(pVCpu);
2177 pVCpu->cpum.GstCtx.cs.ValidSel = pVCpu->cpum.GstCtx.cs.Sel;
2178 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
2179 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
2180 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimit;
2181 pVCpu->cpum.GstCtx.cs.u64Base = u64Base;
2182
2183 /** @todo check if the hidden bits are loaded correctly for 64-bit
2184 * mode. */
2185
2186 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
2187
2188 /* Flush the prefetch buffer. */
2189 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
2190
2191 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
2192}
2193
2194
2195/**
2196 * Implements retf.
2197 *
2198 * @param enmEffOpSize The effective operand size.
2199 * @param cbPop The amount of arguments to pop from the stack
2200 * (bytes).
2201 */
2202IEM_CIMPL_DEF_2(iemCImpl_retf, IEMMODE, enmEffOpSize, uint16_t, cbPop)
2203{
2204 NOREF(cbInstr);
2205
2206 /*
2207 * Read the stack values first.
2208 */
2209 RTUINT64U NewRsp;
2210 uint8_t bUnmapInfo;
2211 RTCPTRUNION uPtrFrame;
2212 uint32_t cbRetPtr = enmEffOpSize == IEMMODE_16BIT ? 2+2
2213 : enmEffOpSize == IEMMODE_32BIT ? 4+4 : 8+8;
2214 VBOXSTRICTRC rcStrict = iemMemStackPopBeginSpecial(pVCpu, cbRetPtr,
2215 enmEffOpSize == IEMMODE_16BIT ? 1 : enmEffOpSize == IEMMODE_32BIT ? 3 : 7,
2216 &uPtrFrame.pv, &bUnmapInfo, &NewRsp.u);
2217 if (rcStrict != VINF_SUCCESS)
2218 return rcStrict;
2219
2220 uint64_t uNewRip;
2221 uint16_t uNewCs;
2222 if (enmEffOpSize == IEMMODE_16BIT)
2223 {
2224 uNewRip = uPtrFrame.pu16[0];
2225 uNewCs = uPtrFrame.pu16[1];
2226 }
2227 else if (enmEffOpSize == IEMMODE_32BIT)
2228 {
2229 uNewRip = uPtrFrame.pu32[0];
2230 uNewCs = uPtrFrame.pu16[2];
2231 }
2232 else
2233 {
2234 uNewRip = uPtrFrame.pu64[0];
2235 uNewCs = uPtrFrame.pu16[4];
2236 }
2237
2238 rcStrict = iemMemStackPopDoneSpecial(pVCpu, bUnmapInfo);
2239 if (RT_LIKELY(rcStrict == VINF_SUCCESS))
2240 { /* extremely likely */ }
2241 else
2242 return rcStrict;
2243
2244 /*
2245 * Real mode and V8086 mode are easy.
2246 */
2247 /** @todo See comment for similar code in iemCImpl_FarJmp */
2248 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
2249 {
2250 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
2251 /** @todo check how this is supposed to work if sp=0xfffe. */
2252
2253 /* Check the limit of the new EIP. */
2254 /** @todo Intel pseudo code only does the limit check for 16-bit
2255 * operands, AMD does not make any distinction. What is right? */
2256 if (uNewRip > pVCpu->cpum.GstCtx.cs.u32Limit)
2257 return iemRaiseSelectorBounds(pVCpu, X86_SREG_CS, IEM_ACCESS_INSTRUCTION);
2258
2259 /* commit the operation. */
2260 if (cbPop)
2261 iemRegAddToRspEx(pVCpu, &NewRsp, cbPop);
2262 pVCpu->cpum.GstCtx.rsp = NewRsp.u;
2263 pVCpu->cpum.GstCtx.rip = uNewRip;
2264 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
2265 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
2266 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
2267 pVCpu->cpum.GstCtx.cs.u64Base = (uint32_t)uNewCs << 4;
2268 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
2269 }
2270
2271 /*
2272 * Protected mode is complicated, of course.
2273 */
2274 if (!(uNewCs & X86_SEL_MASK_OFF_RPL))
2275 {
2276 Log(("retf %04x:%08RX64 -> invalid selector, #GP(0)\n", uNewCs, uNewRip));
2277 return iemRaiseGeneralProtectionFault0(pVCpu);
2278 }
2279
2280 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_MASK | CPUMCTX_EXTRN_GDTR | CPUMCTX_EXTRN_LDTR);
2281
2282 /* Fetch the descriptor. */
2283 IEMSELDESC DescCs;
2284 rcStrict = iemMemFetchSelDesc(pVCpu, &DescCs, uNewCs, X86_XCPT_GP);
2285 if (rcStrict != VINF_SUCCESS)
2286 return rcStrict;
2287
2288 /* Can only return to a code selector. */
2289 if ( !DescCs.Legacy.Gen.u1DescType
2290 || !(DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE) )
2291 {
2292 Log(("retf %04x:%08RX64 -> not a code selector (u1DescType=%u u4Type=%#x).\n",
2293 uNewCs, uNewRip, DescCs.Legacy.Gen.u1DescType, DescCs.Legacy.Gen.u4Type));
2294 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2295 }
2296
2297 /* L vs D. */
2298 if ( DescCs.Legacy.Gen.u1Long /** @todo Testcase: far return to a selector with both L and D set. */
2299 && DescCs.Legacy.Gen.u1DefBig
2300 && IEM_IS_LONG_MODE(pVCpu))
2301 {
2302 Log(("retf %04x:%08RX64 -> both L & D set.\n", uNewCs, uNewRip));
2303 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2304 }
2305
2306 /* DPL/RPL/CPL checks. */
2307 if ((uNewCs & X86_SEL_RPL) < IEM_GET_CPL(pVCpu))
2308 {
2309 Log(("retf %04x:%08RX64 -> RPL < CPL(%d).\n", uNewCs, uNewRip, IEM_GET_CPL(pVCpu)));
2310 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2311 }
2312
2313 if (DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF)
2314 {
2315 if ((uNewCs & X86_SEL_RPL) < DescCs.Legacy.Gen.u2Dpl)
2316 {
2317 Log(("retf %04x:%08RX64 -> DPL violation (conforming); DPL=%u RPL=%u\n",
2318 uNewCs, uNewRip, DescCs.Legacy.Gen.u2Dpl, (uNewCs & X86_SEL_RPL)));
2319 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2320 }
2321 }
2322 else
2323 {
2324 if ((uNewCs & X86_SEL_RPL) != DescCs.Legacy.Gen.u2Dpl)
2325 {
2326 Log(("retf %04x:%08RX64 -> RPL != DPL; DPL=%u RPL=%u\n",
2327 uNewCs, uNewRip, DescCs.Legacy.Gen.u2Dpl, (uNewCs & X86_SEL_RPL)));
2328 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2329 }
2330 }
2331
2332 /* Is it there? */
2333 if (!DescCs.Legacy.Gen.u1Present)
2334 {
2335 Log(("retf %04x:%08RX64 -> segment not present\n", uNewCs, uNewRip));
2336 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewCs);
2337 }
2338
2339 /*
2340 * Return to outer privilege? (We'll typically have entered via a call gate.)
2341 */
2342 if ((uNewCs & X86_SEL_RPL) != IEM_GET_CPL(pVCpu))
2343 {
2344 /* Read the outer stack pointer stored *after* the parameters. */
2345 rcStrict = iemMemStackPopContinueSpecial(pVCpu, cbPop /*off*/, cbRetPtr, &uPtrFrame.pv, &bUnmapInfo, NewRsp.u);
2346 if (rcStrict != VINF_SUCCESS)
2347 return rcStrict;
2348
2349 uint16_t uNewOuterSs;
2350 RTUINT64U NewOuterRsp;
2351 if (enmEffOpSize == IEMMODE_16BIT)
2352 {
2353 NewOuterRsp.u = uPtrFrame.pu16[0];
2354 uNewOuterSs = uPtrFrame.pu16[1];
2355 }
2356 else if (enmEffOpSize == IEMMODE_32BIT)
2357 {
2358 NewOuterRsp.u = uPtrFrame.pu32[0];
2359 uNewOuterSs = uPtrFrame.pu16[2];
2360 }
2361 else
2362 {
2363 NewOuterRsp.u = uPtrFrame.pu64[0];
2364 uNewOuterSs = uPtrFrame.pu16[4];
2365 }
2366 rcStrict = iemMemStackPopDoneSpecial(pVCpu, bUnmapInfo);
2367 if (RT_LIKELY(rcStrict == VINF_SUCCESS))
2368 { /* extremely likely */ }
2369 else
2370 return rcStrict;
2371
2372 /* Check for NULL stack selector (invalid in ring-3 and non-long mode)
2373 and read the selector. */
2374 IEMSELDESC DescSs;
2375 if (!(uNewOuterSs & X86_SEL_MASK_OFF_RPL))
2376 {
2377 if ( !DescCs.Legacy.Gen.u1Long
2378 || (uNewOuterSs & X86_SEL_RPL) == 3)
2379 {
2380 Log(("retf %04x:%08RX64 %04x:%08RX64 -> invalid stack selector, #GP\n",
2381 uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u));
2382 return iemRaiseGeneralProtectionFault0(pVCpu);
2383 }
2384 /** @todo Testcase: Return far to ring-1 or ring-2 with SS=0. */
2385 iemMemFakeStackSelDesc(&DescSs, (uNewOuterSs & X86_SEL_RPL));
2386 }
2387 else
2388 {
2389 /* Fetch the descriptor for the new stack segment. */
2390 rcStrict = iemMemFetchSelDesc(pVCpu, &DescSs, uNewOuterSs, X86_XCPT_GP);
2391 if (rcStrict != VINF_SUCCESS)
2392 return rcStrict;
2393 }
2394
2395 /* Check that RPL of stack and code selectors match. */
2396 if ((uNewCs & X86_SEL_RPL) != (uNewOuterSs & X86_SEL_RPL))
2397 {
2398 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS.RPL != CS.RPL -> #GP(SS)\n", uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u));
2399 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewOuterSs);
2400 }
2401
2402 /* Must be a writable data segment. */
2403 if ( !DescSs.Legacy.Gen.u1DescType
2404 || (DescSs.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE)
2405 || !(DescSs.Legacy.Gen.u4Type & X86_SEL_TYPE_WRITE) )
2406 {
2407 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS not a writable data segment (u1DescType=%u u4Type=%#x) -> #GP(SS).\n",
2408 uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u, DescSs.Legacy.Gen.u1DescType, DescSs.Legacy.Gen.u4Type));
2409 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewOuterSs);
2410 }
2411
2412 /* L vs D. (Not mentioned by intel.) */
2413 if ( DescSs.Legacy.Gen.u1Long /** @todo Testcase: far return to a stack selector with both L and D set. */
2414 && DescSs.Legacy.Gen.u1DefBig
2415 && IEM_IS_LONG_MODE(pVCpu))
2416 {
2417 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS has both L & D set -> #GP(SS).\n",
2418 uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u));
2419 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewOuterSs);
2420 }
2421
2422 /* DPL/RPL/CPL checks. */
2423 if (DescSs.Legacy.Gen.u2Dpl != (uNewCs & X86_SEL_RPL))
2424 {
2425 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS.DPL(%u) != CS.RPL (%u) -> #GP(SS).\n",
2426 uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u, DescSs.Legacy.Gen.u2Dpl, uNewCs & X86_SEL_RPL));
2427 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewOuterSs);
2428 }
2429
2430 /* Is it there? */
2431 if (!DescSs.Legacy.Gen.u1Present)
2432 {
2433 Log(("retf %04x:%08RX64 %04x:%08RX64 - SS not present -> #NP(SS).\n", uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u));
2434 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewCs);
2435 }
2436
2437 /* Calc SS limit.*/
2438 uint64_t u64BaseSs;
2439 uint32_t cbLimitSs = X86DESC_LIMIT_G(&DescSs.Legacy);
2440
2441 /* Is RIP canonical or within CS.limit? */
2442 uint64_t u64BaseCs;
2443 uint32_t cbLimitCs = X86DESC_LIMIT_G(&DescCs.Legacy);
2444
2445 /** @todo Testcase: Is this correct? */
2446 if ( DescCs.Legacy.Gen.u1Long
2447 && IEM_IS_LONG_MODE(pVCpu) )
2448 {
2449 if (!IEM_IS_CANONICAL(uNewRip))
2450 {
2451 Log(("retf %04x:%08RX64 %04x:%08RX64 - not canonical -> #GP.\n", uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u));
2452 return iemRaiseNotCanonical(pVCpu);
2453 }
2454 u64BaseCs = 0;
2455 u64BaseSs = 0;
2456 }
2457 else
2458 {
2459 if (uNewRip > cbLimitCs)
2460 {
2461 Log(("retf %04x:%08RX64 %04x:%08RX64 - out of bounds (%#x)-> #GP(CS).\n",
2462 uNewCs, uNewRip, uNewOuterSs, NewOuterRsp.u, cbLimitCs));
2463 /** @todo Intel says this is \#GP(0)! */
2464 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2465 }
2466 u64BaseCs = X86DESC_BASE(&DescCs.Legacy);
2467 u64BaseSs = X86DESC_BASE(&DescSs.Legacy);
2468 }
2469
2470 /*
2471 * Now set the accessed bit before
2472 * writing the return address to the stack and committing the result into
2473 * CS, CSHID and RIP.
2474 */
2475 /** @todo Testcase: Need to check WHEN exactly the CS accessed bit is set. */
2476 if (!(DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2477 {
2478 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCs);
2479 if (rcStrict != VINF_SUCCESS)
2480 return rcStrict;
2481 /** @todo check what VT-x and AMD-V does. */
2482 DescCs.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2483 }
2484 /** @todo Testcase: Need to check WHEN exactly the SS accessed bit is set. */
2485 if (!(DescSs.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2486 {
2487 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewOuterSs);
2488 if (rcStrict != VINF_SUCCESS)
2489 return rcStrict;
2490 /** @todo check what VT-x and AMD-V does. */
2491 DescSs.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2492 }
2493
2494 /* commit */
2495 if (enmEffOpSize == IEMMODE_16BIT)
2496 pVCpu->cpum.GstCtx.rip = uNewRip & UINT16_MAX; /** @todo Testcase: When exactly does this occur? With call it happens prior to the limit check according to Intel... */
2497 else
2498 pVCpu->cpum.GstCtx.rip = uNewRip;
2499 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
2500 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
2501 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
2502 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCs.Legacy);
2503 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimitCs;
2504 pVCpu->cpum.GstCtx.cs.u64Base = u64BaseCs;
2505 pVCpu->cpum.GstCtx.ss.Sel = uNewOuterSs;
2506 pVCpu->cpum.GstCtx.ss.ValidSel = uNewOuterSs;
2507 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
2508 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSs.Legacy);
2509 pVCpu->cpum.GstCtx.ss.u32Limit = cbLimitSs;
2510 pVCpu->cpum.GstCtx.ss.u64Base = u64BaseSs;
2511
2512 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.ds);
2513 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.es);
2514 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.fs);
2515 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.gs);
2516
2517 iemRecalcExecModeAndCplAndAcFlags(pVCpu); /* Affects iemRegAddToRspEx and the setting of RSP/SP below. */
2518
2519 if (cbPop)
2520 iemRegAddToRspEx(pVCpu, &NewOuterRsp, cbPop);
2521 if (IEM_IS_64BIT_CODE(pVCpu))
2522 pVCpu->cpum.GstCtx.rsp = NewOuterRsp.u;
2523 else if (pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig)
2524 pVCpu->cpum.GstCtx.rsp = (uint32_t)NewOuterRsp.u;
2525 else
2526 pVCpu->cpum.GstCtx.sp = (uint16_t)NewOuterRsp.u;
2527
2528 iemRecalcExecModeAndCplAndAcFlags(pVCpu); /* Affects iemRegAddToRspEx and the setting of RSP/SP below. */
2529
2530 /** @todo check if the hidden bits are loaded correctly for 64-bit
2531 * mode. */
2532 }
2533 /*
2534 * Return to the same privilege level
2535 */
2536 else
2537 {
2538 /* Limit / canonical check. */
2539 uint64_t u64Base;
2540 uint32_t cbLimitCs = X86DESC_LIMIT_G(&DescCs.Legacy);
2541
2542 /** @todo Testcase: Is this correct? */
2543 bool f64BitCs = false;
2544 if ( DescCs.Legacy.Gen.u1Long
2545 && IEM_IS_LONG_MODE(pVCpu) )
2546 {
2547 if (!IEM_IS_CANONICAL(uNewRip))
2548 {
2549 Log(("retf %04x:%08RX64 - not canonical -> #GP\n", uNewCs, uNewRip));
2550 return iemRaiseNotCanonical(pVCpu);
2551 }
2552 u64Base = 0;
2553 f64BitCs = true;
2554 f64BitCs = true;
2555 }
2556 else
2557 {
2558 if (uNewRip > cbLimitCs)
2559 {
2560 Log(("retf %04x:%08RX64 -> out of bounds (%#x)\n", uNewCs, uNewRip, cbLimitCs));
2561 /** @todo Intel says this is \#GP(0)! */
2562 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
2563 }
2564 u64Base = X86DESC_BASE(&DescCs.Legacy);
2565 }
2566
2567 /*
2568 * Now set the accessed bit before
2569 * writing the return address to the stack and committing the result into
2570 * CS, CSHID and RIP.
2571 */
2572 /** @todo Testcase: Need to check WHEN exactly the accessed bit is set. */
2573 if (!(DescCs.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
2574 {
2575 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCs);
2576 if (rcStrict != VINF_SUCCESS)
2577 return rcStrict;
2578 /** @todo check what VT-x and AMD-V does. */
2579 DescCs.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
2580 }
2581
2582 /* commit */
2583 if (cbPop)
2584/** @todo This cannot be right. We're using the old CS mode here, and iemRegAddToRspEx checks fExec. */
2585 iemRegAddToRspEx(pVCpu, &NewRsp, cbPop);
2586 if (pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig || f64BitCs)
2587 pVCpu->cpum.GstCtx.rsp = NewRsp.u;
2588 else
2589 pVCpu->cpum.GstCtx.sp = (uint16_t)NewRsp.u;
2590 if (enmEffOpSize == IEMMODE_16BIT)
2591 pVCpu->cpum.GstCtx.rip = uNewRip & UINT16_MAX; /** @todo Testcase: When exactly does this occur? With call it happens prior to the limit check according to Intel... */
2592 else
2593 pVCpu->cpum.GstCtx.rip = uNewRip;
2594 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
2595 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
2596 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
2597 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCs.Legacy);
2598 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimitCs;
2599 pVCpu->cpum.GstCtx.cs.u64Base = u64Base;
2600 /** @todo check if the hidden bits are loaded correctly for 64-bit
2601 * mode. */
2602
2603 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
2604 }
2605
2606 /* Flush the prefetch buffer. */
2607 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr); /** @todo use light flush for same privilege? */
2608
2609 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
2610}
2611
2612
2613/**
2614 * Implements enter.
2615 *
2616 * We're doing this in C because the instruction is insane, even for the
2617 * u8NestingLevel=0 case dealing with the stack is tedious.
2618 *
2619 * @param enmEffOpSize The effective operand size.
2620 * @param cbFrame Frame size.
2621 * @param cParameters Frame parameter count.
2622 */
2623IEM_CIMPL_DEF_3(iemCImpl_enter, IEMMODE, enmEffOpSize, uint16_t, cbFrame, uint8_t, cParameters)
2624{
2625 /* Push RBP, saving the old value in TmpRbp. */
2626 RTUINT64U NewRsp; NewRsp.u = pVCpu->cpum.GstCtx.rsp;
2627 RTUINT64U TmpRbp; TmpRbp.u = pVCpu->cpum.GstCtx.rbp;
2628 RTUINT64U NewRbp;
2629 VBOXSTRICTRC rcStrict;
2630 if (enmEffOpSize == IEMMODE_64BIT)
2631 {
2632 rcStrict = iemMemStackPushU64Ex(pVCpu, TmpRbp.u, &NewRsp);
2633 NewRbp = NewRsp;
2634 }
2635 else if (enmEffOpSize == IEMMODE_32BIT)
2636 {
2637 rcStrict = iemMemStackPushU32Ex(pVCpu, TmpRbp.DWords.dw0, &NewRsp);
2638 NewRbp = NewRsp;
2639 }
2640 else
2641 {
2642 rcStrict = iemMemStackPushU16Ex(pVCpu, TmpRbp.Words.w0, &NewRsp);
2643 NewRbp = TmpRbp;
2644 NewRbp.Words.w0 = NewRsp.Words.w0;
2645 }
2646 if (rcStrict != VINF_SUCCESS)
2647 return rcStrict;
2648
2649 /* Copy the parameters (aka nesting levels by Intel). */
2650 cParameters &= 0x1f;
2651 if (cParameters > 0)
2652 {
2653 switch (enmEffOpSize)
2654 {
2655 case IEMMODE_16BIT:
2656 if (pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig)
2657 TmpRbp.DWords.dw0 -= 2;
2658 else
2659 TmpRbp.Words.w0 -= 2;
2660 do
2661 {
2662 uint16_t u16Tmp;
2663 rcStrict = iemMemStackPopU16Ex(pVCpu, &u16Tmp, &TmpRbp);
2664 if (rcStrict != VINF_SUCCESS)
2665 break;
2666 rcStrict = iemMemStackPushU16Ex(pVCpu, u16Tmp, &NewRsp);
2667 } while (--cParameters > 0 && rcStrict == VINF_SUCCESS);
2668 break;
2669
2670 case IEMMODE_32BIT:
2671 if (pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig)
2672 TmpRbp.DWords.dw0 -= 4;
2673 else
2674 TmpRbp.Words.w0 -= 4;
2675 do
2676 {
2677 uint32_t u32Tmp;
2678 rcStrict = iemMemStackPopU32Ex(pVCpu, &u32Tmp, &TmpRbp);
2679 if (rcStrict != VINF_SUCCESS)
2680 break;
2681 rcStrict = iemMemStackPushU32Ex(pVCpu, u32Tmp, &NewRsp);
2682 } while (--cParameters > 0 && rcStrict == VINF_SUCCESS);
2683 break;
2684
2685 case IEMMODE_64BIT:
2686 TmpRbp.u -= 8;
2687 do
2688 {
2689 uint64_t u64Tmp;
2690 rcStrict = iemMemStackPopU64Ex(pVCpu, &u64Tmp, &TmpRbp);
2691 if (rcStrict != VINF_SUCCESS)
2692 break;
2693 rcStrict = iemMemStackPushU64Ex(pVCpu, u64Tmp, &NewRsp);
2694 } while (--cParameters > 0 && rcStrict == VINF_SUCCESS);
2695 break;
2696
2697 IEM_NOT_REACHED_DEFAULT_CASE_RET();
2698 }
2699 if (rcStrict != VINF_SUCCESS)
2700 return VINF_SUCCESS;
2701
2702 /* Push the new RBP */
2703 if (enmEffOpSize == IEMMODE_64BIT)
2704 rcStrict = iemMemStackPushU64Ex(pVCpu, NewRbp.u, &NewRsp);
2705 else if (enmEffOpSize == IEMMODE_32BIT)
2706 rcStrict = iemMemStackPushU32Ex(pVCpu, NewRbp.DWords.dw0, &NewRsp);
2707 else
2708 rcStrict = iemMemStackPushU16Ex(pVCpu, NewRbp.Words.w0, &NewRsp);
2709 if (rcStrict != VINF_SUCCESS)
2710 return rcStrict;
2711
2712 }
2713
2714 /* Recalc RSP. */
2715 iemRegSubFromRspEx(pVCpu, &NewRsp, cbFrame);
2716
2717 /** @todo Should probe write access at the new RSP according to AMD. */
2718 /** @todo Should handle accesses to the VMX APIC-access page. */
2719
2720 /* Commit it. */
2721 pVCpu->cpum.GstCtx.rbp = NewRbp.u;
2722 pVCpu->cpum.GstCtx.rsp = NewRsp.u;
2723 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
2724}
2725
2726
2727
2728/**
2729 * Implements leave.
2730 *
2731 * We're doing this in C because messing with the stack registers is annoying
2732 * since they depends on SS attributes.
2733 *
2734 * @param enmEffOpSize The effective operand size.
2735 */
2736IEM_CIMPL_DEF_1(iemCImpl_leave, IEMMODE, enmEffOpSize)
2737{
2738 /* Calculate the intermediate RSP from RBP and the stack attributes. */
2739 RTUINT64U NewRsp;
2740 if (IEM_IS_64BIT_CODE(pVCpu))
2741 NewRsp.u = pVCpu->cpum.GstCtx.rbp;
2742 else if (pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig)
2743 NewRsp.u = pVCpu->cpum.GstCtx.ebp;
2744 else
2745 {
2746 /** @todo Check that LEAVE actually preserve the high EBP bits. */
2747 NewRsp.u = pVCpu->cpum.GstCtx.rsp;
2748 NewRsp.Words.w0 = pVCpu->cpum.GstCtx.bp;
2749 }
2750
2751 /* Pop RBP according to the operand size. */
2752 VBOXSTRICTRC rcStrict;
2753 RTUINT64U NewRbp;
2754 switch (enmEffOpSize)
2755 {
2756 case IEMMODE_16BIT:
2757 NewRbp.u = pVCpu->cpum.GstCtx.rbp;
2758 rcStrict = iemMemStackPopU16Ex(pVCpu, &NewRbp.Words.w0, &NewRsp);
2759 break;
2760 case IEMMODE_32BIT:
2761 NewRbp.u = 0;
2762 rcStrict = iemMemStackPopU32Ex(pVCpu, &NewRbp.DWords.dw0, &NewRsp);
2763 break;
2764 case IEMMODE_64BIT:
2765 rcStrict = iemMemStackPopU64Ex(pVCpu, &NewRbp.u, &NewRsp);
2766 break;
2767 IEM_NOT_REACHED_DEFAULT_CASE_RET();
2768 }
2769 if (rcStrict != VINF_SUCCESS)
2770 return rcStrict;
2771
2772
2773 /* Commit it. */
2774 pVCpu->cpum.GstCtx.rbp = NewRbp.u;
2775 pVCpu->cpum.GstCtx.rsp = NewRsp.u;
2776 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
2777}
2778
2779
2780/**
2781 * Implements int3 and int XX.
2782 *
2783 * @param u8Int The interrupt vector number.
2784 * @param enmInt The int instruction type.
2785 */
2786IEM_CIMPL_DEF_2(iemCImpl_int, uint8_t, u8Int, IEMINT, enmInt)
2787{
2788 Assert(pVCpu->iem.s.cXcptRecursions == 0);
2789
2790 /*
2791 * We must check if this INT3 might belong to DBGF before raising a #BP.
2792 */
2793 if (u8Int == 3)
2794 {
2795 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
2796 if (pVM->dbgf.ro.cEnabledSwBreakpoints == 0)
2797 { /* likely: No vbox debugger breakpoints */ }
2798 else
2799 {
2800 VBOXSTRICTRC rcStrict = DBGFTrap03Handler(pVM, pVCpu, &pVCpu->cpum.GstCtx);
2801 Log(("iemCImpl_int: DBGFTrap03Handler -> %Rrc\n", VBOXSTRICTRC_VAL(rcStrict) ));
2802 if (rcStrict != VINF_EM_RAW_GUEST_TRAP)
2803 return iemSetPassUpStatus(pVCpu, rcStrict);
2804 }
2805 }
2806/** @todo single stepping */
2807 return iemRaiseXcptOrInt(pVCpu,
2808 cbInstr,
2809 u8Int,
2810 IEM_XCPT_FLAGS_T_SOFT_INT | enmInt,
2811 0,
2812 0);
2813}
2814
2815
2816/**
2817 * Implements iret for real mode and V8086 mode.
2818 *
2819 * @param enmEffOpSize The effective operand size.
2820 */
2821IEM_CIMPL_DEF_1(iemCImpl_iret_real_v8086, IEMMODE, enmEffOpSize)
2822{
2823 X86EFLAGS Efl;
2824 Efl.u = IEMMISC_GET_EFL(pVCpu);
2825 NOREF(cbInstr);
2826
2827 /*
2828 * iret throws an exception if VME isn't enabled.
2829 */
2830 if ( Efl.Bits.u1VM
2831 && Efl.Bits.u2IOPL != 3
2832 && !(pVCpu->cpum.GstCtx.cr4 & X86_CR4_VME))
2833 return iemRaiseGeneralProtectionFault0(pVCpu);
2834
2835 /*
2836 * Do the stack bits, but don't commit RSP before everything checks
2837 * out right.
2838 */
2839 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
2840 VBOXSTRICTRC rcStrict;
2841 uint8_t bUnmapInfo;
2842 RTCPTRUNION uFrame;
2843 uint16_t uNewCs;
2844 uint32_t uNewEip;
2845 uint32_t uNewFlags;
2846 uint64_t uNewRsp;
2847 if (enmEffOpSize == IEMMODE_32BIT)
2848 {
2849 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 12, 1, &uFrame.pv, &bUnmapInfo, &uNewRsp);
2850 if (rcStrict != VINF_SUCCESS)
2851 return rcStrict;
2852 uNewEip = uFrame.pu32[0];
2853 if (uNewEip > UINT16_MAX)
2854 return iemRaiseGeneralProtectionFault0(pVCpu);
2855
2856 uNewCs = (uint16_t)uFrame.pu32[1];
2857 uNewFlags = uFrame.pu32[2];
2858 uNewFlags &= X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2859 | X86_EFL_TF | X86_EFL_IF | X86_EFL_DF | X86_EFL_OF | X86_EFL_IOPL | X86_EFL_NT
2860 | X86_EFL_RF /*| X86_EFL_VM*/ | X86_EFL_AC /*|X86_EFL_VIF*/ /*|X86_EFL_VIP*/
2861 | X86_EFL_ID;
2862 if (IEM_GET_TARGET_CPU(pVCpu) <= IEMTARGETCPU_386)
2863 uNewFlags &= ~(X86_EFL_AC | X86_EFL_ID | X86_EFL_VIF | X86_EFL_VIP);
2864 uNewFlags |= Efl.u & (X86_EFL_VM | X86_EFL_VIF | X86_EFL_VIP | X86_EFL_1);
2865 }
2866 else
2867 {
2868 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 6, 1, &uFrame.pv, &bUnmapInfo, &uNewRsp);
2869 if (rcStrict != VINF_SUCCESS)
2870 return rcStrict;
2871 uNewEip = uFrame.pu16[0];
2872 uNewCs = uFrame.pu16[1];
2873 uNewFlags = uFrame.pu16[2];
2874 uNewFlags &= X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
2875 | X86_EFL_TF | X86_EFL_IF | X86_EFL_DF | X86_EFL_OF | X86_EFL_IOPL | X86_EFL_NT;
2876 uNewFlags |= Efl.u & ((UINT32_C(0xffff0000) | X86_EFL_1) & ~X86_EFL_RF);
2877 /** @todo The intel pseudo code does not indicate what happens to
2878 * reserved flags. We just ignore them. */
2879 /* Ancient CPU adjustments: See iemCImpl_popf. */
2880 if (IEM_GET_TARGET_CPU(pVCpu) == IEMTARGETCPU_286)
2881 uNewFlags &= ~(X86_EFL_NT | X86_EFL_IOPL);
2882 }
2883 rcStrict = iemMemStackPopDoneSpecial(pVCpu, bUnmapInfo);
2884 if (RT_LIKELY(rcStrict == VINF_SUCCESS))
2885 { /* extremely likely */ }
2886 else
2887 return rcStrict;
2888
2889 /** @todo Check how this is supposed to work if sp=0xfffe. */
2890 Log7(("iemCImpl_iret_real_v8086: uNewCs=%#06x uNewRip=%#010x uNewFlags=%#x uNewRsp=%#18llx\n",
2891 uNewCs, uNewEip, uNewFlags, uNewRsp));
2892
2893 /*
2894 * Check the limit of the new EIP.
2895 */
2896 /** @todo Only the AMD pseudo code check the limit here, what's
2897 * right? */
2898 if (uNewEip > pVCpu->cpum.GstCtx.cs.u32Limit)
2899 return iemRaiseSelectorBounds(pVCpu, X86_SREG_CS, IEM_ACCESS_INSTRUCTION);
2900
2901 /*
2902 * V8086 checks and flag adjustments
2903 */
2904 if (Efl.Bits.u1VM)
2905 {
2906 if (Efl.Bits.u2IOPL == 3)
2907 {
2908 /* Preserve IOPL and clear RF. */
2909 uNewFlags &= ~(X86_EFL_IOPL | X86_EFL_RF);
2910 uNewFlags |= Efl.u & (X86_EFL_IOPL);
2911 }
2912 else if ( enmEffOpSize == IEMMODE_16BIT
2913 && ( !(uNewFlags & X86_EFL_IF)
2914 || !Efl.Bits.u1VIP )
2915 && !(uNewFlags & X86_EFL_TF) )
2916 {
2917 /* Move IF to VIF, clear RF and preserve IF and IOPL.*/
2918 uNewFlags &= ~X86_EFL_VIF;
2919 uNewFlags |= (uNewFlags & X86_EFL_IF) << (19 - 9);
2920 uNewFlags &= ~(X86_EFL_IF | X86_EFL_IOPL | X86_EFL_RF);
2921 uNewFlags |= Efl.u & (X86_EFL_IF | X86_EFL_IOPL);
2922 }
2923 else
2924 return iemRaiseGeneralProtectionFault0(pVCpu);
2925 Log7(("iemCImpl_iret_real_v8086: u1VM=1: adjusted uNewFlags=%#x\n", uNewFlags));
2926 }
2927
2928 /*
2929 * Commit the operation.
2930 */
2931 IEMTLBTRACE_IRET(pVCpu, uNewCs, uNewEip, uNewFlags);
2932#ifdef DBGFTRACE_ENABLED
2933 RTTraceBufAddMsgF(pVCpu->CTX_SUFF(pVM)->CTX_SUFF(hTraceBuf), "iret/rm %04x:%04x -> %04x:%04x %x %04llx",
2934 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, uNewCs, uNewEip, uNewFlags, uNewRsp);
2935#endif
2936 pVCpu->cpum.GstCtx.rsp = uNewRsp;
2937 pVCpu->cpum.GstCtx.rip = uNewEip;
2938 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
2939 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
2940 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
2941 pVCpu->cpum.GstCtx.cs.u64Base = (uint32_t)uNewCs << 4;
2942 /** @todo do we load attribs and limit as well? */
2943 Assert(uNewFlags & X86_EFL_1);
2944 IEMMISC_SET_EFL(pVCpu, uNewFlags);
2945 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~IEM_F_X86_AC) | iemCalcExecAcFlag(pVCpu);
2946
2947 /* Flush the prefetch buffer. */
2948 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr); /** @todo can do light flush in real mode at least */
2949
2950/** @todo single stepping */
2951 return VINF_SUCCESS;
2952}
2953
2954
2955/**
2956 * Loads a segment register when entering V8086 mode.
2957 *
2958 * @param pSReg The segment register.
2959 * @param uSeg The segment to load.
2960 */
2961static void iemCImplCommonV8086LoadSeg(PCPUMSELREG pSReg, uint16_t uSeg)
2962{
2963 pSReg->Sel = uSeg;
2964 pSReg->ValidSel = uSeg;
2965 pSReg->fFlags = CPUMSELREG_FLAGS_VALID;
2966 pSReg->u64Base = (uint32_t)uSeg << 4;
2967 pSReg->u32Limit = 0xffff;
2968 pSReg->Attr.u = X86_SEL_TYPE_RW_ACC | RT_BIT(4) /*!sys*/ | RT_BIT(7) /*P*/ | (3 /*DPL*/ << 5); /* VT-x wants 0xf3 */
2969 /** @todo Testcase: Check if VT-x really needs this and what it does itself when
2970 * IRET'ing to V8086. */
2971}
2972
2973
2974/**
2975 * Implements iret for protected mode returning to V8086 mode.
2976 *
2977 * @param uNewEip The new EIP.
2978 * @param uNewCs The new CS.
2979 * @param uNewFlags The new EFLAGS.
2980 * @param uNewRsp The RSP after the initial IRET frame.
2981 *
2982 * @note This can only be a 32-bit iret du to the X86_EFL_VM position.
2983 */
2984IEM_CIMPL_DEF_4(iemCImpl_iret_prot_v8086, uint32_t, uNewEip, uint16_t, uNewCs, uint32_t, uNewFlags, uint64_t, uNewRsp)
2985{
2986 RT_NOREF_PV(cbInstr);
2987 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_MASK);
2988
2989 /*
2990 * Pop the V8086 specific frame bits off the stack.
2991 */
2992 uint8_t bUnmapInfo;
2993 RTCPTRUNION uFrame;
2994 VBOXSTRICTRC rcStrict = iemMemStackPopContinueSpecial(pVCpu, 0 /*off*/, 24 /*cbMem*/, &uFrame.pv, &bUnmapInfo, uNewRsp);
2995 if (rcStrict != VINF_SUCCESS)
2996 return rcStrict;
2997 uint32_t uNewEsp = uFrame.pu32[0];
2998 uint16_t uNewSs = uFrame.pu32[1];
2999 uint16_t uNewEs = uFrame.pu32[2];
3000 uint16_t uNewDs = uFrame.pu32[3];
3001 uint16_t uNewFs = uFrame.pu32[4];
3002 uint16_t uNewGs = uFrame.pu32[5];
3003 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo); /* don't use iemMemStackPopCommitSpecial here. */
3004 if (rcStrict != VINF_SUCCESS)
3005 return rcStrict;
3006
3007 /*
3008 * Commit the operation.
3009 */
3010 uNewFlags &= X86_EFL_LIVE_MASK;
3011 uNewFlags |= X86_EFL_RA1_MASK;
3012#ifdef DBGFTRACE_ENABLED
3013 RTTraceBufAddMsgF(pVCpu->CTX_SUFF(pVM)->CTX_SUFF(hTraceBuf), "iret/p/v %04x:%08x -> %04x:%04x %x %04x:%04x",
3014 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, uNewCs, uNewEip, uNewFlags, uNewSs, uNewEsp);
3015#endif
3016 Log7(("iemCImpl_iret_prot_v8086: %04x:%08x -> %04x:%04x %x %04x:%04x\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, uNewCs, uNewEip, uNewFlags, uNewSs, uNewEsp));
3017
3018 IEMMISC_SET_EFL(pVCpu, uNewFlags);
3019 iemCImplCommonV8086LoadSeg(&pVCpu->cpum.GstCtx.cs, uNewCs);
3020 iemCImplCommonV8086LoadSeg(&pVCpu->cpum.GstCtx.ss, uNewSs);
3021 iemCImplCommonV8086LoadSeg(&pVCpu->cpum.GstCtx.es, uNewEs);
3022 iemCImplCommonV8086LoadSeg(&pVCpu->cpum.GstCtx.ds, uNewDs);
3023 iemCImplCommonV8086LoadSeg(&pVCpu->cpum.GstCtx.fs, uNewFs);
3024 iemCImplCommonV8086LoadSeg(&pVCpu->cpum.GstCtx.gs, uNewGs);
3025 pVCpu->cpum.GstCtx.rip = (uint16_t)uNewEip;
3026 pVCpu->cpum.GstCtx.rsp = uNewEsp; /** @todo check this out! */
3027 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
3028 | (3 << IEM_F_X86_CPL_SHIFT)
3029 | IEM_F_MODE_X86_16BIT_PROT_V86
3030 | iemCalcExecAcFlag(pVCpu);
3031
3032 /* Flush the prefetch buffer. */
3033 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
3034
3035/** @todo single stepping */
3036 return VINF_SUCCESS;
3037}
3038
3039
3040/**
3041 * Implements iret for protected mode returning via a nested task.
3042 *
3043 * @param enmEffOpSize The effective operand size.
3044 */
3045IEM_CIMPL_DEF_1(iemCImpl_iret_prot_NestedTask, IEMMODE, enmEffOpSize)
3046{
3047 Log7(("iemCImpl_iret_prot_NestedTask:\n"));
3048#ifndef IEM_IMPLEMENTS_TASKSWITCH
3049 IEM_RETURN_ASPECT_NOT_IMPLEMENTED();
3050#else
3051 RT_NOREF_PV(enmEffOpSize);
3052
3053 /*
3054 * Read the segment selector in the link-field of the current TSS.
3055 */
3056 RTSEL uSelRet;
3057 VBOXSTRICTRC rcStrict = iemMemFetchSysU16(pVCpu, &uSelRet, UINT8_MAX, pVCpu->cpum.GstCtx.tr.u64Base);
3058 if (rcStrict != VINF_SUCCESS)
3059 return rcStrict;
3060
3061 /*
3062 * Fetch the returning task's TSS descriptor from the GDT.
3063 */
3064 if (uSelRet & X86_SEL_LDT)
3065 {
3066 Log(("iret_prot_NestedTask TSS not in LDT. uSelRet=%04x -> #TS\n", uSelRet));
3067 return iemRaiseTaskSwitchFaultBySelector(pVCpu, uSelRet);
3068 }
3069
3070 IEMSELDESC TssDesc;
3071 rcStrict = iemMemFetchSelDesc(pVCpu, &TssDesc, uSelRet, X86_XCPT_GP);
3072 if (rcStrict != VINF_SUCCESS)
3073 return rcStrict;
3074
3075 if (TssDesc.Legacy.Gate.u1DescType)
3076 {
3077 Log(("iret_prot_NestedTask Invalid TSS type. uSelRet=%04x -> #TS\n", uSelRet));
3078 return iemRaiseTaskSwitchFaultBySelector(pVCpu, uSelRet & X86_SEL_MASK_OFF_RPL);
3079 }
3080
3081 if ( TssDesc.Legacy.Gate.u4Type != X86_SEL_TYPE_SYS_286_TSS_BUSY
3082 && TssDesc.Legacy.Gate.u4Type != X86_SEL_TYPE_SYS_386_TSS_BUSY)
3083 {
3084 Log(("iret_prot_NestedTask TSS is not busy. uSelRet=%04x DescType=%#x -> #TS\n", uSelRet, TssDesc.Legacy.Gate.u4Type));
3085 return iemRaiseTaskSwitchFaultBySelector(pVCpu, uSelRet & X86_SEL_MASK_OFF_RPL);
3086 }
3087
3088 if (!TssDesc.Legacy.Gate.u1Present)
3089 {
3090 Log(("iret_prot_NestedTask TSS is not present. uSelRet=%04x -> #NP\n", uSelRet));
3091 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSelRet & X86_SEL_MASK_OFF_RPL);
3092 }
3093
3094 uint32_t uNextEip = pVCpu->cpum.GstCtx.eip + cbInstr;
3095 return iemTaskSwitch(pVCpu, IEMTASKSWITCH_IRET, uNextEip, 0 /* fFlags */, 0 /* uErr */,
3096 0 /* uCr2 */, uSelRet, &TssDesc);
3097#endif
3098}
3099
3100
3101/**
3102 * Implements iret for protected mode
3103 *
3104 * @param enmEffOpSize The effective operand size.
3105 */
3106IEM_CIMPL_DEF_1(iemCImpl_iret_prot, IEMMODE, enmEffOpSize)
3107{
3108 NOREF(cbInstr);
3109 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
3110
3111 /*
3112 * Nested task return.
3113 */
3114 if (pVCpu->cpum.GstCtx.eflags.Bits.u1NT)
3115 return IEM_CIMPL_CALL_1(iemCImpl_iret_prot_NestedTask, enmEffOpSize);
3116
3117 /*
3118 * Normal return.
3119 *
3120 * Do the stack bits, but don't commit RSP before everything checks
3121 * out right.
3122 */
3123 Assert(enmEffOpSize == IEMMODE_32BIT || enmEffOpSize == IEMMODE_16BIT);
3124 uint8_t bUnmapInfo;
3125 VBOXSTRICTRC rcStrict;
3126 RTCPTRUNION uFrame;
3127 uint16_t uNewCs;
3128 uint32_t uNewEip;
3129 uint32_t uNewFlags;
3130 uint64_t uNewRsp;
3131 if (enmEffOpSize == IEMMODE_32BIT)
3132 {
3133 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 12, 3, &uFrame.pv, &bUnmapInfo, &uNewRsp);
3134 if (rcStrict != VINF_SUCCESS)
3135 return rcStrict;
3136 uNewEip = uFrame.pu32[0];
3137 uNewCs = (uint16_t)uFrame.pu32[1];
3138 uNewFlags = uFrame.pu32[2];
3139 }
3140 else
3141 {
3142 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 6, 1, &uFrame.pv, &bUnmapInfo, &uNewRsp);
3143 if (rcStrict != VINF_SUCCESS)
3144 return rcStrict;
3145 uNewEip = uFrame.pu16[0];
3146 uNewCs = uFrame.pu16[1];
3147 uNewFlags = uFrame.pu16[2];
3148 }
3149 rcStrict = iemMemStackPopDoneSpecial(pVCpu, bUnmapInfo); /* don't use iemMemStackPopCommitSpecial here. */
3150 if (RT_LIKELY(rcStrict == VINF_SUCCESS))
3151 { /* extremely likely */ }
3152 else
3153 return rcStrict;
3154 Log7(("iemCImpl_iret_prot: uNewCs=%#06x uNewEip=%#010x uNewFlags=%#x uNewRsp=%#18llx uCpl=%u\n", uNewCs, uNewEip, uNewFlags, uNewRsp, IEM_GET_CPL(pVCpu)));
3155
3156 /*
3157 * We're hopefully not returning to V8086 mode...
3158 */
3159 if ( (uNewFlags & X86_EFL_VM)
3160 && IEM_GET_CPL(pVCpu) == 0)
3161 {
3162 Assert(enmEffOpSize == IEMMODE_32BIT);
3163 return IEM_CIMPL_CALL_4(iemCImpl_iret_prot_v8086, uNewEip, uNewCs, uNewFlags, uNewRsp);
3164 }
3165
3166 /*
3167 * Protected mode.
3168 */
3169 /* Read the CS descriptor. */
3170 if (!(uNewCs & X86_SEL_MASK_OFF_RPL))
3171 {
3172 Log(("iret %04x:%08x -> invalid CS selector, #GP(0)\n", uNewCs, uNewEip));
3173 return iemRaiseGeneralProtectionFault0(pVCpu);
3174 }
3175
3176 IEMSELDESC DescCS;
3177 rcStrict = iemMemFetchSelDesc(pVCpu, &DescCS, uNewCs, X86_XCPT_GP);
3178 if (rcStrict != VINF_SUCCESS)
3179 {
3180 Log(("iret %04x:%08x - rcStrict=%Rrc when fetching CS\n", uNewCs, uNewEip, VBOXSTRICTRC_VAL(rcStrict)));
3181 return rcStrict;
3182 }
3183
3184 /* Must be a code descriptor. */
3185 if (!DescCS.Legacy.Gen.u1DescType)
3186 {
3187 Log(("iret %04x:%08x - CS is system segment (%#x) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u4Type));
3188 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3189 }
3190 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
3191 {
3192 Log(("iret %04x:%08x - not code segment (%#x) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u4Type));
3193 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3194 }
3195
3196 /* Privilege checks. */
3197 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF))
3198 {
3199 if ((uNewCs & X86_SEL_RPL) != DescCS.Legacy.Gen.u2Dpl)
3200 {
3201 Log(("iret %04x:%08x - RPL != DPL (%d) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u2Dpl));
3202 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3203 }
3204 }
3205 else if ((uNewCs & X86_SEL_RPL) < DescCS.Legacy.Gen.u2Dpl)
3206 {
3207 Log(("iret %04x:%08x - RPL < DPL (%d) -> #GP\n", uNewCs, uNewEip, DescCS.Legacy.Gen.u2Dpl));
3208 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3209 }
3210 if ((uNewCs & X86_SEL_RPL) < IEM_GET_CPL(pVCpu))
3211 {
3212 Log(("iret %04x:%08x - RPL < CPL (%d) -> #GP\n", uNewCs, uNewEip, IEM_GET_CPL(pVCpu)));
3213 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3214 }
3215
3216 /* Present? */
3217 if (!DescCS.Legacy.Gen.u1Present)
3218 {
3219 Log(("iret %04x:%08x - CS not present -> #NP\n", uNewCs, uNewEip));
3220 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewCs);
3221 }
3222
3223 uint32_t cbLimitCS = X86DESC_LIMIT_G(&DescCS.Legacy);
3224
3225 /*
3226 * Return to outer level?
3227 */
3228 if ((uNewCs & X86_SEL_RPL) != IEM_GET_CPL(pVCpu))
3229 {
3230 uint16_t uNewSS;
3231 uint32_t uNewESP;
3232 if (enmEffOpSize == IEMMODE_32BIT)
3233 {
3234 rcStrict = iemMemStackPopContinueSpecial(pVCpu, 0/*off*/, 8 /*cbMem*/, &uFrame.pv, &bUnmapInfo, uNewRsp);
3235 if (rcStrict != VINF_SUCCESS)
3236 return rcStrict;
3237/** @todo We might be popping a 32-bit ESP from the IRET frame, but whether
3238 * 16-bit or 32-bit are being loaded into SP depends on the D/B
3239 * bit of the popped SS selector it turns out. */
3240 uNewESP = uFrame.pu32[0];
3241 uNewSS = (uint16_t)uFrame.pu32[1];
3242 }
3243 else
3244 {
3245 rcStrict = iemMemStackPopContinueSpecial(pVCpu, 0 /*off*/, 4 /*cbMem*/, &uFrame.pv, &bUnmapInfo, uNewRsp);
3246 if (rcStrict != VINF_SUCCESS)
3247 return rcStrict;
3248 uNewESP = uFrame.pu16[0];
3249 uNewSS = uFrame.pu16[1];
3250 }
3251 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
3252 if (rcStrict != VINF_SUCCESS)
3253 return rcStrict;
3254 Log7(("iemCImpl_iret_prot: uNewSS=%#06x uNewESP=%#010x\n", uNewSS, uNewESP));
3255
3256 /* Read the SS descriptor. */
3257 if (!(uNewSS & X86_SEL_MASK_OFF_RPL))
3258 {
3259 Log(("iret %04x:%08x/%04x:%08x -> invalid SS selector, #GP(0)\n", uNewCs, uNewEip, uNewSS, uNewESP));
3260 return iemRaiseGeneralProtectionFault0(pVCpu);
3261 }
3262
3263 IEMSELDESC DescSS;
3264 rcStrict = iemMemFetchSelDesc(pVCpu, &DescSS, uNewSS, X86_XCPT_GP); /** @todo Correct exception? */
3265 if (rcStrict != VINF_SUCCESS)
3266 {
3267 Log(("iret %04x:%08x/%04x:%08x - %Rrc when fetching SS\n",
3268 uNewCs, uNewEip, uNewSS, uNewESP, VBOXSTRICTRC_VAL(rcStrict)));
3269 return rcStrict;
3270 }
3271
3272 /* Privilege checks. */
3273 if ((uNewSS & X86_SEL_RPL) != (uNewCs & X86_SEL_RPL))
3274 {
3275 Log(("iret %04x:%08x/%04x:%08x -> SS.RPL != CS.RPL -> #GP\n", uNewCs, uNewEip, uNewSS, uNewESP));
3276 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSS);
3277 }
3278 if (DescSS.Legacy.Gen.u2Dpl != (uNewCs & X86_SEL_RPL))
3279 {
3280 Log(("iret %04x:%08x/%04x:%08x -> SS.DPL (%d) != CS.RPL -> #GP\n",
3281 uNewCs, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u2Dpl));
3282 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSS);
3283 }
3284
3285 /* Must be a writeable data segment descriptor. */
3286 if (!DescSS.Legacy.Gen.u1DescType)
3287 {
3288 Log(("iret %04x:%08x/%04x:%08x -> SS is system segment (%#x) -> #GP\n",
3289 uNewCs, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u4Type));
3290 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSS);
3291 }
3292 if ((DescSS.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE)
3293 {
3294 Log(("iret %04x:%08x/%04x:%08x - not writable data segment (%#x) -> #GP\n",
3295 uNewCs, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u4Type));
3296 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSS);
3297 }
3298
3299 /* Present? */
3300 if (!DescSS.Legacy.Gen.u1Present)
3301 {
3302 Log(("iret %04x:%08x/%04x:%08x -> SS not present -> #SS\n", uNewCs, uNewEip, uNewSS, uNewESP));
3303 return iemRaiseStackSelectorNotPresentBySelector(pVCpu, uNewSS);
3304 }
3305
3306 uint32_t cbLimitSs = X86DESC_LIMIT_G(&DescSS.Legacy);
3307
3308 /* Check EIP. */
3309 if (uNewEip > cbLimitCS)
3310 {
3311 Log(("iret %04x:%08x/%04x:%08x -> EIP is out of bounds (%#x) -> #GP(0)\n",
3312 uNewCs, uNewEip, uNewSS, uNewESP, cbLimitCS));
3313 /** @todo Which is it, \#GP(0) or \#GP(sel)? */
3314 return iemRaiseSelectorBoundsBySelector(pVCpu, uNewCs);
3315 }
3316
3317 /*
3318 * Commit the changes, marking CS and SS accessed first since
3319 * that may fail.
3320 */
3321 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
3322 {
3323 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCs);
3324 if (rcStrict != VINF_SUCCESS)
3325 return rcStrict;
3326 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
3327 }
3328 if (!(DescSS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
3329 {
3330 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewSS);
3331 if (rcStrict != VINF_SUCCESS)
3332 return rcStrict;
3333 DescSS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
3334 }
3335
3336 uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
3337 | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
3338 if (enmEffOpSize != IEMMODE_16BIT)
3339 fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
3340 if (IEM_GET_CPL(pVCpu) == 0)
3341 fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is 0 */
3342 else if (IEM_GET_CPL(pVCpu) <= pVCpu->cpum.GstCtx.eflags.Bits.u2IOPL)
3343 fEFlagsMask |= X86_EFL_IF;
3344 if (IEM_GET_TARGET_CPU(pVCpu) <= IEMTARGETCPU_386)
3345 fEFlagsMask &= ~(X86_EFL_AC | X86_EFL_ID | X86_EFL_VIF | X86_EFL_VIP);
3346 uint32_t fEFlagsNew = IEMMISC_GET_EFL(pVCpu);
3347 fEFlagsNew &= ~fEFlagsMask;
3348 fEFlagsNew |= uNewFlags & fEFlagsMask;
3349 IEMTLBTRACE_IRET(pVCpu, uNewCs, uNewEip, fEFlagsNew);
3350#ifdef DBGFTRACE_ENABLED
3351 RTTraceBufAddMsgF(pVCpu->CTX_SUFF(pVM)->CTX_SUFF(hTraceBuf), "iret/%up%u %04x:%08x -> %04x:%04x %x %04x:%04x",
3352 IEM_GET_CPL(pVCpu), uNewCs & X86_SEL_RPL, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip,
3353 uNewCs, uNewEip, uNewFlags, uNewSS, uNewESP);
3354#endif
3355
3356 IEMMISC_SET_EFL(pVCpu, fEFlagsNew);
3357 pVCpu->cpum.GstCtx.rip = uNewEip;
3358 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
3359 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
3360 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
3361 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
3362 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimitCS;
3363 pVCpu->cpum.GstCtx.cs.u64Base = X86DESC_BASE(&DescCS.Legacy);
3364
3365 pVCpu->cpum.GstCtx.ss.Sel = uNewSS;
3366 pVCpu->cpum.GstCtx.ss.ValidSel = uNewSS;
3367 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
3368 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSS.Legacy);
3369 pVCpu->cpum.GstCtx.ss.u32Limit = cbLimitSs;
3370 pVCpu->cpum.GstCtx.ss.u64Base = X86DESC_BASE(&DescSS.Legacy);
3371 if (!pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig)
3372 pVCpu->cpum.GstCtx.sp = (uint16_t)uNewESP;
3373 else
3374 pVCpu->cpum.GstCtx.rsp = uNewESP;
3375
3376 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.ds);
3377 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.es);
3378 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.fs);
3379 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCs & X86_SEL_RPL, &pVCpu->cpum.GstCtx.gs);
3380
3381 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
3382
3383 /* Done! */
3384
3385 }
3386 /*
3387 * Return to the same level.
3388 */
3389 else
3390 {
3391 /* Check EIP. */
3392 if (uNewEip > cbLimitCS)
3393 {
3394 Log(("iret %04x:%08x - EIP is out of bounds (%#x) -> #GP(0)\n", uNewCs, uNewEip, cbLimitCS));
3395 /** @todo Which is it, \#GP(0) or \#GP(sel)? */
3396 return iemRaiseSelectorBoundsBySelector(pVCpu, uNewCs);
3397 }
3398
3399 /*
3400 * Commit the changes, marking CS first since it may fail.
3401 */
3402 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
3403 {
3404 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCs);
3405 if (rcStrict != VINF_SUCCESS)
3406 return rcStrict;
3407 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
3408 }
3409
3410 X86EFLAGS NewEfl;
3411 NewEfl.u = IEMMISC_GET_EFL(pVCpu);
3412 uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
3413 | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
3414 if (enmEffOpSize != IEMMODE_16BIT)
3415 fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
3416 if (IEM_GET_CPL(pVCpu) == 0)
3417 fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is 0 */
3418 else if (IEM_GET_CPL(pVCpu) <= NewEfl.Bits.u2IOPL)
3419 fEFlagsMask |= X86_EFL_IF;
3420 if (IEM_GET_TARGET_CPU(pVCpu) <= IEMTARGETCPU_386)
3421 fEFlagsMask &= ~(X86_EFL_AC | X86_EFL_ID | X86_EFL_VIF | X86_EFL_VIP);
3422 NewEfl.u &= ~fEFlagsMask;
3423 NewEfl.u |= fEFlagsMask & uNewFlags;
3424 IEMTLBTRACE_IRET(pVCpu, uNewCs, uNewEip, NewEfl.u);
3425#ifdef DBGFTRACE_ENABLED
3426 RTTraceBufAddMsgF(pVCpu->CTX_SUFF(pVM)->CTX_SUFF(hTraceBuf), "iret/%up %04x:%08x -> %04x:%04x %x %04x:%04llx",
3427 IEM_GET_CPL(pVCpu), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip,
3428 uNewCs, uNewEip, uNewFlags, pVCpu->cpum.GstCtx.ss.Sel, uNewRsp);
3429#endif
3430
3431 IEMMISC_SET_EFL(pVCpu, NewEfl.u);
3432 pVCpu->cpum.GstCtx.rip = uNewEip;
3433 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
3434 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
3435 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
3436 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
3437 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimitCS;
3438 pVCpu->cpum.GstCtx.cs.u64Base = X86DESC_BASE(&DescCS.Legacy);
3439 if (!pVCpu->cpum.GstCtx.ss.Attr.n.u1DefBig)
3440 pVCpu->cpum.GstCtx.sp = (uint16_t)uNewRsp;
3441 else
3442 pVCpu->cpum.GstCtx.rsp = uNewRsp;
3443
3444 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
3445
3446 /* Done! */
3447 }
3448
3449 /* Flush the prefetch buffer. */
3450 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr); /** @todo may light flush if same ring? */
3451
3452/** @todo single stepping */
3453 return VINF_SUCCESS;
3454}
3455
3456
3457/**
3458 * Implements iret for long mode
3459 *
3460 * @param enmEffOpSize The effective operand size.
3461 */
3462IEM_CIMPL_DEF_1(iemCImpl_iret_64bit, IEMMODE, enmEffOpSize)
3463{
3464 NOREF(cbInstr);
3465
3466 /*
3467 * Nested task return is not supported in long mode.
3468 */
3469 if (pVCpu->cpum.GstCtx.eflags.Bits.u1NT)
3470 {
3471 Log(("iret/64 with NT=1 (eflags=%#x) -> #GP(0)\n", pVCpu->cpum.GstCtx.eflags.u));
3472 return iemRaiseGeneralProtectionFault0(pVCpu);
3473 }
3474
3475 /*
3476 * Normal return.
3477 *
3478 * Do the stack bits, but don't commit RSP before everything checks
3479 * out right.
3480 */
3481 VBOXSTRICTRC rcStrict;
3482 uint8_t bUnmapInfo;
3483 RTCPTRUNION uFrame;
3484 uint64_t uNewRip;
3485 uint16_t uNewCs;
3486 uint16_t uNewSs;
3487 uint32_t uNewFlags;
3488 uint64_t uNewRsp;
3489 if (enmEffOpSize == IEMMODE_64BIT)
3490 {
3491 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 5*8, 7, &uFrame.pv, &bUnmapInfo, &uNewRsp);
3492 if (rcStrict != VINF_SUCCESS)
3493 return rcStrict;
3494 uNewRip = uFrame.pu64[0];
3495 uNewCs = (uint16_t)uFrame.pu64[1];
3496 uNewFlags = (uint32_t)uFrame.pu64[2];
3497 uNewRsp = uFrame.pu64[3];
3498 uNewSs = (uint16_t)uFrame.pu64[4];
3499 }
3500 else if (enmEffOpSize == IEMMODE_32BIT)
3501 {
3502 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 5*4, 3, &uFrame.pv, &bUnmapInfo, &uNewRsp);
3503 if (rcStrict != VINF_SUCCESS)
3504 return rcStrict;
3505 uNewRip = uFrame.pu32[0];
3506 uNewCs = (uint16_t)uFrame.pu32[1];
3507 uNewFlags = uFrame.pu32[2];
3508 uNewRsp = uFrame.pu32[3];
3509 uNewSs = (uint16_t)uFrame.pu32[4];
3510 }
3511 else
3512 {
3513 Assert(enmEffOpSize == IEMMODE_16BIT);
3514 rcStrict = iemMemStackPopBeginSpecial(pVCpu, 5*2, 1, &uFrame.pv, &bUnmapInfo, &uNewRsp);
3515 if (rcStrict != VINF_SUCCESS)
3516 return rcStrict;
3517 uNewRip = uFrame.pu16[0];
3518 uNewCs = uFrame.pu16[1];
3519 uNewFlags = uFrame.pu16[2];
3520 uNewRsp = uFrame.pu16[3];
3521 uNewSs = uFrame.pu16[4];
3522 }
3523 rcStrict = iemMemStackPopDoneSpecial(pVCpu, bUnmapInfo); /* don't use iemMemStackPopCommitSpecial here. */
3524 if (RT_LIKELY(rcStrict == VINF_SUCCESS))
3525 { /* extremely like */ }
3526 else
3527 return rcStrict;
3528 Log7(("iret/64 stack: cs:rip=%04x:%016RX64 rflags=%016RX64 ss:rsp=%04x:%016RX64\n", uNewCs, uNewRip, uNewFlags, uNewSs, uNewRsp));
3529
3530 /*
3531 * Check stuff.
3532 */
3533 /* Read the CS descriptor. */
3534 if (!(uNewCs & X86_SEL_MASK_OFF_RPL))
3535 {
3536 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> invalid CS selector, #GP(0)\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3537 return iemRaiseGeneralProtectionFault0(pVCpu);
3538 }
3539
3540 IEMSELDESC DescCS;
3541 rcStrict = iemMemFetchSelDesc(pVCpu, &DescCS, uNewCs, X86_XCPT_GP);
3542 if (rcStrict != VINF_SUCCESS)
3543 {
3544 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 - rcStrict=%Rrc when fetching CS\n",
3545 uNewCs, uNewRip, uNewSs, uNewRsp, VBOXSTRICTRC_VAL(rcStrict)));
3546 return rcStrict;
3547 }
3548
3549 /* Must be a code descriptor. */
3550 if ( !DescCS.Legacy.Gen.u1DescType
3551 || !(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE))
3552 {
3553 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 - CS is not a code segment T=%u T=%#xu -> #GP\n",
3554 uNewCs, uNewRip, uNewSs, uNewRsp, DescCS.Legacy.Gen.u1DescType, DescCS.Legacy.Gen.u4Type));
3555 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3556 }
3557
3558 /* Privilege checks. */
3559 uint8_t const uNewCpl = uNewCs & X86_SEL_RPL;
3560 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_CONF))
3561 {
3562 if ((uNewCs & X86_SEL_RPL) != DescCS.Legacy.Gen.u2Dpl)
3563 {
3564 Log(("iret/64 %04x:%016RX64 - RPL != DPL (%d) -> #GP\n", uNewCs, uNewRip, DescCS.Legacy.Gen.u2Dpl));
3565 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3566 }
3567 }
3568 else if ((uNewCs & X86_SEL_RPL) < DescCS.Legacy.Gen.u2Dpl)
3569 {
3570 Log(("iret/64 %04x:%016RX64 - RPL < DPL (%d) -> #GP\n", uNewCs, uNewRip, DescCS.Legacy.Gen.u2Dpl));
3571 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3572 }
3573 if ((uNewCs & X86_SEL_RPL) < IEM_GET_CPL(pVCpu))
3574 {
3575 Log(("iret/64 %04x:%016RX64 - RPL < CPL (%d) -> #GP\n", uNewCs, uNewRip, IEM_GET_CPL(pVCpu)));
3576 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewCs);
3577 }
3578
3579 /* Present? */
3580 if (!DescCS.Legacy.Gen.u1Present)
3581 {
3582 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 - CS not present -> #NP\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3583 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewCs);
3584 }
3585
3586 uint32_t cbLimitCS = X86DESC_LIMIT_G(&DescCS.Legacy);
3587
3588 /* Read the SS descriptor. */
3589 IEMSELDESC DescSS;
3590 if (!(uNewSs & X86_SEL_MASK_OFF_RPL))
3591 {
3592 if ( !DescCS.Legacy.Gen.u1Long
3593 || DescCS.Legacy.Gen.u1DefBig /** @todo exactly how does iret (and others) behave with u1Long=1 and u1DefBig=1? \#GP(sel)? */
3594 || uNewCpl > 2) /** @todo verify SS=0 impossible for ring-3. */
3595 {
3596 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> invalid SS selector, #GP(0)\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3597 return iemRaiseGeneralProtectionFault0(pVCpu);
3598 }
3599 /* Make sure SS is sensible, marked as accessed etc. */
3600 iemMemFakeStackSelDesc(&DescSS, (uNewSs & X86_SEL_RPL));
3601 }
3602 else
3603 {
3604 rcStrict = iemMemFetchSelDesc(pVCpu, &DescSS, uNewSs, X86_XCPT_GP); /** @todo Correct exception? */
3605 if (rcStrict != VINF_SUCCESS)
3606 {
3607 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 - %Rrc when fetching SS\n",
3608 uNewCs, uNewRip, uNewSs, uNewRsp, VBOXSTRICTRC_VAL(rcStrict)));
3609 return rcStrict;
3610 }
3611 }
3612
3613 /* Privilege checks. */
3614 if ((uNewSs & X86_SEL_RPL) != (uNewCs & X86_SEL_RPL))
3615 {
3616 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> SS.RPL != CS.RPL -> #GP\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3617 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSs);
3618 }
3619
3620 uint32_t cbLimitSs;
3621 if (!(uNewSs & X86_SEL_MASK_OFF_RPL))
3622 cbLimitSs = UINT32_MAX;
3623 else
3624 {
3625 if (DescSS.Legacy.Gen.u2Dpl != (uNewCs & X86_SEL_RPL))
3626 {
3627 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> SS.DPL (%d) != CS.RPL -> #GP\n",
3628 uNewCs, uNewRip, uNewSs, uNewRsp, DescSS.Legacy.Gen.u2Dpl));
3629 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSs);
3630 }
3631
3632 /* Must be a writeable data segment descriptor. */
3633 if (!DescSS.Legacy.Gen.u1DescType)
3634 {
3635 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> SS is system segment (%#x) -> #GP\n",
3636 uNewCs, uNewRip, uNewSs, uNewRsp, DescSS.Legacy.Gen.u4Type));
3637 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSs);
3638 }
3639 if ((DescSS.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE)
3640 {
3641 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 - not writable data segment (%#x) -> #GP\n",
3642 uNewCs, uNewRip, uNewSs, uNewRsp, DescSS.Legacy.Gen.u4Type));
3643 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewSs);
3644 }
3645
3646 /* Present? */
3647 if (!DescSS.Legacy.Gen.u1Present)
3648 {
3649 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> SS not present -> #SS\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3650 return iemRaiseStackSelectorNotPresentBySelector(pVCpu, uNewSs);
3651 }
3652 cbLimitSs = X86DESC_LIMIT_G(&DescSS.Legacy);
3653 }
3654
3655 /* Check EIP. */
3656 if (DescCS.Legacy.Gen.u1Long)
3657 {
3658 if (!IEM_IS_CANONICAL(uNewRip))
3659 {
3660 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> RIP is not canonical -> #GP(0)\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3661 return iemRaiseNotCanonical(pVCpu);
3662 }
3663/** @todo check the location of this... Testcase. */
3664 if (RT_LIKELY(!DescCS.Legacy.Gen.u1DefBig))
3665 { /* likely */ }
3666 else
3667 {
3668 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> both L and D are set -> #GP(0)\n", uNewCs, uNewRip, uNewSs, uNewRsp));
3669 return iemRaiseGeneralProtectionFault0(pVCpu);
3670 }
3671 }
3672 else
3673 {
3674 if (uNewRip > cbLimitCS)
3675 {
3676 Log(("iret/64 %04x:%016RX64/%04x:%016RX64 -> EIP is out of bounds (%#x) -> #GP(0)\n",
3677 uNewCs, uNewRip, uNewSs, uNewRsp, cbLimitCS));
3678 /** @todo Which is it, \#GP(0) or \#GP(sel)? */
3679 return iemRaiseSelectorBoundsBySelector(pVCpu, uNewCs);
3680 }
3681 }
3682
3683 /*
3684 * Commit the changes, marking CS and SS accessed first since
3685 * that may fail.
3686 */
3687 /** @todo where exactly are these actually marked accessed by a real CPU? */
3688 if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
3689 {
3690 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewCs);
3691 if (rcStrict != VINF_SUCCESS)
3692 return rcStrict;
3693 DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
3694 }
3695 if (!(DescSS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
3696 {
3697 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uNewSs);
3698 if (rcStrict != VINF_SUCCESS)
3699 return rcStrict;
3700 DescSS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
3701 }
3702
3703 uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF
3704 | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
3705 if (enmEffOpSize != IEMMODE_16BIT)
3706 fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
3707 if (IEM_GET_CPL(pVCpu) == 0)
3708 fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is ignored */
3709 else if (IEM_GET_CPL(pVCpu) <= pVCpu->cpum.GstCtx.eflags.Bits.u2IOPL)
3710 fEFlagsMask |= X86_EFL_IF;
3711 uint32_t fEFlagsNew = IEMMISC_GET_EFL(pVCpu);
3712 fEFlagsNew &= ~fEFlagsMask;
3713 fEFlagsNew |= uNewFlags & fEFlagsMask;
3714 IEMTLBTRACE_IRET(pVCpu, uNewCs, uNewRip, fEFlagsNew);
3715#ifdef DBGFTRACE_ENABLED
3716 RTTraceBufAddMsgF(pVCpu->CTX_SUFF(pVM)->CTX_SUFF(hTraceBuf), "iret/64/%ul%u %08llx -> %04x:%04llx %llx %04x:%04llx",
3717 IEM_GET_CPL(pVCpu), uNewCpl, pVCpu->cpum.GstCtx.rip, uNewCs, uNewRip, uNewFlags, uNewSs, uNewRsp);
3718#endif
3719
3720 IEMMISC_SET_EFL(pVCpu, fEFlagsNew);
3721 pVCpu->cpum.GstCtx.rip = uNewRip;
3722 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
3723 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
3724 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
3725 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESC_GET_HID_ATTR(&DescCS.Legacy);
3726 pVCpu->cpum.GstCtx.cs.u32Limit = cbLimitCS;
3727 pVCpu->cpum.GstCtx.cs.u64Base = X86DESC_BASE(&DescCS.Legacy);
3728 if (pVCpu->cpum.GstCtx.cs.Attr.n.u1Long || pVCpu->cpum.GstCtx.cs.Attr.n.u1DefBig)
3729 pVCpu->cpum.GstCtx.rsp = uNewRsp;
3730 else
3731 pVCpu->cpum.GstCtx.sp = (uint16_t)uNewRsp;
3732 pVCpu->cpum.GstCtx.ss.Sel = uNewSs;
3733 pVCpu->cpum.GstCtx.ss.ValidSel = uNewSs;
3734 if (!(uNewSs & X86_SEL_MASK_OFF_RPL))
3735 {
3736 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
3737 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESCATTR_UNUSABLE | (uNewCpl << X86DESCATTR_DPL_SHIFT);
3738 pVCpu->cpum.GstCtx.ss.u32Limit = UINT32_MAX;
3739 pVCpu->cpum.GstCtx.ss.u64Base = 0;
3740 Log2(("iret/64 new SS: NULL\n"));
3741 }
3742 else
3743 {
3744 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
3745 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESC_GET_HID_ATTR(&DescSS.Legacy);
3746 pVCpu->cpum.GstCtx.ss.u32Limit = cbLimitSs;
3747 pVCpu->cpum.GstCtx.ss.u64Base = X86DESC_BASE(&DescSS.Legacy);
3748 Log2(("iret/64 new SS: base=%#RX64 lim=%#x attr=%#x\n", pVCpu->cpum.GstCtx.ss.u64Base, pVCpu->cpum.GstCtx.ss.u32Limit, pVCpu->cpum.GstCtx.ss.Attr.u));
3749 }
3750
3751 if (IEM_GET_CPL(pVCpu) != uNewCpl)
3752 {
3753 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCpl, &pVCpu->cpum.GstCtx.ds);
3754 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCpl, &pVCpu->cpum.GstCtx.es);
3755 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCpl, &pVCpu->cpum.GstCtx.fs);
3756 iemHlpAdjustSelectorForNewCpl(pVCpu, uNewCpl, &pVCpu->cpum.GstCtx.gs);
3757 }
3758
3759 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
3760
3761 /* Flush the prefetch buffer. */
3762 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr); /** @todo may light flush if the ring + mode doesn't change */
3763
3764/** @todo single stepping */
3765 return VINF_SUCCESS;
3766}
3767
3768
3769/**
3770 * Implements iret.
3771 *
3772 * @param enmEffOpSize The effective operand size.
3773 */
3774IEM_CIMPL_DEF_1(iemCImpl_iret, IEMMODE, enmEffOpSize)
3775{
3776 bool fBlockingNmi = CPUMAreInterruptsInhibitedByNmi(&pVCpu->cpum.GstCtx);
3777
3778 if (!IEM_IS_IN_GUEST(pVCpu))
3779 { /* probable */ }
3780#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
3781 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
3782 {
3783 /*
3784 * Record whether NMI (or virtual-NMI) blocking is in effect during the execution
3785 * of this IRET instruction. We need to provide this information as part of some
3786 * VM-exits.
3787 *
3788 * See Intel spec. 27.2.2 "Information for VM Exits Due to Vectored Events".
3789 */
3790 if (IEM_VMX_IS_PINCTLS_SET(pVCpu, VMX_PIN_CTLS_VIRT_NMI))
3791 pVCpu->cpum.GstCtx.hwvirt.vmx.fNmiUnblockingIret = pVCpu->cpum.GstCtx.hwvirt.vmx.fVirtNmiBlocking;
3792 else
3793 pVCpu->cpum.GstCtx.hwvirt.vmx.fNmiUnblockingIret = fBlockingNmi;
3794
3795 /*
3796 * If "NMI exiting" is set, IRET does not affect blocking of NMIs.
3797 * See Intel Spec. 25.3 "Changes To Instruction Behavior In VMX Non-root Operation".
3798 */
3799 if (IEM_VMX_IS_PINCTLS_SET(pVCpu, VMX_PIN_CTLS_NMI_EXIT))
3800 fBlockingNmi = false;
3801
3802 /* Clear virtual-NMI blocking, if any, before causing any further exceptions. */
3803 pVCpu->cpum.GstCtx.hwvirt.vmx.fVirtNmiBlocking = false;
3804 }
3805#endif
3806 /*
3807 * The SVM nested-guest intercept for IRET takes priority over all exceptions,
3808 * The NMI is still held pending (which I assume means blocking of further NMIs
3809 * is in effect).
3810 *
3811 * See AMD spec. 15.9 "Instruction Intercepts".
3812 * See AMD spec. 15.21.9 "NMI Support".
3813 */
3814 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IRET))
3815 {
3816 Log(("iret: Guest intercept -> #VMEXIT\n"));
3817 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
3818 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_IRET, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
3819 }
3820
3821 /*
3822 * Clear NMI blocking, if any, before causing any further exceptions.
3823 * See Intel spec. 6.7.1 "Handling Multiple NMIs".
3824 */
3825 if (fBlockingNmi)
3826 CPUMClearInterruptInhibitingByNmi(&pVCpu->cpum.GstCtx);
3827
3828 /*
3829 * Call a mode specific worker.
3830 */
3831 VBOXSTRICTRC rcStrict;
3832 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
3833 rcStrict = IEM_CIMPL_CALL_1(iemCImpl_iret_real_v8086, enmEffOpSize);
3834 else
3835 {
3836 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_MASK | CPUMCTX_EXTRN_GDTR | CPUMCTX_EXTRN_LDTR);
3837 if (IEM_IS_64BIT_CODE(pVCpu))
3838 rcStrict = IEM_CIMPL_CALL_1(iemCImpl_iret_64bit, enmEffOpSize);
3839 else
3840 rcStrict = IEM_CIMPL_CALL_1(iemCImpl_iret_prot, enmEffOpSize);
3841 }
3842
3843#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
3844 /*
3845 * Clear NMI unblocking IRET state with the completion of IRET.
3846 */
3847 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
3848 pVCpu->cpum.GstCtx.hwvirt.vmx.fNmiUnblockingIret = false;
3849#endif
3850 return rcStrict;
3851}
3852
3853
3854static void iemLoadallSetSelector(PVMCPUCC pVCpu, uint8_t iSegReg, uint16_t uSel)
3855{
3856 PCPUMSELREGHID pHid = iemSRegGetHid(pVCpu, iSegReg);
3857
3858 pHid->Sel = uSel;
3859 pHid->ValidSel = uSel;
3860 pHid->fFlags = CPUMSELREG_FLAGS_VALID;
3861}
3862
3863
3864static void iemLoadall286SetDescCache(PVMCPUCC pVCpu, uint8_t iSegReg, uint8_t const *pbMem)
3865{
3866 PCPUMSELREGHID pHid = iemSRegGetHid(pVCpu, iSegReg);
3867
3868 /* The base is in the first three bytes. */
3869 pHid->u64Base = pbMem[0] + (pbMem[1] << 8) + (pbMem[2] << 16);
3870 /* The attributes are in the fourth byte. */
3871 pHid->Attr.u = pbMem[3];
3872 pHid->Attr.u &= ~(X86DESCATTR_L | X86DESCATTR_D); /* (just to be on the safe side) */
3873 /* The limit is in the last two bytes. */
3874 pHid->u32Limit = pbMem[4] + (pbMem[5] << 8);
3875}
3876
3877
3878/**
3879 * Implements 286 LOADALL (286 CPUs only).
3880 */
3881IEM_CIMPL_DEF_0(iemCImpl_loadall286)
3882{
3883 NOREF(cbInstr);
3884
3885 /* Data is loaded from a buffer at 800h. No checks are done on the
3886 * validity of loaded state.
3887 *
3888 * LOADALL only loads the internal CPU state, it does not access any
3889 * GDT, LDT, or similar tables.
3890 */
3891
3892 if (IEM_GET_CPL(pVCpu) != 0)
3893 {
3894 Log(("loadall286: CPL must be 0 not %u -> #GP(0)\n", IEM_GET_CPL(pVCpu)));
3895 return iemRaiseGeneralProtectionFault0(pVCpu);
3896 }
3897
3898 uint8_t bUnmapInfo;
3899 uint8_t const *pbMem = NULL;
3900 RTGCPHYS GCPtrStart = 0x800; /* Fixed table location. */
3901 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, (void **)&pbMem, &bUnmapInfo, 0x66, UINT8_MAX, GCPtrStart, IEM_ACCESS_SYS_R, 0);
3902 if (rcStrict != VINF_SUCCESS)
3903 return rcStrict;
3904
3905 /* The MSW is at offset 0x06. */
3906 uint16_t const *pau16Mem = (uint16_t const *)(pbMem + 0x06);
3907 /* Even LOADALL can't clear the MSW.PE bit, though it can set it. */
3908 uint64_t uNewCr0 = pVCpu->cpum.GstCtx.cr0 & ~(X86_CR0_MP | X86_CR0_EM | X86_CR0_TS);
3909 uNewCr0 |= *pau16Mem & (X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS);
3910 uint64_t const uOldCr0 = pVCpu->cpum.GstCtx.cr0;
3911
3912 CPUMSetGuestCR0(pVCpu, uNewCr0);
3913 Assert(pVCpu->cpum.GstCtx.cr0 == uNewCr0);
3914
3915 /* Inform PGM if mode changed. */
3916 if ((uNewCr0 & X86_CR0_PE) != (uOldCr0 & X86_CR0_PE))
3917 {
3918 int rc = PGMFlushTLB(pVCpu, pVCpu->cpum.GstCtx.cr3, true /* global */);
3919 AssertRCReturn(rc, rc);
3920 /* ignore informational status codes */
3921 }
3922 rcStrict = PGMChangeMode(pVCpu, pVCpu->cpum.GstCtx.cr0, pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER,
3923 false /* fForce */);
3924
3925 /* TR selector is at offset 0x16. */
3926 pau16Mem = (uint16_t const *)(pbMem + 0x16);
3927 pVCpu->cpum.GstCtx.tr.Sel = pau16Mem[0];
3928 pVCpu->cpum.GstCtx.tr.ValidSel = pau16Mem[0];
3929 pVCpu->cpum.GstCtx.tr.fFlags = CPUMSELREG_FLAGS_VALID;
3930
3931 /* Followed by FLAGS... */
3932 pVCpu->cpum.GstCtx.eflags.u = pau16Mem[1] | X86_EFL_1;
3933 pVCpu->cpum.GstCtx.ip = pau16Mem[2]; /* ...and IP. */
3934
3935 /* LDT is at offset 0x1C. */
3936 pau16Mem = (uint16_t const *)(pbMem + 0x1C);
3937 pVCpu->cpum.GstCtx.ldtr.Sel = pau16Mem[0];
3938 pVCpu->cpum.GstCtx.ldtr.ValidSel = pau16Mem[0];
3939 pVCpu->cpum.GstCtx.ldtr.fFlags = CPUMSELREG_FLAGS_VALID;
3940
3941 /* Segment registers are at offset 0x1E. */
3942 pau16Mem = (uint16_t const *)(pbMem + 0x1E);
3943 iemLoadallSetSelector(pVCpu, X86_SREG_DS, pau16Mem[0]);
3944 iemLoadallSetSelector(pVCpu, X86_SREG_SS, pau16Mem[1]);
3945 iemLoadallSetSelector(pVCpu, X86_SREG_CS, pau16Mem[2]);
3946 iemLoadallSetSelector(pVCpu, X86_SREG_ES, pau16Mem[3]);
3947
3948 /* GPRs are at offset 0x26. */
3949 pau16Mem = (uint16_t const *)(pbMem + 0x26);
3950 pVCpu->cpum.GstCtx.di = pau16Mem[0];
3951 pVCpu->cpum.GstCtx.si = pau16Mem[1];
3952 pVCpu->cpum.GstCtx.bp = pau16Mem[2];
3953 pVCpu->cpum.GstCtx.sp = pau16Mem[3];
3954 pVCpu->cpum.GstCtx.bx = pau16Mem[4];
3955 pVCpu->cpum.GstCtx.dx = pau16Mem[5];
3956 pVCpu->cpum.GstCtx.cx = pau16Mem[6];
3957 pVCpu->cpum.GstCtx.ax = pau16Mem[7];
3958
3959 /* Descriptor caches are at offset 0x36, 6 bytes per entry. */
3960 iemLoadall286SetDescCache(pVCpu, X86_SREG_ES, pbMem + 0x36);
3961 iemLoadall286SetDescCache(pVCpu, X86_SREG_CS, pbMem + 0x3C);
3962 iemLoadall286SetDescCache(pVCpu, X86_SREG_SS, pbMem + 0x42);
3963 iemLoadall286SetDescCache(pVCpu, X86_SREG_DS, pbMem + 0x48);
3964
3965 /* GDTR contents are at offset 0x4E, 6 bytes. */
3966 uint8_t const *pau8Mem = pbMem + 0x4E;
3967 /* NB: Fourth byte "should be zero"; we are ignoring it. */
3968 RTGCPHYS GCPtrBase = pau8Mem[0] + ((uint32_t)pau8Mem[1] << 8) + ((uint32_t)pau8Mem[2] << 16);
3969 uint16_t cbLimit = pau8Mem[4] + ((uint32_t)pau8Mem[5] << 8);
3970 CPUMSetGuestGDTR(pVCpu, GCPtrBase, cbLimit);
3971
3972 /* IDTR contents are at offset 0x5A, 6 bytes. */
3973 pau8Mem = pbMem + 0x5A;
3974 GCPtrBase = pau8Mem[0] + ((uint32_t)pau8Mem[1] << 8) + ((uint32_t)pau8Mem[2] << 16);
3975 cbLimit = pau8Mem[4] + ((uint32_t)pau8Mem[5] << 8);
3976 CPUMSetGuestIDTR(pVCpu, GCPtrBase, cbLimit);
3977
3978 Log(("LOADALL: GDTR:%08RX64/%04X, IDTR:%08RX64/%04X\n", pVCpu->cpum.GstCtx.gdtr.pGdt, pVCpu->cpum.GstCtx.gdtr.cbGdt, pVCpu->cpum.GstCtx.idtr.pIdt, pVCpu->cpum.GstCtx.idtr.cbIdt));
3979 Log(("LOADALL: CS:%04X, CS base:%08X, limit:%04X, attrs:%02X\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.cs.u64Base, pVCpu->cpum.GstCtx.cs.u32Limit, pVCpu->cpum.GstCtx.cs.Attr.u));
3980 Log(("LOADALL: DS:%04X, DS base:%08X, limit:%04X, attrs:%02X\n", pVCpu->cpum.GstCtx.ds.Sel, pVCpu->cpum.GstCtx.ds.u64Base, pVCpu->cpum.GstCtx.ds.u32Limit, pVCpu->cpum.GstCtx.ds.Attr.u));
3981 Log(("LOADALL: ES:%04X, ES base:%08X, limit:%04X, attrs:%02X\n", pVCpu->cpum.GstCtx.es.Sel, pVCpu->cpum.GstCtx.es.u64Base, pVCpu->cpum.GstCtx.es.u32Limit, pVCpu->cpum.GstCtx.es.Attr.u));
3982 Log(("LOADALL: SS:%04X, SS base:%08X, limit:%04X, attrs:%02X\n", pVCpu->cpum.GstCtx.ss.Sel, pVCpu->cpum.GstCtx.ss.u64Base, pVCpu->cpum.GstCtx.ss.u32Limit, pVCpu->cpum.GstCtx.ss.Attr.u));
3983 Log(("LOADALL: SI:%04X, DI:%04X, AX:%04X, BX:%04X, CX:%04X, DX:%04X\n", pVCpu->cpum.GstCtx.si, pVCpu->cpum.GstCtx.di, pVCpu->cpum.GstCtx.bx, pVCpu->cpum.GstCtx.bx, pVCpu->cpum.GstCtx.cx, pVCpu->cpum.GstCtx.dx));
3984
3985 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
3986 if (rcStrict != VINF_SUCCESS)
3987 return rcStrict;
3988
3989 /*
3990 * The CPL may change and protected mode may change enabled. It is taken
3991 * from the "DPL fields of the SS and CS descriptor caches" but there is no
3992 * word as to what happens if those are not identical (probably bad things).
3993 */
3994 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
3995 Assert(IEM_IS_16BIT_CODE(pVCpu));
3996
3997 CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_HIDDEN_SEL_REGS | CPUM_CHANGED_IDTR | CPUM_CHANGED_GDTR | CPUM_CHANGED_TR | CPUM_CHANGED_LDTR);
3998
3999 /* Flush the prefetch buffer. */
4000 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
4001
4002/** @todo single stepping */
4003 return rcStrict;
4004}
4005
4006
4007/**
4008 * Implements SYSCALL (AMD and Intel64).
4009 */
4010IEM_CIMPL_DEF_0(iemCImpl_syscall)
4011{
4012
4013
4014 /*
4015 * Check preconditions.
4016 *
4017 * Note that CPUs described in the documentation may load a few odd values
4018 * into CS and SS than we allow here. This has yet to be checked on real
4019 * hardware.
4020 */
4021 if (!(pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_SCE))
4022 {
4023 Log(("syscall: Not enabled in EFER -> #UD\n"));
4024 return iemRaiseUndefinedOpcode(pVCpu);
4025 }
4026 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE))
4027 {
4028 Log(("syscall: Protected mode is required -> #GP(0)\n"));
4029 return iemRaiseGeneralProtectionFault0(pVCpu);
4030 }
4031 if ( IEM_IS_GUEST_CPU_INTEL(pVCpu)
4032 && !IEM_IS_64BIT_CODE(pVCpu)) //&& !CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
4033 {
4034 Log(("syscall: Only available in 64-bit mode on intel -> #UD\n"));
4035 return iemRaiseUndefinedOpcode(pVCpu);
4036 }
4037
4038 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SYSCALL_MSRS);
4039
4040 /** @todo verify RPL ignoring and CS=0xfff8 (i.e. SS == 0). */
4041 /** @todo what about LDT selectors? Shouldn't matter, really. */
4042 uint16_t uNewCs = (pVCpu->cpum.GstCtx.msrSTAR >> MSR_K6_STAR_SYSCALL_CS_SS_SHIFT) & X86_SEL_MASK_OFF_RPL;
4043 uint16_t uNewSs = uNewCs + 8;
4044 if (uNewCs == 0 || uNewSs == 0)
4045 {
4046 /** @todo Neither Intel nor AMD document this check. */
4047 Log(("syscall: msrSTAR.CS = 0 or SS = 0 -> #GP(0)\n"));
4048 return iemRaiseGeneralProtectionFault0(pVCpu);
4049 }
4050
4051 /*
4052 * Hack alert! Convert incoming debug events to slient on Intel.
4053 * See the dbg+inhibit+ringxfer test in bs3-cpu-weird-1.
4054 */
4055 if ( !(pVCpu->cpum.GstCtx.eflags.uBoth & CPUMCTX_DBG_HIT_DRX_MASK_NONSILENT)
4056 || !IEM_IS_GUEST_CPU_INTEL(pVCpu))
4057 { /* ignore */ }
4058 else
4059 {
4060 Log(("iemCImpl_syscall: Converting pending %#x debug events to a silent one (intel hack)\n",
4061 pVCpu->cpum.GstCtx.eflags.uBoth & CPUMCTX_DBG_HIT_DRX_MASK));
4062 pVCpu->cpum.GstCtx.eflags.uBoth = (pVCpu->cpum.GstCtx.eflags.uBoth & ~CPUMCTX_DBG_HIT_DRX_MASK)
4063 | CPUMCTX_DBG_HIT_DRX_SILENT;
4064 }
4065
4066 /*
4067 * Long mode and legacy mode differs.
4068 */
4069 if (CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
4070 {
4071 uint64_t uNewRip = IEM_IS_64BIT_CODE(pVCpu) ? pVCpu->cpum.GstCtx.msrLSTAR : pVCpu->cpum.GstCtx. msrCSTAR;
4072
4073 /* This test isn't in the docs, but I'm not trusting the guys writing
4074 the MSRs to have validated the values as canonical like they should. */
4075 if (!IEM_IS_CANONICAL(uNewRip))
4076 {
4077 /** @todo Intel claims this can't happen because IA32_LSTAR MSR can't be written with non-canonical address. */
4078 Log(("syscall: New RIP not canonical -> #UD\n"));
4079 return iemRaiseUndefinedOpcode(pVCpu);
4080 }
4081
4082 /*
4083 * Commit it.
4084 */
4085 Log(("syscall: %04x:%016RX64 [efl=%#llx] -> %04x:%016RX64\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags.u, uNewCs, uNewRip));
4086 pVCpu->cpum.GstCtx.rcx = pVCpu->cpum.GstCtx.rip + cbInstr;
4087 pVCpu->cpum.GstCtx.rip = uNewRip;
4088
4089 pVCpu->cpum.GstCtx.rflags.u &= ~X86_EFL_RF;
4090 pVCpu->cpum.GstCtx.r11 = pVCpu->cpum.GstCtx.rflags.u;
4091 pVCpu->cpum.GstCtx.rflags.u &= ~pVCpu->cpum.GstCtx.msrSFMASK;
4092 pVCpu->cpum.GstCtx.rflags.u |= X86_EFL_RA1_MASK;
4093
4094 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_L | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC;
4095 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_RW_ACC;
4096
4097 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
4098 | IEM_F_MODE_X86_64BIT;
4099 }
4100 else
4101 {
4102 /*
4103 * Commit it.
4104 */
4105 Log(("syscall: %04x:%08RX32 [efl=%#x] -> %04x:%08RX32\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, pVCpu->cpum.GstCtx.eflags.u, uNewCs, (uint32_t)(pVCpu->cpum.GstCtx.msrSTAR & MSR_K6_STAR_SYSCALL_EIP_MASK)));
4106 pVCpu->cpum.GstCtx.rcx = pVCpu->cpum.GstCtx.eip + cbInstr;
4107 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.msrSTAR & MSR_K6_STAR_SYSCALL_EIP_MASK;
4108 pVCpu->cpum.GstCtx.rflags.u &= ~(X86_EFL_VM | X86_EFL_IF | X86_EFL_RF);
4109
4110 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC;
4111 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_RW_ACC;
4112
4113 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
4114 | IEM_F_MODE_X86_32BIT_PROT
4115 | iemCalc32BitFlatIndicatorEsDs(pVCpu);
4116 }
4117 pVCpu->cpum.GstCtx.cs.Sel = uNewCs;
4118 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs;
4119 pVCpu->cpum.GstCtx.cs.u64Base = 0;
4120 pVCpu->cpum.GstCtx.cs.u32Limit = UINT32_MAX;
4121 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
4122
4123 pVCpu->cpum.GstCtx.ss.Sel = uNewSs;
4124 pVCpu->cpum.GstCtx.ss.ValidSel = uNewSs;
4125 pVCpu->cpum.GstCtx.ss.u64Base = 0;
4126 pVCpu->cpum.GstCtx.ss.u32Limit = UINT32_MAX;
4127 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
4128
4129 /* Flush the prefetch buffer. */
4130 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
4131
4132 /*
4133 * Handle debug events.
4134 * If TF isn't masked, we're supposed to raise a single step #DB.
4135 */
4136 return iemRegFinishClearingRF(pVCpu, VINF_SUCCESS);
4137}
4138
4139
4140/**
4141 * Implements SYSRET (AMD and Intel64).
4142 *
4143 * @param enmEffOpSize The effective operand size.
4144 */
4145IEM_CIMPL_DEF_1(iemCImpl_sysret, IEMMODE, enmEffOpSize)
4146
4147{
4148 RT_NOREF_PV(cbInstr);
4149
4150 /*
4151 * Check preconditions.
4152 *
4153 * Note that CPUs described in the documentation may load a few odd values
4154 * into CS and SS than we allow here. This has yet to be checked on real
4155 * hardware.
4156 */
4157 if (!(pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_SCE))
4158 {
4159 Log(("sysret: Not enabled in EFER -> #UD\n"));
4160 return iemRaiseUndefinedOpcode(pVCpu);
4161 }
4162 if (IEM_IS_GUEST_CPU_INTEL(pVCpu) && !CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
4163 {
4164 Log(("sysret: Only available in long mode on intel -> #UD\n"));
4165 return iemRaiseUndefinedOpcode(pVCpu);
4166 }
4167 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE))
4168 {
4169 Log(("sysret: Protected mode is required -> #GP(0)\n"));
4170 return iemRaiseGeneralProtectionFault0(pVCpu);
4171 }
4172 if (IEM_GET_CPL(pVCpu) != 0)
4173 {
4174 Log(("sysret: CPL must be 0 not %u -> #GP(0)\n", IEM_GET_CPL(pVCpu)));
4175 return iemRaiseGeneralProtectionFault0(pVCpu);
4176 }
4177
4178 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SYSCALL_MSRS);
4179
4180 /** @todo Does SYSRET verify CS != 0 and SS != 0? Neither is valid in ring-3. */
4181 uint16_t uNewCs = (pVCpu->cpum.GstCtx.msrSTAR >> MSR_K6_STAR_SYSRET_CS_SS_SHIFT) & X86_SEL_MASK_OFF_RPL;
4182 uint16_t uNewSs = uNewCs + 8;
4183 if (enmEffOpSize == IEMMODE_64BIT)
4184 uNewCs += 16;
4185 if (uNewCs == 0 || uNewSs == 0)
4186 {
4187 Log(("sysret: msrSTAR.CS = 0 or SS = 0 -> #GP(0)\n"));
4188 return iemRaiseGeneralProtectionFault0(pVCpu);
4189 }
4190
4191 /*
4192 * Commit it.
4193 */
4194 bool f32Bit = true;
4195 if (CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
4196 {
4197 if (enmEffOpSize == IEMMODE_64BIT)
4198 {
4199 Log(("sysret: %04x:%016RX64 [efl=%#llx] -> %04x:%016RX64 [r11=%#llx]\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags.u, uNewCs, pVCpu->cpum.GstCtx.rcx, pVCpu->cpum.GstCtx.r11));
4200 /* Note! We disregard intel manual regarding the RCX canonical
4201 check, ask intel+xen why AMD doesn't do it. */
4202 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.rcx;
4203 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_L | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC
4204 | (3 << X86DESCATTR_DPL_SHIFT);
4205 f32Bit = false;
4206 }
4207 else
4208 {
4209 Log(("sysret: %04x:%016RX64 [efl=%#llx] -> %04x:%08RX32 [r11=%#llx]\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags.u, uNewCs, pVCpu->cpum.GstCtx.ecx, pVCpu->cpum.GstCtx.r11));
4210 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.ecx;
4211 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC
4212 | (3 << X86DESCATTR_DPL_SHIFT);
4213 }
4214 /** @todo testcase: See what kind of flags we can make SYSRET restore and
4215 * what it really ignores. RF and VM are hinted at being zero, by AMD.
4216 * Intel says: RFLAGS := (R11 & 3C7FD7H) | 2; */
4217 pVCpu->cpum.GstCtx.rflags.u = pVCpu->cpum.GstCtx.r11 & (X86_EFL_POPF_BITS | X86_EFL_VIF | X86_EFL_VIP);
4218 pVCpu->cpum.GstCtx.rflags.u |= X86_EFL_RA1_MASK;
4219 }
4220 else
4221 {
4222 Log(("sysret: %04x:%08RX32 [efl=%#x] -> %04x:%08RX32\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, pVCpu->cpum.GstCtx.eflags.u, uNewCs, pVCpu->cpum.GstCtx.ecx));
4223 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.rcx;
4224 pVCpu->cpum.GstCtx.rflags.u |= X86_EFL_IF;
4225 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_ER_ACC
4226 | (3 << X86DESCATTR_DPL_SHIFT);
4227 }
4228 pVCpu->cpum.GstCtx.cs.Sel = uNewCs | 3;
4229 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs | 3;
4230 pVCpu->cpum.GstCtx.cs.u64Base = 0;
4231 pVCpu->cpum.GstCtx.cs.u32Limit = UINT32_MAX;
4232 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
4233
4234 /* The SS hidden bits remains unchanged says AMD, we presume they set DPL to 3.
4235 Intel (and presuably VIA) OTOH sets loads valid ring-3 values it seems, see
4236 X86_BUG_SYSRET_SS_ATTRS in linux 5.3. */
4237 if (IEM_IS_GUEST_CPU_AMD(pVCpu))
4238 {
4239 Log(("sysret: ss:rsp=%04x:%08RX64 attr=%x -> %04x:%08RX64 attr=%#x\n", pVCpu->cpum.GstCtx.ss.Sel, pVCpu->cpum.GstCtx.rsp, pVCpu->cpum.GstCtx.ss.Attr.u, uNewSs | 3, pVCpu->cpum.GstCtx.rsp, pVCpu->cpum.GstCtx.ss.Attr.u | (3 << X86DESCATTR_DPL_SHIFT) ));
4240 pVCpu->cpum.GstCtx.ss.Attr.u |= (3 << X86DESCATTR_DPL_SHIFT);
4241 }
4242 else
4243 {
4244 Log(("sysret: ss:rsp=%04x:%08RX64 attr=%x -> %04x:%08RX64 attr=%#x\n", pVCpu->cpum.GstCtx.ss.Sel, pVCpu->cpum.GstCtx.rsp, pVCpu->cpum.GstCtx.ss.Attr.u, uNewSs | 3, pVCpu->cpum.GstCtx.rsp, X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_RW_ACC | (3 << X86DESCATTR_DPL_SHIFT) ));
4245 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESCATTR_P | X86DESCATTR_G | X86DESCATTR_D | X86DESCATTR_DT | X86_SEL_TYPE_RW_ACC
4246 | (3 << X86DESCATTR_DPL_SHIFT);
4247 pVCpu->cpum.GstCtx.ss.u64Base = 0;
4248 pVCpu->cpum.GstCtx.ss.u32Limit = UINT32_MAX;
4249 }
4250 pVCpu->cpum.GstCtx.ss.Sel = uNewSs | 3;
4251 pVCpu->cpum.GstCtx.ss.ValidSel = uNewSs | 3;
4252 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
4253 /** @todo Testcase: verify that SS.u1Long and SS.u1DefBig are left unchanged
4254 * on sysret on AMD and not on intel. */
4255
4256 if (!f32Bit)
4257 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
4258 | (3 << IEM_F_X86_CPL_SHIFT)
4259 | IEM_F_MODE_X86_64BIT
4260 | iemCalcExecAcFlag(pVCpu);
4261 else
4262 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
4263 | (3 << IEM_F_X86_CPL_SHIFT)
4264 | IEM_F_MODE_X86_32BIT_PROT
4265 /** @todo sort out the SS.BASE/LIM/ATTR claim by AMD and maybe we can switch to
4266 * iemCalc32BitFlatIndicatorDsEs and move this up into the above branch. */
4267 | iemCalc32BitFlatIndicator(pVCpu)
4268 | iemCalcExecAcFlag(pVCpu);
4269
4270 /* Flush the prefetch buffer. */
4271 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
4272
4273/** @todo single step */
4274 return VINF_SUCCESS;
4275}
4276
4277
4278/**
4279 * Implements SYSENTER (Intel, 32-bit AMD).
4280 */
4281IEM_CIMPL_DEF_0(iemCImpl_sysenter)
4282{
4283 RT_NOREF(cbInstr);
4284
4285 /*
4286 * Check preconditions.
4287 *
4288 * Note that CPUs described in the documentation may load a few odd values
4289 * into CS and SS than we allow here. This has yet to be checked on real
4290 * hardware.
4291 */
4292 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSysEnter)
4293 {
4294 Log(("sysenter: not supported -=> #UD\n"));
4295 return iemRaiseUndefinedOpcode(pVCpu);
4296 }
4297 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE))
4298 {
4299 Log(("sysenter: Protected or long mode is required -> #GP(0)\n"));
4300 return iemRaiseGeneralProtectionFault0(pVCpu);
4301 }
4302 bool fIsLongMode = CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu));
4303 if (IEM_IS_GUEST_CPU_AMD(pVCpu) && fIsLongMode)
4304 {
4305 Log(("sysenter: Only available in protected mode on AMD -> #UD\n"));
4306 return iemRaiseUndefinedOpcode(pVCpu);
4307 }
4308 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SYSENTER_MSRS);
4309 uint16_t uNewCs = pVCpu->cpum.GstCtx.SysEnter.cs;
4310 if ((uNewCs & X86_SEL_MASK_OFF_RPL) == 0)
4311 {
4312 Log(("sysenter: SYSENTER_CS = %#x -> #GP(0)\n", uNewCs));
4313 return iemRaiseGeneralProtectionFault0(pVCpu);
4314 }
4315
4316 /* This test isn't in the docs, it's just a safeguard against missing
4317 canonical checks when writing the registers. */
4318 if (RT_LIKELY( !fIsLongMode
4319 || ( IEM_IS_CANONICAL(pVCpu->cpum.GstCtx.SysEnter.eip)
4320 && IEM_IS_CANONICAL(pVCpu->cpum.GstCtx.SysEnter.esp))))
4321 { /* likely */ }
4322 else
4323 {
4324 Log(("sysenter: SYSENTER_EIP = %#RX64 or/and SYSENTER_ESP = %#RX64 not canonical -> #GP(0)\n",
4325 pVCpu->cpum.GstCtx.SysEnter.eip, pVCpu->cpum.GstCtx.SysEnter.esp));
4326 return iemRaiseUndefinedOpcode(pVCpu);
4327 }
4328
4329/** @todo Test: Sysenter from ring-0, ring-1 and ring-2. */
4330
4331 /*
4332 * Update registers and commit.
4333 */
4334 if (fIsLongMode)
4335 {
4336 Log(("sysenter: %04x:%016RX64 [efl=%#llx] -> %04x:%016RX64\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
4337 pVCpu->cpum.GstCtx.rflags.u, uNewCs & X86_SEL_MASK_OFF_RPL, pVCpu->cpum.GstCtx.SysEnter.eip));
4338 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.SysEnter.eip;
4339 pVCpu->cpum.GstCtx.rsp = pVCpu->cpum.GstCtx.SysEnter.esp;
4340 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_L | X86DESCATTR_G | X86DESCATTR_P | X86DESCATTR_DT
4341 | X86DESCATTR_LIMIT_HIGH | X86_SEL_TYPE_ER_ACC;
4342 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
4343 | IEM_F_MODE_X86_64BIT;
4344 }
4345 else
4346 {
4347 Log(("sysenter: %04x:%08RX32 [efl=%#llx] -> %04x:%08RX32\n", pVCpu->cpum.GstCtx.cs.Sel, (uint32_t)pVCpu->cpum.GstCtx.rip,
4348 pVCpu->cpum.GstCtx.rflags.u, uNewCs & X86_SEL_MASK_OFF_RPL, (uint32_t)pVCpu->cpum.GstCtx.SysEnter.eip));
4349 pVCpu->cpum.GstCtx.rip = (uint32_t)pVCpu->cpum.GstCtx.SysEnter.eip;
4350 pVCpu->cpum.GstCtx.rsp = (uint32_t)pVCpu->cpum.GstCtx.SysEnter.esp;
4351 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_D | X86DESCATTR_G | X86DESCATTR_P | X86DESCATTR_DT
4352 | X86DESCATTR_LIMIT_HIGH | X86_SEL_TYPE_ER_ACC;
4353 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK | IEM_F_X86_AC))
4354 | IEM_F_MODE_X86_32BIT_PROT
4355 | iemCalc32BitFlatIndicatorEsDs(pVCpu);
4356 }
4357 pVCpu->cpum.GstCtx.cs.Sel = uNewCs & X86_SEL_MASK_OFF_RPL;
4358 pVCpu->cpum.GstCtx.cs.ValidSel = uNewCs & X86_SEL_MASK_OFF_RPL;
4359 pVCpu->cpum.GstCtx.cs.u64Base = 0;
4360 pVCpu->cpum.GstCtx.cs.u32Limit = UINT32_MAX;
4361 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
4362
4363 pVCpu->cpum.GstCtx.ss.Sel = (uNewCs & X86_SEL_MASK_OFF_RPL) + 8;
4364 pVCpu->cpum.GstCtx.ss.ValidSel = (uNewCs & X86_SEL_MASK_OFF_RPL) + 8;
4365 pVCpu->cpum.GstCtx.ss.u64Base = 0;
4366 pVCpu->cpum.GstCtx.ss.u32Limit = UINT32_MAX;
4367 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESCATTR_D | X86DESCATTR_G | X86DESCATTR_P | X86DESCATTR_DT
4368 | X86DESCATTR_LIMIT_HIGH | X86_SEL_TYPE_RW_ACC;
4369 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
4370
4371 pVCpu->cpum.GstCtx.rflags.Bits.u1IF = 0;
4372 pVCpu->cpum.GstCtx.rflags.Bits.u1VM = 0;
4373 pVCpu->cpum.GstCtx.rflags.Bits.u1RF = 0;
4374
4375 /* Flush the prefetch buffer. */
4376 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
4377
4378/** @todo single stepping */
4379 return VINF_SUCCESS;
4380}
4381
4382
4383/**
4384 * Implements SYSEXIT (Intel, 32-bit AMD).
4385 *
4386 * @param enmEffOpSize The effective operand size.
4387 */
4388IEM_CIMPL_DEF_1(iemCImpl_sysexit, IEMMODE, enmEffOpSize)
4389{
4390 RT_NOREF(cbInstr);
4391
4392 /*
4393 * Check preconditions.
4394 *
4395 * Note that CPUs described in the documentation may load a few odd values
4396 * into CS and SS than we allow here. This has yet to be checked on real
4397 * hardware.
4398 */
4399 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSysEnter)
4400 {
4401 Log(("sysexit: not supported -=> #UD\n"));
4402 return iemRaiseUndefinedOpcode(pVCpu);
4403 }
4404 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE))
4405 {
4406 Log(("sysexit: Protected or long mode is required -> #GP(0)\n"));
4407 return iemRaiseGeneralProtectionFault0(pVCpu);
4408 }
4409 bool fIsLongMode = CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu));
4410 if (IEM_IS_GUEST_CPU_AMD(pVCpu) && fIsLongMode)
4411 {
4412 Log(("sysexit: Only available in protected mode on AMD -> #UD\n"));
4413 return iemRaiseUndefinedOpcode(pVCpu);
4414 }
4415 if (IEM_GET_CPL(pVCpu) != 0)
4416 {
4417 Log(("sysexit: CPL(=%u) != 0 -> #GP(0)\n", IEM_GET_CPL(pVCpu)));
4418 return iemRaiseGeneralProtectionFault0(pVCpu);
4419 }
4420 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SYSENTER_MSRS);
4421 uint16_t uNewCs = pVCpu->cpum.GstCtx.SysEnter.cs;
4422 if ((uNewCs & X86_SEL_MASK_OFF_RPL) == 0)
4423 {
4424 Log(("sysexit: SYSENTER_CS = %#x -> #GP(0)\n", uNewCs));
4425 return iemRaiseGeneralProtectionFault0(pVCpu);
4426 }
4427
4428 /*
4429 * Update registers and commit.
4430 */
4431 if (enmEffOpSize == IEMMODE_64BIT)
4432 {
4433 Log(("sysexit: %04x:%016RX64 [efl=%#llx] -> %04x:%016RX64\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
4434 pVCpu->cpum.GstCtx.rflags.u, (uNewCs | 3) + 32, pVCpu->cpum.GstCtx.rcx));
4435 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.rdx;
4436 pVCpu->cpum.GstCtx.rsp = pVCpu->cpum.GstCtx.rcx;
4437 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_L | X86DESCATTR_G | X86DESCATTR_P | X86DESCATTR_DT
4438 | X86DESCATTR_LIMIT_HIGH | X86_SEL_TYPE_ER_ACC | (3 << X86DESCATTR_DPL_SHIFT);
4439 pVCpu->cpum.GstCtx.cs.Sel = (uNewCs | 3) + 32;
4440 pVCpu->cpum.GstCtx.cs.ValidSel = (uNewCs | 3) + 32;
4441 pVCpu->cpum.GstCtx.ss.Sel = (uNewCs | 3) + 40;
4442 pVCpu->cpum.GstCtx.ss.ValidSel = (uNewCs | 3) + 40;
4443
4444 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK))
4445 | (3 << IEM_F_X86_CPL_SHIFT)
4446 | IEM_F_MODE_X86_64BIT
4447 | iemCalcExecAcFlag(pVCpu);
4448 }
4449 else
4450 {
4451 Log(("sysexit: %04x:%08RX64 [efl=%#llx] -> %04x:%08RX32\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
4452 pVCpu->cpum.GstCtx.rflags.u, (uNewCs | 3) + 16, (uint32_t)pVCpu->cpum.GstCtx.edx));
4453 pVCpu->cpum.GstCtx.rip = pVCpu->cpum.GstCtx.edx;
4454 pVCpu->cpum.GstCtx.rsp = pVCpu->cpum.GstCtx.ecx;
4455 pVCpu->cpum.GstCtx.cs.Attr.u = X86DESCATTR_D | X86DESCATTR_G | X86DESCATTR_P | X86DESCATTR_DT
4456 | X86DESCATTR_LIMIT_HIGH | X86_SEL_TYPE_ER_ACC | (3 << X86DESCATTR_DPL_SHIFT);
4457 pVCpu->cpum.GstCtx.cs.Sel = (uNewCs | 3) + 16;
4458 pVCpu->cpum.GstCtx.cs.ValidSel = (uNewCs | 3) + 16;
4459 pVCpu->cpum.GstCtx.ss.Sel = (uNewCs | 3) + 24;
4460 pVCpu->cpum.GstCtx.ss.ValidSel = (uNewCs | 3) + 24;
4461
4462 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~(IEM_F_MODE_MASK | IEM_F_X86_CPL_MASK))
4463 | (3 << IEM_F_X86_CPL_SHIFT)
4464 | IEM_F_MODE_X86_32BIT_PROT
4465 | iemCalc32BitFlatIndicatorEsDs(pVCpu)
4466 | iemCalcExecAcFlag(pVCpu);
4467 }
4468 pVCpu->cpum.GstCtx.cs.u64Base = 0;
4469 pVCpu->cpum.GstCtx.cs.u32Limit = UINT32_MAX;
4470 pVCpu->cpum.GstCtx.cs.fFlags = CPUMSELREG_FLAGS_VALID;
4471
4472 pVCpu->cpum.GstCtx.ss.u64Base = 0;
4473 pVCpu->cpum.GstCtx.ss.u32Limit = UINT32_MAX;
4474 pVCpu->cpum.GstCtx.ss.Attr.u = X86DESCATTR_D | X86DESCATTR_G | X86DESCATTR_P | X86DESCATTR_DT
4475 | X86DESCATTR_LIMIT_HIGH | X86_SEL_TYPE_RW_ACC | (3 << X86DESCATTR_DPL_SHIFT);
4476 pVCpu->cpum.GstCtx.ss.fFlags = CPUMSELREG_FLAGS_VALID;
4477 pVCpu->cpum.GstCtx.rflags.Bits.u1RF = 0;
4478
4479/** @todo single stepping */
4480
4481 /* Flush the prefetch buffer. */
4482 IEM_FLUSH_PREFETCH_HEAVY(pVCpu, cbInstr);
4483
4484 return VINF_SUCCESS;
4485}
4486
4487
4488/**
4489 * Completes a MOV SReg,XXX or POP SReg instruction.
4490 *
4491 * When not modifying SS or when we're already in an interrupt shadow we
4492 * can update RIP and finish the instruction the normal way.
4493 *
4494 * Otherwise, the MOV/POP SS interrupt shadow that we now enable will block
4495 * both TF and DBx events. The TF will be ignored while the DBx ones will
4496 * be delayed till the next instruction boundrary. For more details see
4497 * @sdmv3{077,200,6.8.3,Masking Exceptions and Interrupts When Switching Stacks}.
4498 */
4499DECLINLINE(VBOXSTRICTRC) iemCImpl_LoadSRegFinish(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iSegReg)
4500{
4501 if (iSegReg != X86_SREG_SS || CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx))
4502 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
4503
4504 iemRegAddToRip(pVCpu, cbInstr);
4505 pVCpu->cpum.GstCtx.eflags.uBoth &= ~X86_EFL_RF; /* Shadow int isn't set and DRx is delayed, so only clear RF. */
4506 CPUMSetInInterruptShadowSs(&pVCpu->cpum.GstCtx);
4507
4508 return VINF_SUCCESS;
4509}
4510
4511
4512/**
4513 * Common worker for 'pop SReg', 'mov SReg, GReg' and 'lXs GReg, reg/mem'.
4514 *
4515 * @param pVCpu The cross context virtual CPU structure of the calling
4516 * thread.
4517 * @param iSegReg The segment register number (valid).
4518 * @param uSel The new selector value.
4519 */
4520static VBOXSTRICTRC iemCImpl_LoadSRegWorker(PVMCPUCC pVCpu, uint8_t iSegReg, uint16_t uSel)
4521{
4522 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_FROM_IDX(iSegReg));
4523 uint16_t *pSel = iemSRegRef(pVCpu, iSegReg);
4524 PCPUMSELREGHID pHid = iemSRegGetHid(pVCpu, iSegReg);
4525
4526 Assert(iSegReg <= X86_SREG_GS && iSegReg != X86_SREG_CS);
4527
4528 /*
4529 * Real mode and V8086 mode are easy.
4530 */
4531 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
4532 {
4533 *pSel = uSel;
4534 pHid->u64Base = (uint32_t)uSel << 4;
4535 pHid->ValidSel = uSel;
4536 pHid->fFlags = CPUMSELREG_FLAGS_VALID;
4537#if 0 /* AMD Volume 2, chapter 4.1 - "real mode segmentation" - states that limit and attributes are untouched. */
4538 /** @todo Does the CPU actually load limits and attributes in the
4539 * real/V8086 mode segment load case? It doesn't for CS in far
4540 * jumps... Affects unreal mode. */
4541 pHid->u32Limit = 0xffff;
4542 pHid->Attr.u = 0;
4543 pHid->Attr.n.u1Present = 1;
4544 pHid->Attr.n.u1DescType = 1;
4545 pHid->Attr.n.u4Type = iSegReg != X86_SREG_CS
4546 ? X86_SEL_TYPE_RW
4547 : X86_SEL_TYPE_READ | X86_SEL_TYPE_CODE;
4548#endif
4549
4550 /* Update the FLAT 32-bit mode flag, if we're in 32-bit unreal mode (unlikely): */
4551 if (RT_LIKELY(!IEM_IS_32BIT_CODE(pVCpu)))
4552 { /* likely */ }
4553 else if (uSel != 0)
4554 pVCpu->iem.s.fExec &= ~IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK;
4555 else
4556 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK)
4557 | iemCalc32BitFlatIndicator(pVCpu);
4558 }
4559 /*
4560 * Protected / long mode - null segment.
4561 *
4562 * Check if it's a null segment selector value first, that's OK for DS, ES,
4563 * FS and GS. If not null, then we have to load and parse the descriptor.
4564 */
4565 else if (!(uSel & X86_SEL_MASK_OFF_RPL))
4566 {
4567 Assert(iSegReg != X86_SREG_CS); /** @todo testcase for \#UD on MOV CS, ax! */
4568 if (iSegReg == X86_SREG_SS)
4569 {
4570 /* In 64-bit kernel mode, the stack can be 0 because of the way
4571 interrupts are dispatched. AMD seems to have a slighly more
4572 relaxed relationship to SS.RPL than intel does. */
4573 /** @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! */
4574 if ( !IEM_IS_64BIT_CODE(pVCpu)
4575 || IEM_GET_CPL(pVCpu) > 2
4576 || ( uSel != IEM_GET_CPL(pVCpu)
4577 && !IEM_IS_GUEST_CPU_AMD(pVCpu)) )
4578 {
4579 Log(("load sreg %#x -> invalid stack selector, #GP(0)\n", uSel));
4580 return iemRaiseGeneralProtectionFault0(pVCpu);
4581 }
4582 }
4583
4584 *pSel = uSel; /* Not RPL, remember :-) */
4585 iemHlpLoadNullDataSelectorProt(pVCpu, pHid, uSel);
4586 if (iSegReg == X86_SREG_SS)
4587 pHid->Attr.u |= IEM_GET_CPL(pVCpu) << X86DESCATTR_DPL_SHIFT;
4588
4589 /* This will affect the FLAT 32-bit mode flag: */
4590 if ( iSegReg < X86_SREG_FS
4591 && IEM_IS_32BIT_CODE(pVCpu))
4592 pVCpu->iem.s.fExec &= ~IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK;
4593 }
4594 /*
4595 * Protected / long mode.
4596 */
4597 else
4598 {
4599 /* Fetch the descriptor. */
4600 IEMSELDESC Desc;
4601 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pVCpu, &Desc, uSel, X86_XCPT_GP); /** @todo Correct exception? */
4602 if (rcStrict != VINF_SUCCESS)
4603 return rcStrict;
4604
4605 /* Check GPs first. */
4606 if (!Desc.Legacy.Gen.u1DescType)
4607 {
4608 Log(("load sreg %d (=%#x) - system selector (%#x) -> #GP\n", iSegReg, uSel, Desc.Legacy.Gen.u4Type));
4609 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4610 }
4611 if (iSegReg == X86_SREG_SS) /* SS gets different treatment */
4612 {
4613 if ( (Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_CODE)
4614 || !(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_WRITE) )
4615 {
4616 Log(("load sreg SS, %#x - code or read only (%#x) -> #GP\n", uSel, Desc.Legacy.Gen.u4Type));
4617 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4618 }
4619 if ((uSel & X86_SEL_RPL) != IEM_GET_CPL(pVCpu))
4620 {
4621 Log(("load sreg SS, %#x - RPL and CPL (%d) differs -> #GP\n", uSel, IEM_GET_CPL(pVCpu)));
4622 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4623 }
4624 if (Desc.Legacy.Gen.u2Dpl != IEM_GET_CPL(pVCpu))
4625 {
4626 Log(("load sreg SS, %#x - DPL (%d) and CPL (%d) differs -> #GP\n", uSel, Desc.Legacy.Gen.u2Dpl, IEM_GET_CPL(pVCpu)));
4627 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4628 }
4629 }
4630 else
4631 {
4632 if ((Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_READ)) == X86_SEL_TYPE_CODE)
4633 {
4634 Log(("load sreg%u, %#x - execute only segment -> #GP\n", iSegReg, uSel));
4635 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4636 }
4637 if ( (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
4638 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
4639 {
4640#if 0 /* this is what intel says. */
4641 if ( (uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl
4642 && IEM_GET_CPL(pVCpu) > Desc.Legacy.Gen.u2Dpl)
4643 {
4644 Log(("load sreg%u, %#x - both RPL (%d) and CPL (%d) are greater than DPL (%d) -> #GP\n",
4645 iSegReg, uSel, (uSel & X86_SEL_RPL), IEM_GET_CPL(pVCpu), Desc.Legacy.Gen.u2Dpl));
4646 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4647 }
4648#else /* this is what makes more sense. */
4649 if ((unsigned)(uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl)
4650 {
4651 Log(("load sreg%u, %#x - RPL (%d) is greater than DPL (%d) -> #GP\n",
4652 iSegReg, uSel, (uSel & X86_SEL_RPL), Desc.Legacy.Gen.u2Dpl));
4653 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4654 }
4655 if (IEM_GET_CPL(pVCpu) > Desc.Legacy.Gen.u2Dpl)
4656 {
4657 Log(("load sreg%u, %#x - CPL (%d) is greater than DPL (%d) -> #GP\n",
4658 iSegReg, uSel, IEM_GET_CPL(pVCpu), Desc.Legacy.Gen.u2Dpl));
4659 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uSel);
4660 }
4661#endif
4662 }
4663 }
4664
4665 /* Is it there? */
4666 if (!Desc.Legacy.Gen.u1Present)
4667 {
4668 Log(("load sreg%d,%#x - segment not present -> #NP\n", iSegReg, uSel));
4669 return iemRaiseSelectorNotPresentBySelector(pVCpu, uSel);
4670 }
4671
4672 /* The base and limit. */
4673 uint32_t cbLimit = X86DESC_LIMIT_G(&Desc.Legacy);
4674 uint64_t u64Base = X86DESC_BASE(&Desc.Legacy);
4675
4676 /*
4677 * Ok, everything checked out fine. Now set the accessed bit before
4678 * committing the result into the registers.
4679 */
4680 if (!(Desc.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
4681 {
4682 rcStrict = iemMemMarkSelDescAccessed(pVCpu, uSel);
4683 if (rcStrict != VINF_SUCCESS)
4684 return rcStrict;
4685 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
4686 }
4687
4688 /* commit */
4689 *pSel = uSel;
4690 pHid->Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
4691 pHid->u32Limit = cbLimit;
4692 pHid->u64Base = u64Base;
4693 pHid->ValidSel = uSel;
4694 pHid->fFlags = CPUMSELREG_FLAGS_VALID;
4695
4696 /** @todo check if the hidden bits are loaded correctly for 64-bit
4697 * mode. */
4698
4699 /* This will affect the FLAT 32-bit mode flag: */
4700 if ( iSegReg < X86_SREG_FS
4701 && IEM_IS_32BIT_CODE(pVCpu))
4702 pVCpu->iem.s.fExec = (pVCpu->iem.s.fExec & ~IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK)
4703 | iemCalc32BitFlatIndicator(pVCpu);
4704 }
4705
4706 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pHid));
4707 CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_HIDDEN_SEL_REGS);
4708 return VINF_SUCCESS;
4709}
4710
4711
4712/**
4713 * Implements 'mov SReg, r/m'.
4714 *
4715 * @param iSegReg The segment register number (valid).
4716 * @param uSel The new selector value.
4717 */
4718IEM_CIMPL_DEF_2(iemCImpl_load_SReg, uint8_t, iSegReg, uint16_t, uSel)
4719{
4720 VBOXSTRICTRC rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, uSel);
4721 if (rcStrict == VINF_SUCCESS)
4722 rcStrict = iemCImpl_LoadSRegFinish(pVCpu, cbInstr, iSegReg);
4723 return rcStrict;
4724}
4725
4726
4727/**
4728 * Implements 'pop SReg'.
4729 *
4730 * @param iSegReg The segment register number (valid).
4731 * @param enmEffOpSize The efficient operand size (valid).
4732 */
4733IEM_CIMPL_DEF_2(iemCImpl_pop_Sreg, uint8_t, iSegReg, IEMMODE, enmEffOpSize)
4734{
4735 VBOXSTRICTRC rcStrict;
4736
4737 /*
4738 * Read the selector off the stack and join paths with mov ss, reg.
4739 */
4740 RTUINT64U TmpRsp;
4741 TmpRsp.u = pVCpu->cpum.GstCtx.rsp;
4742 switch (enmEffOpSize)
4743 {
4744 case IEMMODE_16BIT:
4745 {
4746 uint16_t uSel;
4747 rcStrict = iemMemStackPopU16Ex(pVCpu, &uSel, &TmpRsp);
4748 if (rcStrict == VINF_SUCCESS)
4749 rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, uSel);
4750 break;
4751 }
4752
4753 case IEMMODE_32BIT:
4754 {
4755 /* Modern Intel CPU only does a WORD sized access here, both as
4756 segmentation and paging is concerned. So, we have to emulate
4757 this to make bs3-cpu-weird-1 happy. */
4758 if (IEM_IS_GUEST_CPU_INTEL(pVCpu))
4759 {
4760 /* We don't have flexible enough stack primitives here, so just
4761 do a word pop and add two bytes to SP/RSP on success. */
4762 uint16_t uSel;
4763 rcStrict = iemMemStackPopU16Ex(pVCpu, &uSel, &TmpRsp);
4764 if (rcStrict == VINF_SUCCESS)
4765 {
4766 iemRegAddToRspEx(pVCpu, &TmpRsp, sizeof(uint32_t) - sizeof(uint16_t));
4767 rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, uSel);
4768 }
4769 }
4770 else
4771 {
4772 uint32_t u32Value;
4773 rcStrict = iemMemStackPopU32Ex(pVCpu, &u32Value, &TmpRsp);
4774 if (rcStrict == VINF_SUCCESS)
4775 rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, (uint16_t)u32Value);
4776 }
4777 break;
4778 }
4779
4780 case IEMMODE_64BIT:
4781 {
4782 /* Like for the 32-bit case above, intel only does a WORD access. */
4783 if (IEM_IS_GUEST_CPU_INTEL(pVCpu))
4784 {
4785 uint16_t uSel;
4786 rcStrict = iemMemStackPopU16Ex(pVCpu, &uSel, &TmpRsp);
4787 if (rcStrict == VINF_SUCCESS)
4788 {
4789 iemRegAddToRspEx(pVCpu, &TmpRsp, sizeof(uint64_t) - sizeof(uint16_t));
4790 rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, uSel);
4791 }
4792 }
4793 else
4794 {
4795 uint64_t u64Value;
4796 rcStrict = iemMemStackPopU64Ex(pVCpu, &u64Value, &TmpRsp);
4797 if (rcStrict == VINF_SUCCESS)
4798 rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, (uint16_t)u64Value);
4799 }
4800 break;
4801 }
4802 IEM_NOT_REACHED_DEFAULT_CASE_RET();
4803 }
4804
4805 /*
4806 * If the load succeeded, commit the stack change and finish the instruction.
4807 */
4808 if (rcStrict == VINF_SUCCESS)
4809 {
4810 pVCpu->cpum.GstCtx.rsp = TmpRsp.u;
4811 rcStrict = iemCImpl_LoadSRegFinish(pVCpu, cbInstr, iSegReg);
4812 }
4813
4814 return rcStrict;
4815}
4816
4817
4818/**
4819 * Implements lgs, lfs, les, lds & lss.
4820 */
4821IEM_CIMPL_DEF_5(iemCImpl_load_SReg_Greg, uint16_t, uSel, uint64_t, offSeg, uint8_t, iSegReg, uint8_t, iGReg, IEMMODE, enmEffOpSize)
4822{
4823 /*
4824 * Use iemCImpl_LoadSRegWorker to do the tricky segment register loading.
4825 */
4826 /** @todo verify and test that mov, pop and lXs works the segment
4827 * register loading in the exact same way. */
4828 VBOXSTRICTRC rcStrict = iemCImpl_LoadSRegWorker(pVCpu, iSegReg, uSel);
4829 if (rcStrict == VINF_SUCCESS)
4830 {
4831 switch (enmEffOpSize)
4832 {
4833 case IEMMODE_16BIT:
4834 iemGRegStoreU16(pVCpu, iGReg, offSeg);
4835 break;
4836 case IEMMODE_32BIT:
4837 case IEMMODE_64BIT:
4838 iemGRegStoreU64(pVCpu, iGReg, offSeg);
4839 break;
4840 IEM_NOT_REACHED_DEFAULT_CASE_RET();
4841 }
4842 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
4843 }
4844 return rcStrict;
4845}
4846
4847
4848/**
4849 * Helper for VERR, VERW, LAR, and LSL and loads the descriptor into memory.
4850 *
4851 * @retval VINF_SUCCESS on success.
4852 * @retval VINF_IEM_SELECTOR_NOT_OK if the selector isn't ok.
4853 * @retval iemMemFetchSysU64 return value.
4854 *
4855 * @param pVCpu The cross context virtual CPU structure of the calling thread.
4856 * @param uSel The selector value.
4857 * @param fAllowSysDesc Whether system descriptors are OK or not.
4858 * @param pDesc Where to return the descriptor on success.
4859 */
4860static VBOXSTRICTRC iemCImpl_LoadDescHelper(PVMCPUCC pVCpu, uint16_t uSel, bool fAllowSysDesc, PIEMSELDESC pDesc)
4861{
4862 pDesc->Long.au64[0] = 0;
4863 pDesc->Long.au64[1] = 0;
4864
4865 if (!(uSel & X86_SEL_MASK_OFF_RPL)) /** @todo test this on 64-bit. */
4866 return VINF_IEM_SELECTOR_NOT_OK;
4867
4868 /* Within the table limits? */
4869 RTGCPTR GCPtrBase;
4870 if (uSel & X86_SEL_LDT)
4871 {
4872 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_LDTR);
4873 if ( !pVCpu->cpum.GstCtx.ldtr.Attr.n.u1Present
4874 || (uSel | X86_SEL_RPL_LDT) > pVCpu->cpum.GstCtx.ldtr.u32Limit )
4875 return VINF_IEM_SELECTOR_NOT_OK;
4876 GCPtrBase = pVCpu->cpum.GstCtx.ldtr.u64Base;
4877 }
4878 else
4879 {
4880 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_GDTR);
4881 if ((uSel | X86_SEL_RPL_LDT) > pVCpu->cpum.GstCtx.gdtr.cbGdt)
4882 return VINF_IEM_SELECTOR_NOT_OK;
4883 GCPtrBase = pVCpu->cpum.GstCtx.gdtr.pGdt;
4884 }
4885
4886 /* Fetch the descriptor. */
4887 VBOXSTRICTRC rcStrict = iemMemFetchSysU64(pVCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
4888 if (rcStrict != VINF_SUCCESS)
4889 return rcStrict;
4890 if (!pDesc->Legacy.Gen.u1DescType)
4891 {
4892 if (!fAllowSysDesc)
4893 return VINF_IEM_SELECTOR_NOT_OK;
4894 if (CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
4895 {
4896 rcStrict = iemMemFetchSysU64(pVCpu, &pDesc->Long.au64[1], UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK) + 8);
4897 if (rcStrict != VINF_SUCCESS)
4898 return rcStrict;
4899 }
4900
4901 }
4902
4903 return VINF_SUCCESS;
4904}
4905
4906
4907/**
4908 * Implements verr (fWrite = false) and verw (fWrite = true).
4909 */
4910IEM_CIMPL_DEF_2(iemCImpl_VerX, uint16_t, uSel, bool, fWrite)
4911{
4912 Assert(!IEM_IS_REAL_OR_V86_MODE(pVCpu));
4913
4914 /** @todo figure whether the accessed bit is set or not. */
4915
4916 bool fAccessible = true;
4917 IEMSELDESC Desc;
4918 VBOXSTRICTRC rcStrict = iemCImpl_LoadDescHelper(pVCpu, uSel, false /*fAllowSysDesc*/, &Desc);
4919 if (rcStrict == VINF_SUCCESS)
4920 {
4921 /* Check the descriptor, order doesn't matter much here. */
4922 if ( !Desc.Legacy.Gen.u1DescType
4923 || !Desc.Legacy.Gen.u1Present)
4924 fAccessible = false;
4925 else
4926 {
4927 if ( fWrite
4928 ? (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE
4929 : (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_READ)) == X86_SEL_TYPE_CODE)
4930 fAccessible = false;
4931
4932 /** @todo testcase for the conforming behavior. */
4933 if ( (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
4934 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
4935 {
4936 if ((unsigned)(uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl)
4937 fAccessible = false;
4938 else if (IEM_GET_CPL(pVCpu) > Desc.Legacy.Gen.u2Dpl)
4939 fAccessible = false;
4940 }
4941 }
4942
4943 }
4944 else if (rcStrict == VINF_IEM_SELECTOR_NOT_OK)
4945 fAccessible = false;
4946 else
4947 return rcStrict;
4948
4949 /* commit */
4950 pVCpu->cpum.GstCtx.eflags.Bits.u1ZF = fAccessible;
4951
4952 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
4953}
4954
4955
4956/**
4957 * Implements LAR and LSL with 64-bit operand size.
4958 *
4959 * @returns VINF_SUCCESS.
4960 * @param pu64Dst Pointer to the destination register.
4961 * @param uSel The selector to load details for.
4962 * @param fIsLar true = LAR, false = LSL.
4963 */
4964IEM_CIMPL_DEF_3(iemCImpl_LarLsl_u64, uint64_t *, pu64Dst, uint16_t, uSel, bool, fIsLar)
4965{
4966 Assert(!IEM_IS_REAL_OR_V86_MODE(pVCpu));
4967
4968 /** @todo figure whether the accessed bit is set or not. */
4969
4970 bool fDescOk = true;
4971 IEMSELDESC Desc;
4972 VBOXSTRICTRC rcStrict = iemCImpl_LoadDescHelper(pVCpu, uSel, true /*fAllowSysDesc*/, &Desc);
4973 if (rcStrict == VINF_SUCCESS)
4974 {
4975 /*
4976 * Check the descriptor type.
4977 */
4978 if (!Desc.Legacy.Gen.u1DescType)
4979 {
4980 if (CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
4981 {
4982 if (Desc.Long.Gen.u5Zeros)
4983 fDescOk = false;
4984 else
4985 switch (Desc.Long.Gen.u4Type)
4986 {
4987 /** @todo Intel lists 0 as valid for LSL, verify whether that's correct */
4988 case AMD64_SEL_TYPE_SYS_TSS_AVAIL:
4989 case AMD64_SEL_TYPE_SYS_TSS_BUSY:
4990 case AMD64_SEL_TYPE_SYS_LDT: /** @todo Intel lists this as invalid for LAR, AMD and 32-bit does otherwise. */
4991 break;
4992 case AMD64_SEL_TYPE_SYS_CALL_GATE:
4993 fDescOk = fIsLar;
4994 break;
4995 default:
4996 fDescOk = false;
4997 break;
4998 }
4999 }
5000 else
5001 {
5002 switch (Desc.Long.Gen.u4Type)
5003 {
5004 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
5005 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
5006 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
5007 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
5008 case X86_SEL_TYPE_SYS_LDT:
5009 break;
5010 case X86_SEL_TYPE_SYS_286_CALL_GATE:
5011 case X86_SEL_TYPE_SYS_TASK_GATE:
5012 case X86_SEL_TYPE_SYS_386_CALL_GATE:
5013 fDescOk = fIsLar;
5014 break;
5015 default:
5016 fDescOk = false;
5017 break;
5018 }
5019 }
5020 }
5021 if (fDescOk)
5022 {
5023 /*
5024 * Check the RPL/DPL/CPL interaction..
5025 */
5026 /** @todo testcase for the conforming behavior. */
5027 if ( (Desc.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF)) != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF)
5028 || !Desc.Legacy.Gen.u1DescType)
5029 {
5030 if ((unsigned)(uSel & X86_SEL_RPL) > Desc.Legacy.Gen.u2Dpl)
5031 fDescOk = false;
5032 else if (IEM_GET_CPL(pVCpu) > Desc.Legacy.Gen.u2Dpl)
5033 fDescOk = false;
5034 }
5035 }
5036
5037 if (fDescOk)
5038 {
5039 /*
5040 * All fine, start committing the result.
5041 */
5042 if (fIsLar)
5043 *pu64Dst = Desc.Legacy.au32[1] & UINT32_C(0x00ffff00);
5044 else
5045 *pu64Dst = X86DESC_LIMIT_G(&Desc.Legacy);
5046 }
5047
5048 }
5049 else if (rcStrict == VINF_IEM_SELECTOR_NOT_OK)
5050 fDescOk = false;
5051 else
5052 return rcStrict;
5053
5054 /* commit flags value and advance rip. */
5055 pVCpu->cpum.GstCtx.eflags.Bits.u1ZF = fDescOk;
5056 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5057}
5058
5059
5060/**
5061 * Implements LAR and LSL with 16-bit operand size.
5062 *
5063 * @returns VINF_SUCCESS.
5064 * @param pu16Dst Pointer to the destination register.
5065 * @param uSel The selector to load details for.
5066 * @param fIsLar true = LAR, false = LSL.
5067 */
5068IEM_CIMPL_DEF_3(iemCImpl_LarLsl_u16, uint16_t *, pu16Dst, uint16_t, uSel, bool, fIsLar)
5069{
5070 uint64_t u64TmpDst = *pu16Dst;
5071 IEM_CIMPL_CALL_3(iemCImpl_LarLsl_u64, &u64TmpDst, uSel, fIsLar);
5072 *pu16Dst = u64TmpDst;
5073 return VINF_SUCCESS;
5074}
5075
5076
5077/**
5078 * Implements lgdt.
5079 *
5080 * @param iEffSeg The segment of the new gdtr contents
5081 * @param GCPtrEffSrc The address of the new gdtr contents.
5082 * @param enmEffOpSize The effective operand size.
5083 */
5084IEM_CIMPL_DEF_3(iemCImpl_lgdt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc, IEMMODE, enmEffOpSize)
5085{
5086 if (IEM_GET_CPL(pVCpu) != 0)
5087 return iemRaiseGeneralProtectionFault0(pVCpu);
5088 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
5089
5090 if (!IEM_IS_IN_GUEST(pVCpu))
5091 { /* probable */ }
5092 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5093 && IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5094 {
5095 Log(("lgdt: Guest intercept -> VM-exit\n"));
5096 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_GDTR_IDTR_ACCESS, VMXINSTRID_LGDT, cbInstr);
5097 }
5098 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_GDTR_WRITES))
5099 {
5100 Log(("lgdt: Guest intercept -> #VMEXIT\n"));
5101 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5102 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_GDTR_WRITE, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5103 }
5104
5105 /*
5106 * Fetch the limit and base address.
5107 */
5108 uint16_t cbLimit;
5109 RTGCPTR GCPtrBase;
5110 VBOXSTRICTRC rcStrict = iemMemFetchDataXdtr(pVCpu, &cbLimit, &GCPtrBase, iEffSeg, GCPtrEffSrc, enmEffOpSize);
5111 if (rcStrict == VINF_SUCCESS)
5112 {
5113 if ( !IEM_IS_64BIT_CODE(pVCpu)
5114 || X86_IS_CANONICAL(GCPtrBase))
5115 {
5116 rcStrict = CPUMSetGuestGDTR(pVCpu, GCPtrBase, cbLimit);
5117 if (rcStrict == VINF_SUCCESS)
5118 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5119 }
5120 else
5121 {
5122 Log(("iemCImpl_lgdt: Non-canonical base %04x:%RGv\n", cbLimit, GCPtrBase));
5123 return iemRaiseGeneralProtectionFault0(pVCpu);
5124 }
5125 }
5126 return rcStrict;
5127}
5128
5129
5130/**
5131 * Implements sgdt.
5132 *
5133 * @param iEffSeg The segment where to store the gdtr content.
5134 * @param GCPtrEffDst The address where to store the gdtr content.
5135 */
5136IEM_CIMPL_DEF_2(iemCImpl_sgdt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5137{
5138 /*
5139 * Join paths with sidt.
5140 * Note! No CPL or V8086 checks here, it's a really sad story, ask Intel if
5141 * you really must know.
5142 */
5143 if (!IEM_IS_IN_GUEST(pVCpu))
5144 { /* probable */ }
5145 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5146 && IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5147 {
5148 Log(("sgdt: Guest intercept -> VM-exit\n"));
5149 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_GDTR_IDTR_ACCESS, VMXINSTRID_SGDT, cbInstr);
5150 }
5151 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_GDTR_READS))
5152 {
5153 Log(("sgdt: Guest intercept -> #VMEXIT\n"));
5154 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5155 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_GDTR_READ, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5156 }
5157
5158 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_GDTR);
5159 VBOXSTRICTRC rcStrict = iemMemStoreDataXdtr(pVCpu, pVCpu->cpum.GstCtx.gdtr.cbGdt, pVCpu->cpum.GstCtx.gdtr.pGdt, iEffSeg, GCPtrEffDst);
5160 if (rcStrict == VINF_SUCCESS)
5161 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5162 return rcStrict;
5163}
5164
5165
5166/**
5167 * Implements lidt.
5168 *
5169 * @param iEffSeg The segment of the new idtr contents
5170 * @param GCPtrEffSrc The address of the new idtr contents.
5171 * @param enmEffOpSize The effective operand size.
5172 */
5173IEM_CIMPL_DEF_3(iemCImpl_lidt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc, IEMMODE, enmEffOpSize)
5174{
5175 if (IEM_GET_CPL(pVCpu) != 0)
5176 return iemRaiseGeneralProtectionFault0(pVCpu);
5177 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
5178
5179 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IDTR_WRITES))
5180 { /* probable */ }
5181 else
5182 {
5183 Log(("lidt: Guest intercept -> #VMEXIT\n"));
5184 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5185 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_IDTR_WRITE, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5186 }
5187
5188 /*
5189 * Fetch the limit and base address.
5190 */
5191 uint16_t cbLimit;
5192 RTGCPTR GCPtrBase;
5193 VBOXSTRICTRC rcStrict = iemMemFetchDataXdtr(pVCpu, &cbLimit, &GCPtrBase, iEffSeg, GCPtrEffSrc, enmEffOpSize);
5194 if (rcStrict == VINF_SUCCESS)
5195 {
5196 if ( !IEM_IS_64BIT_CODE(pVCpu)
5197 || X86_IS_CANONICAL(GCPtrBase))
5198 {
5199 CPUMSetGuestIDTR(pVCpu, GCPtrBase, cbLimit);
5200 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5201 }
5202 else
5203 {
5204 Log(("iemCImpl_lidt: Non-canonical base %04x:%RGv\n", cbLimit, GCPtrBase));
5205 return iemRaiseGeneralProtectionFault0(pVCpu);
5206 }
5207 }
5208 return rcStrict;
5209}
5210
5211
5212/**
5213 * Implements sidt.
5214 *
5215 * @param iEffSeg The segment where to store the idtr content.
5216 * @param GCPtrEffDst The address where to store the idtr content.
5217 */
5218IEM_CIMPL_DEF_2(iemCImpl_sidt, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5219{
5220 /*
5221 * Join paths with sgdt.
5222 * Note! No CPL or V8086 checks here, it's a really sad story, ask Intel if
5223 * you really must know.
5224 */
5225 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IDTR_READS))
5226 { /* probable */ }
5227 else
5228 {
5229 Log(("sidt: Guest intercept -> #VMEXIT\n"));
5230 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5231 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_IDTR_READ, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5232 }
5233
5234 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_IDTR);
5235 VBOXSTRICTRC rcStrict = iemMemStoreDataXdtr(pVCpu, pVCpu->cpum.GstCtx.idtr.cbIdt, pVCpu->cpum.GstCtx.idtr.pIdt, iEffSeg, GCPtrEffDst);
5236 if (rcStrict == VINF_SUCCESS)
5237 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5238 return rcStrict;
5239}
5240
5241
5242/**
5243 * Implements lldt.
5244 *
5245 * @param uNewLdt The new LDT selector value.
5246 */
5247IEM_CIMPL_DEF_1(iemCImpl_lldt, uint16_t, uNewLdt)
5248{
5249 /*
5250 * Check preconditions.
5251 */
5252 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
5253 {
5254 Log(("lldt %04x - real or v8086 mode -> #GP(0)\n", uNewLdt));
5255 return iemRaiseUndefinedOpcode(pVCpu);
5256 }
5257 if (IEM_GET_CPL(pVCpu) != 0)
5258 {
5259 Log(("lldt %04x - CPL is %d -> #GP(0)\n", uNewLdt, IEM_GET_CPL(pVCpu)));
5260 return iemRaiseGeneralProtectionFault0(pVCpu);
5261 }
5262
5263 /* Nested-guest VMX intercept (SVM is after all checks). */
5264 /** @todo testcase: exit vs check order. */
5265 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5266 || !IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5267 { /* probable */ }
5268 else
5269 {
5270 Log(("lldt: Guest intercept -> VM-exit\n"));
5271 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_LDTR_TR_ACCESS, VMXINSTRID_LLDT, cbInstr);
5272 }
5273
5274 if (uNewLdt & X86_SEL_LDT)
5275 {
5276 Log(("lldt %04x - LDT selector -> #GP\n", uNewLdt));
5277 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewLdt);
5278 }
5279
5280 /*
5281 * Now, loading a NULL selector is easy.
5282 */
5283 if (!(uNewLdt & X86_SEL_MASK_OFF_RPL))
5284 {
5285 /* Nested-guest SVM intercept. */
5286 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_LDTR_WRITES))
5287 { /* probable */ }
5288 else
5289 {
5290 Log(("lldt: Guest intercept -> #VMEXIT\n"));
5291 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5292 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_LDTR_WRITE, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5293 }
5294
5295 Log(("lldt %04x: Loading NULL selector.\n", uNewLdt));
5296 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_LDTR;
5297 CPUMSetGuestLDTR(pVCpu, uNewLdt);
5298 pVCpu->cpum.GstCtx.ldtr.ValidSel = uNewLdt;
5299 pVCpu->cpum.GstCtx.ldtr.fFlags = CPUMSELREG_FLAGS_VALID;
5300 if (IEM_IS_GUEST_CPU_AMD(pVCpu))
5301 {
5302 /* AMD-V seems to leave the base and limit alone. */
5303 pVCpu->cpum.GstCtx.ldtr.Attr.u = X86DESCATTR_UNUSABLE;
5304 }
5305 else
5306 {
5307 /* VT-x (Intel 3960x) seems to be doing the following. */
5308 pVCpu->cpum.GstCtx.ldtr.Attr.u = X86DESCATTR_UNUSABLE | X86DESCATTR_G | X86DESCATTR_D;
5309 pVCpu->cpum.GstCtx.ldtr.u64Base = 0;
5310 pVCpu->cpum.GstCtx.ldtr.u32Limit = UINT32_MAX;
5311 }
5312
5313 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5314 }
5315
5316 /*
5317 * Read the descriptor.
5318 */
5319 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_LDTR | CPUMCTX_EXTRN_GDTR);
5320 IEMSELDESC Desc;
5321 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pVCpu, &Desc, uNewLdt, X86_XCPT_GP); /** @todo Correct exception? */
5322 if (rcStrict != VINF_SUCCESS)
5323 return rcStrict;
5324
5325 /* Check GPs first. */
5326 if (Desc.Legacy.Gen.u1DescType)
5327 {
5328 Log(("lldt %#x - not system selector (type %x) -> #GP\n", uNewLdt, Desc.Legacy.Gen.u4Type));
5329 return iemRaiseGeneralProtectionFault(pVCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
5330 }
5331 if (Desc.Legacy.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
5332 {
5333 Log(("lldt %#x - not LDT selector (type %x) -> #GP\n", uNewLdt, Desc.Legacy.Gen.u4Type));
5334 return iemRaiseGeneralProtectionFault(pVCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
5335 }
5336 uint64_t u64Base;
5337 if (!IEM_IS_LONG_MODE(pVCpu))
5338 u64Base = X86DESC_BASE(&Desc.Legacy);
5339 else
5340 {
5341 if (Desc.Long.Gen.u5Zeros)
5342 {
5343 Log(("lldt %#x - u5Zeros=%#x -> #GP\n", uNewLdt, Desc.Long.Gen.u5Zeros));
5344 return iemRaiseGeneralProtectionFault(pVCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
5345 }
5346
5347 u64Base = X86DESC64_BASE(&Desc.Long);
5348 if (!IEM_IS_CANONICAL(u64Base))
5349 {
5350 Log(("lldt %#x - non-canonical base address %#llx -> #GP\n", uNewLdt, u64Base));
5351 return iemRaiseGeneralProtectionFault(pVCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
5352 }
5353 }
5354
5355 /* NP */
5356 if (!Desc.Legacy.Gen.u1Present)
5357 {
5358 Log(("lldt %#x - segment not present -> #NP\n", uNewLdt));
5359 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewLdt);
5360 }
5361
5362 /* Nested-guest SVM intercept. */
5363 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_LDTR_WRITES))
5364 { /* probable */ }
5365 else
5366 {
5367 Log(("lldt: Guest intercept -> #VMEXIT\n"));
5368 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5369 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_LDTR_WRITE, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5370 }
5371
5372 /*
5373 * It checks out alright, update the registers.
5374 */
5375/** @todo check if the actual value is loaded or if the RPL is dropped */
5376 CPUMSetGuestLDTR(pVCpu, uNewLdt & X86_SEL_MASK_OFF_RPL);
5377 pVCpu->cpum.GstCtx.ldtr.ValidSel = uNewLdt & X86_SEL_MASK_OFF_RPL;
5378 pVCpu->cpum.GstCtx.ldtr.fFlags = CPUMSELREG_FLAGS_VALID;
5379 pVCpu->cpum.GstCtx.ldtr.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
5380 pVCpu->cpum.GstCtx.ldtr.u32Limit = X86DESC_LIMIT_G(&Desc.Legacy);
5381 pVCpu->cpum.GstCtx.ldtr.u64Base = u64Base;
5382
5383 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5384}
5385
5386
5387/**
5388 * Implements sldt GReg
5389 *
5390 * @param iGReg The general register to store the CRx value in.
5391 * @param enmEffOpSize The operand size.
5392 */
5393IEM_CIMPL_DEF_2(iemCImpl_sldt_reg, uint8_t, iGReg, uint8_t, enmEffOpSize)
5394{
5395 if (!IEM_IS_IN_GUEST(pVCpu))
5396 { /* probable */ }
5397 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5398 && IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5399 {
5400 Log(("sldt: Guest intercept -> VM-exit\n"));
5401 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_LDTR_TR_ACCESS, VMXINSTRID_SLDT, cbInstr);
5402 }
5403 else
5404 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_LDTR_READS, SVM_EXIT_LDTR_READ, 0, 0, cbInstr);
5405
5406 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_LDTR);
5407 switch (enmEffOpSize)
5408 {
5409 case IEMMODE_16BIT:
5410 iemGRegStoreU16(pVCpu, iGReg, pVCpu->cpum.GstCtx.ldtr.Sel);
5411 break;
5412 case IEMMODE_32BIT:
5413 case IEMMODE_64BIT:
5414 iemGRegStoreU64(pVCpu, iGReg, pVCpu->cpum.GstCtx.ldtr.Sel);
5415 break;
5416 IEM_NOT_REACHED_DEFAULT_CASE_RET();
5417 }
5418 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5419}
5420
5421
5422/**
5423 * Implements sldt mem.
5424 *
5425 * @param iEffSeg The effective segment register to use with @a GCPtrMem.
5426 * @param GCPtrEffDst Where to store the 16-bit CR0 value.
5427 */
5428IEM_CIMPL_DEF_2(iemCImpl_sldt_mem, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5429{
5430 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_LDTR_READS, SVM_EXIT_LDTR_READ, 0, 0, cbInstr);
5431
5432 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_LDTR);
5433 VBOXSTRICTRC rcStrict = iemMemStoreDataU16(pVCpu, iEffSeg, GCPtrEffDst, pVCpu->cpum.GstCtx.ldtr.Sel);
5434 if (rcStrict == VINF_SUCCESS)
5435 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5436 return rcStrict;
5437}
5438
5439
5440/**
5441 * Implements ltr.
5442 *
5443 * @param uNewTr The new TSS selector value.
5444 */
5445IEM_CIMPL_DEF_1(iemCImpl_ltr, uint16_t, uNewTr)
5446{
5447 /*
5448 * Check preconditions.
5449 */
5450 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
5451 {
5452 Log(("ltr %04x - real or v8086 mode -> #GP(0)\n", uNewTr));
5453 return iemRaiseUndefinedOpcode(pVCpu);
5454 }
5455 if (IEM_GET_CPL(pVCpu) != 0)
5456 {
5457 Log(("ltr %04x - CPL is %d -> #GP(0)\n", uNewTr, IEM_GET_CPL(pVCpu)));
5458 return iemRaiseGeneralProtectionFault0(pVCpu);
5459 }
5460 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5461 || !IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5462 { /* probable */ }
5463 else
5464 {
5465 Log(("ltr: Guest intercept -> VM-exit\n"));
5466 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_LDTR_TR_ACCESS, VMXINSTRID_LTR, cbInstr);
5467 }
5468 if (uNewTr & X86_SEL_LDT)
5469 {
5470 Log(("ltr %04x - LDT selector -> #GP\n", uNewTr));
5471 return iemRaiseGeneralProtectionFaultBySelector(pVCpu, uNewTr);
5472 }
5473 if (!(uNewTr & X86_SEL_MASK_OFF_RPL))
5474 {
5475 Log(("ltr %04x - NULL selector -> #GP(0)\n", uNewTr));
5476 return iemRaiseGeneralProtectionFault0(pVCpu);
5477 }
5478 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_TR_WRITES))
5479 { /* probable */ }
5480 else
5481 {
5482 Log(("ltr: Guest intercept -> #VMEXIT\n"));
5483 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5484 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_TR_WRITE, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
5485 }
5486
5487 /*
5488 * Read the descriptor.
5489 */
5490 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_LDTR | CPUMCTX_EXTRN_GDTR | CPUMCTX_EXTRN_TR);
5491 IEMSELDESC Desc;
5492 VBOXSTRICTRC rcStrict = iemMemFetchSelDesc(pVCpu, &Desc, uNewTr, X86_XCPT_GP); /** @todo Correct exception? */
5493 if (rcStrict != VINF_SUCCESS)
5494 return rcStrict;
5495
5496 /* Check GPs first. */
5497 if (Desc.Legacy.Gen.u1DescType)
5498 {
5499 Log(("ltr %#x - not system selector (type %x) -> #GP\n", uNewTr, Desc.Legacy.Gen.u4Type));
5500 return iemRaiseGeneralProtectionFault(pVCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
5501 }
5502 if ( Desc.Legacy.Gen.u4Type != X86_SEL_TYPE_SYS_386_TSS_AVAIL /* same as AMD64_SEL_TYPE_SYS_TSS_AVAIL */
5503 && ( Desc.Legacy.Gen.u4Type != X86_SEL_TYPE_SYS_286_TSS_AVAIL
5504 || IEM_IS_LONG_MODE(pVCpu)) )
5505 {
5506 Log(("ltr %#x - not an available TSS selector (type %x) -> #GP\n", uNewTr, Desc.Legacy.Gen.u4Type));
5507 return iemRaiseGeneralProtectionFault(pVCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
5508 }
5509 uint64_t u64Base;
5510 if (!IEM_IS_LONG_MODE(pVCpu))
5511 u64Base = X86DESC_BASE(&Desc.Legacy);
5512 else
5513 {
5514 if (Desc.Long.Gen.u5Zeros)
5515 {
5516 Log(("ltr %#x - u5Zeros=%#x -> #GP\n", uNewTr, Desc.Long.Gen.u5Zeros));
5517 return iemRaiseGeneralProtectionFault(pVCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
5518 }
5519
5520 u64Base = X86DESC64_BASE(&Desc.Long);
5521 if (!IEM_IS_CANONICAL(u64Base))
5522 {
5523 Log(("ltr %#x - non-canonical base address %#llx -> #GP\n", uNewTr, u64Base));
5524 return iemRaiseGeneralProtectionFault(pVCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
5525 }
5526 }
5527
5528 /* NP */
5529 if (!Desc.Legacy.Gen.u1Present)
5530 {
5531 Log(("ltr %#x - segment not present -> #NP\n", uNewTr));
5532 return iemRaiseSelectorNotPresentBySelector(pVCpu, uNewTr);
5533 }
5534
5535 /*
5536 * Set it busy.
5537 * Note! Intel says this should lock down the whole descriptor, but we'll
5538 * restrict our selves to 32-bit for now due to lack of inline
5539 * assembly and such.
5540 */
5541 uint8_t bUnmapInfo;
5542 void *pvDesc;
5543 rcStrict = iemMemMap(pVCpu, &pvDesc, &bUnmapInfo, 8, UINT8_MAX,
5544 pVCpu->cpum.GstCtx.gdtr.pGdt + (uNewTr & X86_SEL_MASK_OFF_RPL), IEM_ACCESS_DATA_RW, 0);
5545 if (rcStrict != VINF_SUCCESS)
5546 return rcStrict;
5547 switch ((uintptr_t)pvDesc & 3)
5548 {
5549 case 0: ASMAtomicBitSet(pvDesc, 40 + 1); break;
5550 case 1: ASMAtomicBitSet((uint8_t *)pvDesc + 3, 40 + 1 - 24); break;
5551 case 2: ASMAtomicBitSet((uint8_t *)pvDesc + 2, 40 + 1 - 16); break;
5552 case 3: ASMAtomicBitSet((uint8_t *)pvDesc + 1, 40 + 1 - 8); break;
5553 }
5554 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
5555 if (rcStrict != VINF_SUCCESS)
5556 return rcStrict;
5557 Desc.Legacy.Gen.u4Type |= X86_SEL_TYPE_SYS_TSS_BUSY_MASK;
5558
5559 /*
5560 * It checks out alright, update the registers.
5561 */
5562/** @todo check if the actual value is loaded or if the RPL is dropped */
5563 CPUMSetGuestTR(pVCpu, uNewTr & X86_SEL_MASK_OFF_RPL);
5564 pVCpu->cpum.GstCtx.tr.ValidSel = uNewTr & X86_SEL_MASK_OFF_RPL;
5565 pVCpu->cpum.GstCtx.tr.fFlags = CPUMSELREG_FLAGS_VALID;
5566 pVCpu->cpum.GstCtx.tr.Attr.u = X86DESC_GET_HID_ATTR(&Desc.Legacy);
5567 pVCpu->cpum.GstCtx.tr.u32Limit = X86DESC_LIMIT_G(&Desc.Legacy);
5568 pVCpu->cpum.GstCtx.tr.u64Base = u64Base;
5569
5570 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5571}
5572
5573
5574/**
5575 * Implements str GReg
5576 *
5577 * @param iGReg The general register to store the CRx value in.
5578 * @param enmEffOpSize The operand size.
5579 */
5580IEM_CIMPL_DEF_2(iemCImpl_str_reg, uint8_t, iGReg, uint8_t, enmEffOpSize)
5581{
5582 if (!IEM_IS_IN_GUEST(pVCpu))
5583 { /* probable */ }
5584 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5585 && IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5586 {
5587 Log(("str_reg: Guest intercept -> VM-exit\n"));
5588 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_LDTR_TR_ACCESS, VMXINSTRID_STR, cbInstr);
5589 }
5590 else
5591 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_TR_READS, SVM_EXIT_TR_READ, 0, 0, cbInstr);
5592
5593 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_TR);
5594 switch (enmEffOpSize)
5595 {
5596 case IEMMODE_16BIT:
5597 iemGRegStoreU16(pVCpu, iGReg, pVCpu->cpum.GstCtx.tr.Sel);
5598 break;
5599 case IEMMODE_32BIT:
5600 case IEMMODE_64BIT:
5601 iemGRegStoreU64(pVCpu, iGReg, pVCpu->cpum.GstCtx.tr.Sel);
5602 break;
5603 IEM_NOT_REACHED_DEFAULT_CASE_RET();
5604 }
5605 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5606}
5607
5608
5609/**
5610 * Implements str mem.
5611 *
5612 * @param iEffSeg The effective segment register to use with @a GCPtrMem.
5613 * @param GCPtrEffDst Where to store the 16-bit CR0 value.
5614 */
5615IEM_CIMPL_DEF_2(iemCImpl_str_mem, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5616{
5617 if (!IEM_IS_IN_GUEST(pVCpu))
5618 { /* probable */ }
5619 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
5620 && IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_DESC_TABLE_EXIT))
5621 {
5622 Log(("str_mem: Guest intercept -> VM-exit\n"));
5623 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_LDTR_TR_ACCESS, VMXINSTRID_STR, cbInstr);
5624 }
5625 else
5626 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_TR_READS, SVM_EXIT_TR_READ, 0, 0, cbInstr);
5627
5628 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_TR);
5629 VBOXSTRICTRC rcStrict = iemMemStoreDataU16(pVCpu, iEffSeg, GCPtrEffDst, pVCpu->cpum.GstCtx.tr.Sel);
5630 if (rcStrict == VINF_SUCCESS)
5631 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5632 return rcStrict;
5633}
5634
5635
5636/**
5637 * Implements mov GReg,CRx.
5638 *
5639 * @param iGReg The general register to store the CRx value in.
5640 * @param iCrReg The CRx register to read (valid).
5641 */
5642IEM_CIMPL_DEF_2(iemCImpl_mov_Rd_Cd, uint8_t, iGReg, uint8_t, iCrReg)
5643{
5644 if (IEM_GET_CPL(pVCpu) != 0)
5645 return iemRaiseGeneralProtectionFault0(pVCpu);
5646 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
5647
5648 if (!IEM_SVM_IS_READ_CR_INTERCEPT_SET(pVCpu, iCrReg))
5649 { /* probable */ }
5650 else
5651 {
5652 Log(("iemCImpl_mov_Rd_Cd: Guest intercept CR%u -> #VMEXIT\n", iCrReg));
5653 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5654 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_READ_CR0 + iCrReg, IEMACCESSCRX_MOV_CRX, iGReg);
5655 }
5656
5657 /* Read it. */
5658 uint64_t crX;
5659 switch (iCrReg)
5660 {
5661 case 0:
5662 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0);
5663 crX = pVCpu->cpum.GstCtx.cr0;
5664 if (IEM_GET_TARGET_CPU(pVCpu) <= IEMTARGETCPU_386)
5665 crX |= UINT32_C(0x7fffffe0); /* All reserved CR0 flags are set on a 386, just like MSW on 286. */
5666 break;
5667 case 2:
5668 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_CR2);
5669 crX = pVCpu->cpum.GstCtx.cr2;
5670 break;
5671 case 3:
5672 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR3);
5673 crX = pVCpu->cpum.GstCtx.cr3;
5674 break;
5675 case 4:
5676 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR4);
5677 crX = pVCpu->cpum.GstCtx.cr4;
5678 break;
5679 case 8:
5680 {
5681 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_APIC_TPR);
5682 if (!IEM_IS_IN_GUEST(pVCpu))
5683 { /* probable */ }
5684#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
5685 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
5686 {
5687 VBOXSTRICTRC rcStrict = iemVmxVmexitInstrMovFromCr8(pVCpu, iGReg, cbInstr);
5688 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
5689 return rcStrict;
5690
5691 /*
5692 * If the Mov-from-CR8 doesn't cause a VM-exit, bits 7:4 of the VTPR is copied
5693 * to bits 0:3 of the destination operand. Bits 63:4 of the destination operand
5694 * are cleared.
5695 *
5696 * See Intel Spec. 29.3 "Virtualizing CR8-based TPR Accesses"
5697 */
5698 if (IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_USE_TPR_SHADOW))
5699 {
5700 uint32_t const uTpr = iemVmxVirtApicReadRaw32(pVCpu, XAPIC_OFF_TPR);
5701 crX = (uTpr >> 4) & 0xf;
5702 break;
5703 }
5704 }
5705#endif
5706#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
5707 else if (pVCpu->iem.s.fExec & IEM_F_X86_CTX_SVM)
5708 {
5709 PCSVMVMCBCTRL pVmcbCtrl = &pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.ctrl;
5710 if (CPUMIsGuestSvmVirtIntrMasking(pVCpu, IEM_GET_CTX(pVCpu)))
5711 {
5712 crX = pVmcbCtrl->IntCtrl.n.u8VTPR & 0xf;
5713 break;
5714 }
5715 }
5716#endif
5717 uint8_t uTpr;
5718 int rc = PDMApicGetTpr(pVCpu, &uTpr, NULL, NULL);
5719 if (RT_SUCCESS(rc))
5720 crX = uTpr >> 4;
5721 else
5722 crX = 0;
5723 break;
5724 }
5725 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
5726 }
5727
5728#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
5729 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
5730 { /* probable */ }
5731 else
5732 switch (iCrReg)
5733 {
5734 /* CR0/CR4 reads are subject to masking when in VMX non-root mode. */
5735 case 0: crX = CPUMGetGuestVmxMaskedCr0(&pVCpu->cpum.GstCtx, pVCpu->cpum.GstCtx.hwvirt.vmx.Vmcs.u64Cr0Mask.u); break;
5736 case 4: crX = CPUMGetGuestVmxMaskedCr4(&pVCpu->cpum.GstCtx, pVCpu->cpum.GstCtx.hwvirt.vmx.Vmcs.u64Cr4Mask.u); break;
5737 case 3:
5738 {
5739 VBOXSTRICTRC rcStrict = iemVmxVmexitInstrMovFromCr3(pVCpu, iGReg, cbInstr);
5740 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
5741 return rcStrict;
5742 break;
5743 }
5744 }
5745#endif
5746
5747 /* Store it. */
5748 if (IEM_IS_64BIT_CODE(pVCpu))
5749 iemGRegStoreU64(pVCpu, iGReg, crX);
5750 else
5751 iemGRegStoreU64(pVCpu, iGReg, (uint32_t)crX);
5752
5753 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5754}
5755
5756
5757/**
5758 * Implements smsw GReg.
5759 *
5760 * @param iGReg The general register to store the CRx value in.
5761 * @param enmEffOpSize The operand size.
5762 */
5763IEM_CIMPL_DEF_2(iemCImpl_smsw_reg, uint8_t, iGReg, uint8_t, enmEffOpSize)
5764{
5765 IEM_SVM_CHECK_READ_CR0_INTERCEPT(pVCpu, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */, cbInstr);
5766
5767#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
5768 uint64_t u64MaskedCr0;
5769 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
5770 u64MaskedCr0 = pVCpu->cpum.GstCtx.cr0;
5771 else
5772 u64MaskedCr0 = CPUMGetGuestVmxMaskedCr0(&pVCpu->cpum.GstCtx, pVCpu->cpum.GstCtx.hwvirt.vmx.Vmcs.u64Cr0Mask.u);
5773 uint64_t const u64GuestCr0 = u64MaskedCr0;
5774#else
5775 uint64_t const u64GuestCr0 = pVCpu->cpum.GstCtx.cr0;
5776#endif
5777
5778 switch (enmEffOpSize)
5779 {
5780 case IEMMODE_16BIT:
5781 if (IEM_GET_TARGET_CPU(pVCpu) > IEMTARGETCPU_386)
5782 iemGRegStoreU16(pVCpu, iGReg, (uint16_t)u64GuestCr0);
5783 /* Unused bits are set on 386 and older CPU: */
5784 else if (IEM_GET_TARGET_CPU(pVCpu) >= IEMTARGETCPU_386)
5785 iemGRegStoreU16(pVCpu, iGReg, (uint16_t)u64GuestCr0 | 0xffe0);
5786 else
5787 iemGRegStoreU16(pVCpu, iGReg, (uint16_t)u64GuestCr0 | 0xfff0);
5788 break;
5789
5790/** @todo testcase for bits 31:16. We're not doing that correctly. */
5791
5792 case IEMMODE_32BIT:
5793 if (IEM_GET_TARGET_CPU(pVCpu) >= IEMTARGETCPU_386)
5794 iemGRegStoreU32(pVCpu, iGReg, (uint32_t)u64GuestCr0);
5795 else /** @todo test this! */
5796 iemGRegStoreU32(pVCpu, iGReg, (uint32_t)u64GuestCr0 | UINT32_C(0x7fffffe0)); /* Unused bits are set on 386. */
5797 break;
5798
5799 case IEMMODE_64BIT:
5800 iemGRegStoreU64(pVCpu, iGReg, u64GuestCr0);
5801 break;
5802
5803 IEM_NOT_REACHED_DEFAULT_CASE_RET();
5804 }
5805
5806 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5807}
5808
5809
5810/**
5811 * Implements smsw mem.
5812 *
5813 * @param iEffSeg The effective segment register to use with @a GCPtrMem.
5814 * @param GCPtrEffDst Where to store the 16-bit CR0 value.
5815 */
5816IEM_CIMPL_DEF_2(iemCImpl_smsw_mem, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
5817{
5818 uint64_t u64GuestCr0 = pVCpu->cpum.GstCtx.cr0;
5819 if (!IEM_IS_IN_GUEST(pVCpu))
5820 { /* probable */ }
5821 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
5822 u64GuestCr0 = CPUMGetGuestVmxMaskedCr0(&pVCpu->cpum.GstCtx, pVCpu->cpum.GstCtx.hwvirt.vmx.Vmcs.u64Cr0Mask.u);
5823 else
5824 IEM_SVM_CHECK_READ_CR0_INTERCEPT(pVCpu, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */, cbInstr);
5825
5826 uint16_t u16Value;
5827 if (IEM_GET_TARGET_CPU(pVCpu) > IEMTARGETCPU_386)
5828 u16Value = (uint16_t)u64GuestCr0;
5829 else if (IEM_GET_TARGET_CPU(pVCpu) >= IEMTARGETCPU_386)
5830 u16Value = (uint16_t)u64GuestCr0 | 0xffe0;
5831 else
5832 u16Value = (uint16_t)u64GuestCr0 | 0xfff0;
5833
5834 VBOXSTRICTRC rcStrict = iemMemStoreDataU16(pVCpu, iEffSeg, GCPtrEffDst, u16Value);
5835 if (rcStrict == VINF_SUCCESS)
5836 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
5837 return rcStrict;
5838}
5839
5840
5841/**
5842 * Helper for mapping CR3 and PAE PDPEs for 'mov CRx,GReg'.
5843 */
5844#define IEM_MAP_PAE_PDPES_AT_CR3_RET(a_pVCpu, a_iCrReg, a_uCr3) \
5845 do \
5846 { \
5847 int const rcX = PGMGstMapPaePdpesAtCr3(a_pVCpu, a_uCr3); \
5848 if (RT_SUCCESS(rcX)) \
5849 { /* likely */ } \
5850 else \
5851 { \
5852 /* Either invalid PDPTEs or CR3 second-level translation failed. Raise #GP(0) either way. */ \
5853 Log(("iemCImpl_load_Cr%#x: Trying to load invalid PAE PDPEs\n", a_iCrReg)); \
5854 return iemRaiseGeneralProtectionFault0(a_pVCpu); \
5855 } \
5856 } while (0)
5857
5858
5859/**
5860 * Used to implemented 'mov CRx,GReg' and 'lmsw r/m16'.
5861 *
5862 * @param iCrReg The CRx register to write (valid).
5863 * @param uNewCrX The new value.
5864 * @param enmAccessCrX The instruction that caused the CrX load.
5865 * @param iGReg The general register in case of a 'mov CRx,GReg'
5866 * instruction.
5867 */
5868IEM_CIMPL_DEF_4(iemCImpl_load_CrX, uint8_t, iCrReg, uint64_t, uNewCrX, IEMACCESSCRX, enmAccessCrX, uint8_t, iGReg)
5869{
5870 VBOXSTRICTRC rcStrict;
5871 int rc;
5872#ifndef VBOX_WITH_NESTED_HWVIRT_SVM
5873 RT_NOREF2(iGReg, enmAccessCrX);
5874#endif
5875
5876 /*
5877 * Try store it.
5878 * Unfortunately, CPUM only does a tiny bit of the work.
5879 */
5880 switch (iCrReg)
5881 {
5882 case 0:
5883 {
5884 /*
5885 * Perform checks.
5886 */
5887 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0);
5888
5889 uint64_t const uOldCrX = pVCpu->cpum.GstCtx.cr0;
5890 uint32_t const fValid = CPUMGetGuestCR0ValidMask();
5891
5892 /* ET is hardcoded on 486 and later. */
5893 if (IEM_GET_TARGET_CPU(pVCpu) > IEMTARGETCPU_486)
5894 uNewCrX |= X86_CR0_ET;
5895 /* The 386 and 486 didn't #GP(0) on attempting to set reserved CR0 bits. ET was settable on 386. */
5896 else if (IEM_GET_TARGET_CPU(pVCpu) == IEMTARGETCPU_486)
5897 {
5898 uNewCrX &= fValid;
5899 uNewCrX |= X86_CR0_ET;
5900 }
5901 else
5902 uNewCrX &= X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS | X86_CR0_PG | X86_CR0_ET;
5903
5904 /* Check for reserved bits. */
5905 if (uNewCrX & ~(uint64_t)fValid)
5906 {
5907 Log(("Trying to set reserved CR0 bits: NewCR0=%#llx InvalidBits=%#llx\n", uNewCrX, uNewCrX & ~(uint64_t)fValid));
5908 return iemRaiseGeneralProtectionFault0(pVCpu);
5909 }
5910
5911 /* Check for invalid combinations. */
5912 if ( (uNewCrX & X86_CR0_PG)
5913 && !(uNewCrX & X86_CR0_PE) )
5914 {
5915 Log(("Trying to set CR0.PG without CR0.PE\n"));
5916 return iemRaiseGeneralProtectionFault0(pVCpu);
5917 }
5918
5919 if ( !(uNewCrX & X86_CR0_CD)
5920 && (uNewCrX & X86_CR0_NW) )
5921 {
5922 Log(("Trying to clear CR0.CD while leaving CR0.NW set\n"));
5923 return iemRaiseGeneralProtectionFault0(pVCpu);
5924 }
5925
5926 if ( !(uNewCrX & X86_CR0_PG)
5927 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_PCIDE))
5928 {
5929 Log(("Trying to clear CR0.PG while leaving CR4.PCID set\n"));
5930 return iemRaiseGeneralProtectionFault0(pVCpu);
5931 }
5932
5933 /* Long mode consistency checks. */
5934 if ( (uNewCrX & X86_CR0_PG)
5935 && !(uOldCrX & X86_CR0_PG)
5936 && (pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_LME) )
5937 {
5938 if (!(pVCpu->cpum.GstCtx.cr4 & X86_CR4_PAE))
5939 {
5940 Log(("Trying to enabled long mode paging without CR4.PAE set\n"));
5941 return iemRaiseGeneralProtectionFault0(pVCpu);
5942 }
5943 if (pVCpu->cpum.GstCtx.cs.Attr.n.u1Long)
5944 {
5945 Log(("Trying to enabled long mode paging with a long CS descriptor loaded.\n"));
5946 return iemRaiseGeneralProtectionFault0(pVCpu);
5947 }
5948 }
5949
5950 /** @todo testcase: what happens if we disable paging while in 64-bit code? */
5951
5952 if (!IEM_IS_IN_GUEST(pVCpu))
5953 { /* probable */ }
5954#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
5955 /* Check for bits that must remain set or cleared in VMX operation,
5956 see Intel spec. 23.8 "Restrictions on VMX operation". */
5957 else if (IEM_VMX_IS_ROOT_MODE(pVCpu))
5958 {
5959 uint64_t const uCr0Fixed0 = iemVmxGetCr0Fixed0(pVCpu, IEM_VMX_IS_NON_ROOT_MODE(pVCpu));
5960 if ((uNewCrX & uCr0Fixed0) != uCr0Fixed0)
5961 {
5962 Log(("Trying to clear reserved CR0 bits in VMX operation: NewCr0=%#llx MB1=%#llx\n", uNewCrX, uCr0Fixed0));
5963 return iemRaiseGeneralProtectionFault0(pVCpu);
5964 }
5965
5966 uint64_t const uCr0Fixed1 = pVCpu->cpum.GstCtx.hwvirt.vmx.Msrs.u64Cr0Fixed1;
5967 if (uNewCrX & ~uCr0Fixed1)
5968 {
5969 Log(("Trying to set reserved CR0 bits in VMX operation: NewCr0=%#llx MB0=%#llx\n", uNewCrX, uCr0Fixed1));
5970 return iemRaiseGeneralProtectionFault0(pVCpu);
5971 }
5972 }
5973#endif
5974 /*
5975 * SVM nested-guest CR0 write intercepts.
5976 */
5977 else if (IEM_SVM_IS_WRITE_CR_INTERCEPT_SET(pVCpu, iCrReg))
5978 {
5979 Log(("iemCImpl_load_Cr%#x: Guest intercept -> #VMEXIT\n", iCrReg));
5980 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5981 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_WRITE_CR0, enmAccessCrX, iGReg);
5982 }
5983 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_CR0_SEL_WRITE))
5984 {
5985 /* 'lmsw' intercepts regardless of whether the TS/MP bits are actually toggled. */
5986 if ( enmAccessCrX == IEMACCESSCRX_LMSW
5987 || (uNewCrX & ~(X86_CR0_TS | X86_CR0_MP)) != (uOldCrX & ~(X86_CR0_TS | X86_CR0_MP)))
5988 {
5989 Assert(enmAccessCrX != IEMACCESSCRX_CLTS);
5990 Log(("iemCImpl_load_Cr%#x: lmsw or bits other than TS/MP changed: Guest intercept -> #VMEXIT\n", iCrReg));
5991 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
5992 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_CR0_SEL_WRITE, enmAccessCrX, iGReg);
5993 }
5994 }
5995
5996 /*
5997 * Change EFER.LMA if entering or leaving long mode.
5998 */
5999 uint64_t NewEFER = pVCpu->cpum.GstCtx.msrEFER;
6000 if ( (uNewCrX & X86_CR0_PG) != (uOldCrX & X86_CR0_PG)
6001 && (pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_LME) )
6002 {
6003 if (uNewCrX & X86_CR0_PG)
6004 NewEFER |= MSR_K6_EFER_LMA;
6005 else
6006 NewEFER &= ~MSR_K6_EFER_LMA;
6007
6008 CPUMSetGuestEFER(pVCpu, NewEFER);
6009 Assert(pVCpu->cpum.GstCtx.msrEFER == NewEFER);
6010 }
6011
6012 IEMTLBTRACE_LOAD_CR0(pVCpu, uNewCrX, uOldCrX);
6013
6014 /*
6015 * Inform PGM.
6016 */
6017 if ( (uNewCrX & (X86_CR0_PG | X86_CR0_WP | X86_CR0_PE | X86_CR0_CD | X86_CR0_NW))
6018 != (uOldCrX & (X86_CR0_PG | X86_CR0_WP | X86_CR0_PE | X86_CR0_CD | X86_CR0_NW)) )
6019 {
6020 if ( enmAccessCrX != IEMACCESSCRX_MOV_CRX
6021 || !CPUMIsPaePagingEnabled(uNewCrX, pVCpu->cpum.GstCtx.cr4, NewEFER)
6022 || CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)))
6023 { /* likely */ }
6024 else
6025 IEM_MAP_PAE_PDPES_AT_CR3_RET(pVCpu, iCrReg, pVCpu->cpum.GstCtx.cr3);
6026 rc = PGMFlushTLB(pVCpu, pVCpu->cpum.GstCtx.cr3, true /* global */);
6027 AssertRCReturn(rc, rc);
6028 /* ignore informational status codes */
6029 }
6030
6031 /*
6032 * Change CR0.
6033 */
6034 CPUMSetGuestCR0(pVCpu, uNewCrX);
6035 Assert(pVCpu->cpum.GstCtx.cr0 == uNewCrX);
6036
6037 /* Update the fExec flags if PE changed. */
6038 if ((uNewCrX ^ uOldCrX) & X86_CR0_PE)
6039 iemRecalcExecModeAndCplAndAcFlags(pVCpu);
6040
6041 /*
6042 * Inform PGM some more...
6043 */
6044 rcStrict = PGMChangeMode(pVCpu, pVCpu->cpum.GstCtx.cr0, pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER,
6045 false /* fForce */);
6046 break;
6047 }
6048
6049 /*
6050 * CR2 can be changed without any restrictions.
6051 */
6052 case 2:
6053 {
6054 if (!IEM_SVM_IS_WRITE_CR_INTERCEPT_SET(pVCpu, /*cr*/ 2))
6055 { /* probable */ }
6056 else
6057 {
6058 Log(("iemCImpl_load_Cr%#x: Guest intercept -> #VMEXIT\n", iCrReg));
6059 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6060 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_WRITE_CR2, enmAccessCrX, iGReg);
6061 }
6062 pVCpu->cpum.GstCtx.cr2 = uNewCrX;
6063 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_CR2;
6064 rcStrict = VINF_SUCCESS;
6065 break;
6066 }
6067
6068 /*
6069 * CR3 is relatively simple, although AMD and Intel have different
6070 * accounts of how setting reserved bits are handled. We take intel's
6071 * word for the lower bits and AMD's for the high bits (63:52). The
6072 * lower reserved bits are ignored and left alone; OpenBSD 5.8 relies
6073 * on this.
6074 */
6075 /** @todo Testcase: Setting reserved bits in CR3, especially before
6076 * enabling paging. */
6077 case 3:
6078 {
6079 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR3);
6080
6081 /* Bit 63 being clear in the source operand with PCIDE indicates no invalidations are required. */
6082 if ( (pVCpu->cpum.GstCtx.cr4 & X86_CR4_PCIDE)
6083 && (uNewCrX & RT_BIT_64(63)))
6084 {
6085 /** @todo r=ramshankar: avoiding a TLB flush altogether here causes Windows 10
6086 * SMP(w/o nested-paging) to hang during bootup on Skylake systems, see
6087 * Intel spec. 4.10.4.1 "Operations that Invalidate TLBs and
6088 * Paging-Structure Caches". */
6089 uNewCrX &= ~RT_BIT_64(63);
6090 }
6091
6092 /* Check / mask the value. */
6093#ifdef VBOX_WITH_NESTED_HWVIRT_VMX_EPT
6094 /* See Intel spec. 27.2.2 "EPT Translation Mechanism" footnote. */
6095 uint64_t const fInvPhysMask = !CPUMIsGuestVmxEptPagingEnabledEx(IEM_GET_CTX(pVCpu))
6096 ? (UINT64_MAX << IEM_GET_GUEST_CPU_FEATURES(pVCpu)->cMaxPhysAddrWidth)
6097 : (~X86_CR3_EPT_PAGE_MASK & X86_PAGE_4K_BASE_MASK);
6098#else
6099 uint64_t const fInvPhysMask = UINT64_C(0xfff0000000000000);
6100#endif
6101 if (uNewCrX & fInvPhysMask)
6102 {
6103 /** @todo Should we raise this only for 64-bit mode like Intel claims? AMD is
6104 * very vague in this area. As mentioned above, need testcase on real
6105 * hardware... Sigh. */
6106 Log(("Trying to load CR3 with invalid high bits set: %#llx\n", uNewCrX));
6107 return iemRaiseGeneralProtectionFault0(pVCpu);
6108 }
6109
6110 uint64_t fValid;
6111 if ( (pVCpu->cpum.GstCtx.cr4 & X86_CR4_PAE)
6112 && (pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_LME))
6113 {
6114 /** @todo Redundant? This value has already been validated above. */
6115 fValid = UINT64_C(0x000fffffffffffff);
6116 }
6117 else
6118 fValid = UINT64_C(0xffffffff);
6119 if (uNewCrX & ~fValid)
6120 {
6121 Log(("Automatically clearing reserved MBZ bits in CR3 load: NewCR3=%#llx ClearedBits=%#llx\n",
6122 uNewCrX, uNewCrX & ~fValid));
6123 uNewCrX &= fValid;
6124 }
6125
6126 if (!IEM_SVM_IS_WRITE_CR_INTERCEPT_SET(pVCpu, /*cr*/ 3))
6127 { /* probable */ }
6128 else
6129 {
6130 Log(("iemCImpl_load_Cr%#x: Guest intercept -> #VMEXIT\n", iCrReg));
6131 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6132 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_WRITE_CR3, enmAccessCrX, iGReg);
6133 }
6134
6135 IEMTLBTRACE_LOAD_CR3(pVCpu, uNewCrX, pVCpu->cpum.GstCtx.cr3);
6136
6137 /* Inform PGM. */
6138 if (pVCpu->cpum.GstCtx.cr0 & X86_CR0_PG)
6139 {
6140 if ( !CPUMIsGuestInPAEModeEx(IEM_GET_CTX(pVCpu))
6141 || CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)))
6142 { /* likely */ }
6143 else
6144 {
6145 Assert(enmAccessCrX == IEMACCESSCRX_MOV_CRX);
6146 IEM_MAP_PAE_PDPES_AT_CR3_RET(pVCpu, iCrReg, uNewCrX);
6147 }
6148 rc = PGMFlushTLB(pVCpu, uNewCrX, !(pVCpu->cpum.GstCtx.cr4 & X86_CR4_PGE));
6149 AssertRCReturn(rc, rc);
6150 /* ignore informational status codes */
6151 }
6152
6153 /* Make the change. */
6154 rc = CPUMSetGuestCR3(pVCpu, uNewCrX);
6155 AssertRCSuccessReturn(rc, rc);
6156
6157 rcStrict = VINF_SUCCESS;
6158 break;
6159 }
6160
6161 /*
6162 * CR4 is a bit more tedious as there are bits which cannot be cleared
6163 * under some circumstances and such.
6164 */
6165 case 4:
6166 {
6167 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR4);
6168 uint64_t const uOldCrX = pVCpu->cpum.GstCtx.cr4;
6169
6170 /* Reserved bits. */
6171 uint32_t const fValid = CPUMGetGuestCR4ValidMask(pVCpu->CTX_SUFF(pVM));
6172 if (uNewCrX & ~(uint64_t)fValid)
6173 {
6174 Log(("Trying to set reserved CR4 bits: NewCR4=%#llx InvalidBits=%#llx\n", uNewCrX, uNewCrX & ~(uint64_t)fValid));
6175 return iemRaiseGeneralProtectionFault0(pVCpu);
6176 }
6177
6178 bool const fPcide = !(uOldCrX & X86_CR4_PCIDE) && (uNewCrX & X86_CR4_PCIDE);
6179 bool const fLongMode = CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu));
6180
6181 /* PCIDE check. */
6182 if ( fPcide
6183 && ( !fLongMode
6184 || (pVCpu->cpum.GstCtx.cr3 & UINT64_C(0xfff))))
6185 {
6186 Log(("Trying to set PCIDE with invalid PCID or outside long mode. Pcid=%#x\n", (pVCpu->cpum.GstCtx.cr3 & UINT64_C(0xfff))));
6187 return iemRaiseGeneralProtectionFault0(pVCpu);
6188 }
6189
6190 /* PAE check. */
6191 if ( fLongMode
6192 && (uOldCrX & X86_CR4_PAE)
6193 && !(uNewCrX & X86_CR4_PAE))
6194 {
6195 Log(("Trying to set clear CR4.PAE while long mode is active\n"));
6196 return iemRaiseGeneralProtectionFault0(pVCpu);
6197 }
6198
6199 if (!IEM_SVM_IS_WRITE_CR_INTERCEPT_SET(pVCpu, /*cr*/ 4))
6200 { /* probable */ }
6201 else
6202 {
6203 Log(("iemCImpl_load_Cr%#x: Guest intercept -> #VMEXIT\n", iCrReg));
6204 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6205 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_WRITE_CR4, enmAccessCrX, iGReg);
6206 }
6207
6208 /* Check for bits that must remain set or cleared in VMX operation,
6209 see Intel spec. 23.8 "Restrictions on VMX operation". */
6210 if (!IEM_VMX_IS_ROOT_MODE(pVCpu))
6211 { /* probable */ }
6212 else
6213 {
6214 uint64_t const uCr4Fixed0 = pVCpu->cpum.GstCtx.hwvirt.vmx.Msrs.u64Cr4Fixed0;
6215 if ((uNewCrX & uCr4Fixed0) != uCr4Fixed0)
6216 {
6217 Log(("Trying to clear reserved CR4 bits in VMX operation: NewCr4=%#llx MB1=%#llx\n", uNewCrX, uCr4Fixed0));
6218 return iemRaiseGeneralProtectionFault0(pVCpu);
6219 }
6220
6221 uint64_t const uCr4Fixed1 = pVCpu->cpum.GstCtx.hwvirt.vmx.Msrs.u64Cr4Fixed1;
6222 if (uNewCrX & ~uCr4Fixed1)
6223 {
6224 Log(("Trying to set reserved CR4 bits in VMX operation: NewCr4=%#llx MB0=%#llx\n", uNewCrX, uCr4Fixed1));
6225 return iemRaiseGeneralProtectionFault0(pVCpu);
6226 }
6227 }
6228
6229 IEMTLBTRACE_LOAD_CR4(pVCpu, uNewCrX, uOldCrX);
6230
6231 /*
6232 * Notify PGM.
6233 */
6234 if ((uNewCrX ^ uOldCrX) & (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_PGE | X86_CR4_PCIDE /* | X86_CR4_SMEP */))
6235 {
6236 if ( !CPUMIsPaePagingEnabled(pVCpu->cpum.GstCtx.cr0, uNewCrX, pVCpu->cpum.GstCtx.msrEFER)
6237 || CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)))
6238 { /* likely */ }
6239 else
6240 {
6241 Assert(enmAccessCrX == IEMACCESSCRX_MOV_CRX);
6242 IEM_MAP_PAE_PDPES_AT_CR3_RET(pVCpu, iCrReg, pVCpu->cpum.GstCtx.cr3);
6243 }
6244 rc = PGMFlushTLB(pVCpu, pVCpu->cpum.GstCtx.cr3, true /* global */);
6245 AssertRCReturn(rc, rc);
6246 /* ignore informational status codes */
6247 }
6248
6249 /*
6250 * Change it.
6251 */
6252 rc = CPUMSetGuestCR4(pVCpu, uNewCrX);
6253 AssertRCSuccessReturn(rc, rc);
6254 Assert(pVCpu->cpum.GstCtx.cr4 == uNewCrX);
6255
6256 rcStrict = PGMChangeMode(pVCpu, pVCpu->cpum.GstCtx.cr0, pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER,
6257 false /* fForce */);
6258 break;
6259 }
6260
6261 /*
6262 * CR8 maps to the APIC TPR.
6263 */
6264 case 8:
6265 {
6266 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_APIC_TPR);
6267 if (uNewCrX & ~(uint64_t)0xf)
6268 {
6269 Log(("Trying to set reserved CR8 bits (%#RX64)\n", uNewCrX));
6270 return iemRaiseGeneralProtectionFault0(pVCpu);
6271 }
6272
6273 if (!IEM_IS_IN_GUEST(pVCpu))
6274 { /* probable */ }
6275#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6276 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
6277 && IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_USE_TPR_SHADOW))
6278 {
6279 /*
6280 * If the Mov-to-CR8 doesn't cause a VM-exit, bits 0:3 of the source operand
6281 * is copied to bits 7:4 of the VTPR. Bits 0:3 and bits 31:8 of the VTPR are
6282 * cleared. Following this the processor performs TPR virtualization.
6283 *
6284 * However, we should not perform TPR virtualization immediately here but
6285 * after this instruction has completed.
6286 *
6287 * See Intel spec. 29.3 "Virtualizing CR8-based TPR Accesses"
6288 * See Intel spec. 27.1 "Architectural State Before A VM-exit"
6289 */
6290 uint32_t const uTpr = (uNewCrX & 0xf) << 4;
6291 Log(("iemCImpl_load_Cr%#x: Virtualizing TPR (%#x) write\n", iCrReg, uTpr));
6292 iemVmxVirtApicWriteRaw32(pVCpu, XAPIC_OFF_TPR, uTpr);
6293 iemVmxVirtApicSetPendingWrite(pVCpu, XAPIC_OFF_TPR);
6294 rcStrict = VINF_SUCCESS;
6295 break;
6296 }
6297#endif
6298#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
6299 else if (pVCpu->iem.s.fExec & IEM_F_X86_CTX_SVM)
6300 {
6301 if (IEM_SVM_IS_WRITE_CR_INTERCEPT_SET(pVCpu, /*cr*/ 8))
6302 {
6303 Log(("iemCImpl_load_Cr%#x: Guest intercept -> #VMEXIT\n", iCrReg));
6304 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6305 IEM_SVM_CRX_VMEXIT_RET(pVCpu, SVM_EXIT_WRITE_CR8, enmAccessCrX, iGReg);
6306 }
6307
6308 pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.ctrl.IntCtrl.n.u8VTPR = uNewCrX;
6309 if (CPUMIsGuestSvmVirtIntrMasking(pVCpu, IEM_GET_CTX(pVCpu)))
6310 {
6311 rcStrict = VINF_SUCCESS;
6312 break;
6313 }
6314 }
6315#endif
6316 uint8_t const u8Tpr = (uint8_t)uNewCrX << 4;
6317 PDMApicSetTpr(pVCpu, u8Tpr);
6318 rcStrict = VINF_SUCCESS;
6319 break;
6320 }
6321
6322 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
6323 }
6324
6325 /*
6326 * Advance the RIP on success.
6327 */
6328 if (RT_SUCCESS(rcStrict))
6329 {
6330 if (rcStrict != VINF_SUCCESS)
6331 iemSetPassUpStatus(pVCpu, rcStrict);
6332 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6333 }
6334
6335 return rcStrict;
6336}
6337
6338
6339/**
6340 * Implements mov CRx,GReg.
6341 *
6342 * @param iCrReg The CRx register to write (valid).
6343 * @param iGReg The general register to load the CRx value from.
6344 */
6345IEM_CIMPL_DEF_2(iemCImpl_mov_Cd_Rd, uint8_t, iCrReg, uint8_t, iGReg)
6346{
6347 if (IEM_GET_CPL(pVCpu) != 0)
6348 return iemRaiseGeneralProtectionFault0(pVCpu);
6349 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6350
6351 /*
6352 * Read the new value from the source register and call common worker.
6353 */
6354 uint64_t uNewCrX;
6355 if (IEM_IS_64BIT_CODE(pVCpu))
6356 uNewCrX = iemGRegFetchU64(pVCpu, iGReg);
6357 else
6358 uNewCrX = iemGRegFetchU32(pVCpu, iGReg);
6359
6360#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6361 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
6362 { /* probable */ }
6363 else
6364 {
6365 VBOXSTRICTRC rcStrict = VINF_VMX_INTERCEPT_NOT_ACTIVE;
6366 switch (iCrReg)
6367 {
6368 case 0:
6369 case 4: rcStrict = iemVmxVmexitInstrMovToCr0Cr4(pVCpu, iCrReg, &uNewCrX, iGReg, cbInstr); break;
6370 case 3: rcStrict = iemVmxVmexitInstrMovToCr3(pVCpu, uNewCrX, iGReg, cbInstr); break;
6371 case 8: rcStrict = iemVmxVmexitInstrMovToCr8(pVCpu, iGReg, cbInstr); break;
6372 }
6373 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
6374 return rcStrict;
6375 }
6376#endif
6377
6378 return IEM_CIMPL_CALL_4(iemCImpl_load_CrX, iCrReg, uNewCrX, IEMACCESSCRX_MOV_CRX, iGReg);
6379}
6380
6381
6382/**
6383 * Implements 'LMSW r/m16'
6384 *
6385 * @param u16NewMsw The new value.
6386 * @param GCPtrEffDst The guest-linear address of the source operand in case
6387 * of a memory operand. For register operand, pass
6388 * NIL_RTGCPTR.
6389 */
6390IEM_CIMPL_DEF_2(iemCImpl_lmsw, uint16_t, u16NewMsw, RTGCPTR, GCPtrEffDst)
6391{
6392 if (IEM_GET_CPL(pVCpu) != 0)
6393 return iemRaiseGeneralProtectionFault0(pVCpu);
6394 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6395 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0);
6396
6397#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6398 /* Check nested-guest VMX intercept and get updated MSW if there's no VM-exit. */
6399 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
6400 { /* probable */ }
6401 else
6402 {
6403 VBOXSTRICTRC rcStrict = iemVmxVmexitInstrLmsw(pVCpu, pVCpu->cpum.GstCtx.cr0, &u16NewMsw, GCPtrEffDst, cbInstr);
6404 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
6405 return rcStrict;
6406 }
6407#else
6408 RT_NOREF_PV(GCPtrEffDst);
6409#endif
6410
6411 /*
6412 * Compose the new CR0 value and call common worker.
6413 */
6414 uint64_t uNewCr0 = pVCpu->cpum.GstCtx.cr0 & ~(X86_CR0_MP | X86_CR0_EM | X86_CR0_TS);
6415 uNewCr0 |= u16NewMsw & (X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS);
6416 return IEM_CIMPL_CALL_4(iemCImpl_load_CrX, /*cr*/ 0, uNewCr0, IEMACCESSCRX_LMSW, UINT8_MAX /* iGReg */);
6417}
6418
6419
6420/**
6421 * Implements 'CLTS'.
6422 */
6423IEM_CIMPL_DEF_0(iemCImpl_clts)
6424{
6425 if (IEM_GET_CPL(pVCpu) != 0)
6426 return iemRaiseGeneralProtectionFault0(pVCpu);
6427
6428 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0);
6429 uint64_t uNewCr0 = pVCpu->cpum.GstCtx.cr0;
6430 uNewCr0 &= ~X86_CR0_TS;
6431
6432#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6433 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
6434 { /* probable */ }
6435 else
6436 {
6437 VBOXSTRICTRC rcStrict = iemVmxVmexitInstrClts(pVCpu, cbInstr);
6438 if (rcStrict == VINF_VMX_MODIFIES_BEHAVIOR)
6439 uNewCr0 |= (pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS);
6440 else if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
6441 return rcStrict;
6442 }
6443#endif
6444
6445 return IEM_CIMPL_CALL_4(iemCImpl_load_CrX, /*cr*/ 0, uNewCr0, IEMACCESSCRX_CLTS, UINT8_MAX /* iGReg */);
6446}
6447
6448
6449/**
6450 * Implements mov GReg,DRx.
6451 *
6452 * @param iGReg The general register to store the DRx value in.
6453 * @param iDrReg The DRx register to read (0-7).
6454 */
6455IEM_CIMPL_DEF_2(iemCImpl_mov_Rd_Dd, uint8_t, iGReg, uint8_t, iDrReg)
6456{
6457#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6458 /*
6459 * Check nested-guest VMX intercept.
6460 * Unlike most other intercepts, the Mov DRx intercept takes preceedence
6461 * over CPL and CR4.DE and even DR4/DR5 checks.
6462 *
6463 * See Intel spec. 25.1.3 "Instructions That Cause VM Exits Conditionally".
6464 */
6465 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
6466 { /* probable */ }
6467 else
6468 {
6469 VBOXSTRICTRC rcStrict = iemVmxVmexitInstrMovDrX(pVCpu, VMXINSTRID_MOV_FROM_DRX, iDrReg, iGReg, cbInstr);
6470 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
6471 return rcStrict;
6472 }
6473#endif
6474
6475 /*
6476 * Check preconditions.
6477 */
6478 /* Raise GPs. */
6479 if (IEM_GET_CPL(pVCpu) != 0)
6480 return iemRaiseGeneralProtectionFault0(pVCpu);
6481 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6482 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_DR7);
6483
6484 /** @todo \#UD in outside ring-0 too? */
6485 if (iDrReg == 4 || iDrReg == 5)
6486 {
6487 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_CR4);
6488 if (pVCpu->cpum.GstCtx.cr4 & X86_CR4_DE)
6489 {
6490 Log(("mov r%u,dr%u: CR4.DE=1 -> #GP(0)\n", iGReg, iDrReg));
6491 return iemRaiseGeneralProtectionFault0(pVCpu);
6492 }
6493 iDrReg += 2;
6494 }
6495
6496 /* Raise #DB if general access detect is enabled. */
6497 if (pVCpu->cpum.GstCtx.dr[7] & X86_DR7_GD)
6498 {
6499 Log(("mov r%u,dr%u: DR7.GD=1 -> #DB\n", iGReg, iDrReg));
6500 return iemRaiseDebugException(pVCpu);
6501 }
6502
6503 /*
6504 * Read the debug register and store it in the specified general register.
6505 */
6506 uint64_t drX;
6507 switch (iDrReg)
6508 {
6509 case 0:
6510 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3);
6511 drX = pVCpu->cpum.GstCtx.dr[0];
6512 break;
6513 case 1:
6514 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3);
6515 drX = pVCpu->cpum.GstCtx.dr[1];
6516 break;
6517 case 2:
6518 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3);
6519 drX = pVCpu->cpum.GstCtx.dr[2];
6520 break;
6521 case 3:
6522 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3);
6523 drX = pVCpu->cpum.GstCtx.dr[3];
6524 break;
6525 case 6:
6526 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR6);
6527 drX = pVCpu->cpum.GstCtx.dr[6];
6528 drX |= X86_DR6_RA1_MASK;
6529 drX &= ~X86_DR6_RAZ_MASK;
6530 break;
6531 case 7:
6532 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_DR7);
6533 drX = pVCpu->cpum.GstCtx.dr[7];
6534 drX |=X86_DR7_RA1_MASK;
6535 drX &= ~X86_DR7_RAZ_MASK;
6536 break;
6537 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* caller checks */
6538 }
6539
6540 /** @todo SVM nested-guest intercept for DR8-DR15? */
6541 /*
6542 * Check for any SVM nested-guest intercepts for the DRx read.
6543 */
6544 if (!IEM_SVM_IS_READ_DR_INTERCEPT_SET(pVCpu, iDrReg))
6545 { /* probable */ }
6546 else
6547 {
6548 Log(("mov r%u,dr%u: Guest intercept -> #VMEXIT\n", iGReg, iDrReg));
6549 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6550 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_READ_DR0 + (iDrReg & 0xf),
6551 IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmDecodeAssists ? (iGReg & 7) : 0, 0 /* uExitInfo2 */);
6552 }
6553
6554 if (IEM_IS_64BIT_CODE(pVCpu))
6555 iemGRegStoreU64(pVCpu, iGReg, drX);
6556 else
6557 iemGRegStoreU32(pVCpu, iGReg, (uint32_t)drX);
6558
6559 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6560}
6561
6562
6563/**
6564 * Implements mov DRx,GReg.
6565 *
6566 * @param iDrReg The DRx register to write (valid).
6567 * @param iGReg The general register to load the DRx value from.
6568 */
6569IEM_CIMPL_DEF_2(iemCImpl_mov_Dd_Rd, uint8_t, iDrReg, uint8_t, iGReg)
6570{
6571#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6572 /*
6573 * Check nested-guest VMX intercept.
6574 * Unlike most other intercepts, the Mov DRx intercept takes preceedence
6575 * over CPL and CR4.DE and even DR4/DR5 checks.
6576 *
6577 * See Intel spec. 25.1.3 "Instructions That Cause VM Exits Conditionally".
6578 */
6579 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
6580 { /* probable */ }
6581 else
6582 {
6583 VBOXSTRICTRC rcStrict = iemVmxVmexitInstrMovDrX(pVCpu, VMXINSTRID_MOV_TO_DRX, iDrReg, iGReg, cbInstr);
6584 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
6585 return rcStrict;
6586 }
6587#endif
6588
6589 /*
6590 * Check preconditions.
6591 */
6592 if (IEM_GET_CPL(pVCpu) != 0)
6593 return iemRaiseGeneralProtectionFault0(pVCpu);
6594 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6595 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_DR7);
6596
6597 if (iDrReg == 4 || iDrReg == 5)
6598 {
6599 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_CR4);
6600 if (pVCpu->cpum.GstCtx.cr4 & X86_CR4_DE)
6601 {
6602 Log(("mov dr%u,r%u: CR4.DE=1 -> #GP(0)\n", iDrReg, iGReg));
6603 return iemRaiseGeneralProtectionFault0(pVCpu);
6604 }
6605 iDrReg += 2;
6606 }
6607
6608 /* Raise #DB if general access detect is enabled. */
6609 /** @todo is \#DB/DR7.GD raised before any reserved high bits in DR7/DR6
6610 * \#GP? */
6611 if (pVCpu->cpum.GstCtx.dr[7] & X86_DR7_GD)
6612 {
6613 Log(("mov dr%u,r%u: DR7.GD=1 -> #DB\n", iDrReg, iGReg));
6614 return iemRaiseDebugException(pVCpu);
6615 }
6616
6617 /*
6618 * Read the new value from the source register.
6619 */
6620 uint64_t uNewDrX;
6621 if (IEM_IS_64BIT_CODE(pVCpu))
6622 uNewDrX = iemGRegFetchU64(pVCpu, iGReg);
6623 else
6624 uNewDrX = iemGRegFetchU32(pVCpu, iGReg);
6625
6626 /*
6627 * Adjust it.
6628 */
6629 switch (iDrReg)
6630 {
6631 case 0:
6632 case 1:
6633 case 2:
6634 case 3:
6635 /* nothing to adjust */
6636 break;
6637
6638 case 6:
6639 if (uNewDrX & X86_DR6_MBZ_MASK)
6640 {
6641 Log(("mov dr%u,%#llx: DR6 high bits are not zero -> #GP(0)\n", iDrReg, uNewDrX));
6642 return iemRaiseGeneralProtectionFault0(pVCpu);
6643 }
6644 uNewDrX |= X86_DR6_RA1_MASK;
6645 uNewDrX &= ~X86_DR6_RAZ_MASK;
6646 break;
6647
6648 case 7:
6649 if (uNewDrX & X86_DR7_MBZ_MASK)
6650 {
6651 Log(("mov dr%u,%#llx: DR7 high bits are not zero -> #GP(0)\n", iDrReg, uNewDrX));
6652 return iemRaiseGeneralProtectionFault0(pVCpu);
6653 }
6654 uNewDrX |= X86_DR7_RA1_MASK;
6655 uNewDrX &= ~X86_DR7_RAZ_MASK;
6656 break;
6657
6658 IEM_NOT_REACHED_DEFAULT_CASE_RET();
6659 }
6660
6661 /** @todo SVM nested-guest intercept for DR8-DR15? */
6662 /*
6663 * Check for any SVM nested-guest intercepts for the DRx write.
6664 */
6665 if (!IEM_SVM_IS_WRITE_DR_INTERCEPT_SET(pVCpu, iDrReg))
6666 { /* probable */ }
6667 else
6668 {
6669 Log2(("mov dr%u,r%u: Guest intercept -> #VMEXIT\n", iDrReg, iGReg));
6670 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6671 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_WRITE_DR0 + (iDrReg & 0xf),
6672 IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmDecodeAssists ? (iGReg & 7) : 0, 0 /* uExitInfo2 */);
6673 }
6674
6675 /*
6676 * Do the actual setting.
6677 */
6678 if (iDrReg < 4)
6679 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3);
6680 else if (iDrReg == 6)
6681 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR6);
6682
6683 int rc = CPUMSetGuestDRx(pVCpu, iDrReg, uNewDrX);
6684 AssertRCSuccessReturn(rc, RT_SUCCESS_NP(rc) ? VERR_IEM_IPE_1 : rc);
6685
6686 /*
6687 * Re-init hardware breakpoint summary if it was DR7 that got changed.
6688 *
6689 * We also do this when an active data breakpoint is updated so that the
6690 * TLB entry can be correctly invalidated.
6691 */
6692 if ( iDrReg == 7
6693#ifdef IEM_WITH_DATA_TLB
6694 || ( iDrReg <= 3
6695 && (X86_DR7_L_G(iDrReg) & pVCpu->cpum.GstCtx.dr[7])
6696 && X86_DR7_IS_W_CFG(pVCpu->cpum.GstCtx.dr[7], iDrReg) )
6697#endif
6698 )
6699 iemRecalcExecDbgFlags(pVCpu);
6700
6701 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6702}
6703
6704
6705/**
6706 * Implements mov GReg,TRx.
6707 *
6708 * @param iGReg The general register to store the
6709 * TRx value in.
6710 * @param iTrReg The TRx register to read (6/7).
6711 */
6712IEM_CIMPL_DEF_2(iemCImpl_mov_Rd_Td, uint8_t, iGReg, uint8_t, iTrReg)
6713{
6714 /*
6715 * Check preconditions. NB: This instruction is 386/486 only.
6716 */
6717
6718 /* Raise GPs. */
6719 if (IEM_GET_CPL(pVCpu) != 0)
6720 return iemRaiseGeneralProtectionFault0(pVCpu);
6721 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6722
6723 if (iTrReg < 6 || iTrReg > 7)
6724 {
6725 /** @todo Do Intel CPUs reject this or are the TRs aliased? */
6726 Log(("mov r%u,tr%u: invalid register -> #GP(0)\n", iGReg, iTrReg));
6727 return iemRaiseGeneralProtectionFault0(pVCpu);
6728 }
6729
6730 /*
6731 * Read the test register and store it in the specified general register.
6732 * This is currently a dummy implementation that only exists to satisfy
6733 * old debuggers like WDEB386 or OS/2 KDB which unconditionally read the
6734 * TR6/TR7 registers. Software which actually depends on the TR values
6735 * (different on 386/486) is exceedingly rare.
6736 */
6737 uint32_t trX;
6738 switch (iTrReg)
6739 {
6740 case 6:
6741 trX = 0; /* Currently a dummy. */
6742 break;
6743 case 7:
6744 trX = 0; /* Currently a dummy. */
6745 break;
6746 IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
6747 }
6748
6749 iemGRegStoreU32(pVCpu, iGReg, trX);
6750
6751 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6752}
6753
6754
6755/**
6756 * Implements mov TRx,GReg.
6757 *
6758 * @param iTrReg The TRx register to write (valid).
6759 * @param iGReg The general register to load the TRx
6760 * value from.
6761 */
6762IEM_CIMPL_DEF_2(iemCImpl_mov_Td_Rd, uint8_t, iTrReg, uint8_t, iGReg)
6763{
6764 /*
6765 * Check preconditions. NB: This instruction is 386/486 only.
6766 */
6767
6768 /* Raise GPs. */
6769 if (IEM_GET_CPL(pVCpu) != 0)
6770 return iemRaiseGeneralProtectionFault0(pVCpu);
6771 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6772
6773 if (iTrReg < 6 || iTrReg > 7)
6774 {
6775 /** @todo Do Intel CPUs reject this or are the TRs aliased? */
6776 Log(("mov r%u,tr%u: invalid register -> #GP(0)\n", iGReg, iTrReg));
6777 return iemRaiseGeneralProtectionFault0(pVCpu);
6778 }
6779
6780 /*
6781 * Read the new value from the source register.
6782 */
6783 uint32_t uNewTrX = iemGRegFetchU32(pVCpu, iGReg);
6784
6785 /*
6786 * Here we would do the actual setting if this weren't a dummy implementation.
6787 * This is currently a dummy implementation that only exists to prevent
6788 * old debuggers like WDEB386 or OS/2 KDB from crashing.
6789 */
6790 RT_NOREF(uNewTrX);
6791
6792 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6793}
6794
6795
6796/**
6797 * Implements 'INVLPG m'.
6798 *
6799 * @param GCPtrPage The effective address of the page to invalidate.
6800 * @remarks Updates the RIP.
6801 */
6802IEM_CIMPL_DEF_1(iemCImpl_invlpg, RTGCPTR, GCPtrPage)
6803{
6804 /* ring-0 only. */
6805 if (IEM_GET_CPL(pVCpu) != 0)
6806 return iemRaiseGeneralProtectionFault0(pVCpu);
6807 Assert(!pVCpu->cpum.GstCtx.eflags.Bits.u1VM);
6808 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4 | CPUMCTX_EXTRN_EFER);
6809
6810 if (!IEM_IS_IN_GUEST(pVCpu))
6811 { /* probable */ }
6812#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
6813 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
6814 && IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_INVLPG_EXIT))
6815 {
6816 Log(("invlpg: Guest intercept (%RGp) -> VM-exit\n", GCPtrPage));
6817 return iemVmxVmexitInstrInvlpg(pVCpu, GCPtrPage, cbInstr);
6818 }
6819#endif
6820 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_INVLPG))
6821 {
6822 Log(("invlpg: Guest intercept (%RGp) -> #VMEXIT\n", GCPtrPage));
6823 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
6824 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_INVLPG,
6825 IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmDecodeAssists ? GCPtrPage : 0, 0 /* uExitInfo2 */);
6826 }
6827
6828 int rc = PGMInvalidatePage(pVCpu, GCPtrPage);
6829 if (rc == VINF_SUCCESS)
6830 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6831 if (rc == VINF_PGM_SYNC_CR3)
6832 {
6833 iemSetPassUpStatus(pVCpu, rc);
6834 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6835 }
6836
6837 AssertMsg(RT_FAILURE_NP(rc), ("%Rrc\n", rc));
6838 Log(("PGMInvalidatePage(%RGv) -> %Rrc\n", GCPtrPage, rc));
6839 return rc;
6840}
6841
6842
6843/**
6844 * Implements INVPCID.
6845 *
6846 * @param iEffSeg The segment of the invpcid descriptor.
6847 * @param GCPtrInvpcidDesc The address of invpcid descriptor.
6848 * @param uInvpcidType The invalidation type.
6849 * @remarks Updates the RIP.
6850 */
6851IEM_CIMPL_DEF_3(iemCImpl_invpcid, uint8_t, iEffSeg, RTGCPTR, GCPtrInvpcidDesc, uint64_t, uInvpcidType)
6852{
6853 /*
6854 * Check preconditions.
6855 */
6856 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fInvpcid)
6857 return iemRaiseUndefinedOpcode(pVCpu);
6858
6859 /* When in VMX non-root mode and INVPCID is not enabled, it results in #UD. */
6860 if (RT_LIKELY( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
6861 || IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_INVPCID)))
6862 { /* likely */ }
6863 else
6864 {
6865 Log(("invpcid: Not enabled for nested-guest execution -> #UD\n"));
6866 return iemRaiseUndefinedOpcode(pVCpu);
6867 }
6868
6869 if (IEM_GET_CPL(pVCpu) != 0)
6870 {
6871 Log(("invpcid: CPL != 0 -> #GP(0)\n"));
6872 return iemRaiseGeneralProtectionFault0(pVCpu);
6873 }
6874
6875 if (IEM_IS_V86_MODE(pVCpu))
6876 {
6877 Log(("invpcid: v8086 mode -> #GP(0)\n"));
6878 return iemRaiseGeneralProtectionFault0(pVCpu);
6879 }
6880
6881 /*
6882 * Check nested-guest intercept.
6883 *
6884 * INVPCID causes a VM-exit if "enable INVPCID" and "INVLPG exiting" are
6885 * both set. We have already checked the former earlier in this function.
6886 *
6887 * CPL and virtual-8086 mode checks take priority over this VM-exit.
6888 * See Intel spec. "25.1.1 Relative Priority of Faults and VM Exits".
6889 */
6890 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
6891 || !IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_INVLPG_EXIT))
6892 { /* probable */ }
6893 else
6894 {
6895 Log(("invpcid: Guest intercept -> #VM-exit\n"));
6896 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_INVPCID, VMXINSTRID_NONE, cbInstr);
6897 }
6898
6899 if (uInvpcidType > X86_INVPCID_TYPE_MAX_VALID)
6900 {
6901 Log(("invpcid: invalid/unrecognized invpcid type %#RX64 -> #GP(0)\n", uInvpcidType));
6902 return iemRaiseGeneralProtectionFault0(pVCpu);
6903 }
6904 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4 | CPUMCTX_EXTRN_EFER);
6905
6906 /*
6907 * Fetch the invpcid descriptor from guest memory.
6908 */
6909/** @todo Check if the entire 128 bits are always read for all types. Check for invalid types as well. */
6910 RTUINT128U uDesc;
6911 VBOXSTRICTRC rcStrict = iemMemFetchDataU128(pVCpu, &uDesc, iEffSeg, GCPtrInvpcidDesc);
6912 if (rcStrict == VINF_SUCCESS)
6913 {
6914 /*
6915 * Validate the descriptor.
6916 */
6917 if (uDesc.s.Lo > 0xfff)
6918 {
6919 Log(("invpcid: reserved bits set in invpcid descriptor %#RX64 -> #GP(0)\n", uDesc.s.Lo));
6920 return iemRaiseGeneralProtectionFault0(pVCpu);
6921 }
6922
6923 RTGCUINTPTR64 const GCPtrInvAddr = uDesc.s.Hi;
6924 uint8_t const uPcid = uDesc.s.Lo & UINT64_C(0xfff);
6925 uint32_t const uCr4 = pVCpu->cpum.GstCtx.cr4;
6926 uint64_t const uCr3 = pVCpu->cpum.GstCtx.cr3;
6927 switch (uInvpcidType)
6928 {
6929 case X86_INVPCID_TYPE_INDV_ADDR:
6930 {
6931 if (!IEM_IS_CANONICAL(GCPtrInvAddr))
6932 {
6933 Log(("invpcid: invalidation address %#RGP is not canonical -> #GP(0)\n", GCPtrInvAddr));
6934 return iemRaiseGeneralProtectionFault0(pVCpu);
6935 }
6936 if ( !(uCr4 & X86_CR4_PCIDE)
6937 && uPcid != 0)
6938 {
6939 Log(("invpcid: invalid pcid %#x\n", uPcid));
6940 return iemRaiseGeneralProtectionFault0(pVCpu);
6941 }
6942
6943 /* Invalidate mappings for the linear address tagged with PCID except global translations. */
6944/** @todo PGMFlushTLB is overkill for X86_INVPCID_TYPE_INDV_ADDR. Add a fGlobal parameter
6945 * to PGMInvalidatePage or add a new function to support this variation of invlpg. */
6946 PGMFlushTLB(pVCpu, uCr3, false /* fGlobal */);
6947 break;
6948 }
6949
6950 case X86_INVPCID_TYPE_SINGLE_CONTEXT:
6951 {
6952 if ( !(uCr4 & X86_CR4_PCIDE)
6953 && uPcid != 0)
6954 {
6955 Log(("invpcid: invalid pcid %#x\n", uPcid));
6956 return iemRaiseGeneralProtectionFault0(pVCpu);
6957 }
6958 /* Invalidate all mappings associated with PCID except global translations. */
6959 PGMFlushTLB(pVCpu, uCr3, false /* fGlobal */);
6960 break;
6961 }
6962
6963 case X86_INVPCID_TYPE_ALL_CONTEXT_INCL_GLOBAL:
6964 {
6965 PGMFlushTLB(pVCpu, uCr3, true /* fGlobal */);
6966 break;
6967 }
6968
6969 case X86_INVPCID_TYPE_ALL_CONTEXT_EXCL_GLOBAL:
6970 {
6971 PGMFlushTLB(pVCpu, uCr3, false /* fGlobal */);
6972 break;
6973 }
6974 IEM_NOT_REACHED_DEFAULT_CASE_RET();
6975 }
6976 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
6977 }
6978 return rcStrict;
6979}
6980
6981
6982/**
6983 * Implements INVD.
6984 */
6985IEM_CIMPL_DEF_0(iemCImpl_invd)
6986{
6987 if (IEM_GET_CPL(pVCpu) != 0)
6988 {
6989 Log(("invd: CPL != 0 -> #GP(0)\n"));
6990 return iemRaiseGeneralProtectionFault0(pVCpu);
6991 }
6992
6993 if (!IEM_IS_IN_GUEST(pVCpu))
6994 { /* probable */ }
6995 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
6996 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_INVD, cbInstr);
6997 else
6998 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_INVD, SVM_EXIT_INVD, 0, 0, cbInstr);
6999
7000 /* We currently take no action here. */
7001 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7002}
7003
7004
7005/**
7006 * Implements WBINVD.
7007 */
7008IEM_CIMPL_DEF_0(iemCImpl_wbinvd)
7009{
7010 if (IEM_GET_CPL(pVCpu) != 0)
7011 {
7012 Log(("wbinvd: CPL != 0 -> #GP(0)\n"));
7013 return iemRaiseGeneralProtectionFault0(pVCpu);
7014 }
7015
7016 if (!IEM_IS_IN_GUEST(pVCpu))
7017 { /* probable */ }
7018 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
7019 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_WBINVD, cbInstr);
7020 else
7021 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_WBINVD, SVM_EXIT_WBINVD, 0, 0, cbInstr);
7022
7023 /* We currently take no action here. */
7024 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7025}
7026
7027
7028/** Opcode 0x0f 0xaa. */
7029IEM_CIMPL_DEF_0(iemCImpl_rsm)
7030{
7031 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_RSM, SVM_EXIT_RSM, 0, 0, cbInstr);
7032 NOREF(cbInstr);
7033 return iemRaiseUndefinedOpcode(pVCpu);
7034}
7035
7036
7037/**
7038 * Implements RDTSC.
7039 */
7040IEM_CIMPL_DEF_0(iemCImpl_rdtsc)
7041{
7042 /*
7043 * Check preconditions.
7044 */
7045 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fTsc)
7046 return iemRaiseUndefinedOpcode(pVCpu);
7047
7048 if (IEM_GET_CPL(pVCpu) != 0)
7049 {
7050 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR4);
7051 if (pVCpu->cpum.GstCtx.cr4 & X86_CR4_TSD)
7052 {
7053 Log(("rdtsc: CR4.TSD and CPL=%u -> #GP(0)\n", IEM_GET_CPL(pVCpu)));
7054 return iemRaiseGeneralProtectionFault0(pVCpu);
7055 }
7056 }
7057
7058 if (!IEM_IS_IN_GUEST(pVCpu))
7059 { /* probable */ }
7060 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7061 && IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_RDTSC_EXIT))
7062 {
7063 Log(("rdtsc: Guest intercept -> VM-exit\n"));
7064 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_RDTSC, cbInstr);
7065 }
7066 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_RDTSC))
7067 {
7068 Log(("rdtsc: Guest intercept -> #VMEXIT\n"));
7069 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7070 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_RDTSC, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7071 }
7072
7073 /*
7074 * Do the job.
7075 */
7076 uint64_t uTicks = TMCpuTickGet(pVCpu);
7077#if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX)
7078 uTicks = CPUMApplyNestedGuestTscOffset(pVCpu, uTicks);
7079#endif
7080 pVCpu->cpum.GstCtx.rax = RT_LO_U32(uTicks);
7081 pVCpu->cpum.GstCtx.rdx = RT_HI_U32(uTicks);
7082 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX); /* For IEMExecDecodedRdtsc. */
7083 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7084}
7085
7086
7087/**
7088 * Implements RDTSC.
7089 */
7090IEM_CIMPL_DEF_0(iemCImpl_rdtscp)
7091{
7092 /*
7093 * Check preconditions.
7094 */
7095 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fRdTscP)
7096 return iemRaiseUndefinedOpcode(pVCpu);
7097
7098 if (RT_LIKELY( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7099 || IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_RDTSCP)))
7100 { /* likely */ }
7101 else
7102 {
7103 Log(("rdtscp: Not enabled for VMX non-root mode -> #UD\n"));
7104 return iemRaiseUndefinedOpcode(pVCpu);
7105 }
7106
7107 if (IEM_GET_CPL(pVCpu) != 0)
7108 {
7109 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR4);
7110 if (pVCpu->cpum.GstCtx.cr4 & X86_CR4_TSD)
7111 {
7112 Log(("rdtscp: CR4.TSD and CPL=%u -> #GP(0)\n", IEM_GET_CPL(pVCpu)));
7113 return iemRaiseGeneralProtectionFault0(pVCpu);
7114 }
7115 }
7116
7117 if (!IEM_IS_IN_GUEST(pVCpu))
7118 { /* probable */ }
7119 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7120 && IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_RDTSC_EXIT))
7121 {
7122 Log(("rdtscp: Guest intercept -> VM-exit\n"));
7123 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_RDTSCP, cbInstr);
7124 }
7125 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_RDTSCP))
7126 {
7127 Log(("rdtscp: Guest intercept -> #VMEXIT\n"));
7128 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7129 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_RDTSCP, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7130 }
7131
7132 /*
7133 * Do the job.
7134 * Query the MSR first in case of trips to ring-3.
7135 */
7136 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_TSC_AUX);
7137 VBOXSTRICTRC rcStrict = CPUMQueryGuestMsr(pVCpu, MSR_K8_TSC_AUX, &pVCpu->cpum.GstCtx.rcx);
7138 if (rcStrict == VINF_SUCCESS)
7139 {
7140 /* Low dword of the TSC_AUX msr only. */
7141 pVCpu->cpum.GstCtx.rcx &= UINT32_C(0xffffffff);
7142
7143 uint64_t uTicks = TMCpuTickGet(pVCpu);
7144#if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX)
7145 uTicks = CPUMApplyNestedGuestTscOffset(pVCpu, uTicks);
7146#endif
7147 pVCpu->cpum.GstCtx.rax = RT_LO_U32(uTicks);
7148 pVCpu->cpum.GstCtx.rdx = RT_HI_U32(uTicks);
7149 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RCX); /* For IEMExecDecodedRdtscp. */
7150 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7151 }
7152 return rcStrict;
7153}
7154
7155
7156/**
7157 * Implements RDPMC.
7158 */
7159IEM_CIMPL_DEF_0(iemCImpl_rdpmc)
7160{
7161 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR4);
7162
7163 if ( IEM_GET_CPL(pVCpu) != 0
7164 && !(pVCpu->cpum.GstCtx.cr4 & X86_CR4_PCE))
7165 return iemRaiseGeneralProtectionFault0(pVCpu);
7166
7167 if (!IEM_IS_IN_GUEST(pVCpu))
7168 { /* probable */ }
7169 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7170 && IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_RDPMC_EXIT))
7171 {
7172 Log(("rdpmc: Guest intercept -> VM-exit\n"));
7173 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_RDPMC, cbInstr);
7174 }
7175 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_RDPMC))
7176 {
7177 Log(("rdpmc: Guest intercept -> #VMEXIT\n"));
7178 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7179 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_RDPMC, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7180 }
7181
7182 /** @todo Emulate performance counters, for now just return 0. */
7183 pVCpu->cpum.GstCtx.rax = 0;
7184 pVCpu->cpum.GstCtx.rdx = 0;
7185 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX);
7186 /** @todo We should trigger a \#GP here if the CPU doesn't support the index in
7187 * ecx but see @bugref{3472}! */
7188
7189 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7190}
7191
7192
7193/**
7194 * Implements RDMSR.
7195 */
7196IEM_CIMPL_DEF_0(iemCImpl_rdmsr)
7197{
7198 /*
7199 * Check preconditions.
7200 */
7201 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fMsr)
7202 return iemRaiseUndefinedOpcode(pVCpu);
7203 if (IEM_GET_CPL(pVCpu) != 0)
7204 return iemRaiseGeneralProtectionFault0(pVCpu);
7205
7206 /*
7207 * Check nested-guest intercepts.
7208 */
7209 if (!IEM_IS_IN_GUEST(pVCpu))
7210 { /* probable */ }
7211#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
7212 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
7213 {
7214 if (iemVmxIsRdmsrWrmsrInterceptSet(pVCpu, VMX_EXIT_RDMSR, pVCpu->cpum.GstCtx.ecx))
7215 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_RDMSR, cbInstr);
7216 }
7217#endif
7218#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
7219 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_MSR_PROT))
7220 {
7221 VBOXSTRICTRC rcStrict = iemSvmHandleMsrIntercept(pVCpu, pVCpu->cpum.GstCtx.ecx, false /* fWrite */, cbInstr);
7222 if (rcStrict == VINF_SVM_VMEXIT)
7223 return VINF_SUCCESS;
7224 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
7225 {
7226 Log(("IEM: SVM intercepted rdmsr(%#x) failed. rc=%Rrc\n", pVCpu->cpum.GstCtx.ecx, VBOXSTRICTRC_VAL(rcStrict)));
7227 return rcStrict;
7228 }
7229 }
7230#endif
7231
7232 /*
7233 * Do the job.
7234 */
7235 RTUINT64U uValue;
7236 /** @todo make CPUMAllMsrs.cpp import the necessary MSR state. */
7237 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ALL_MSRS);
7238
7239 VBOXSTRICTRC rcStrict = CPUMQueryGuestMsr(pVCpu, pVCpu->cpum.GstCtx.ecx, &uValue.u);
7240 if (rcStrict == VINF_SUCCESS)
7241 {
7242 pVCpu->cpum.GstCtx.rax = uValue.s.Lo;
7243 pVCpu->cpum.GstCtx.rdx = uValue.s.Hi;
7244 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX);
7245
7246 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7247 }
7248
7249#ifndef IN_RING3
7250 /* Deferred to ring-3. */
7251 if (rcStrict == VINF_CPUM_R3_MSR_READ)
7252 {
7253 Log(("IEM: rdmsr(%#x) -> ring-3\n", pVCpu->cpum.GstCtx.ecx));
7254 return rcStrict;
7255 }
7256#endif
7257
7258 /* Often a unimplemented MSR or MSR bit, so worth logging. */
7259 if (pVCpu->iem.s.cLogRelRdMsr < 32)
7260 {
7261 pVCpu->iem.s.cLogRelRdMsr++;
7262 LogRel(("IEM: rdmsr(%#x) -> #GP(0)\n", pVCpu->cpum.GstCtx.ecx));
7263 }
7264 else
7265 Log(( "IEM: rdmsr(%#x) -> #GP(0)\n", pVCpu->cpum.GstCtx.ecx));
7266 AssertMsgReturn(rcStrict == VERR_CPUM_RAISE_GP_0, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)), VERR_IPE_UNEXPECTED_STATUS);
7267 return iemRaiseGeneralProtectionFault0(pVCpu);
7268}
7269
7270
7271/**
7272 * Implements WRMSR.
7273 */
7274IEM_CIMPL_DEF_0(iemCImpl_wrmsr)
7275{
7276 /*
7277 * Check preconditions.
7278 */
7279 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fMsr)
7280 return iemRaiseUndefinedOpcode(pVCpu);
7281 if (IEM_GET_CPL(pVCpu) != 0)
7282 return iemRaiseGeneralProtectionFault0(pVCpu);
7283
7284 RTUINT64U uValue;
7285 uValue.s.Lo = pVCpu->cpum.GstCtx.eax;
7286 uValue.s.Hi = pVCpu->cpum.GstCtx.edx;
7287
7288 uint32_t const idMsr = pVCpu->cpum.GstCtx.ecx;
7289
7290 /** @todo make CPUMAllMsrs.cpp import the necessary MSR state. */
7291 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ALL_MSRS);
7292
7293 /*
7294 * Check nested-guest intercepts.
7295 */
7296 if (!IEM_IS_IN_GUEST(pVCpu))
7297 { /* probable */ }
7298#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
7299 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
7300 {
7301 if (iemVmxIsRdmsrWrmsrInterceptSet(pVCpu, VMX_EXIT_WRMSR, idMsr))
7302 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_WRMSR, cbInstr);
7303 }
7304#endif
7305#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
7306 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_MSR_PROT))
7307 {
7308 VBOXSTRICTRC rcStrict = iemSvmHandleMsrIntercept(pVCpu, idMsr, true /* fWrite */, cbInstr);
7309 if (rcStrict == VINF_SVM_VMEXIT)
7310 return VINF_SUCCESS;
7311 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
7312 {
7313 Log(("IEM: SVM intercepted rdmsr(%#x) failed. rc=%Rrc\n", idMsr, VBOXSTRICTRC_VAL(rcStrict)));
7314 return rcStrict;
7315 }
7316 }
7317#endif
7318
7319 if (idMsr == MSR_K6_EFER)
7320 IEMTLBTRACE_LOAD_EFER(pVCpu, uValue.u, pVCpu->cpum.GstCtx.msrEFER);
7321
7322 /*
7323 * Do the job.
7324 */
7325 VBOXSTRICTRC rcStrict = CPUMSetGuestMsr(pVCpu, idMsr, uValue.u);
7326 if (rcStrict == VINF_SUCCESS)
7327 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7328
7329#ifndef IN_RING3
7330 /* Deferred to ring-3. */
7331 if (rcStrict == VINF_CPUM_R3_MSR_WRITE)
7332 {
7333 Log(("IEM: wrmsr(%#x) -> ring-3\n", idMsr));
7334 return rcStrict;
7335 }
7336#endif
7337
7338 /* Often a unimplemented MSR or MSR bit, so worth logging. */
7339 if (pVCpu->iem.s.cLogRelWrMsr < 32)
7340 {
7341 pVCpu->iem.s.cLogRelWrMsr++;
7342 LogRel(("IEM: wrmsr(%#x,%#x`%08x) -> #GP(0)\n", idMsr, uValue.s.Hi, uValue.s.Lo));
7343 }
7344 else
7345 Log(( "IEM: wrmsr(%#x,%#x`%08x) -> #GP(0)\n", idMsr, uValue.s.Hi, uValue.s.Lo));
7346 AssertMsgReturn(rcStrict == VERR_CPUM_RAISE_GP_0, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)), VERR_IPE_UNEXPECTED_STATUS);
7347 return iemRaiseGeneralProtectionFault0(pVCpu);
7348}
7349
7350
7351/**
7352 * Implements 'IN eAX, port'.
7353 *
7354 * @param u16Port The source port.
7355 * @param cbReg The register size.
7356 * @param bImmAndEffAddrMode Bit 7: Whether the port was specified through an
7357 * immediate operand or the implicit DX register.
7358 * Bits 3-0: Effective address mode.
7359 */
7360IEM_CIMPL_DEF_3(iemCImpl_in, uint16_t, u16Port, uint8_t, cbReg, uint8_t, bImmAndEffAddrMode)
7361{
7362 /*
7363 * GCM intercept.
7364 *
7365 * This must be placed before the IOPL check as the mesa driver intercept
7366 * would otherwise trigger a #GP(0).
7367 */
7368 if (!IEM_IS_IN_GUEST(pVCpu) && GCMIsInterceptingIOPortRead(pVCpu, u16Port, cbReg))
7369 {
7370 VBOXSTRICTRC rcStrict = GCMInterceptedIOPortRead(pVCpu, &pVCpu->cpum.GstCtx, u16Port, cbReg);
7371 if (rcStrict == VINF_GCM_HANDLED_ADVANCE_RIP || rcStrict == VINF_GCM_HANDLED)
7372 {
7373 Log(("iemCImpl_in: u16Port=%#x cbReg=%d was handled by GCMIOPortRead (%d)\n", u16Port, cbReg, VBOXSTRICTRC_VAL(rcStrict)));
7374 if (rcStrict == VINF_GCM_HANDLED_ADVANCE_RIP)
7375 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7376 else
7377 rcStrict = VINF_SUCCESS;
7378 return rcStrict;
7379 }
7380 Assert(rcStrict == VERR_GCM_NOT_HANDLED);
7381 }
7382
7383 /*
7384 * CPL check
7385 */
7386 VBOXSTRICTRC rcStrict = iemHlpCheckPortIOPermission(pVCpu, u16Port, cbReg);
7387 if (rcStrict != VINF_SUCCESS)
7388 return rcStrict;
7389
7390 if (!IEM_IS_IN_GUEST(pVCpu))
7391 { /* probable */ }
7392
7393 /*
7394 * Check VMX nested-guest IO intercept.
7395 */
7396#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
7397 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
7398 {
7399 rcStrict = iemVmxVmexitInstrIo(pVCpu, VMXINSTRID_IO_IN, u16Port, RT_BOOL(bImmAndEffAddrMode & 0x80), cbReg, cbInstr);
7400 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
7401 return rcStrict;
7402 }
7403#endif
7404
7405 /*
7406 * Check SVM nested-guest IO intercept.
7407 */
7408#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
7409 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
7410 {
7411 uint8_t cAddrSizeBits;
7412 switch (bImmAndEffAddrMode & 0xf)
7413 {
7414 case IEMMODE_16BIT: cAddrSizeBits = 16; break;
7415 case IEMMODE_32BIT: cAddrSizeBits = 32; break;
7416 case IEMMODE_64BIT: cAddrSizeBits = 64; break;
7417 IEM_NOT_REACHED_DEFAULT_CASE_RET();
7418 }
7419 rcStrict = iemSvmHandleIOIntercept(pVCpu, u16Port, SVMIOIOTYPE_IN, cbReg, cAddrSizeBits, 0 /* N/A - iEffSeg */,
7420 false /* fRep */, false /* fStrIo */, cbInstr);
7421 if (rcStrict == VINF_SVM_VMEXIT)
7422 return VINF_SUCCESS;
7423 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
7424 {
7425 Log(("iemCImpl_in: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", u16Port, cbReg,
7426 VBOXSTRICTRC_VAL(rcStrict)));
7427 return rcStrict;
7428 }
7429 }
7430#endif
7431#if !defined(VBOX_WITH_NESTED_HWVIRT_VMX) && !defined(VBOX_WITH_NESTED_HWVIRT_SVM)
7432 RT_NOREF(bImmAndEffAddrMode);
7433#endif
7434
7435 /*
7436 * Perform the I/O.
7437 */
7438 PVMCC const pVM = pVCpu->CTX_SUFF(pVM);
7439 uint32_t u32Value = 0;
7440 rcStrict = IOMIOPortRead(pVM, pVCpu, u16Port, &u32Value, cbReg);
7441 if (IOM_SUCCESS(rcStrict))
7442 {
7443 switch (cbReg)
7444 {
7445 case 1: pVCpu->cpum.GstCtx.al = (uint8_t)u32Value; break;
7446 case 2: pVCpu->cpum.GstCtx.ax = (uint16_t)u32Value; break;
7447 case 4: pVCpu->cpum.GstCtx.rax = u32Value; break;
7448 default: AssertFailedReturn(VERR_IEM_IPE_3);
7449 }
7450
7451 pVCpu->iem.s.cPotentialExits++;
7452 if (rcStrict != VINF_SUCCESS)
7453 iemSetPassUpStatus(pVCpu, rcStrict);
7454
7455 /*
7456 * Check for I/O breakpoints before we complete the instruction.
7457 */
7458 uint32_t const fDr7 = pVCpu->cpum.GstCtx.dr[7];
7459 if (RT_UNLIKELY( ( ( (fDr7 & X86_DR7_ENABLED_MASK)
7460 && X86_DR7_ANY_RW_IO(fDr7)
7461 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_DE))
7462 || pVM->dbgf.ro.cEnabledHwIoBreakpoints > 0)
7463 && rcStrict == VINF_SUCCESS))
7464 {
7465 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3 | CPUMCTX_EXTRN_DR6);
7466 pVCpu->cpum.GstCtx.eflags.uBoth |= DBGFBpCheckIo2(pVM, pVCpu, u16Port, cbReg);
7467 }
7468
7469 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7470 }
7471
7472 return rcStrict;
7473}
7474
7475
7476/**
7477 * Implements 'IN eAX, DX'.
7478 *
7479 * @param cbReg The register size.
7480 * @param enmEffAddrMode Effective address mode.
7481 */
7482IEM_CIMPL_DEF_2(iemCImpl_in_eAX_DX, uint8_t, cbReg, IEMMODE, enmEffAddrMode)
7483{
7484 return IEM_CIMPL_CALL_3(iemCImpl_in, pVCpu->cpum.GstCtx.dx, cbReg, 0 /* fImm */ | enmEffAddrMode);
7485}
7486
7487
7488/**
7489 * Implements 'OUT port, eAX'.
7490 *
7491 * @param u16Port The destination port.
7492 * @param cbReg The register size.
7493 * @param bImmAndEffAddrMode Bit 7: Whether the port was specified through an
7494 * immediate operand or the implicit DX register.
7495 * Bits 3-0: Effective address mode.
7496 */
7497IEM_CIMPL_DEF_3(iemCImpl_out, uint16_t, u16Port, uint8_t, cbReg, uint8_t, bImmAndEffAddrMode)
7498{
7499 /*
7500 * CPL check
7501 */
7502 VBOXSTRICTRC rcStrict = iemHlpCheckPortIOPermission(pVCpu, u16Port, cbReg);
7503 if (rcStrict != VINF_SUCCESS)
7504 return rcStrict;
7505
7506 if (!IEM_IS_IN_GUEST(pVCpu))
7507 { /* probable */ }
7508
7509 /*
7510 * Check VMX nested-guest I/O intercept.
7511 */
7512#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
7513 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
7514 {
7515 rcStrict = iemVmxVmexitInstrIo(pVCpu, VMXINSTRID_IO_OUT, u16Port, RT_BOOL(bImmAndEffAddrMode & 0x80), cbReg, cbInstr);
7516 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
7517 return rcStrict;
7518 }
7519#endif
7520
7521 /*
7522 * Check SVM nested-guest I/O intercept.
7523 */
7524#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
7525 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
7526 {
7527 uint8_t cAddrSizeBits;
7528 switch (bImmAndEffAddrMode & 0xf)
7529 {
7530 case IEMMODE_16BIT: cAddrSizeBits = 16; break;
7531 case IEMMODE_32BIT: cAddrSizeBits = 32; break;
7532 case IEMMODE_64BIT: cAddrSizeBits = 64; break;
7533 IEM_NOT_REACHED_DEFAULT_CASE_RET();
7534 }
7535 rcStrict = iemSvmHandleIOIntercept(pVCpu, u16Port, SVMIOIOTYPE_OUT, cbReg, cAddrSizeBits, 0 /* N/A - iEffSeg */,
7536 false /* fRep */, false /* fStrIo */, cbInstr);
7537 if (rcStrict == VINF_SVM_VMEXIT)
7538 return VINF_SUCCESS;
7539 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
7540 {
7541 Log(("iemCImpl_out: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", u16Port, cbReg,
7542 VBOXSTRICTRC_VAL(rcStrict)));
7543 return rcStrict;
7544 }
7545 }
7546#endif
7547#if !defined(VBOX_WITH_NESTED_HWVIRT_VMX) && !defined(VBOX_WITH_NESTED_HWVIRT_SVM)
7548 RT_NOREF(bImmAndEffAddrMode);
7549#endif
7550
7551 /*
7552 * Perform the I/O.
7553 */
7554 PVMCC const pVM = pVCpu->CTX_SUFF(pVM);
7555 uint32_t u32Value;
7556 switch (cbReg)
7557 {
7558 case 1: u32Value = pVCpu->cpum.GstCtx.al; break;
7559 case 2: u32Value = pVCpu->cpum.GstCtx.ax; break;
7560 case 4: u32Value = pVCpu->cpum.GstCtx.eax; break;
7561 default: AssertFailedReturn(VERR_IEM_IPE_4);
7562 }
7563 rcStrict = IOMIOPortWrite(pVM, pVCpu, u16Port, u32Value, cbReg);
7564 if (IOM_SUCCESS(rcStrict))
7565 {
7566 pVCpu->iem.s.cPotentialExits++;
7567 if (rcStrict != VINF_SUCCESS)
7568 iemSetPassUpStatus(pVCpu, rcStrict);
7569
7570 /*
7571 * Check for I/O breakpoints before we complete the instruction.
7572 */
7573 uint32_t const fDr7 = pVCpu->cpum.GstCtx.dr[7];
7574 if (RT_UNLIKELY( ( ( (fDr7 & X86_DR7_ENABLED_MASK)
7575 && X86_DR7_ANY_RW_IO(fDr7)
7576 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_DE))
7577 || pVM->dbgf.ro.cEnabledHwIoBreakpoints > 0)
7578 && rcStrict == VINF_SUCCESS))
7579 {
7580 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3 | CPUMCTX_EXTRN_DR6);
7581 pVCpu->cpum.GstCtx.eflags.uBoth |= DBGFBpCheckIo2(pVM, pVCpu, u16Port, cbReg);
7582 }
7583
7584 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7585 }
7586 return rcStrict;
7587}
7588
7589
7590/**
7591 * Implements 'OUT DX, eAX'.
7592 *
7593 * @param cbReg The register size.
7594 * @param enmEffAddrMode Effective address mode.
7595 */
7596IEM_CIMPL_DEF_2(iemCImpl_out_DX_eAX, uint8_t, cbReg, IEMMODE, enmEffAddrMode)
7597{
7598 return IEM_CIMPL_CALL_3(iemCImpl_out, pVCpu->cpum.GstCtx.dx, cbReg, 0 /* fImm */ | enmEffAddrMode);
7599}
7600
7601
7602/**
7603 * Implements 'CLI'.
7604 */
7605IEM_CIMPL_DEF_0(iemCImpl_cli)
7606{
7607 uint32_t fEfl = IEMMISC_GET_EFL(pVCpu);
7608#ifdef LOG_ENABLED
7609 uint32_t const fEflOld = fEfl;
7610#endif
7611
7612 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_CR4);
7613 if (pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE)
7614 {
7615 uint8_t const uIopl = X86_EFL_GET_IOPL(fEfl);
7616 if (!(fEfl & X86_EFL_VM))
7617 {
7618 if (IEM_GET_CPL(pVCpu) <= uIopl)
7619 fEfl &= ~X86_EFL_IF;
7620 else if ( IEM_GET_CPL(pVCpu) == 3
7621 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_PVI) )
7622 fEfl &= ~X86_EFL_VIF;
7623 else
7624 return iemRaiseGeneralProtectionFault0(pVCpu);
7625 }
7626 /* V8086 */
7627 else if (uIopl == 3)
7628 fEfl &= ~X86_EFL_IF;
7629 else if ( uIopl < 3
7630 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_VME) )
7631 fEfl &= ~X86_EFL_VIF;
7632 else
7633 return iemRaiseGeneralProtectionFault0(pVCpu);
7634 }
7635 /* real mode */
7636 else
7637 fEfl &= ~X86_EFL_IF;
7638
7639 /* Commit. */
7640 IEMMISC_SET_EFL(pVCpu, fEfl);
7641 VBOXSTRICTRC const rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7642 Log2(("CLI: %#x -> %#x\n", fEflOld, fEfl));
7643 return rcStrict;
7644}
7645
7646
7647/**
7648 * Implements 'STI'.
7649 */
7650IEM_CIMPL_DEF_0(iemCImpl_sti)
7651{
7652 uint32_t fEfl = IEMMISC_GET_EFL(pVCpu);
7653 uint32_t const fEflOld = fEfl;
7654
7655 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_CR4);
7656 if (pVCpu->cpum.GstCtx.cr0 & X86_CR0_PE)
7657 {
7658 uint8_t const uIopl = X86_EFL_GET_IOPL(fEfl);
7659 if (!(fEfl & X86_EFL_VM))
7660 {
7661 if (IEM_GET_CPL(pVCpu) <= uIopl)
7662 fEfl |= X86_EFL_IF;
7663 else if ( IEM_GET_CPL(pVCpu) == 3
7664 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_PVI)
7665 && !(fEfl & X86_EFL_VIP) )
7666 fEfl |= X86_EFL_VIF;
7667 else
7668 return iemRaiseGeneralProtectionFault0(pVCpu);
7669 }
7670 /* V8086 */
7671 else if (uIopl == 3)
7672 fEfl |= X86_EFL_IF;
7673 else if ( uIopl < 3
7674 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_VME)
7675 && !(fEfl & X86_EFL_VIP) )
7676 fEfl |= X86_EFL_VIF;
7677 else
7678 return iemRaiseGeneralProtectionFault0(pVCpu);
7679 }
7680 /* real mode */
7681 else
7682 fEfl |= X86_EFL_IF;
7683
7684 /*
7685 * Commit.
7686 *
7687 * Note! Setting the shadow interrupt flag must be done after RIP updating.
7688 */
7689 IEMMISC_SET_EFL(pVCpu, fEfl);
7690 VBOXSTRICTRC const rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7691 if (!(fEflOld & X86_EFL_IF) && (fEfl & X86_EFL_IF))
7692 {
7693 /** @todo only set it the shadow flag if it was clear before? */
7694 CPUMSetInInterruptShadowSti(&pVCpu->cpum.GstCtx);
7695 }
7696 pVCpu->iem.s.fTbCurInstrIsSti = true;
7697 Log2(("STI: %#x -> %#x\n", fEflOld, fEfl));
7698 return rcStrict;
7699}
7700
7701
7702/**
7703 * Implements 'HLT'.
7704 */
7705IEM_CIMPL_DEF_0(iemCImpl_hlt)
7706{
7707 if (IEM_GET_CPL(pVCpu) != 0)
7708 return iemRaiseGeneralProtectionFault0(pVCpu);
7709
7710 if (!IEM_IS_IN_GUEST(pVCpu))
7711 { /* probable */ }
7712 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7713 && IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_HLT_EXIT))
7714 {
7715 Log2(("hlt: Guest intercept -> VM-exit\n"));
7716 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_HLT, cbInstr);
7717 }
7718 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_HLT))
7719 {
7720 Log2(("hlt: Guest intercept -> #VMEXIT\n"));
7721 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7722 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_HLT, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7723 }
7724
7725 /** @todo finish: This ASSUMES that iemRegAddToRipAndFinishingClearingRF won't
7726 * be returning any status codes relating to non-guest events being raised, as
7727 * we'll mess up the guest HALT otherwise. */
7728 VBOXSTRICTRC rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7729 if (rcStrict == VINF_SUCCESS)
7730 rcStrict = VINF_EM_HALT;
7731 return rcStrict;
7732}
7733
7734
7735/**
7736 * Implements 'MONITOR'.
7737 */
7738IEM_CIMPL_DEF_1(iemCImpl_monitor, uint8_t, iEffSeg)
7739{
7740 /*
7741 * Permission checks.
7742 */
7743 if (IEM_GET_CPL(pVCpu) != 0)
7744 {
7745 Log2(("monitor: CPL != 0\n"));
7746 return iemRaiseUndefinedOpcode(pVCpu); /** @todo MSR[0xC0010015].MonMwaitUserEn if we care. */
7747 }
7748 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fMonitorMWait)
7749 {
7750 Log2(("monitor: Not in CPUID\n"));
7751 return iemRaiseUndefinedOpcode(pVCpu);
7752 }
7753
7754 /*
7755 * Check VMX guest-intercept.
7756 * This should be considered a fault-like VM-exit.
7757 * See Intel spec. 25.1.1 "Relative Priority of Faults and VM Exits".
7758 */
7759 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7760 || !IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_MONITOR_EXIT))
7761 { /* probable */ }
7762 else
7763 {
7764 Log2(("monitor: Guest intercept -> #VMEXIT\n"));
7765 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_MONITOR, cbInstr);
7766 }
7767
7768 /*
7769 * Gather the operands and validate them.
7770 */
7771 RTGCPTR GCPtrMem = IEM_IS_64BIT_CODE(pVCpu) ? pVCpu->cpum.GstCtx.rax : pVCpu->cpum.GstCtx.eax;
7772 uint32_t uEcx = pVCpu->cpum.GstCtx.ecx;
7773 uint32_t uEdx = pVCpu->cpum.GstCtx.edx;
7774/** @todo Test whether EAX or ECX is processed first, i.e. do we get \#PF or
7775 * \#GP first. */
7776 if (uEcx != 0)
7777 {
7778 Log2(("monitor rax=%RX64, ecx=%RX32, edx=%RX32; ECX != 0 -> #GP(0)\n", GCPtrMem, uEcx, uEdx)); NOREF(uEdx);
7779 return iemRaiseGeneralProtectionFault0(pVCpu);
7780 }
7781
7782 VBOXSTRICTRC rcStrict = iemMemApplySegment(pVCpu, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, iEffSeg, 1, &GCPtrMem);
7783 if (rcStrict != VINF_SUCCESS)
7784 return rcStrict;
7785
7786 RTGCPHYS GCPhysMem;
7787 /** @todo access size */
7788 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, GCPtrMem, 1, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, &GCPhysMem);
7789 if (rcStrict != VINF_SUCCESS)
7790 return rcStrict;
7791
7792 if (!IEM_IS_IN_GUEST(pVCpu))
7793 { /* probable */ }
7794#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
7795 else if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7796 && IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_VIRT_APIC_ACCESS))
7797 {
7798 /*
7799 * MONITOR does not access the memory, just monitors the address. However,
7800 * if the address falls in the APIC-access page, the address monitored must
7801 * instead be the corresponding address in the virtual-APIC page.
7802 *
7803 * See Intel spec. 29.4.4 "Instruction-Specific Considerations".
7804 */
7805 rcStrict = iemVmxVirtApicAccessUnused(pVCpu, &GCPhysMem, 1, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA);
7806 if ( rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE
7807 && rcStrict != VINF_VMX_MODIFIES_BEHAVIOR)
7808 return rcStrict;
7809 }
7810#endif
7811 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_MONITOR))
7812 {
7813 Log2(("monitor: Guest intercept -> #VMEXIT\n"));
7814 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7815 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_MONITOR, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7816 }
7817
7818 /*
7819 * Call EM to prepare the monitor/wait.
7820 */
7821 rcStrict = EMMonitorWaitPrepare(pVCpu, pVCpu->cpum.GstCtx.rax, pVCpu->cpum.GstCtx.rcx, pVCpu->cpum.GstCtx.rdx, GCPhysMem);
7822 Assert(rcStrict == VINF_SUCCESS);
7823 if (rcStrict == VINF_SUCCESS)
7824 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7825 return rcStrict;
7826}
7827
7828
7829/**
7830 * Implements 'MWAIT'.
7831 */
7832IEM_CIMPL_DEF_0(iemCImpl_mwait)
7833{
7834 /*
7835 * Permission checks.
7836 */
7837 if (IEM_GET_CPL(pVCpu) != 0)
7838 {
7839 Log2(("mwait: CPL != 0\n"));
7840 /** @todo MSR[0xC0010015].MonMwaitUserEn if we care. (Remember to check
7841 * EFLAGS.VM then.) */
7842 return iemRaiseUndefinedOpcode(pVCpu);
7843 }
7844 if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fMonitorMWait)
7845 {
7846 Log2(("mwait: Not in CPUID\n"));
7847 return iemRaiseUndefinedOpcode(pVCpu);
7848 }
7849
7850 /* Check VMX nested-guest intercept. */
7851 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7852 || !IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_MWAIT_EXIT))
7853 { /* probable */ }
7854 else
7855 IEM_VMX_VMEXIT_MWAIT_RET(pVCpu, EMMonitorIsArmed(pVCpu), cbInstr);
7856
7857 /*
7858 * Gather the operands and validate them.
7859 */
7860 uint32_t const uEax = pVCpu->cpum.GstCtx.eax;
7861 uint32_t const uEcx = pVCpu->cpum.GstCtx.ecx;
7862 if (uEcx != 0)
7863 {
7864 /* Only supported extension is break on IRQ when IF=0. */
7865 if (uEcx > 1)
7866 {
7867 Log2(("mwait eax=%RX32, ecx=%RX32; ECX > 1 -> #GP(0)\n", uEax, uEcx));
7868 return iemRaiseGeneralProtectionFault0(pVCpu);
7869 }
7870 uint32_t fMWaitFeatures = 0;
7871 uint32_t uIgnore = 0;
7872 CPUMGetGuestCpuId(pVCpu, 5, 0, -1 /*f64BitMode*/, &uIgnore, &uIgnore, &fMWaitFeatures, &uIgnore);
7873 if ( (fMWaitFeatures & (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
7874 != (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
7875 {
7876 Log2(("mwait eax=%RX32, ecx=%RX32; break-on-IRQ-IF=0 extension not enabled -> #GP(0)\n", uEax, uEcx));
7877 return iemRaiseGeneralProtectionFault0(pVCpu);
7878 }
7879
7880#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
7881 /*
7882 * If the interrupt-window exiting control is set or a virtual-interrupt is pending
7883 * for delivery; and interrupts are disabled the processor does not enter its
7884 * mwait state but rather passes control to the next instruction.
7885 *
7886 * See Intel spec. 25.3 "Changes to Instruction Behavior In VMX Non-root Operation".
7887 */
7888 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
7889 || pVCpu->cpum.GstCtx.eflags.Bits.u1IF)
7890 { /* probable */ }
7891 else if ( IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_INT_WINDOW_EXIT)
7892 || VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST))
7893 /** @todo finish: check up this out after we move int window stuff out of the
7894 * run loop and into the instruction finishing logic here. */
7895 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7896#endif
7897 }
7898
7899 /*
7900 * Check SVM nested-guest mwait intercepts.
7901 */
7902 if (!IEM_IS_IN_GUEST(pVCpu))
7903 { /* probable */ }
7904 else if ( IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_MWAIT_ARMED)
7905 && EMMonitorIsArmed(pVCpu))
7906 {
7907 Log2(("mwait: Guest intercept (monitor hardware armed) -> #VMEXIT\n"));
7908 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7909 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_MWAIT_ARMED, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7910 }
7911 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_MWAIT))
7912 {
7913 Log2(("mwait: Guest intercept -> #VMEXIT\n"));
7914 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
7915 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_MWAIT, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
7916 }
7917
7918 /*
7919 * Call EM to prepare the monitor/wait.
7920 *
7921 * This will return VINF_EM_HALT. If there the trap flag is set, we may
7922 * override it when executing iemRegAddToRipAndFinishingClearingRF ASSUMING
7923 * that will only return guest related events.
7924 */
7925 VBOXSTRICTRC rcStrict = EMMonitorWaitPerform(pVCpu, uEax, uEcx);
7926
7927 /** @todo finish: This needs more thinking as we should suppress internal
7928 * debugger events here, or we'll bugger up the guest state even more than we
7929 * alread do around VINF_EM_HALT. */
7930 VBOXSTRICTRC rcStrict2 = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7931 if (rcStrict2 != VINF_SUCCESS)
7932 {
7933 Log2(("mwait: %Rrc (perform) -> %Rrc (finish)!\n", VBOXSTRICTRC_VAL(rcStrict), VBOXSTRICTRC_VAL(rcStrict2) ));
7934 rcStrict = rcStrict2;
7935 }
7936
7937 return rcStrict;
7938}
7939
7940
7941/**
7942 * Implements 'SWAPGS'.
7943 */
7944IEM_CIMPL_DEF_0(iemCImpl_swapgs)
7945{
7946 Assert(IEM_IS_64BIT_CODE(pVCpu)); /* Caller checks this. */
7947
7948 /*
7949 * Permission checks.
7950 */
7951 if (IEM_GET_CPL(pVCpu) != 0)
7952 {
7953 Log2(("swapgs: CPL != 0\n"));
7954 return iemRaiseUndefinedOpcode(pVCpu);
7955 }
7956
7957 /*
7958 * Do the job.
7959 */
7960 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_KERNEL_GS_BASE | CPUMCTX_EXTRN_GS);
7961 uint64_t uOtherGsBase = pVCpu->cpum.GstCtx.msrKERNELGSBASE;
7962 pVCpu->cpum.GstCtx.msrKERNELGSBASE = pVCpu->cpum.GstCtx.gs.u64Base;
7963 pVCpu->cpum.GstCtx.gs.u64Base = uOtherGsBase;
7964
7965 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
7966}
7967
7968
7969#ifndef VBOX_WITHOUT_CPUID_HOST_CALL
7970/**
7971 * Handles a CPUID call.
7972 */
7973static VBOXSTRICTRC iemCpuIdVBoxCall(PVMCPUCC pVCpu, uint32_t iFunction,
7974 uint32_t *pEax, uint32_t *pEbx, uint32_t *pEcx, uint32_t *pEdx)
7975{
7976 switch (iFunction)
7977 {
7978 case VBOX_CPUID_FN_ID:
7979 LogFlow(("iemCpuIdVBoxCall: VBOX_CPUID_FN_ID\n"));
7980 *pEax = VBOX_CPUID_RESP_ID_EAX;
7981 *pEbx = VBOX_CPUID_RESP_ID_EBX;
7982 *pEcx = VBOX_CPUID_RESP_ID_ECX;
7983 *pEdx = VBOX_CPUID_RESP_ID_EDX;
7984 break;
7985
7986 case VBOX_CPUID_FN_LOG:
7987 {
7988 CPUM_IMPORT_EXTRN_RET(pVCpu, CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX | CPUMCTX_EXTRN_RSI
7989 | IEM_CPUMCTX_EXTRN_EXEC_DECODED_MEM_MASK);
7990
7991 /* Validate input. */
7992 uint32_t cchToLog = *pEdx;
7993 if (cchToLog <= _2M)
7994 {
7995 uint32_t const uLogPicker = *pEbx;
7996 if (uLogPicker <= 1)
7997 {
7998 /* Resolve the logger. */
7999 PRTLOGGER const pLogger = !uLogPicker
8000 ? RTLogDefaultInstanceEx(UINT32_MAX) : RTLogRelGetDefaultInstanceEx(UINT32_MAX);
8001 if (pLogger)
8002 {
8003 /* Copy over the data: */
8004 RTGCPTR GCPtrSrc = pVCpu->cpum.GstCtx.rsi;
8005 while (cchToLog > 0)
8006 {
8007 uint32_t cbToMap = GUEST_PAGE_SIZE - (GCPtrSrc & GUEST_PAGE_OFFSET_MASK);
8008 if (cbToMap > cchToLog)
8009 cbToMap = cchToLog;
8010 /** @todo Extend iemMemMap to allowing page size accessing and avoid 7
8011 * unnecessary calls & iterations per pages. */
8012 if (cbToMap > 512)
8013 cbToMap = 512;
8014 uint8_t bUnmapInfo;
8015 void *pvSrc = NULL;
8016 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvSrc, &bUnmapInfo, cbToMap,
8017 UINT8_MAX, GCPtrSrc, IEM_ACCESS_DATA_R, 0);
8018 if (rcStrict == VINF_SUCCESS)
8019 {
8020 RTLogBulkNestedWrite(pLogger, (const char *)pvSrc, cbToMap, "Gst:");
8021 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
8022 AssertRCSuccessReturn(VBOXSTRICTRC_VAL(rcStrict), rcStrict);
8023 }
8024 else
8025 {
8026 Log(("iemCpuIdVBoxCall: %Rrc at %RGp LB %#x\n", VBOXSTRICTRC_VAL(rcStrict), GCPtrSrc, cbToMap));
8027 return rcStrict;
8028 }
8029
8030 /* Advance. */
8031 pVCpu->cpum.GstCtx.rsi = GCPtrSrc += cbToMap;
8032 *pEdx = cchToLog -= cbToMap;
8033 }
8034 *pEax = VINF_SUCCESS;
8035 }
8036 else
8037 *pEax = (uint32_t)VERR_NOT_FOUND;
8038 }
8039 else
8040 *pEax = (uint32_t)VERR_NOT_FOUND;
8041 }
8042 else
8043 *pEax = (uint32_t)VERR_TOO_MUCH_DATA;
8044 *pEdx = VBOX_CPUID_RESP_GEN_EDX;
8045 *pEcx = VBOX_CPUID_RESP_GEN_ECX;
8046 *pEbx = VBOX_CPUID_RESP_GEN_EBX;
8047 break;
8048 }
8049
8050 default:
8051 LogFlow(("iemCpuIdVBoxCall: Invalid function %#x (%#x, %#x)\n", iFunction, *pEbx, *pEdx));
8052 *pEax = (uint32_t)VERR_INVALID_FUNCTION;
8053 *pEbx = (uint32_t)VERR_INVALID_FUNCTION;
8054 *pEcx = (uint32_t)VERR_INVALID_FUNCTION;
8055 *pEdx = (uint32_t)VERR_INVALID_FUNCTION;
8056 break;
8057 }
8058 return VINF_SUCCESS;
8059}
8060#endif /* VBOX_WITHOUT_CPUID_HOST_CALL */
8061
8062/**
8063 * Implements 'CPUID'.
8064 */
8065IEM_CIMPL_DEF_0(iemCImpl_cpuid)
8066{
8067 if (!IEM_IS_IN_GUEST(pVCpu))
8068 { /* probable */ }
8069 else if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
8070 {
8071 Log2(("cpuid: Guest intercept -> VM-exit\n"));
8072 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_CPUID, cbInstr);
8073 }
8074 else if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_CPUID))
8075 {
8076 Log2(("cpuid: Guest intercept -> #VMEXIT\n"));
8077 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
8078 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_CPUID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
8079 }
8080
8081
8082 uint32_t const uEax = pVCpu->cpum.GstCtx.eax;
8083 uint32_t const uEcx = pVCpu->cpum.GstCtx.ecx;
8084
8085#ifndef VBOX_WITHOUT_CPUID_HOST_CALL
8086 /*
8087 * CPUID host call backdoor.
8088 */
8089 if ( uEax == VBOX_CPUID_REQ_EAX_FIXED
8090 && (uEcx & VBOX_CPUID_REQ_ECX_FIXED_MASK) == VBOX_CPUID_REQ_ECX_FIXED
8091 && pVCpu->CTX_SUFF(pVM)->iem.s.fCpuIdHostCall)
8092 {
8093 VBOXSTRICTRC rcStrict = iemCpuIdVBoxCall(pVCpu, uEcx & VBOX_CPUID_REQ_ECX_FN_MASK,
8094 &pVCpu->cpum.GstCtx.eax, &pVCpu->cpum.GstCtx.ebx,
8095 &pVCpu->cpum.GstCtx.ecx, &pVCpu->cpum.GstCtx.edx);
8096 if (rcStrict != VINF_SUCCESS)
8097 return rcStrict;
8098 }
8099 /*
8100 * Regular CPUID.
8101 */
8102 else
8103#endif
8104 CPUMGetGuestCpuId(pVCpu, uEax, uEcx, pVCpu->cpum.GstCtx.cs.Attr.n.u1Long,
8105 &pVCpu->cpum.GstCtx.eax, &pVCpu->cpum.GstCtx.ebx, &pVCpu->cpum.GstCtx.ecx, &pVCpu->cpum.GstCtx.edx);
8106
8107 pVCpu->cpum.GstCtx.rax &= UINT32_C(0xffffffff);
8108 pVCpu->cpum.GstCtx.rbx &= UINT32_C(0xffffffff);
8109 pVCpu->cpum.GstCtx.rcx &= UINT32_C(0xffffffff);
8110 pVCpu->cpum.GstCtx.rdx &= UINT32_C(0xffffffff);
8111 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
8112
8113 pVCpu->iem.s.cPotentialExits++;
8114 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8115}
8116
8117
8118/**
8119 * Implements 'AAD'.
8120 *
8121 * @param bImm The immediate operand.
8122 */
8123IEM_CIMPL_DEF_1(iemCImpl_aad, uint8_t, bImm)
8124{
8125 uint16_t const ax = pVCpu->cpum.GstCtx.ax;
8126 uint8_t const al = (uint8_t)ax + (uint8_t)(ax >> 8) * bImm;
8127 pVCpu->cpum.GstCtx.ax = al;
8128 iemHlpUpdateArithEFlagsU8(pVCpu, al,
8129 X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF,
8130 X86_EFL_OF | X86_EFL_AF | X86_EFL_CF);
8131
8132 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8133}
8134
8135
8136/**
8137 * Implements 'AAM'.
8138 *
8139 * @param bImm The immediate operand. Cannot be 0.
8140 */
8141IEM_CIMPL_DEF_1(iemCImpl_aam, uint8_t, bImm)
8142{
8143 Assert(bImm != 0); /* #DE on 0 is handled in the decoder. */
8144
8145 uint16_t const ax = pVCpu->cpum.GstCtx.ax;
8146 uint8_t const al = (uint8_t)ax % bImm;
8147 uint8_t const ah = (uint8_t)ax / bImm;
8148 pVCpu->cpum.GstCtx.ax = (ah << 8) + al;
8149 iemHlpUpdateArithEFlagsU8(pVCpu, al,
8150 X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF,
8151 X86_EFL_OF | X86_EFL_AF | X86_EFL_CF);
8152
8153 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8154}
8155
8156
8157/**
8158 * Implements 'DAA'.
8159 */
8160IEM_CIMPL_DEF_0(iemCImpl_daa)
8161{
8162 uint8_t const al = pVCpu->cpum.GstCtx.al;
8163 bool const fCarry = pVCpu->cpum.GstCtx.eflags.Bits.u1CF;
8164
8165 if ( pVCpu->cpum.GstCtx.eflags.Bits.u1AF
8166 || (al & 0xf) >= 10)
8167 {
8168 pVCpu->cpum.GstCtx.al = al + 6;
8169 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 1;
8170 }
8171 else
8172 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 0;
8173
8174 if (al >= 0x9a || fCarry)
8175 {
8176 pVCpu->cpum.GstCtx.al += 0x60;
8177 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8178 }
8179 else
8180 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 0;
8181
8182 iemHlpUpdateArithEFlagsU8(pVCpu, pVCpu->cpum.GstCtx.al, X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF, X86_EFL_OF);
8183 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8184}
8185
8186
8187/**
8188 * Implements 'DAS'.
8189 */
8190IEM_CIMPL_DEF_0(iemCImpl_das)
8191{
8192 uint8_t const uInputAL = pVCpu->cpum.GstCtx.al;
8193 bool const fCarry = pVCpu->cpum.GstCtx.eflags.Bits.u1CF;
8194
8195 if ( pVCpu->cpum.GstCtx.eflags.Bits.u1AF
8196 || (uInputAL & 0xf) >= 10)
8197 {
8198 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 1;
8199 if (uInputAL < 6)
8200 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8201 pVCpu->cpum.GstCtx.al = uInputAL - 6;
8202 }
8203 else
8204 {
8205 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 0;
8206 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 0;
8207 }
8208
8209 if (uInputAL >= 0x9a || fCarry)
8210 {
8211 pVCpu->cpum.GstCtx.al -= 0x60;
8212 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8213 }
8214
8215 iemHlpUpdateArithEFlagsU8(pVCpu, pVCpu->cpum.GstCtx.al, X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF, X86_EFL_OF);
8216 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8217}
8218
8219
8220/**
8221 * Implements 'AAA'.
8222 */
8223IEM_CIMPL_DEF_0(iemCImpl_aaa)
8224{
8225 if (IEM_IS_GUEST_CPU_AMD(pVCpu))
8226 {
8227 if ( pVCpu->cpum.GstCtx.eflags.Bits.u1AF
8228 || (pVCpu->cpum.GstCtx.ax & 0xf) >= 10)
8229 {
8230 pVCpu->cpum.GstCtx.eflags.uBoth = iemAImpl_add_u16(pVCpu->cpum.GstCtx.eflags.uBoth, &pVCpu->cpum.GstCtx.ax, 0x106);
8231 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 1;
8232 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8233 }
8234 else
8235 {
8236 iemHlpUpdateArithEFlagsU16(pVCpu, pVCpu->cpum.GstCtx.ax, X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF, X86_EFL_OF);
8237 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 0;
8238 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 0;
8239 }
8240 pVCpu->cpum.GstCtx.ax &= UINT16_C(0xff0f);
8241 }
8242 else
8243 {
8244 if ( pVCpu->cpum.GstCtx.eflags.Bits.u1AF
8245 || (pVCpu->cpum.GstCtx.ax & 0xf) >= 10)
8246 {
8247 pVCpu->cpum.GstCtx.ax += UINT16_C(0x106);
8248 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 1;
8249 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8250 }
8251 else
8252 {
8253 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 0;
8254 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 0;
8255 }
8256 pVCpu->cpum.GstCtx.ax &= UINT16_C(0xff0f);
8257 iemHlpUpdateArithEFlagsU8(pVCpu, pVCpu->cpum.GstCtx.al, X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF, X86_EFL_OF);
8258 }
8259
8260 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8261}
8262
8263
8264/**
8265 * Implements 'AAS'.
8266 */
8267IEM_CIMPL_DEF_0(iemCImpl_aas)
8268{
8269 if (IEM_IS_GUEST_CPU_AMD(pVCpu))
8270 {
8271 if ( pVCpu->cpum.GstCtx.eflags.Bits.u1AF
8272 || (pVCpu->cpum.GstCtx.ax & 0xf) >= 10)
8273 {
8274 pVCpu->cpum.GstCtx.eflags.uBoth = iemAImpl_sub_u16(pVCpu->cpum.GstCtx.eflags.uBoth, &pVCpu->cpum.GstCtx.ax, 0x106);
8275 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 1;
8276 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8277 }
8278 else
8279 {
8280 iemHlpUpdateArithEFlagsU16(pVCpu, pVCpu->cpum.GstCtx.ax, X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF, X86_EFL_OF);
8281 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 0;
8282 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 0;
8283 }
8284 pVCpu->cpum.GstCtx.ax &= UINT16_C(0xff0f);
8285 }
8286 else
8287 {
8288 if ( pVCpu->cpum.GstCtx.eflags.Bits.u1AF
8289 || (pVCpu->cpum.GstCtx.ax & 0xf) >= 10)
8290 {
8291 pVCpu->cpum.GstCtx.ax -= UINT16_C(0x106);
8292 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 1;
8293 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 1;
8294 }
8295 else
8296 {
8297 pVCpu->cpum.GstCtx.eflags.Bits.u1AF = 0;
8298 pVCpu->cpum.GstCtx.eflags.Bits.u1CF = 0;
8299 }
8300 pVCpu->cpum.GstCtx.ax &= UINT16_C(0xff0f);
8301 iemHlpUpdateArithEFlagsU8(pVCpu, pVCpu->cpum.GstCtx.al, X86_EFL_SF | X86_EFL_ZF | X86_EFL_PF, X86_EFL_OF);
8302 }
8303
8304 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8305}
8306
8307
8308/**
8309 * Implements the 16-bit version of 'BOUND'.
8310 *
8311 * @note We have separate 16-bit and 32-bit variants of this function due to
8312 * the decoder using unsigned parameters, whereas we want signed one to
8313 * do the job. This is significant for a recompiler.
8314 */
8315IEM_CIMPL_DEF_3(iemCImpl_bound_16, int16_t, idxArray, int16_t, idxLowerBound, int16_t, idxUpperBound)
8316{
8317 /*
8318 * Check if the index is inside the bounds, otherwise raise #BR.
8319 */
8320 if ( idxArray >= idxLowerBound
8321 && idxArray <= idxUpperBound)
8322 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8323 return iemRaiseBoundRangeExceeded(pVCpu);
8324}
8325
8326
8327/**
8328 * Implements the 32-bit version of 'BOUND'.
8329 */
8330IEM_CIMPL_DEF_3(iemCImpl_bound_32, int32_t, idxArray, int32_t, idxLowerBound, int32_t, idxUpperBound)
8331{
8332 /*
8333 * Check if the index is inside the bounds, otherwise raise #BR.
8334 */
8335 if ( idxArray >= idxLowerBound
8336 && idxArray <= idxUpperBound)
8337 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8338 return iemRaiseBoundRangeExceeded(pVCpu);
8339}
8340
8341
8342
8343/*
8344 * Instantiate the various string operation combinations.
8345 */
8346#define OP_SIZE 8
8347#define ADDR_SIZE 16
8348#include "IEMAllCImplStrInstr-x86.cpp.h"
8349#define OP_SIZE 8
8350#define ADDR_SIZE 32
8351#include "IEMAllCImplStrInstr-x86.cpp.h"
8352#define OP_SIZE 8
8353#define ADDR_SIZE 64
8354#include "IEMAllCImplStrInstr-x86.cpp.h"
8355
8356#define OP_SIZE 16
8357#define ADDR_SIZE 16
8358#include "IEMAllCImplStrInstr-x86.cpp.h"
8359#define OP_SIZE 16
8360#define ADDR_SIZE 32
8361#include "IEMAllCImplStrInstr-x86.cpp.h"
8362#define OP_SIZE 16
8363#define ADDR_SIZE 64
8364#include "IEMAllCImplStrInstr-x86.cpp.h"
8365
8366#define OP_SIZE 32
8367#define ADDR_SIZE 16
8368#include "IEMAllCImplStrInstr-x86.cpp.h"
8369#define OP_SIZE 32
8370#define ADDR_SIZE 32
8371#include "IEMAllCImplStrInstr-x86.cpp.h"
8372#define OP_SIZE 32
8373#define ADDR_SIZE 64
8374#include "IEMAllCImplStrInstr-x86.cpp.h"
8375
8376#define OP_SIZE 64
8377#define ADDR_SIZE 32
8378#include "IEMAllCImplStrInstr-x86.cpp.h"
8379#define OP_SIZE 64
8380#define ADDR_SIZE 64
8381#include "IEMAllCImplStrInstr-x86.cpp.h"
8382
8383
8384/**
8385 * Implements 'XGETBV'.
8386 */
8387IEM_CIMPL_DEF_0(iemCImpl_xgetbv)
8388{
8389 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR4);
8390 if (pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSXSAVE)
8391 {
8392 uint32_t uEcx = pVCpu->cpum.GstCtx.ecx;
8393 switch (uEcx)
8394 {
8395 case 0:
8396 break;
8397
8398 case 1: /** @todo Implement XCR1 support. */
8399 default:
8400 Log(("xgetbv ecx=%RX32 -> #GP(0)\n", uEcx));
8401 return iemRaiseGeneralProtectionFault0(pVCpu);
8402
8403 }
8404 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_XCRx);
8405 pVCpu->cpum.GstCtx.rax = RT_LO_U32(pVCpu->cpum.GstCtx.aXcr[uEcx]);
8406 pVCpu->cpum.GstCtx.rdx = RT_HI_U32(pVCpu->cpum.GstCtx.aXcr[uEcx]);
8407
8408 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8409 }
8410 Log(("xgetbv CR4.OSXSAVE=0 -> UD\n"));
8411 return iemRaiseUndefinedOpcode(pVCpu);
8412}
8413
8414
8415/**
8416 * Implements 'XSETBV'.
8417 */
8418IEM_CIMPL_DEF_0(iemCImpl_xsetbv)
8419{
8420 if (pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSXSAVE)
8421 {
8422 /** @todo explain why this happens before the CPL check. */
8423 if (!IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_XSETBV))
8424 { /* probable */ }
8425 else
8426 {
8427 Log2(("xsetbv: Guest intercept -> #VMEXIT\n"));
8428 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
8429 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_XSETBV, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
8430 }
8431
8432 if (IEM_GET_CPL(pVCpu) == 0)
8433 {
8434 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_XCRx);
8435
8436 if (!IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
8437 { /* probable */ }
8438 else
8439 IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_XSETBV, cbInstr);
8440
8441 uint32_t uEcx = pVCpu->cpum.GstCtx.ecx;
8442 uint64_t uNewValue = RT_MAKE_U64(pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.edx);
8443 switch (uEcx)
8444 {
8445 case 0:
8446 {
8447 int rc = CPUMSetGuestXcr0(pVCpu, uNewValue);
8448 if (rc == VINF_SUCCESS)
8449 break;
8450 Assert(rc == VERR_CPUM_RAISE_GP_0);
8451 Log(("xsetbv ecx=%RX32 (newvalue=%RX64) -> #GP(0)\n", uEcx, uNewValue));
8452 return iemRaiseGeneralProtectionFault0(pVCpu);
8453 }
8454
8455 case 1: /** @todo Implement XCR1 support. */
8456 default:
8457 Log(("xsetbv ecx=%RX32 (newvalue=%RX64) -> #GP(0)\n", uEcx, uNewValue));
8458 return iemRaiseGeneralProtectionFault0(pVCpu);
8459
8460 }
8461
8462 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8463 }
8464
8465 Log(("xsetbv cpl=%u -> GP(0)\n", IEM_GET_CPL(pVCpu)));
8466 return iemRaiseGeneralProtectionFault0(pVCpu);
8467 }
8468 Log(("xsetbv CR4.OSXSAVE=0 -> UD\n"));
8469 return iemRaiseUndefinedOpcode(pVCpu);
8470}
8471
8472#ifndef RT_ARCH_ARM64
8473# ifdef IN_RING3
8474
8475/** Argument package for iemCImpl_cmpxchg16b_fallback_rendezvous_callback. */
8476struct IEMCIMPLCX16ARGS
8477{
8478 PRTUINT128U pu128Dst;
8479 PRTUINT128U pu128RaxRdx;
8480 PRTUINT128U pu128RbxRcx;
8481 uint32_t *pEFlags;
8482# ifdef VBOX_STRICT
8483 uint32_t cCalls;
8484# endif
8485};
8486
8487/**
8488 * @callback_method_impl{FNVMMEMTRENDEZVOUS,
8489 * Worker for iemCImpl_cmpxchg16b_fallback_rendezvous}
8490 */
8491static DECLCALLBACK(VBOXSTRICTRC) iemCImpl_cmpxchg16b_fallback_rendezvous_callback(PVM pVM, PVMCPUCC pVCpu, void *pvUser)
8492{
8493 RT_NOREF(pVM, pVCpu);
8494 struct IEMCIMPLCX16ARGS *pArgs = (struct IEMCIMPLCX16ARGS *)pvUser;
8495# ifdef VBOX_STRICT
8496 Assert(pArgs->cCalls == 0);
8497 pArgs->cCalls++;
8498# endif
8499
8500 iemAImpl_cmpxchg16b_fallback(pArgs->pu128Dst, pArgs->pu128RaxRdx, pArgs->pu128RbxRcx, pArgs->pEFlags);
8501 return VINF_SUCCESS;
8502}
8503
8504# endif /* IN_RING3 */
8505
8506/**
8507 * Implements 'CMPXCHG16B' fallback using rendezvous.
8508 */
8509IEM_CIMPL_DEF_5(iemCImpl_cmpxchg16b_fallback_rendezvous, PRTUINT128U, pu128Dst, PRTUINT128U, pu128RaxRdx,
8510 PRTUINT128U, pu128RbxRcx, uint32_t *, pEFlags, uint8_t, bUnmapInfo)
8511{
8512# ifdef IN_RING3
8513 struct IEMCIMPLCX16ARGS Args;
8514 Args.pu128Dst = pu128Dst;
8515 Args.pu128RaxRdx = pu128RaxRdx;
8516 Args.pu128RbxRcx = pu128RbxRcx;
8517 Args.pEFlags = pEFlags;
8518# ifdef VBOX_STRICT
8519 Args.cCalls = 0;
8520# endif
8521 VBOXSTRICTRC rcStrict = VMMR3EmtRendezvous(pVCpu->CTX_SUFF(pVM), VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE,
8522 iemCImpl_cmpxchg16b_fallback_rendezvous_callback, &Args);
8523 Assert(Args.cCalls == 1);
8524 if (rcStrict == VINF_SUCCESS)
8525 {
8526 /* Duplicated tail code. */
8527 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
8528 if (rcStrict == VINF_SUCCESS)
8529 {
8530 pVCpu->cpum.GstCtx.eflags.u = *pEFlags; /* IEM_MC_COMMIT_EFLAGS */
8531 if (!(*pEFlags & X86_EFL_ZF))
8532 {
8533 pVCpu->cpum.GstCtx.rax = pu128RaxRdx->s.Lo;
8534 pVCpu->cpum.GstCtx.rdx = pu128RaxRdx->s.Hi;
8535 }
8536 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8537 }
8538 }
8539 return rcStrict;
8540# else
8541 RT_NOREF(pVCpu, cbInstr, pu128Dst, pu128RaxRdx, pu128RbxRcx, pEFlags, bUnmapInfo);
8542 return VERR_IEM_ASPECT_NOT_IMPLEMENTED; /* This should get us to ring-3 for now. Should perhaps be replaced later. */
8543# endif
8544}
8545
8546#endif /* RT_ARCH_ARM64 */
8547
8548/**
8549 * Implements 'CLFLUSH' and 'CLFLUSHOPT'.
8550 *
8551 * This is implemented in C because it triggers a load like behaviour without
8552 * actually reading anything. Since that's not so common, it's implemented
8553 * here.
8554 *
8555 * @param iEffSeg The effective segment.
8556 * @param GCPtrEff The address of the image.
8557 */
8558IEM_CIMPL_DEF_2(iemCImpl_clflush_clflushopt, uint8_t, iEffSeg, RTGCPTR, GCPtrEff)
8559{
8560 /*
8561 * Pretend to do a load w/o reading (see also iemCImpl_monitor and iemMemMap).
8562 */
8563 VBOXSTRICTRC rcStrict = iemMemApplySegment(pVCpu, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, iEffSeg, 1, &GCPtrEff);
8564 if (rcStrict == VINF_SUCCESS)
8565 {
8566 RTGCPHYS GCPhysMem;
8567 /** @todo access size. */
8568 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, GCPtrEff, 1, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, &GCPhysMem);
8569 if (rcStrict == VINF_SUCCESS)
8570 {
8571#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
8572 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
8573 || !IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_VIRT_APIC_ACCESS))
8574 { /* probable */ }
8575 else
8576 {
8577 /*
8578 * CLFLUSH/CLFLUSHOPT does not access the memory, but flushes the cache-line
8579 * that contains the address. However, if the address falls in the APIC-access
8580 * page, the address flushed must instead be the corresponding address in the
8581 * virtual-APIC page.
8582 *
8583 * See Intel spec. 29.4.4 "Instruction-Specific Considerations".
8584 */
8585 rcStrict = iemVmxVirtApicAccessUnused(pVCpu, &GCPhysMem, 1, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA);
8586 if ( rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE
8587 && rcStrict != VINF_VMX_MODIFIES_BEHAVIOR)
8588 return rcStrict;
8589 }
8590#endif
8591 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8592 }
8593 }
8594
8595 return rcStrict;
8596}
8597
8598
8599/**
8600 * Implements 'FINIT' and 'FNINIT'.
8601 *
8602 * @param fCheckXcpts Whether to check for umasked pending exceptions or
8603 * not.
8604 */
8605IEM_CIMPL_DEF_1(iemCImpl_finit, bool, fCheckXcpts)
8606{
8607 /*
8608 * Exceptions.
8609 */
8610 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0);
8611 if (pVCpu->cpum.GstCtx.cr0 & (X86_CR0_EM | X86_CR0_TS))
8612 return iemRaiseDeviceNotAvailable(pVCpu);
8613
8614 iemFpuActualizeStateForChange(pVCpu);
8615 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_X87);
8616
8617 /* FINIT: Raise #MF on pending exception(s): */
8618 if (fCheckXcpts && (pVCpu->cpum.GstCtx.XState.x87.FSW & X86_FSW_ES))
8619 return iemRaiseMathFault(pVCpu);
8620
8621 /*
8622 * Reset the state.
8623 */
8624 PX86XSAVEAREA pXState = &pVCpu->cpum.GstCtx.XState;
8625
8626 /* Rotate the stack to account for changed TOS. */
8627 iemFpuRotateStackSetTop(&pXState->x87, 0);
8628
8629 pXState->x87.FCW = 0x37f;
8630 pXState->x87.FSW = 0;
8631 pXState->x87.FTW = 0x00; /* 0 - empty. */
8632 /** @todo Intel says the instruction and data pointers are not cleared on
8633 * 387, presume that 8087 and 287 doesn't do so either. */
8634 /** @todo test this stuff. */
8635 if (IEM_GET_TARGET_CPU(pVCpu) > IEMTARGETCPU_386)
8636 {
8637 pXState->x87.FPUDP = 0;
8638 pXState->x87.DS = 0; //??
8639 pXState->x87.Rsrvd2 = 0;
8640 pXState->x87.FPUIP = 0;
8641 pXState->x87.CS = 0; //??
8642 pXState->x87.Rsrvd1 = 0;
8643 }
8644 pXState->x87.FOP = 0;
8645
8646 iemHlpUsedFpu(pVCpu);
8647 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8648}
8649
8650
8651/**
8652 * Implements 'FXSAVE'.
8653 *
8654 * @param iEffSeg The effective segment.
8655 * @param GCPtrEff The address of the image.
8656 * @param enmEffOpSize The operand size (only REX.W really matters).
8657 */
8658IEM_CIMPL_DEF_3(iemCImpl_fxsave, uint8_t, iEffSeg, RTGCPTR, GCPtrEff, IEMMODE, enmEffOpSize)
8659{
8660 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX);
8661
8662 /** @todo check out bugref{1529} and AMD behaviour */
8663
8664 /*
8665 * Raise exceptions.
8666 */
8667 if (pVCpu->cpum.GstCtx.cr0 & (X86_CR0_TS | X86_CR0_EM))
8668 return iemRaiseDeviceNotAvailable(pVCpu);
8669
8670 /*
8671 * Access the memory.
8672 */
8673 uint8_t bUnmapInfo;
8674 void *pvMem512;
8675 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMem512, &bUnmapInfo, 512,
8676 iEffSeg, GCPtrEff, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE,
8677 15 | IEM_MEMMAP_F_ALIGN_GP | IEM_MEMMAP_F_ALIGN_GP_OR_AC);
8678 if (rcStrict != VINF_SUCCESS)
8679 return rcStrict;
8680 PX86FXSTATE pDst = (PX86FXSTATE)pvMem512;
8681 PCX86FXSTATE pSrc = &pVCpu->cpum.GstCtx.XState.x87;
8682
8683 /*
8684 * Store the registers.
8685 */
8686 /** @todo CPU/VM detection possible! If CR4.OSFXSR=0 MXCSR it's
8687 * implementation specific whether MXCSR and XMM0-XMM7 are saved. */
8688
8689 /* common for all formats */
8690 pDst->FCW = pSrc->FCW;
8691 pDst->FSW = pSrc->FSW;
8692 pDst->FTW = pSrc->FTW & UINT16_C(0xff);
8693 pDst->FOP = pSrc->FOP;
8694 pDst->MXCSR = pSrc->MXCSR;
8695 pDst->MXCSR_MASK = CPUMGetGuestMxCsrMask(pVCpu->CTX_SUFF(pVM));
8696 for (uint32_t i = 0; i < RT_ELEMENTS(pDst->aRegs); i++)
8697 {
8698 /** @todo Testcase: What actually happens to the 6 reserved bytes? I'm clearing
8699 * them for now... */
8700 pDst->aRegs[i].au32[0] = pSrc->aRegs[i].au32[0];
8701 pDst->aRegs[i].au32[1] = pSrc->aRegs[i].au32[1];
8702 pDst->aRegs[i].au32[2] = pSrc->aRegs[i].au32[2] & UINT32_C(0xffff);
8703 pDst->aRegs[i].au32[3] = 0;
8704 }
8705
8706 /* FPU IP, CS, DP and DS. */
8707 pDst->FPUIP = pSrc->FPUIP;
8708 pDst->CS = pSrc->CS;
8709 pDst->FPUDP = pSrc->FPUDP;
8710 pDst->DS = pSrc->DS;
8711 if (enmEffOpSize == IEMMODE_64BIT)
8712 {
8713 /* Save upper 16-bits of FPUIP (IP:CS:Rsvd1) and FPUDP (DP:DS:Rsvd2). */
8714 pDst->Rsrvd1 = pSrc->Rsrvd1;
8715 pDst->Rsrvd2 = pSrc->Rsrvd2;
8716 }
8717 else
8718 {
8719 pDst->Rsrvd1 = 0;
8720 pDst->Rsrvd2 = 0;
8721 }
8722
8723 /* XMM registers. Skipped in 64-bit CPL0 if EFER.FFXSR (AMD only) is set. */
8724 if ( !(pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_FFXSR)
8725 || !IEM_IS_64BIT_CODE(pVCpu)
8726 || IEM_GET_CPL(pVCpu) != 0)
8727 {
8728 uint32_t cXmmRegs = IEM_IS_64BIT_CODE(pVCpu) ? 16 : 8;
8729 for (uint32_t i = 0; i < cXmmRegs; i++)
8730 pDst->aXMM[i] = pSrc->aXMM[i];
8731 /** @todo Testcase: What happens to the reserved XMM registers? Untouched,
8732 * right? */
8733 }
8734
8735 /*
8736 * Commit the memory.
8737 */
8738 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
8739 if (rcStrict != VINF_SUCCESS)
8740 return rcStrict;
8741
8742 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8743}
8744
8745
8746/**
8747 * Implements 'FXRSTOR'.
8748 *
8749 * @param iEffSeg The effective segment register for @a GCPtrEff.
8750 * @param GCPtrEff The address of the image.
8751 * @param enmEffOpSize The operand size (only REX.W really matters).
8752 */
8753IEM_CIMPL_DEF_3(iemCImpl_fxrstor, uint8_t, iEffSeg, RTGCPTR, GCPtrEff, IEMMODE, enmEffOpSize)
8754{
8755 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX);
8756
8757 /** @todo check out bugref{1529} and AMD behaviour */
8758
8759 /*
8760 * Raise exceptions.
8761 */
8762 if (pVCpu->cpum.GstCtx.cr0 & (X86_CR0_TS | X86_CR0_EM))
8763 return iemRaiseDeviceNotAvailable(pVCpu);
8764
8765 /*
8766 * Access the memory.
8767 */
8768 uint8_t bUnmapInfo;
8769 void *pvMem512;
8770 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMem512, &bUnmapInfo, 512, iEffSeg, GCPtrEff, IEM_ACCESS_DATA_R,
8771 15 | IEM_MEMMAP_F_ALIGN_GP | IEM_MEMMAP_F_ALIGN_GP_OR_AC);
8772 if (rcStrict != VINF_SUCCESS)
8773 return rcStrict;
8774 PCX86FXSTATE pSrc = (PCX86FXSTATE)pvMem512;
8775 PX86FXSTATE pDst = &pVCpu->cpum.GstCtx.XState.x87;
8776
8777 /*
8778 * Check the state for stuff which will #GP(0).
8779 */
8780 uint32_t const fMXCSR = pSrc->MXCSR;
8781 uint32_t const fMXCSR_MASK = CPUMGetGuestMxCsrMask(pVCpu->CTX_SUFF(pVM));
8782 if (fMXCSR & ~fMXCSR_MASK)
8783 {
8784 Log(("fxrstor: MXCSR=%#x (MXCSR_MASK=%#x) -> #GP(0)\n", fMXCSR, fMXCSR_MASK));
8785 return iemRaiseGeneralProtectionFault0(pVCpu);
8786 }
8787
8788 /*
8789 * Load the registers.
8790 */
8791 /** @todo CPU/VM detection possible! If CR4.OSFXSR=0 MXCSR it's
8792 * implementation specific whether MXCSR and XMM0-XMM7 are
8793 * restored according to Intel.
8794 * AMD says MXCSR and XMM registers are never loaded if
8795 * CR4.OSFXSR=0.
8796 */
8797
8798 /* common for all formats */
8799 pDst->FCW = pSrc->FCW;
8800 pDst->FSW = pSrc->FSW;
8801 pDst->FTW = pSrc->FTW & UINT16_C(0xff);
8802 pDst->FOP = pSrc->FOP;
8803 pDst->MXCSR = fMXCSR;
8804 /* (MXCSR_MASK is read-only) */
8805 for (uint32_t i = 0; i < RT_ELEMENTS(pSrc->aRegs); i++)
8806 {
8807 pDst->aRegs[i].au32[0] = pSrc->aRegs[i].au32[0];
8808 pDst->aRegs[i].au32[1] = pSrc->aRegs[i].au32[1];
8809 pDst->aRegs[i].au32[2] = pSrc->aRegs[i].au32[2] & UINT32_C(0xffff);
8810 pDst->aRegs[i].au32[3] = 0;
8811 }
8812
8813 /* FPU IP, CS, DP and DS. */
8814 /** @todo AMD says this is only done if FSW.ES is set after loading. */
8815 if (enmEffOpSize == IEMMODE_64BIT)
8816 {
8817 pDst->FPUIP = pSrc->FPUIP;
8818 pDst->CS = pSrc->CS;
8819 pDst->Rsrvd1 = pSrc->Rsrvd1;
8820 pDst->FPUDP = pSrc->FPUDP;
8821 pDst->DS = pSrc->DS;
8822 pDst->Rsrvd2 = pSrc->Rsrvd2;
8823 }
8824 else
8825 {
8826 pDst->FPUIP = pSrc->FPUIP;
8827 pDst->CS = pSrc->CS;
8828 pDst->Rsrvd1 = 0;
8829 pDst->FPUDP = pSrc->FPUDP;
8830 pDst->DS = pSrc->DS;
8831 pDst->Rsrvd2 = 0;
8832 }
8833
8834 /* XMM registers. Skipped in 64-bit CPL0 if EFER.FFXSR (AMD only) is set.
8835 * Does not affect MXCSR, only registers.
8836 */
8837 if ( !(pVCpu->cpum.GstCtx.msrEFER & MSR_K6_EFER_FFXSR)
8838 || !IEM_IS_64BIT_CODE(pVCpu)
8839 || IEM_GET_CPL(pVCpu) != 0)
8840 {
8841 uint32_t cXmmRegs = IEM_IS_64BIT_CODE(pVCpu) ? 16 : 8;
8842 for (uint32_t i = 0; i < cXmmRegs; i++)
8843 pDst->aXMM[i] = pSrc->aXMM[i];
8844 }
8845
8846 pDst->FCW &= ~X86_FCW_ZERO_MASK | X86_FCW_IC_MASK; /* Intel 10980xe allows setting the IC bit. Win 3.11 CALC.EXE sets it. */
8847 iemFpuRecalcExceptionStatus(pDst);
8848
8849 if (pDst->FSW & X86_FSW_ES)
8850 Log11(("fxrstor: %04x:%08RX64: loading state with pending FPU exception (FSW=%#x)\n",
8851 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pSrc->FSW));
8852
8853 /*
8854 * Unmap the memory.
8855 */
8856 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
8857 if (rcStrict != VINF_SUCCESS)
8858 return rcStrict;
8859
8860 iemHlpUsedFpu(pVCpu);
8861 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
8862}
8863
8864
8865/**
8866 * Implements 'XSAVE'.
8867 *
8868 * @param iEffSeg The effective segment.
8869 * @param GCPtrEff The address of the image.
8870 * @param enmEffOpSize The operand size (only REX.W really matters).
8871 */
8872IEM_CIMPL_DEF_3(iemCImpl_xsave, uint8_t, iEffSeg, RTGCPTR, GCPtrEff, IEMMODE, enmEffOpSize)
8873{
8874 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX | CPUMCTX_EXTRN_OTHER_XSAVE | CPUMCTX_EXTRN_XCRx);
8875
8876 /*
8877 * Raise exceptions.
8878 */
8879 if (!(pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSXSAVE))
8880 return iemRaiseUndefinedOpcode(pVCpu);
8881 /* When in VMX non-root mode and XSAVE/XRSTOR is not enabled, it results in #UD. */
8882 if (RT_LIKELY( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
8883 || IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_XSAVES_XRSTORS)))
8884 { /* likely */ }
8885 else
8886 {
8887 Log(("xrstor: Not enabled for nested-guest execution -> #UD\n"));
8888 return iemRaiseUndefinedOpcode(pVCpu);
8889 }
8890 if (pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS)
8891 return iemRaiseDeviceNotAvailable(pVCpu);
8892
8893 /*
8894 * Calc the requested mask.
8895 */
8896 uint64_t const fReqComponents = RT_MAKE_U64(pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.edx) & pVCpu->cpum.GstCtx.aXcr[0];
8897 AssertLogRelReturn(!(fReqComponents & ~(XSAVE_C_X87 | XSAVE_C_SSE | XSAVE_C_YMM)), VERR_IEM_ASPECT_NOT_IMPLEMENTED);
8898 uint64_t const fXInUse = pVCpu->cpum.GstCtx.aXcr[0];
8899
8900/** @todo figure out the exact protocol for the memory access. Currently we
8901 * just need this crap to work halfways to make it possible to test
8902 * AVX instructions. */
8903/** @todo figure out the XINUSE and XMODIFIED */
8904
8905 /*
8906 * Access the x87 memory state.
8907 */
8908 /* The x87+SSE state. */
8909 uint8_t bUnmapInfoMem512;
8910 void *pvMem512;
8911 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMem512, &bUnmapInfoMem512, 512,
8912 iEffSeg, GCPtrEff, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE,
8913 63 | IEM_MEMMAP_F_ALIGN_GP | IEM_MEMMAP_F_ALIGN_GP_OR_AC);
8914 if (rcStrict != VINF_SUCCESS)
8915 return rcStrict;
8916 PX86FXSTATE pDst = (PX86FXSTATE)pvMem512;
8917 PCX86FXSTATE pSrc = &pVCpu->cpum.GstCtx.XState.x87;
8918
8919 /* The header. */
8920 uint8_t bUnmapInfoHdr;
8921 PX86XSAVEHDR pHdr;
8922 rcStrict = iemMemMap(pVCpu, (void **)&pHdr, &bUnmapInfoHdr, sizeof(pHdr),
8923 iEffSeg, GCPtrEff + 512, IEM_ACCESS_DATA_RW, 0 /* checked above */);
8924 if (rcStrict != VINF_SUCCESS)
8925 return rcStrict;
8926
8927 /*
8928 * Store the X87 state.
8929 */
8930 if (fReqComponents & XSAVE_C_X87)
8931 {
8932 /* common for all formats */
8933 pDst->FCW = pSrc->FCW;
8934 pDst->FSW = pSrc->FSW;
8935 pDst->FTW = pSrc->FTW & UINT16_C(0xff);
8936 pDst->FOP = pSrc->FOP;
8937 pDst->FPUIP = pSrc->FPUIP;
8938 pDst->CS = pSrc->CS;
8939 pDst->FPUDP = pSrc->FPUDP;
8940 pDst->DS = pSrc->DS;
8941 if (enmEffOpSize == IEMMODE_64BIT)
8942 {
8943 /* Save upper 16-bits of FPUIP (IP:CS:Rsvd1) and FPUDP (DP:DS:Rsvd2). */
8944 pDst->Rsrvd1 = pSrc->Rsrvd1;
8945 pDst->Rsrvd2 = pSrc->Rsrvd2;
8946 }
8947 else
8948 {
8949 pDst->Rsrvd1 = 0;
8950 pDst->Rsrvd2 = 0;
8951 }
8952 for (uint32_t i = 0; i < RT_ELEMENTS(pDst->aRegs); i++)
8953 {
8954 /** @todo Testcase: What actually happens to the 6 reserved bytes? I'm clearing
8955 * them for now... */
8956 pDst->aRegs[i].au32[0] = pSrc->aRegs[i].au32[0];
8957 pDst->aRegs[i].au32[1] = pSrc->aRegs[i].au32[1];
8958 pDst->aRegs[i].au32[2] = pSrc->aRegs[i].au32[2] & UINT32_C(0xffff);
8959 pDst->aRegs[i].au32[3] = 0;
8960 }
8961
8962 }
8963
8964 if (fReqComponents & (XSAVE_C_SSE | XSAVE_C_YMM))
8965 {
8966 pDst->MXCSR = pSrc->MXCSR;
8967 pDst->MXCSR_MASK = CPUMGetGuestMxCsrMask(pVCpu->CTX_SUFF(pVM));
8968 }
8969
8970 if (fReqComponents & XSAVE_C_SSE)
8971 {
8972 /* XMM registers. */
8973 uint32_t cXmmRegs = enmEffOpSize == IEMMODE_64BIT ? 16 : 8;
8974 for (uint32_t i = 0; i < cXmmRegs; i++)
8975 pDst->aXMM[i] = pSrc->aXMM[i];
8976 /** @todo Testcase: What happens to the reserved XMM registers? Untouched,
8977 * right? */
8978 }
8979
8980 /* Commit the x87 state bits. (probably wrong) */
8981 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfoMem512);
8982 if (rcStrict != VINF_SUCCESS)
8983 return rcStrict;
8984
8985 /*
8986 * Store AVX state.
8987 */
8988 if (fReqComponents & XSAVE_C_YMM)
8989 {
8990 /** @todo testcase: xsave64 vs xsave32 wrt XSAVE_C_YMM. */
8991 AssertLogRelReturn(pVCpu->cpum.GstCtx.aoffXState[XSAVE_C_YMM_BIT] != UINT16_MAX, VERR_IEM_IPE_9);
8992 uint8_t bUnmapInfoComp;
8993 PCX86XSAVEYMMHI pCompSrc = CPUMCTX_XSAVE_C_PTR(IEM_GET_CTX(pVCpu), XSAVE_C_YMM_BIT, PCX86XSAVEYMMHI);
8994 PX86XSAVEYMMHI pCompDst;
8995 rcStrict = iemMemMap(pVCpu, (void **)&pCompDst, &bUnmapInfoComp, sizeof(*pCompDst), iEffSeg,
8996 GCPtrEff + pVCpu->cpum.GstCtx.aoffXState[XSAVE_C_YMM_BIT],
8997 IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE, 0 /* checked above */);
8998 if (rcStrict != VINF_SUCCESS)
8999 return rcStrict;
9000
9001 uint32_t cXmmRegs = enmEffOpSize == IEMMODE_64BIT ? 16 : 8;
9002 for (uint32_t i = 0; i < cXmmRegs; i++)
9003 pCompDst->aYmmHi[i] = pCompSrc->aYmmHi[i];
9004
9005 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfoComp);
9006 if (rcStrict != VINF_SUCCESS)
9007 return rcStrict;
9008 }
9009
9010 /*
9011 * Update the header.
9012 */
9013 pHdr->bmXState = (pHdr->bmXState & ~fReqComponents)
9014 | (fReqComponents & fXInUse);
9015
9016 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfoHdr);
9017 if (rcStrict != VINF_SUCCESS)
9018 return rcStrict;
9019
9020 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9021}
9022
9023
9024/**
9025 * Implements 'XRSTOR'.
9026 *
9027 * @param iEffSeg The effective segment.
9028 * @param GCPtrEff The address of the image.
9029 * @param enmEffOpSize The operand size (only REX.W really matters).
9030 */
9031IEM_CIMPL_DEF_3(iemCImpl_xrstor, uint8_t, iEffSeg, RTGCPTR, GCPtrEff, IEMMODE, enmEffOpSize)
9032{
9033 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX | CPUMCTX_EXTRN_OTHER_XSAVE | CPUMCTX_EXTRN_XCRx);
9034
9035 /*
9036 * Raise exceptions.
9037 */
9038 if (!(pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSXSAVE))
9039 return iemRaiseUndefinedOpcode(pVCpu);
9040 /* When in VMX non-root mode and XSAVE/XRSTOR is not enabled, it results in #UD. */
9041 if (RT_LIKELY( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
9042 || IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_XSAVES_XRSTORS)))
9043 { /* likely */ }
9044 else
9045 {
9046 Log(("xrstor: Not enabled for nested-guest execution -> #UD\n"));
9047 return iemRaiseUndefinedOpcode(pVCpu);
9048 }
9049 if (pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS)
9050 return iemRaiseDeviceNotAvailable(pVCpu);
9051 if (GCPtrEff & 63)
9052 {
9053 /** @todo CPU/VM detection possible! \#AC might not be signal for
9054 * all/any misalignment sizes, intel says its an implementation detail. */
9055 if ( (pVCpu->cpum.GstCtx.cr0 & X86_CR0_AM)
9056 && pVCpu->cpum.GstCtx.eflags.Bits.u1AC
9057 && IEM_GET_CPL(pVCpu) == 3)
9058 return iemRaiseAlignmentCheckException(pVCpu);
9059 return iemRaiseGeneralProtectionFault0(pVCpu);
9060 }
9061
9062/** @todo figure out the exact protocol for the memory access. Currently we
9063 * just need this crap to work halfways to make it possible to test
9064 * AVX instructions. */
9065/** @todo figure out the XINUSE and XMODIFIED */
9066
9067 /*
9068 * Access the x87 memory state.
9069 */
9070 /* The x87+SSE state. */
9071 uint8_t bUnmapInfoMem512;
9072 void *pvMem512;
9073 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMem512, &bUnmapInfoMem512, 512, iEffSeg, GCPtrEff, IEM_ACCESS_DATA_R,
9074 63 | IEM_MEMMAP_F_ALIGN_GP | IEM_MEMMAP_F_ALIGN_GP_OR_AC);
9075 if (rcStrict != VINF_SUCCESS)
9076 return rcStrict;
9077 PCX86FXSTATE pSrc = (PCX86FXSTATE)pvMem512;
9078 PX86FXSTATE pDst = &pVCpu->cpum.GstCtx.XState.x87;
9079
9080 /*
9081 * Calc the requested mask
9082 */
9083 uint8_t bUnmapInfoHdr;
9084 PX86XSAVEHDR pHdrDst = &pVCpu->cpum.GstCtx.XState.Hdr;
9085 PCX86XSAVEHDR pHdrSrc;
9086 rcStrict = iemMemMap(pVCpu, (void **)&pHdrSrc, &bUnmapInfoHdr, sizeof(*pHdrSrc), iEffSeg, GCPtrEff + 512,
9087 IEM_ACCESS_DATA_R, 0 /* checked above */);
9088 if (rcStrict != VINF_SUCCESS)
9089 return rcStrict;
9090
9091 uint64_t const fReqComponents = RT_MAKE_U64(pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.edx) & pVCpu->cpum.GstCtx.aXcr[0];
9092 AssertLogRelReturn(!(fReqComponents & ~(XSAVE_C_X87 | XSAVE_C_SSE | XSAVE_C_YMM)), VERR_IEM_ASPECT_NOT_IMPLEMENTED);
9093 //uint64_t const fXInUse = pVCpu->cpum.GstCtx.aXcr[0];
9094 uint64_t const fRstorMask = pHdrSrc->bmXState;
9095 uint64_t const fCompMask = pHdrSrc->bmXComp;
9096
9097 AssertLogRelReturn(!(fCompMask & XSAVE_C_X), VERR_IEM_ASPECT_NOT_IMPLEMENTED);
9098
9099 uint32_t const cXmmRegs = enmEffOpSize == IEMMODE_64BIT ? 16 : 8;
9100
9101 /* We won't need this any longer. */
9102 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfoHdr);
9103 if (rcStrict != VINF_SUCCESS)
9104 return rcStrict;
9105
9106 /*
9107 * Load the X87 state.
9108 */
9109 if (fReqComponents & XSAVE_C_X87)
9110 {
9111 if (fRstorMask & XSAVE_C_X87)
9112 {
9113 pDst->FCW = pSrc->FCW;
9114 pDst->FSW = pSrc->FSW;
9115 pDst->FTW = pSrc->FTW & UINT16_C(0xff);
9116 pDst->FOP = pSrc->FOP;
9117 pDst->FPUIP = pSrc->FPUIP;
9118 pDst->CS = pSrc->CS;
9119 pDst->FPUDP = pSrc->FPUDP;
9120 pDst->DS = pSrc->DS;
9121 if (enmEffOpSize == IEMMODE_64BIT)
9122 {
9123 /* Load upper 16-bits of FPUIP (IP:CS:Rsvd1) and FPUDP (DP:DS:Rsvd2). */
9124 pDst->Rsrvd1 = pSrc->Rsrvd1;
9125 pDst->Rsrvd2 = pSrc->Rsrvd2;
9126 }
9127 else
9128 {
9129 pDst->Rsrvd1 = 0;
9130 pDst->Rsrvd2 = 0;
9131 }
9132 for (uint32_t i = 0; i < RT_ELEMENTS(pDst->aRegs); i++)
9133 {
9134 pDst->aRegs[i].au32[0] = pSrc->aRegs[i].au32[0];
9135 pDst->aRegs[i].au32[1] = pSrc->aRegs[i].au32[1];
9136 pDst->aRegs[i].au32[2] = pSrc->aRegs[i].au32[2] & UINT32_C(0xffff);
9137 pDst->aRegs[i].au32[3] = 0;
9138 }
9139
9140 pDst->FCW &= ~X86_FCW_ZERO_MASK | X86_FCW_IC_MASK; /* Intel 10980xe allows setting the IC bit. Win 3.11 CALC.EXE sets it. */
9141 iemFpuRecalcExceptionStatus(pDst);
9142
9143 if (pDst->FSW & X86_FSW_ES)
9144 Log11(("xrstor: %04x:%08RX64: loading state with pending FPU exception (FSW=%#x)\n",
9145 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pSrc->FSW));
9146 }
9147 else
9148 {
9149 pDst->FCW = 0x37f;
9150 pDst->FSW = 0;
9151 pDst->FTW = 0x00; /* 0 - empty. */
9152 pDst->FPUDP = 0;
9153 pDst->DS = 0; //??
9154 pDst->Rsrvd2= 0;
9155 pDst->FPUIP = 0;
9156 pDst->CS = 0; //??
9157 pDst->Rsrvd1= 0;
9158 pDst->FOP = 0;
9159 for (uint32_t i = 0; i < RT_ELEMENTS(pSrc->aRegs); i++)
9160 {
9161 pDst->aRegs[i].au32[0] = 0;
9162 pDst->aRegs[i].au32[1] = 0;
9163 pDst->aRegs[i].au32[2] = 0;
9164 pDst->aRegs[i].au32[3] = 0;
9165 }
9166 }
9167 pHdrDst->bmXState |= XSAVE_C_X87; /* playing safe for now */
9168 }
9169
9170 /* MXCSR */
9171 if (fReqComponents & (XSAVE_C_SSE | XSAVE_C_YMM))
9172 {
9173 if (fRstorMask & (XSAVE_C_SSE | XSAVE_C_YMM))
9174 pDst->MXCSR = pSrc->MXCSR;
9175 else
9176 pDst->MXCSR = 0x1f80;
9177 }
9178
9179 /* XMM registers. */
9180 if (fReqComponents & XSAVE_C_SSE)
9181 {
9182 if (fRstorMask & XSAVE_C_SSE)
9183 {
9184 for (uint32_t i = 0; i < cXmmRegs; i++)
9185 pDst->aXMM[i] = pSrc->aXMM[i];
9186 /** @todo Testcase: What happens to the reserved XMM registers? Untouched,
9187 * right? */
9188 }
9189 else
9190 {
9191 for (uint32_t i = 0; i < cXmmRegs; i++)
9192 {
9193 pDst->aXMM[i].au64[0] = 0;
9194 pDst->aXMM[i].au64[1] = 0;
9195 }
9196 }
9197 pHdrDst->bmXState |= XSAVE_C_SSE; /* playing safe for now */
9198 }
9199
9200 /* Unmap the x87 state bits (so we've don't run out of mapping). */
9201 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfoMem512);
9202 if (rcStrict != VINF_SUCCESS)
9203 return rcStrict;
9204
9205 /*
9206 * Restore AVX state.
9207 */
9208 if (fReqComponents & XSAVE_C_YMM)
9209 {
9210 AssertLogRelReturn(pVCpu->cpum.GstCtx.aoffXState[XSAVE_C_YMM_BIT] != UINT16_MAX, VERR_IEM_IPE_9);
9211 PX86XSAVEYMMHI pCompDst = CPUMCTX_XSAVE_C_PTR(IEM_GET_CTX(pVCpu), XSAVE_C_YMM_BIT, PX86XSAVEYMMHI);
9212
9213 if (fRstorMask & XSAVE_C_YMM)
9214 {
9215 /** @todo testcase: xsave64 vs xsave32 wrt XSAVE_C_YMM. */
9216 uint8_t bUnmapInfoComp;
9217 PCX86XSAVEYMMHI pCompSrc;
9218 rcStrict = iemMemMap(pVCpu, (void **)&pCompSrc, &bUnmapInfoComp, sizeof(*pCompDst),
9219 iEffSeg, GCPtrEff + pVCpu->cpum.GstCtx.aoffXState[XSAVE_C_YMM_BIT],
9220 IEM_ACCESS_DATA_R, 0 /* checked above */);
9221 if (rcStrict != VINF_SUCCESS)
9222 return rcStrict;
9223
9224 for (uint32_t i = 0; i < cXmmRegs; i++)
9225 {
9226 pCompDst->aYmmHi[i].au64[0] = pCompSrc->aYmmHi[i].au64[0];
9227 pCompDst->aYmmHi[i].au64[1] = pCompSrc->aYmmHi[i].au64[1];
9228 }
9229
9230 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfoComp);
9231 if (rcStrict != VINF_SUCCESS)
9232 return rcStrict;
9233 }
9234 else
9235 {
9236 for (uint32_t i = 0; i < cXmmRegs; i++)
9237 {
9238 pCompDst->aYmmHi[i].au64[0] = 0;
9239 pCompDst->aYmmHi[i].au64[1] = 0;
9240 }
9241 }
9242 pHdrDst->bmXState |= XSAVE_C_YMM; /* playing safe for now */
9243 }
9244
9245 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9246}
9247
9248
9249
9250
9251/**
9252 * Implements 'STMXCSR'.
9253 *
9254 * @param iEffSeg The effective segment register for @a GCPtrEff.
9255 * @param GCPtrEff The address of the image.
9256 */
9257IEM_CIMPL_DEF_2(iemCImpl_stmxcsr, uint8_t, iEffSeg, RTGCPTR, GCPtrEff)
9258{
9259 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX);
9260
9261 /*
9262 * Raise exceptions.
9263 */
9264 if ( !(pVCpu->cpum.GstCtx.cr0 & X86_CR0_EM)
9265 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSFXSR))
9266 {
9267 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS))
9268 {
9269 /*
9270 * Do the job.
9271 */
9272 VBOXSTRICTRC rcStrict = iemMemStoreDataU32(pVCpu, iEffSeg, GCPtrEff, pVCpu->cpum.GstCtx.XState.x87.MXCSR);
9273 if (rcStrict == VINF_SUCCESS)
9274 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9275 return rcStrict;
9276 }
9277 return iemRaiseDeviceNotAvailable(pVCpu);
9278 }
9279 return iemRaiseUndefinedOpcode(pVCpu);
9280}
9281
9282
9283/**
9284 * Implements 'VSTMXCSR'.
9285 *
9286 * @param iEffSeg The effective segment register for @a GCPtrEff.
9287 * @param GCPtrEff The address of the image.
9288 */
9289IEM_CIMPL_DEF_2(iemCImpl_vstmxcsr, uint8_t, iEffSeg, RTGCPTR, GCPtrEff)
9290{
9291 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX | CPUMCTX_EXTRN_XCRx);
9292
9293 /*
9294 * Raise exceptions.
9295 */
9296 if ( ( !IEM_IS_GUEST_CPU_AMD(pVCpu)
9297 ? (pVCpu->cpum.GstCtx.aXcr[0] & (XSAVE_C_SSE | XSAVE_C_YMM)) == (XSAVE_C_SSE | XSAVE_C_YMM)
9298 : !(pVCpu->cpum.GstCtx.cr0 & X86_CR0_EM)) /* AMD Jaguar CPU (f0x16,m0,s1) behaviour */
9299 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSXSAVE))
9300 {
9301 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS))
9302 {
9303 /*
9304 * Do the job.
9305 */
9306 VBOXSTRICTRC rcStrict = iemMemStoreDataU32(pVCpu, iEffSeg, GCPtrEff, pVCpu->cpum.GstCtx.XState.x87.MXCSR);
9307 if (rcStrict == VINF_SUCCESS)
9308 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9309 return rcStrict;
9310 }
9311 return iemRaiseDeviceNotAvailable(pVCpu);
9312 }
9313 return iemRaiseUndefinedOpcode(pVCpu);
9314}
9315
9316
9317/**
9318 * Implements 'LDMXCSR'.
9319 *
9320 * @param iEffSeg The effective segment register for @a GCPtrEff.
9321 * @param GCPtrEff The address of the image.
9322 */
9323IEM_CIMPL_DEF_2(iemCImpl_ldmxcsr, uint8_t, iEffSeg, RTGCPTR, GCPtrEff)
9324{
9325 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX);
9326
9327 /*
9328 * Raise exceptions.
9329 */
9330 /** @todo testcase - order of LDMXCSR faults. Does \#PF, \#GP and \#SS
9331 * happen after or before \#UD and \#EM? */
9332 if ( !(pVCpu->cpum.GstCtx.cr0 & X86_CR0_EM)
9333 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSFXSR))
9334 {
9335 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS))
9336 {
9337 /*
9338 * Do the job.
9339 */
9340 uint32_t fNewMxCsr;
9341 VBOXSTRICTRC rcStrict = iemMemFetchDataU32(pVCpu, &fNewMxCsr, iEffSeg, GCPtrEff);
9342 if (rcStrict == VINF_SUCCESS)
9343 {
9344 uint32_t const fMxCsrMask = CPUMGetGuestMxCsrMask(pVCpu->CTX_SUFF(pVM));
9345 if (!(fNewMxCsr & ~fMxCsrMask))
9346 {
9347 pVCpu->cpum.GstCtx.XState.x87.MXCSR = fNewMxCsr;
9348 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9349 }
9350 Log(("ldmxcsr: New MXCSR=%#RX32 & ~MASK=%#RX32 = %#RX32 -> #GP(0)\n",
9351 fNewMxCsr, fMxCsrMask, fNewMxCsr & ~fMxCsrMask));
9352 return iemRaiseGeneralProtectionFault0(pVCpu);
9353 }
9354 return rcStrict;
9355 }
9356 return iemRaiseDeviceNotAvailable(pVCpu);
9357 }
9358 return iemRaiseUndefinedOpcode(pVCpu);
9359}
9360
9361
9362/**
9363 * Implements 'VSTMXCSR'.
9364 *
9365 * @param iEffSeg The effective segment register for @a GCPtrEff.
9366 * @param GCPtrEff The address of the image.
9367 */
9368IEM_CIMPL_DEF_2(iemCImpl_vldmxcsr, uint8_t, iEffSeg, RTGCPTR, GCPtrEff)
9369{
9370 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX | CPUMCTX_EXTRN_XCRx);
9371
9372 /*
9373 * Raise exceptions.
9374 */
9375 if ( ( !IEM_IS_GUEST_CPU_AMD(pVCpu)
9376 ? (pVCpu->cpum.GstCtx.aXcr[0] & (XSAVE_C_SSE | XSAVE_C_YMM)) == (XSAVE_C_SSE | XSAVE_C_YMM)
9377 : !(pVCpu->cpum.GstCtx.cr0 & X86_CR0_EM))
9378 && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_OSXSAVE))
9379 {
9380 if (!(pVCpu->cpum.GstCtx.cr0 & X86_CR0_TS))
9381 {
9382 /*
9383 * Do the job.
9384 */
9385 uint32_t fNewMxCsr;
9386 VBOXSTRICTRC rcStrict = iemMemFetchDataU32(pVCpu, &fNewMxCsr, iEffSeg, GCPtrEff);
9387 if (rcStrict == VINF_SUCCESS)
9388 {
9389 uint32_t const fMxCsrMask = CPUMGetGuestMxCsrMask(pVCpu->CTX_SUFF(pVM));
9390 if (!(fNewMxCsr & ~fMxCsrMask))
9391 {
9392 pVCpu->cpum.GstCtx.XState.x87.MXCSR = fNewMxCsr;
9393 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9394 }
9395 Log(("ldmxcsr: New MXCSR=%#RX32 & ~MASK=%#RX32 = %#RX32 -> #GP(0)\n",
9396 fNewMxCsr, fMxCsrMask, fNewMxCsr & ~fMxCsrMask));
9397 return iemRaiseGeneralProtectionFault0(pVCpu);
9398 }
9399 return rcStrict;
9400 }
9401 return iemRaiseDeviceNotAvailable(pVCpu);
9402 }
9403 return iemRaiseUndefinedOpcode(pVCpu);
9404}
9405
9406
9407/**
9408 * Commmon routine for fnstenv and fnsave.
9409 *
9410 * @param pVCpu The cross context virtual CPU structure of the calling thread.
9411 * @param enmEffOpSize The effective operand size.
9412 * @param uPtr Where to store the state.
9413 */
9414static void iemCImplCommonFpuStoreEnv(PVMCPUCC pVCpu, IEMMODE enmEffOpSize, RTPTRUNION uPtr)
9415{
9416 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87);
9417 PCX86FXSTATE pSrcX87 = &pVCpu->cpum.GstCtx.XState.x87;
9418 if (enmEffOpSize == IEMMODE_16BIT)
9419 {
9420 uPtr.pu16[0] = pSrcX87->FCW;
9421 uPtr.pu16[1] = pSrcX87->FSW;
9422 uPtr.pu16[2] = iemFpuCalcFullFtw(pSrcX87);
9423 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
9424 {
9425 /** @todo Testcase: How does this work when the FPUIP/CS was saved in
9426 * protected mode or long mode and we save it in real mode? And vice
9427 * versa? And with 32-bit operand size? I think CPU is storing the
9428 * effective address ((CS << 4) + IP) in the offset register and not
9429 * doing any address calculations here. */
9430 uPtr.pu16[3] = (uint16_t)pSrcX87->FPUIP;
9431 uPtr.pu16[4] = ((pSrcX87->FPUIP >> 4) & UINT16_C(0xf000)) | pSrcX87->FOP;
9432 uPtr.pu16[5] = (uint16_t)pSrcX87->FPUDP;
9433 uPtr.pu16[6] = (pSrcX87->FPUDP >> 4) & UINT16_C(0xf000);
9434 }
9435 else
9436 {
9437 uPtr.pu16[3] = pSrcX87->FPUIP;
9438 uPtr.pu16[4] = pSrcX87->CS;
9439 uPtr.pu16[5] = pSrcX87->FPUDP;
9440 uPtr.pu16[6] = pSrcX87->DS;
9441 }
9442 }
9443 else
9444 {
9445 /** @todo Testcase: what is stored in the "gray" areas? (figure 8-9 and 8-10) */
9446 uPtr.pu16[0*2] = pSrcX87->FCW;
9447 uPtr.pu16[0*2+1] = 0xffff; /* (0xffff observed on intel skylake.) */
9448 uPtr.pu16[1*2] = pSrcX87->FSW;
9449 uPtr.pu16[1*2+1] = 0xffff;
9450 uPtr.pu16[2*2] = iemFpuCalcFullFtw(pSrcX87);
9451 uPtr.pu16[2*2+1] = 0xffff;
9452 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
9453 {
9454 uPtr.pu16[3*2] = (uint16_t)pSrcX87->FPUIP;
9455 uPtr.pu32[4] = ((pSrcX87->FPUIP & UINT32_C(0xffff0000)) >> 4) | pSrcX87->FOP;
9456 uPtr.pu16[5*2] = (uint16_t)pSrcX87->FPUDP;
9457 uPtr.pu32[6] = (pSrcX87->FPUDP & UINT32_C(0xffff0000)) >> 4;
9458 }
9459 else
9460 {
9461 uPtr.pu32[3] = pSrcX87->FPUIP;
9462 uPtr.pu16[4*2] = pSrcX87->CS;
9463 uPtr.pu16[4*2+1] = pSrcX87->FOP;
9464 uPtr.pu32[5] = pSrcX87->FPUDP;
9465 uPtr.pu16[6*2] = pSrcX87->DS;
9466 uPtr.pu16[6*2+1] = 0xffff;
9467 }
9468 }
9469}
9470
9471
9472/**
9473 * Commmon routine for fldenv and frstor
9474 *
9475 * @param pVCpu The cross context virtual CPU structure of the calling thread.
9476 * @param enmEffOpSize The effective operand size.
9477 * @param uPtr Where to store the state.
9478 */
9479static void iemCImplCommonFpuRestoreEnv(PVMCPUCC pVCpu, IEMMODE enmEffOpSize, RTCPTRUNION uPtr)
9480{
9481 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87);
9482 PX86FXSTATE pDstX87 = &pVCpu->cpum.GstCtx.XState.x87;
9483 if (enmEffOpSize == IEMMODE_16BIT)
9484 {
9485 pDstX87->FCW = uPtr.pu16[0];
9486 pDstX87->FSW = uPtr.pu16[1];
9487 pDstX87->FTW = uPtr.pu16[2];
9488 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
9489 {
9490 pDstX87->FPUIP = uPtr.pu16[3] | ((uint32_t)(uPtr.pu16[4] & UINT16_C(0xf000)) << 4);
9491 pDstX87->FPUDP = uPtr.pu16[5] | ((uint32_t)(uPtr.pu16[6] & UINT16_C(0xf000)) << 4);
9492 pDstX87->FOP = uPtr.pu16[4] & UINT16_C(0x07ff);
9493 pDstX87->CS = 0;
9494 pDstX87->Rsrvd1= 0;
9495 pDstX87->DS = 0;
9496 pDstX87->Rsrvd2= 0;
9497 }
9498 else
9499 {
9500 pDstX87->FPUIP = uPtr.pu16[3];
9501 pDstX87->CS = uPtr.pu16[4];
9502 pDstX87->Rsrvd1= 0;
9503 pDstX87->FPUDP = uPtr.pu16[5];
9504 pDstX87->DS = uPtr.pu16[6];
9505 pDstX87->Rsrvd2= 0;
9506 /** @todo Testcase: Is FOP cleared when doing 16-bit protected mode fldenv? */
9507 }
9508 }
9509 else
9510 {
9511 pDstX87->FCW = uPtr.pu16[0*2];
9512 pDstX87->FSW = uPtr.pu16[1*2];
9513 pDstX87->FTW = uPtr.pu16[2*2];
9514 if (IEM_IS_REAL_OR_V86_MODE(pVCpu))
9515 {
9516 pDstX87->FPUIP = uPtr.pu16[3*2] | ((uPtr.pu32[4] & UINT32_C(0x0ffff000)) << 4);
9517 pDstX87->FOP = uPtr.pu32[4] & UINT16_C(0x07ff);
9518 pDstX87->FPUDP = uPtr.pu16[5*2] | ((uPtr.pu32[6] & UINT32_C(0x0ffff000)) << 4);
9519 pDstX87->CS = 0;
9520 pDstX87->Rsrvd1= 0;
9521 pDstX87->DS = 0;
9522 pDstX87->Rsrvd2= 0;
9523 }
9524 else
9525 {
9526 pDstX87->FPUIP = uPtr.pu32[3];
9527 pDstX87->CS = uPtr.pu16[4*2];
9528 pDstX87->Rsrvd1= 0;
9529 pDstX87->FOP = uPtr.pu16[4*2+1];
9530 pDstX87->FPUDP = uPtr.pu32[5];
9531 pDstX87->DS = uPtr.pu16[6*2];
9532 pDstX87->Rsrvd2= 0;
9533 }
9534 }
9535
9536 /* Make adjustments. */
9537 pDstX87->FTW = iemFpuCompressFtw(pDstX87->FTW);
9538#ifdef LOG_ENABLED
9539 uint16_t const fOldFsw = pDstX87->FSW;
9540#endif
9541 pDstX87->FCW &= ~X86_FCW_ZERO_MASK | X86_FCW_IC_MASK; /* Intel 10980xe allows setting the IC bit. Win 3.11 CALC.EXE sets it. */
9542 iemFpuRecalcExceptionStatus(pDstX87);
9543#ifdef LOG_ENABLED
9544 if ((pDstX87->FSW & X86_FSW_ES) ^ (fOldFsw & X86_FSW_ES))
9545 Log11(("iemCImplCommonFpuRestoreEnv: %04x:%08RX64: %s FPU exception (FCW=%#x FSW=%#x -> %#x)\n",
9546 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, fOldFsw & X86_FSW_ES ? "Suppressed" : "Raised",
9547 pDstX87->FCW, fOldFsw, pDstX87->FSW));
9548#endif
9549
9550 /** @todo Testcase: Check if ES and/or B are automatically cleared if no
9551 * exceptions are pending after loading the saved state? */
9552}
9553
9554
9555/**
9556 * Implements 'FNSTENV'.
9557 *
9558 * @param enmEffOpSize The operand size (only REX.W really matters).
9559 * @param iEffSeg The effective segment register for @a GCPtrEffDst.
9560 * @param GCPtrEffDst The address of the image.
9561 */
9562IEM_CIMPL_DEF_3(iemCImpl_fnstenv, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
9563{
9564 uint8_t bUnmapInfo;
9565 RTPTRUNION uPtr;
9566 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &uPtr.pv, &bUnmapInfo, enmEffOpSize == IEMMODE_16BIT ? 14 : 28,
9567 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE,
9568 enmEffOpSize == IEMMODE_16BIT ? 1 : 3 /** @todo ? */);
9569 if (rcStrict != VINF_SUCCESS)
9570 return rcStrict;
9571
9572 iemCImplCommonFpuStoreEnv(pVCpu, enmEffOpSize, uPtr);
9573
9574 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
9575 if (rcStrict != VINF_SUCCESS)
9576 return rcStrict;
9577
9578 /* Mask all math exceptions. Any possibly pending exceptions will be cleared. */
9579 PX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87;
9580 pFpuCtx->FCW |= X86_FCW_XCPT_MASK;
9581#ifdef LOG_ENABLED
9582 uint16_t fOldFsw = pFpuCtx->FSW;
9583#endif
9584 iemFpuRecalcExceptionStatus(pFpuCtx);
9585#ifdef LOG_ENABLED
9586 if ((pFpuCtx->FSW & X86_FSW_ES) ^ (fOldFsw & X86_FSW_ES))
9587 Log11(("fnstenv: %04x:%08RX64: %s FPU exception (FCW=%#x, FSW %#x -> %#x)\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
9588 fOldFsw & X86_FSW_ES ? "Suppressed" : "Raised", pFpuCtx->FCW, fOldFsw, pFpuCtx->FSW));
9589#endif
9590
9591 iemHlpUsedFpu(pVCpu);
9592
9593 /* Note: C0, C1, C2 and C3 are documented as undefined, we leave them untouched! */
9594 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9595}
9596
9597
9598/**
9599 * Implements 'FNSAVE'.
9600 *
9601 * @param enmEffOpSize The operand size.
9602 * @param iEffSeg The effective segment register for @a GCPtrEffDst.
9603 * @param GCPtrEffDst The address of the image.
9604 */
9605IEM_CIMPL_DEF_3(iemCImpl_fnsave, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst)
9606{
9607 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87);
9608
9609 uint8_t bUnmapInfo;
9610 RTPTRUNION uPtr;
9611 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &uPtr.pv, &bUnmapInfo, enmEffOpSize == IEMMODE_16BIT ? 94 : 108,
9612 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_W | IEM_ACCESS_PARTIAL_WRITE, 3 /** @todo ? */);
9613 if (rcStrict != VINF_SUCCESS)
9614 return rcStrict;
9615
9616 PX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87;
9617 iemCImplCommonFpuStoreEnv(pVCpu, enmEffOpSize, uPtr);
9618 PRTFLOAT80U paRegs = (PRTFLOAT80U)(uPtr.pu8 + (enmEffOpSize == IEMMODE_16BIT ? 14 : 28));
9619 for (uint32_t i = 0; i < RT_ELEMENTS(pFpuCtx->aRegs); i++)
9620 {
9621 paRegs[i].au32[0] = pFpuCtx->aRegs[i].au32[0];
9622 paRegs[i].au32[1] = pFpuCtx->aRegs[i].au32[1];
9623 paRegs[i].au16[4] = pFpuCtx->aRegs[i].au16[4];
9624 }
9625
9626 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
9627 if (rcStrict != VINF_SUCCESS)
9628 return rcStrict;
9629
9630 /* Rotate the stack to account for changed TOS. */
9631 iemFpuRotateStackSetTop(pFpuCtx, 0);
9632
9633 /*
9634 * Re-initialize the FPU context.
9635 */
9636 pFpuCtx->FCW = 0x37f;
9637 pFpuCtx->FSW = 0;
9638 pFpuCtx->FTW = 0x00; /* 0 - empty */
9639 pFpuCtx->FPUDP = 0;
9640 pFpuCtx->DS = 0;
9641 pFpuCtx->Rsrvd2= 0;
9642 pFpuCtx->FPUIP = 0;
9643 pFpuCtx->CS = 0;
9644 pFpuCtx->Rsrvd1= 0;
9645 pFpuCtx->FOP = 0;
9646
9647 iemHlpUsedFpu(pVCpu);
9648 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9649}
9650
9651
9652
9653/**
9654 * Implements 'FLDENV'.
9655 *
9656 * @param enmEffOpSize The operand size (only REX.W really matters).
9657 * @param iEffSeg The effective segment register for @a GCPtrEffSrc.
9658 * @param GCPtrEffSrc The address of the image.
9659 */
9660IEM_CIMPL_DEF_3(iemCImpl_fldenv, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
9661{
9662 uint8_t bUnmapInfo;
9663 RTCPTRUNION uPtr;
9664 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, (void **)&uPtr.pv, &bUnmapInfo, enmEffOpSize == IEMMODE_16BIT ? 14 : 28,
9665 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R,
9666 enmEffOpSize == IEMMODE_16BIT ? 1 : 3 /** @todo ?*/);
9667 if (rcStrict != VINF_SUCCESS)
9668 return rcStrict;
9669
9670 iemCImplCommonFpuRestoreEnv(pVCpu, enmEffOpSize, uPtr);
9671
9672 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
9673 if (rcStrict != VINF_SUCCESS)
9674 return rcStrict;
9675
9676 iemHlpUsedFpu(pVCpu);
9677 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9678}
9679
9680
9681/**
9682 * Implements 'FRSTOR'.
9683 *
9684 * @param enmEffOpSize The operand size.
9685 * @param iEffSeg The effective segment register for @a GCPtrEffSrc.
9686 * @param GCPtrEffSrc The address of the image.
9687 */
9688IEM_CIMPL_DEF_3(iemCImpl_frstor, IEMMODE, enmEffOpSize, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
9689{
9690 uint8_t bUnmapInfo;
9691 RTCPTRUNION uPtr;
9692 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, (void **)&uPtr.pv, &bUnmapInfo, enmEffOpSize == IEMMODE_16BIT ? 94 : 108,
9693 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R, 3 /** @todo ?*/ );
9694 if (rcStrict != VINF_SUCCESS)
9695 return rcStrict;
9696
9697 PX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87;
9698 iemCImplCommonFpuRestoreEnv(pVCpu, enmEffOpSize, uPtr);
9699 PCRTFLOAT80U paRegs = (PCRTFLOAT80U)(uPtr.pu8 + (enmEffOpSize == IEMMODE_16BIT ? 14 : 28));
9700 for (uint32_t i = 0; i < RT_ELEMENTS(pFpuCtx->aRegs); i++)
9701 {
9702 pFpuCtx->aRegs[i].au32[0] = paRegs[i].au32[0];
9703 pFpuCtx->aRegs[i].au32[1] = paRegs[i].au32[1];
9704 pFpuCtx->aRegs[i].au32[2] = paRegs[i].au16[4];
9705 pFpuCtx->aRegs[i].au32[3] = 0;
9706 }
9707
9708 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
9709 if (rcStrict != VINF_SUCCESS)
9710 return rcStrict;
9711
9712 iemHlpUsedFpu(pVCpu);
9713 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9714}
9715
9716
9717/**
9718 * Implements 'FLDCW'.
9719 *
9720 * @param u16Fcw The new FCW.
9721 */
9722IEM_CIMPL_DEF_1(iemCImpl_fldcw, uint16_t, u16Fcw)
9723{
9724 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87);
9725
9726 /** @todo Testcase: Check what happens when trying to load X86_FCW_PC_RSVD. */
9727 /** @todo Testcase: Try see what happens when trying to set undefined bits
9728 * (other than 6 and 7). Currently ignoring them. */
9729 /** @todo Testcase: Test that it raises and loweres the FPU exception bits
9730 * according to FSW. (This is what is currently implemented.) */
9731 PX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87;
9732 pFpuCtx->FCW = u16Fcw & (~X86_FCW_ZERO_MASK | X86_FCW_IC_MASK); /* Intel 10980xe allows setting the IC bit. Win 3.11 CALC.EXE sets it. */
9733#ifdef LOG_ENABLED
9734 uint16_t fOldFsw = pFpuCtx->FSW;
9735#endif
9736 iemFpuRecalcExceptionStatus(pFpuCtx);
9737#ifdef LOG_ENABLED
9738 if ((pFpuCtx->FSW & X86_FSW_ES) ^ (fOldFsw & X86_FSW_ES))
9739 Log11(("fldcw: %04x:%08RX64: %s FPU exception (FCW=%#x, FSW %#x -> %#x)\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
9740 fOldFsw & X86_FSW_ES ? "Suppressed" : "Raised", pFpuCtx->FCW, fOldFsw, pFpuCtx->FSW));
9741#endif
9742
9743 /* Note: C0, C1, C2 and C3 are documented as undefined, we leave them untouched! */
9744 iemHlpUsedFpu(pVCpu);
9745 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9746}
9747
9748
9749
9750/**
9751 * Implements the underflow case of fxch.
9752 *
9753 * @param iStReg The other stack register.
9754 * @param uFpuOpcode The FPU opcode (for simplicity).
9755 */
9756IEM_CIMPL_DEF_2(iemCImpl_fxch_underflow, uint8_t, iStReg, uint16_t, uFpuOpcode)
9757{
9758 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87);
9759
9760 PX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87;
9761 unsigned const iReg1 = X86_FSW_TOP_GET(pFpuCtx->FSW);
9762 unsigned const iReg2 = (iReg1 + iStReg) & X86_FSW_TOP_SMASK;
9763 Assert(!(RT_BIT(iReg1) & pFpuCtx->FTW) || !(RT_BIT(iReg2) & pFpuCtx->FTW));
9764
9765 /** @todo Testcase: fxch underflow. Making assumptions that underflowed
9766 * registers are read as QNaN and then exchanged. This could be
9767 * wrong... */
9768 if (pFpuCtx->FCW & X86_FCW_IM)
9769 {
9770 if (RT_BIT(iReg1) & pFpuCtx->FTW)
9771 {
9772 if (RT_BIT(iReg2) & pFpuCtx->FTW)
9773 iemFpuStoreQNan(&pFpuCtx->aRegs[0].r80);
9774 else
9775 pFpuCtx->aRegs[0].r80 = pFpuCtx->aRegs[iStReg].r80;
9776 iemFpuStoreQNan(&pFpuCtx->aRegs[iStReg].r80);
9777 }
9778 else
9779 {
9780 pFpuCtx->aRegs[iStReg].r80 = pFpuCtx->aRegs[0].r80;
9781 iemFpuStoreQNan(&pFpuCtx->aRegs[0].r80);
9782 }
9783 pFpuCtx->FSW &= ~X86_FSW_C_MASK;
9784 pFpuCtx->FSW |= X86_FSW_C1 | X86_FSW_IE | X86_FSW_SF;
9785 }
9786 else
9787 {
9788 /* raise underflow exception, don't change anything. */
9789 pFpuCtx->FSW &= ~(X86_FSW_TOP_MASK | X86_FSW_XCPT_MASK);
9790 pFpuCtx->FSW |= X86_FSW_C1 | X86_FSW_IE | X86_FSW_SF | X86_FSW_ES | X86_FSW_B;
9791 Log11(("fxch: %04x:%08RX64: Underflow exception (FSW=%#x)\n",
9792 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pFpuCtx->FSW));
9793 }
9794
9795 iemFpuUpdateOpcodeAndIpWorkerEx(pVCpu, pFpuCtx, uFpuOpcode);
9796 iemHlpUsedFpu(pVCpu);
9797 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9798}
9799
9800
9801/**
9802 * Implements 'FCOMI', 'FCOMIP', 'FUCOMI', and 'FUCOMIP'.
9803 *
9804 * @param iStReg The other stack register.
9805 * @param fUCmp true for FUCOMI[P], false for FCOMI[P].
9806 * @param uPopAndFpuOpcode Bits 15-0: The FPU opcode.
9807 * Bit 31: Whether we should pop the stack when
9808 * done or not.
9809 */
9810IEM_CIMPL_DEF_3(iemCImpl_fcomi_fucomi, uint8_t, iStReg, bool, fUCmp, uint32_t, uPopAndFpuOpcode)
9811{
9812 Assert(iStReg < 8);
9813 IEM_CTX_ASSERT(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_X87);
9814
9815 /*
9816 * Raise exceptions.
9817 */
9818 if (pVCpu->cpum.GstCtx.cr0 & (X86_CR0_EM | X86_CR0_TS))
9819 return iemRaiseDeviceNotAvailable(pVCpu);
9820
9821 PX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87;
9822 uint16_t u16Fsw = pFpuCtx->FSW;
9823 if (u16Fsw & X86_FSW_ES)
9824 return iemRaiseMathFault(pVCpu);
9825
9826 /*
9827 * Check if any of the register accesses causes #SF + #IA.
9828 */
9829 bool fPop = RT_BOOL(uPopAndFpuOpcode & RT_BIT_32(31));
9830 unsigned const iReg1 = X86_FSW_TOP_GET(u16Fsw);
9831 unsigned const iReg2 = (iReg1 + iStReg) & X86_FSW_TOP_SMASK;
9832 if ((pFpuCtx->FTW & (RT_BIT(iReg1) | RT_BIT(iReg2))) == (RT_BIT(iReg1) | RT_BIT(iReg2)))
9833 {
9834 uint32_t u32Eflags;
9835 if (!fUCmp)
9836 u32Eflags = iemAImpl_fcomi_r80_by_r80(pFpuCtx, &u16Fsw, &pFpuCtx->aRegs[0].r80, &pFpuCtx->aRegs[iStReg].r80);
9837 else
9838 u32Eflags = iemAImpl_fucomi_r80_by_r80(pFpuCtx, &u16Fsw, &pFpuCtx->aRegs[0].r80, &pFpuCtx->aRegs[iStReg].r80);
9839
9840 pFpuCtx->FSW &= ~X86_FSW_C1;
9841 pFpuCtx->FSW |= u16Fsw & ~X86_FSW_TOP_MASK;
9842 if ( !(u16Fsw & X86_FSW_IE)
9843 || (pFpuCtx->FCW & X86_FCW_IM) )
9844 {
9845 pVCpu->cpum.GstCtx.eflags.u &= ~(X86_EFL_OF | X86_EFL_SF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF);
9846 pVCpu->cpum.GstCtx.eflags.u |= u32Eflags & (X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF);
9847 }
9848 }
9849 else if (pFpuCtx->FCW & X86_FCW_IM)
9850 {
9851 /* Masked underflow. */
9852 pFpuCtx->FSW &= ~X86_FSW_C1;
9853 pFpuCtx->FSW |= X86_FSW_IE | X86_FSW_SF;
9854 pVCpu->cpum.GstCtx.eflags.u &= ~(X86_EFL_OF | X86_EFL_SF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF);
9855 pVCpu->cpum.GstCtx.eflags.u |= X86_EFL_ZF | X86_EFL_PF | X86_EFL_CF;
9856 }
9857 else
9858 {
9859 /* Raise underflow - don't touch EFLAGS or TOP. */
9860 pFpuCtx->FSW &= ~X86_FSW_C1;
9861 pFpuCtx->FSW |= X86_FSW_IE | X86_FSW_SF | X86_FSW_ES | X86_FSW_B;
9862 Log11(("fxch: %04x:%08RX64: Raising IE+SF exception (FSW=%#x)\n",
9863 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pFpuCtx->FSW));
9864 fPop = false;
9865 }
9866
9867 /*
9868 * Pop if necessary.
9869 */
9870 if (fPop)
9871 {
9872 pFpuCtx->FTW &= ~RT_BIT(iReg1);
9873 iemFpuStackIncTop(pVCpu);
9874 }
9875
9876 iemFpuUpdateOpcodeAndIpWorkerEx(pVCpu, pFpuCtx, (uint16_t)uPopAndFpuOpcode);
9877 iemHlpUsedFpu(pVCpu);
9878 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9879}
9880
9881
9882/**
9883 * Implements 'RDSEED'.
9884 *
9885 * @returns VINF_SUCCESS.
9886 * @param iReg The register.
9887 * @param enmEffOpSize The operand size.
9888 */
9889IEM_CIMPL_DEF_2(iemCImpl_rdseed, uint8_t, iReg, IEMMODE, enmEffOpSize)
9890{
9891#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
9892 /* Nested-guest VMX intercept. */
9893 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
9894 || !IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_RDSEED_EXIT))
9895 { /* probable */ }
9896 else
9897 {
9898 Log(("rdseed: Guest intercept -> VM-exit\n"));
9899 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_RDSEED, VMXINSTRID_RDSEED, cbInstr);
9900 }
9901#endif
9902
9903 uint32_t *pEFlags = &pVCpu->cpum.GstCtx.eflags.uBoth;
9904 switch (enmEffOpSize)
9905 {
9906 case IEMMODE_16BIT:
9907 {
9908 PFNIEMAIMPLRDRANDSEEDU16 pfnImpl = IEM_SELECT_HOST_OR_FALLBACK(fRdSeed,
9909 &iemAImpl_rdseed_u16,
9910 &iemAImpl_rdseed_u16_fallback);
9911 uint16_t *pu16Dst = iemGRegRefU16(pVCpu, iReg);
9912 (pfnImpl)(pu16Dst, pEFlags);
9913 break;
9914 }
9915 case IEMMODE_32BIT:
9916 {
9917 PFNIEMAIMPLRDRANDSEEDU32 pfnImpl = IEM_SELECT_HOST_OR_FALLBACK(fRdSeed,
9918 &iemAImpl_rdseed_u32,
9919 &iemAImpl_rdseed_u32_fallback);
9920 uint32_t *pu32Dst = iemGRegRefU32(pVCpu, iReg);
9921 (pfnImpl)(pu32Dst, pEFlags);
9922 iemGRegStoreU32(pVCpu, iReg, *pu32Dst);
9923 break;
9924 }
9925 case IEMMODE_64BIT:
9926 {
9927 PFNIEMAIMPLRDRANDSEEDU64 pfnImpl = IEM_SELECT_HOST_OR_FALLBACK(fRdSeed,
9928 &iemAImpl_rdseed_u64,
9929 &iemAImpl_rdseed_u64_fallback);
9930 uint64_t *pu64Dst = iemGRegRefU64(pVCpu, iReg);
9931 (pfnImpl)(pu64Dst, pEFlags);
9932 break;
9933 }
9934 IEM_NOT_REACHED_DEFAULT_CASE_RET();
9935 }
9936 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9937}
9938
9939
9940/**
9941 * Implements 'RDRAND'.
9942 *
9943 * @returns VINF_SUCCESS.
9944 * @param iReg The register.
9945 * @param enmEffOpSize The operand size.
9946 */
9947IEM_CIMPL_DEF_2(iemCImpl_rdrand, uint8_t, iReg, IEMMODE, enmEffOpSize)
9948{
9949#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
9950 /* Nested-guest VMX intercept. */
9951 if ( !IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
9952 || !IEM_VMX_IS_PROCCTLS2_SET(pVCpu, VMX_PROC_CTLS2_RDRAND_EXIT))
9953 { /* probable */ }
9954 else
9955 {
9956 Log(("rdrand: Guest intercept -> VM-exit\n"));
9957 IEM_VMX_VMEXIT_INSTR_NEEDS_INFO_RET(pVCpu, VMX_EXIT_RDRAND, VMXINSTRID_RDRAND, cbInstr);
9958 }
9959#endif
9960
9961 uint32_t *pEFlags = &pVCpu->cpum.GstCtx.eflags.uBoth;
9962 switch (enmEffOpSize)
9963 {
9964 case IEMMODE_16BIT:
9965 {
9966 PFNIEMAIMPLRDRANDSEEDU16 pfnImpl = IEM_SELECT_HOST_OR_FALLBACK(fRdRand, &iemAImpl_rdrand_u16,
9967 &iemAImpl_rdrand_u16_fallback);
9968 uint16_t *pu16Dst = iemGRegRefU16(pVCpu, iReg);
9969 (pfnImpl)(pu16Dst, pEFlags);
9970 break;
9971 }
9972 case IEMMODE_32BIT:
9973 {
9974 PFNIEMAIMPLRDRANDSEEDU32 pfnImpl = IEM_SELECT_HOST_OR_FALLBACK(fRdRand, &iemAImpl_rdrand_u32,
9975 &iemAImpl_rdrand_u32_fallback);
9976 uint32_t *pu32Dst = iemGRegRefU32(pVCpu, iReg);
9977 (pfnImpl)(pu32Dst, pEFlags);
9978 iemGRegStoreU32(pVCpu, iReg, *pu32Dst);
9979 break;
9980 }
9981 case IEMMODE_64BIT:
9982 {
9983 PFNIEMAIMPLRDRANDSEEDU64 pfnImpl = IEM_SELECT_HOST_OR_FALLBACK(fRdRand, &iemAImpl_rdrand_u64,
9984 &iemAImpl_rdrand_u64_fallback);
9985 uint64_t *pu64Dst = iemGRegRefU64(pVCpu, iReg);
9986 (pfnImpl)(pu64Dst, pEFlags);
9987 break;
9988 }
9989 IEM_NOT_REACHED_DEFAULT_CASE_RET();
9990 }
9991 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
9992}
9993
9994
9995/**
9996 * Worker for 'VMASKMOVPS / VPMASKMOVD' 128-bit 32-bit-masked load.
9997 *
9998 * @param pVCpu The cross context virtual CPU structure of the calling thread.
9999 * @param cbInstr The current instruction length.
10000 * @param iXRegDst The destination XMM register index.
10001 * @param iXRegMsk The mask XMM register index.
10002 * @param iEffSeg The effective segment.
10003 * @param GCPtrEffSrc The source memory address.
10004 */
10005static VBOXSTRICTRC iemCImpl_maskmov_load_u128_32_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iXRegDst, uint8_t iXRegMsk, uint8_t iEffSeg, RTGCPTR GCPtrEffSrc)
10006{
10007 uint32_t fAccessed = 0;
10008
10009 PRTUINT128U puDst = (PRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegDst];
10010 PCRTUINT128U puMsk = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegMsk];
10011 PCRTUINT128U puSrc;
10012
10013 for (uint32_t i = 0; i < RT_ELEMENTS(puMsk->au32); i++)
10014 {
10015 fAccessed |= puMsk->au32[i];
10016 }
10017
10018 if (fAccessed & RT_BIT(31)) {
10019 /*
10020 * Access the source memory.
10021 */
10022 uint8_t bUnmapInfo;
10023 void *pvMemSrc;
10024 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemSrc, &bUnmapInfo, sizeof(*puSrc),
10025 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R, 0);
10026 if (rcStrict != VINF_SUCCESS)
10027 return rcStrict;
10028
10029 puSrc = (PCRTUINT128U)pvMemSrc;
10030
10031 for (uint32_t i = 0; i < RT_ELEMENTS(puSrc->au32); i++)
10032 {
10033 puDst->au32[i] = (puMsk->au32[i] & RT_BIT(31)) ? puSrc->au32[i] : 0;
10034 }
10035 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[0] = 0;
10036 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[1] = 0;
10037
10038 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10039 if (rcStrict != VINF_SUCCESS)
10040 return rcStrict;
10041 }
10042 else
10043 {
10044 puDst->au64[0] = 0;
10045 puDst->au64[1] = 0;
10046 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[0] = 0;
10047 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[1] = 0;
10048 }
10049
10050 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10051}
10052
10053
10054
10055/**
10056 * Worker for 'VMASKMOVPS / VPMASKMOVD' 256-bit 32-bit-masked load.
10057 *
10058 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10059 * @param cbInstr The current instruction length.
10060 * @param iYRegDst The destination YMM register index.
10061 * @param iYRegMsk The mask YMM register index.
10062 * @param iEffSeg The effective segment.
10063 * @param GCPtrEffSrc The source memory address.
10064 */
10065static VBOXSTRICTRC iemCImpl_maskmov_load_u256_32_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iYRegDst, uint8_t iYRegMsk, uint8_t iEffSeg, RTGCPTR GCPtrEffSrc)
10066{
10067 uint32_t fAccessed = 0;
10068
10069 PRTUINT128U puDstLo = (PRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegDst];
10070 PRTUINT128U puDstHi = (PRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegDst];
10071 PCRTUINT128U puMskLo = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegMsk];
10072 PCRTUINT128U puMskHi = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegMsk];
10073 PCRTUINT256U puSrc;
10074
10075 for (uint32_t i = 0; i < RT_ELEMENTS(puMskLo->au32); i++)
10076 {
10077 fAccessed |= puMskLo->au32[i] | puMskHi->au32[i];
10078 }
10079
10080 if (fAccessed & RT_BIT(31)) {
10081 /*
10082 * Access the source memory.
10083 */
10084 uint8_t bUnmapInfo;
10085 void *pvMemSrc;
10086 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemSrc, &bUnmapInfo, sizeof(*puSrc),
10087 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R, 0);
10088 if (rcStrict != VINF_SUCCESS)
10089 return rcStrict;
10090
10091 puSrc = (PCRTUINT256U)pvMemSrc;
10092
10093 uint8_t const iHalf = RT_ELEMENTS(puSrc->au32) / 2;
10094
10095 for (uint32_t i = 0; i < iHalf; i++)
10096 {
10097 puDstLo->au32[i] = (puMskLo->au32[i] & RT_BIT(31)) ? puSrc->au32[i] : 0;
10098 }
10099 for (uint32_t i = iHalf; i < RT_ELEMENTS(puSrc->au32); i++)
10100 {
10101 puDstHi->au32[i - iHalf] = (puMskHi->au32[i - iHalf] & RT_BIT(31)) ? puSrc->au32[i] : 0;
10102 }
10103
10104 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10105 if (rcStrict != VINF_SUCCESS)
10106 return rcStrict;
10107 }
10108 else
10109 {
10110 puDstLo->au64[0] = 0;
10111 puDstLo->au64[1] = 0;
10112 puDstHi->au64[0] = 0;
10113 puDstHi->au64[1] = 0;
10114 }
10115
10116 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10117}
10118
10119
10120/**
10121 * Worker for 'VMASKMOVPS / VPMASKMOVD' 128-bit 32-bit-masked store.
10122 *
10123 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10124 * @param cbInstr The current instruction length.
10125 * @param iEffSeg The effective segment.
10126 * @param GCPtrEffDst The destination memory address.
10127 * @param iXRegMsk The mask XMM register index.
10128 * @param iXRegSrc The source XMM register index.
10129 */
10130static VBOXSTRICTRC iemCImpl_maskmov_store_u128_32_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iEffSeg, RTGCPTR GCPtrEffDst, uint8_t iXRegMsk, uint8_t iXRegSrc)
10131{
10132 uint32_t fAccessed = 0;
10133
10134 PRTUINT128U puDst;
10135 PCRTUINT128U puMsk = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegMsk];
10136 PCRTUINT128U puSrc = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegSrc];
10137
10138 for (uint32_t i = 0; i < RT_ELEMENTS(puMsk->au32); i++)
10139 {
10140 fAccessed |= puMsk->au32[i];
10141 }
10142
10143 if (fAccessed & RT_BIT(31)) {
10144 /*
10145 * Access the destination memory.
10146 */
10147 uint8_t bUnmapInfo;
10148 void *pvMemDst;
10149 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemDst, &bUnmapInfo, sizeof(*puDst),
10150 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_RW, 0);
10151 if (rcStrict != VINF_SUCCESS)
10152 return rcStrict;
10153
10154 puDst = (PRTUINT128U)pvMemDst;
10155
10156 for (uint32_t i = 0; i < RT_ELEMENTS(puDst->au32); i++)
10157 {
10158 if (puMsk->au32[i] & RT_BIT(31))
10159 puDst->au32[i] = puSrc->au32[i];
10160 }
10161
10162 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10163 if (rcStrict != VINF_SUCCESS)
10164 return rcStrict;
10165 }
10166
10167 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10168}
10169
10170
10171
10172/**
10173 * Worker for 'VMASKMOVPS / VPMASKMOVD' 256-bit 32-bit-masked store.
10174 *
10175 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10176 * @param cbInstr The current instruction length.
10177 * @param iEffSeg The effective segment.
10178 * @param GCPtrEffDst The destination memory address.
10179 * @param iYRegMsk The mask YMM register index.
10180 * @param iYRegSrc The source YMM register index.
10181 */
10182static VBOXSTRICTRC iemCImpl_maskmov_store_u256_32_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iEffSeg, RTGCPTR GCPtrEffDst, uint8_t iYRegMsk, uint8_t iYRegSrc)
10183{
10184 uint32_t fAccessed = 0;
10185
10186 PRTUINT256U puDst;
10187 PCRTUINT128U puMskLo = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegMsk];
10188 PCRTUINT128U puMskHi = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegMsk];
10189 PCRTUINT128U puSrcLo = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegSrc];
10190 PCRTUINT128U puSrcHi = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegSrc];
10191
10192 for (uint32_t i = 0; i < RT_ELEMENTS(puMskLo->au32); i++)
10193 {
10194 fAccessed |= puMskLo->au32[i] | puMskHi->au32[i];
10195 }
10196
10197 if (fAccessed & RT_BIT(31)) {
10198 /*
10199 * Access the destination memory.
10200 */
10201 uint8_t bUnmapInfo;
10202 void *pvMemDst;
10203 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemDst, &bUnmapInfo, sizeof(*puDst),
10204 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_RW, 0);
10205 if (rcStrict != VINF_SUCCESS)
10206 return rcStrict;
10207
10208 puDst = (PRTUINT256U)pvMemDst;
10209
10210 uint8_t const iHalf = RT_ELEMENTS(puDst->au32) / 2;
10211
10212 for (uint32_t i = 0; i < iHalf; i++)
10213 {
10214 if (puMskLo->au32[i] & RT_BIT(31))
10215 puDst->au32[i] = puSrcLo->au32[i];
10216 }
10217 for (uint32_t i = iHalf; i < RT_ELEMENTS(puDst->au32); i++)
10218 {
10219 if (puMskHi->au32[i - iHalf] & RT_BIT(31))
10220 puDst->au32[i] = puSrcHi->au32[i - iHalf];
10221 }
10222
10223 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10224 if (rcStrict != VINF_SUCCESS)
10225 return rcStrict;
10226 }
10227
10228 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10229}
10230
10231
10232/**
10233 * Worker for 'VMASKMOVPD / VPMASKMOVQ' 128-bit 64-bit-masked load.
10234 *
10235 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10236 * @param cbInstr The current instruction length.
10237 * @param iXRegDst The destination XMM register index.
10238 * @param iXRegMsk The mask XMM register index.
10239 * @param iEffSeg The effective segment.
10240 * @param GCPtrEffSrc The source memory address.
10241 */
10242static VBOXSTRICTRC iemCImpl_maskmov_load_u128_64_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iXRegDst, uint8_t iXRegMsk, uint8_t iEffSeg, RTGCPTR GCPtrEffSrc)
10243{
10244 uint64_t fAccessed = 0;
10245
10246 PRTUINT128U puDst = (PRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegDst];
10247 PCRTUINT128U puMsk = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegMsk];
10248 PCRTUINT128U puSrc;
10249
10250 for (uint32_t i = 0; i < RT_ELEMENTS(puMsk->au64); i++)
10251 {
10252 fAccessed |= puMsk->au64[i];
10253 }
10254
10255 if (fAccessed & RT_BIT_64(63)) {
10256 /*
10257 * Access the source memory.
10258 */
10259 uint8_t bUnmapInfo;
10260 void *pvMemSrc;
10261 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemSrc, &bUnmapInfo, sizeof(*puSrc),
10262 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R, 0);
10263 if (rcStrict != VINF_SUCCESS)
10264 return rcStrict;
10265
10266 puSrc = (PCRTUINT128U)pvMemSrc;
10267
10268 for (uint32_t i = 0; i < RT_ELEMENTS(puSrc->au64); i++)
10269 {
10270 puDst->au64[i] = (puMsk->au64[i] & RT_BIT_64(63)) ? puSrc->au64[i] : 0;
10271 }
10272 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[0] = 0;
10273 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[1] = 0;
10274
10275 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10276 if (rcStrict != VINF_SUCCESS)
10277 return rcStrict;
10278 }
10279 else
10280 {
10281 puDst->au64[0] = 0;
10282 puDst->au64[1] = 0;
10283 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[0] = 0;
10284 pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iXRegDst].au64[1] = 0;
10285 }
10286
10287 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10288}
10289
10290
10291
10292/**
10293 * Worker for 'VMASKMOVPD / VPMASKMOVQ' 256-bit 64-bit-masked load.
10294 *
10295 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10296 * @param cbInstr The current instruction length.
10297 * @param iYRegDst The destination YMM register index.
10298 * @param iYRegMsk The mask YMM register index.
10299 * @param iEffSeg The effective segment.
10300 * @param GCPtrEffSrc The source memory address.
10301 */
10302static VBOXSTRICTRC iemCImpl_maskmov_load_u256_64_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iYRegDst, uint8_t iYRegMsk, uint8_t iEffSeg, RTGCPTR GCPtrEffSrc)
10303{
10304 uint64_t fAccessed = 0;
10305
10306 PRTUINT128U puDstLo = (PRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegDst];
10307 PRTUINT128U puDstHi = (PRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegDst];
10308 PCRTUINT128U puMskLo = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegMsk];
10309 PCRTUINT128U puMskHi = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegMsk];
10310 PCRTUINT256U puSrc;
10311
10312 for (uint32_t i = 0; i < RT_ELEMENTS(puMskLo->au64); i++)
10313 {
10314 fAccessed |= puMskLo->au64[i] | puMskHi->au64[i];
10315 }
10316
10317 if (fAccessed & RT_BIT_64(63)) {
10318 /*
10319 * Access the source memory.
10320 */
10321 uint8_t bUnmapInfo;
10322 void *pvMemSrc;
10323 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemSrc, &bUnmapInfo, sizeof(*puSrc),
10324 iEffSeg, GCPtrEffSrc, IEM_ACCESS_DATA_R, 0);
10325 if (rcStrict != VINF_SUCCESS)
10326 return rcStrict;
10327
10328 puSrc = (PCRTUINT256U)pvMemSrc;
10329
10330 uint8_t const iHalf = RT_ELEMENTS(puSrc->au64) / 2;
10331
10332 for (uint32_t i = 0; i < iHalf; i++)
10333 {
10334 puDstLo->au64[i] = (puMskLo->au64[i] & RT_BIT_64(63)) ? puSrc->au64[i] : 0;
10335 }
10336 for (uint32_t i = iHalf; i < RT_ELEMENTS(puSrc->au64); i++)
10337 {
10338 puDstHi->au64[i - iHalf] = (puMskHi->au64[i - iHalf] & RT_BIT_64(63)) ? puSrc->au64[i] : 0;
10339 }
10340
10341 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10342 if (rcStrict != VINF_SUCCESS)
10343 return rcStrict;
10344 }
10345 else
10346 {
10347 puDstLo->au64[0] = 0;
10348 puDstLo->au64[1] = 0;
10349 puDstHi->au64[0] = 0;
10350 puDstHi->au64[1] = 0;
10351 }
10352
10353 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10354}
10355
10356
10357/**
10358 * Worker for 'VMASKMOVPD / VPMASKMOVQ' 128-bit 64-bit-masked store.
10359 *
10360 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10361 * @param cbInstr The current instruction length.
10362 * @param iEffSeg The effective segment.
10363 * @param GCPtrEffDst The destination memory address.
10364 * @param iXRegMsk The mask XMM register index.
10365 * @param iXRegSrc The source XMM register index.
10366 */
10367static VBOXSTRICTRC iemCImpl_maskmov_store_u128_64_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iEffSeg, RTGCPTR GCPtrEffDst, uint8_t iXRegMsk, uint8_t iXRegSrc)
10368{
10369 uint64_t fAccessed = 0;
10370
10371 PRTUINT128U puDst;
10372 PCRTUINT128U puMsk = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegMsk];
10373 PCRTUINT128U puSrc = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iXRegSrc];
10374
10375 for (uint32_t i = 0; i < RT_ELEMENTS(puMsk->au64); i++)
10376 {
10377 fAccessed |= puMsk->au64[i];
10378 }
10379
10380 if (fAccessed & RT_BIT_64(63)) {
10381 /*
10382 * Access the destination memory.
10383 */
10384 uint8_t bUnmapInfo;
10385 void *pvMemDst;
10386 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemDst, &bUnmapInfo, sizeof(*puDst),
10387 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_RW, 0);
10388 if (rcStrict != VINF_SUCCESS)
10389 return rcStrict;
10390
10391 puDst = (PRTUINT128U)pvMemDst;
10392
10393 for (uint32_t i = 0; i < RT_ELEMENTS(puDst->au64); i++)
10394 {
10395 if (puMsk->au64[i] & RT_BIT_64(63))
10396 puDst->au64[i] = puSrc->au64[i];
10397 }
10398
10399 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10400 if (rcStrict != VINF_SUCCESS)
10401 return rcStrict;
10402 }
10403
10404 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10405}
10406
10407
10408
10409/**
10410 * Worker for 'VMASKMOVPD / VPMASKMOVQ' 256-bit 64-bit-masked store.
10411 *
10412 * @param pVCpu The cross context virtual CPU structure of the calling thread.
10413 * @param cbInstr The current instruction length.
10414 * @param iEffSeg The effective segment.
10415 * @param GCPtrEffDst The destination memory address.
10416 * @param iYRegMsk The mask YMM register index.
10417 * @param iYRegSrc The source YMM register index.
10418 */
10419static VBOXSTRICTRC iemCImpl_maskmov_store_u256_64_worker(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t iEffSeg, RTGCPTR GCPtrEffDst, uint8_t iYRegMsk, uint8_t iYRegSrc)
10420{
10421 uint64_t fAccessed = 0;
10422
10423 PRTUINT256U puDst;
10424 PCRTUINT128U puMskLo = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegMsk];
10425 PCRTUINT128U puMskHi = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegMsk];
10426 PCRTUINT128U puSrcLo = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.x87.aXMM[iYRegSrc];
10427 PCRTUINT128U puSrcHi = (PCRTUINT128U)&pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[iYRegSrc];
10428
10429 for (uint32_t i = 0; i < RT_ELEMENTS(puMskLo->au64); i++)
10430 {
10431 fAccessed |= puMskLo->au64[i] | puMskHi->au64[i];
10432 }
10433
10434 if (fAccessed & RT_BIT_64(63)) {
10435 /*
10436 * Access the destination memory.
10437 */
10438 uint8_t bUnmapInfo;
10439 void *pvMemDst;
10440 VBOXSTRICTRC rcStrict = iemMemMap(pVCpu, &pvMemDst, &bUnmapInfo, sizeof(*puDst),
10441 iEffSeg, GCPtrEffDst, IEM_ACCESS_DATA_RW, 0);
10442 if (rcStrict != VINF_SUCCESS)
10443 return rcStrict;
10444
10445 puDst = (PRTUINT256U)pvMemDst;
10446
10447 uint8_t const iHalf = RT_ELEMENTS(puDst->au64) / 2;
10448
10449 for (uint32_t i = 0; i < iHalf; i++)
10450 {
10451 if (puMskLo->au64[i] & RT_BIT_64(63))
10452 puDst->au64[i] = puSrcLo->au64[i];
10453 }
10454 for (uint32_t i = iHalf; i < RT_ELEMENTS(puDst->au64); i++)
10455 {
10456 if (puMskHi->au64[i - iHalf] & RT_BIT_64(63))
10457 puDst->au64[i] = puSrcHi->au64[i - iHalf];
10458 }
10459
10460 rcStrict = iemMemCommitAndUnmap(pVCpu, bUnmapInfo);
10461 if (rcStrict != VINF_SUCCESS)
10462 return rcStrict;
10463 }
10464
10465 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10466}
10467
10468
10469/**
10470 * Implements 'VMASKMOVPS' 128-bit 32-bit-masked load.
10471 *
10472 * @param iXRegDst The destination XMM register index.
10473 * @param iXRegMsk The mask XMM register index.
10474 * @param iEffSeg The effective segment.
10475 * @param GCPtrEffSrc The source memory address.
10476 */
10477IEM_CIMPL_DEF_4(iemCImpl_vmaskmovps_load_u128, uint8_t, iXRegDst, uint8_t, iXRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10478{
10479 return iemCImpl_maskmov_load_u128_32_worker(pVCpu, cbInstr, iXRegDst, iXRegMsk, iEffSeg, GCPtrEffSrc);
10480}
10481
10482
10483/**
10484 * Implements 'VMASKMOVPS' 256-bit 32-bit-masked load.
10485 *
10486 * @param iYRegDst The destination YMM register index.
10487 * @param iYRegMsk The mask YMM register index.
10488 * @param iEffSeg The effective segment.
10489 * @param GCPtrEffSrc The source memory address.
10490 */
10491IEM_CIMPL_DEF_4(iemCImpl_vmaskmovps_load_u256, uint8_t, iYRegDst, uint8_t, iYRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10492{
10493 return iemCImpl_maskmov_load_u256_32_worker(pVCpu, cbInstr, iYRegDst, iYRegMsk, iEffSeg, GCPtrEffSrc);
10494}
10495
10496
10497/**
10498 * Implements 'VMASKMOVPS' 128-bit 32-bit-masked store.
10499 *
10500 * @param iEffSeg The effective segment.
10501 * @param GCPtrEffDst The destination memory address.
10502 * @param iXRegMsk The mask XMM register index.
10503 * @param iXRegSrc The source XMM register index.
10504 */
10505IEM_CIMPL_DEF_4(iemCImpl_vmaskmovps_store_u128, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iXRegMsk, uint8_t, iXRegSrc)
10506{
10507 return iemCImpl_maskmov_store_u128_32_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iXRegMsk, iXRegSrc);
10508}
10509
10510
10511/**
10512 * Implements 'VMASKMOVPS' 256-bit 32-bit-masked store.
10513 *
10514 * @param iEffSeg The effective segment.
10515 * @param GCPtrEffDst The destination memory address.
10516 * @param iYRegMsk The mask YMM register index.
10517 * @param iYRegSrc The source YMM register index.
10518 */
10519IEM_CIMPL_DEF_4(iemCImpl_vmaskmovps_store_u256, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iYRegMsk, uint8_t, iYRegSrc)
10520{
10521 return iemCImpl_maskmov_store_u256_32_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iYRegMsk, iYRegSrc);
10522}
10523
10524
10525/**
10526 * Implements 'VPMASKMOVD' 128-bit 32-bit-masked load.
10527 *
10528 * @param iXRegDst The destination XMM register index.
10529 * @param iXRegMsk The mask XMM register index.
10530 * @param iEffSeg The effective segment.
10531 * @param GCPtrEffSrc The source memory address.
10532 */
10533IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovd_load_u128, uint8_t, iXRegDst, uint8_t, iXRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10534{
10535 return iemCImpl_maskmov_load_u128_32_worker(pVCpu, cbInstr, iXRegDst, iXRegMsk, iEffSeg, GCPtrEffSrc);
10536}
10537
10538
10539/**
10540 * Implements 'VPMASKMOVD' 256-bit 32-bit-masked load.
10541 *
10542 * @param iYRegDst The destination YMM register index.
10543 * @param iYRegMsk The mask YMM register index.
10544 * @param iEffSeg The effective segment.
10545 * @param GCPtrEffSrc The source memory address.
10546 */
10547IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovd_load_u256, uint8_t, iYRegDst, uint8_t, iYRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10548{
10549 return iemCImpl_maskmov_load_u256_32_worker(pVCpu, cbInstr, iYRegDst, iYRegMsk, iEffSeg, GCPtrEffSrc);
10550}
10551
10552
10553/**
10554 * Implements 'VPMASKMOVD' 128-bit 32-bit-masked store.
10555 *
10556 * @param iEffSeg The effective segment.
10557 * @param GCPtrEffDst The destination memory address.
10558 * @param iXRegMsk The mask XMM register index.
10559 * @param iXRegSrc The source XMM register index.
10560 */
10561IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovd_store_u128, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iXRegMsk, uint8_t, iXRegSrc)
10562{
10563 return iemCImpl_maskmov_store_u128_32_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iXRegMsk, iXRegSrc);
10564}
10565
10566
10567/**
10568 * Implements 'VPMASKMOVD' 256-bit 32-bit-masked store.
10569 *
10570 * @param iEffSeg The effective segment.
10571 * @param GCPtrEffDst The destination memory address.
10572 * @param iYRegMsk The mask YMM register index.
10573 * @param iYRegSrc The source YMM register index.
10574 */
10575IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovd_store_u256, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iYRegMsk, uint8_t, iYRegSrc)
10576{
10577 return iemCImpl_maskmov_store_u256_32_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iYRegMsk, iYRegSrc);
10578}
10579
10580
10581/**
10582 * Implements 'VMASKMOVPD' 128-bit 64-bit-masked load.
10583 *
10584 * @param iXRegDst The destination XMM register index.
10585 * @param iXRegMsk The mask XMM register index.
10586 * @param iEffSeg The effective segment.
10587 * @param GCPtrEffSrc The source memory address.
10588 */
10589IEM_CIMPL_DEF_4(iemCImpl_vmaskmovpd_load_u128, uint8_t, iXRegDst, uint8_t, iXRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10590{
10591 return iemCImpl_maskmov_load_u128_64_worker(pVCpu, cbInstr, iXRegDst, iXRegMsk, iEffSeg, GCPtrEffSrc);
10592}
10593
10594
10595/**
10596 * Implements 'VMASKMOVPD' 256-bit 64-bit-masked load.
10597 *
10598 * @param iYRegDst The destination YMM register index.
10599 * @param iYRegMsk The mask YMM register index.
10600 * @param iEffSeg The effective segment.
10601 * @param GCPtrEffSrc The source memory address.
10602 */
10603IEM_CIMPL_DEF_4(iemCImpl_vmaskmovpd_load_u256, uint8_t, iYRegDst, uint8_t, iYRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10604{
10605 return iemCImpl_maskmov_load_u256_64_worker(pVCpu, cbInstr, iYRegDst, iYRegMsk, iEffSeg, GCPtrEffSrc);
10606}
10607
10608
10609/**
10610 * Implements 'VMASKMOVPD' 128-bit 64-bit-masked store.
10611 *
10612 * @param iEffSeg The effective segment.
10613 * @param GCPtrEffDst The destination memory address.
10614 * @param iXRegMsk The mask XMM register index.
10615 * @param iXRegSrc The source XMM register index.
10616 */
10617IEM_CIMPL_DEF_4(iemCImpl_vmaskmovpd_store_u128, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iXRegMsk, uint8_t, iXRegSrc)
10618{
10619 return iemCImpl_maskmov_store_u128_64_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iXRegMsk, iXRegSrc);
10620}
10621
10622
10623/**
10624 * Implements 'VMASKMOVPD' 256-bit 64-bit-masked store.
10625 *
10626 * @param iEffSeg The effective segment.
10627 * @param GCPtrEffDst The destination memory address.
10628 * @param iYRegMsk The mask YMM register index.
10629 * @param iYRegSrc The source YMM register index.
10630 */
10631IEM_CIMPL_DEF_4(iemCImpl_vmaskmovpd_store_u256, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iYRegMsk, uint8_t, iYRegSrc)
10632{
10633 return iemCImpl_maskmov_store_u256_64_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iYRegMsk, iYRegSrc);
10634}
10635
10636
10637/**
10638 * Implements 'VPMASKMOVQ' 128-bit 64-bit-masked load.
10639 *
10640 * @param iXRegDst The destination XMM register index.
10641 * @param iXRegMsk The mask XMM register index.
10642 * @param iEffSeg The effective segment.
10643 * @param GCPtrEffSrc The source memory address.
10644 */
10645IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovq_load_u128, uint8_t, iXRegDst, uint8_t, iXRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10646{
10647 return iemCImpl_maskmov_load_u128_64_worker(pVCpu, cbInstr, iXRegDst, iXRegMsk, iEffSeg, GCPtrEffSrc);
10648}
10649
10650
10651/**
10652 * Implements 'VPMASKMOVQ' 256-bit 64-bit-masked load.
10653 *
10654 * @param iYRegDst The destination YMM register index.
10655 * @param iYRegMsk The mask YMM register index.
10656 * @param iEffSeg The effective segment.
10657 * @param GCPtrEffSrc The source memory address.
10658 */
10659IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovq_load_u256, uint8_t, iYRegDst, uint8_t, iYRegMsk, uint8_t, iEffSeg, RTGCPTR, GCPtrEffSrc)
10660{
10661 return iemCImpl_maskmov_load_u256_64_worker(pVCpu, cbInstr, iYRegDst, iYRegMsk, iEffSeg, GCPtrEffSrc);
10662}
10663
10664
10665/**
10666 * Implements 'VPMASKMOVQ' 128-bit 64-bit-masked store.
10667 *
10668 * @param iEffSeg The effective segment.
10669 * @param GCPtrEffDst The destination memory address.
10670 * @param iXRegMsk The mask XMM register index.
10671 * @param iXRegSrc The source XMM register index.
10672 */
10673IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovq_store_u128, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iXRegMsk, uint8_t, iXRegSrc)
10674{
10675 return iemCImpl_maskmov_store_u128_64_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iXRegMsk, iXRegSrc);
10676}
10677
10678
10679/**
10680 * Implements 'VPMASKMOVQ' 256-bit 64-bit-masked store.
10681 *
10682 * @param iEffSeg The effective segment.
10683 * @param GCPtrEffDst The destination memory address.
10684 * @param iYRegMsk The mask YMM register index.
10685 * @param iYRegSrc The source YMM register index.
10686 */
10687IEM_CIMPL_DEF_4(iemCImpl_vpmaskmovq_store_u256, uint8_t, iEffSeg, RTGCPTR, GCPtrEffDst, uint8_t, iYRegMsk, uint8_t, iYRegSrc)
10688{
10689 return iemCImpl_maskmov_store_u256_64_worker(pVCpu, cbInstr, iEffSeg, GCPtrEffDst, iYRegMsk, iYRegSrc);
10690}
10691
10692
10693/**
10694 * Worker for 'VGATHERcxx' / 'VPGATHERxx' masked loads.
10695 *
10696 * @param u32PackedArgs Arguments packed to the tune of IEMGATHERARGS.
10697 * @param u32Disp The address displacement for the indices.
10698 */
10699IEM_CIMPL_DEF_2(iemCImpl_vpgather_worker_xx, uint32_t, u32PackedArgs, uint32_t, u32Disp)
10700{
10701 IEMGATHERARGS const PackedArgs = { u32PackedArgs };
10702 int32_t const offDisp = (int32_t)u32Disp;
10703
10704 if (PackedArgs.s.iYRegDst == PackedArgs.s.iYRegIdc ||
10705 PackedArgs.s.iYRegIdc == PackedArgs.s.iYRegMsk ||
10706 PackedArgs.s.iYRegDst == PackedArgs.s.iYRegMsk) return iemRaiseUndefinedOpcode(pVCpu);
10707
10708 Assert(PackedArgs.s.enmEffOpSize <= IEMMODE_64BIT);
10709 Assert(PackedArgs.s.enmEffAddrMode <= IEMMODE_64BIT);
10710
10711 uint32_t const cbMaxWidth = PackedArgs.s.fVex256 ? 32 : 16; /* Width of widest XMM / YMM register we will use: 32 or 16 */
10712 uint32_t const cbIdxWidth = PackedArgs.s.fIdxQword ? 8 : 4; /* Width of one index: 4-byte dword or 8-byte qword */
10713 uint32_t const cbValWidth = PackedArgs.s.fValQword ? 8 : 4; /* Width of one value: 4-byte dword or 8-byte qword */
10714 uint32_t const cMasks = cbMaxWidth / cbValWidth; /* Count of masks: 8 or 4 or 2 */
10715 uint32_t const cIndices = cbMaxWidth / cbIdxWidth; /* Count of indices: 8 or 4 or 2 */
10716 uint32_t const cValues = RT_MIN(cMasks, cIndices); /* Count of values to gather: 8 or 4 or 2 */
10717 Assert(cValues == 2 || cValues == 4 || cValues == 8);
10718 uint32_t const cbDstWidth = cValues * cbValWidth; /* Width of the destination & mask XMM / YMM registers: 32 or 16 or 8 */
10719 Assert(cbDstWidth == 8 || cbDstWidth == 16 || cbDstWidth == 32);
10720
10721 /*
10722 * Get the base pointer.
10723 */
10724 uint64_t u64Base = iemGRegFetchU64(pVCpu, PackedArgs.s.iGRegBase);
10725 if (PackedArgs.s.enmEffAddrMode != IEMMODE_64BIT)
10726 u64Base &= (PackedArgs.s.enmEffAddrMode == IEMMODE_16BIT ? UINT16_MAX : UINT32_MAX);
10727
10728 PRTUINT128U const apuDst[2] =
10729 {
10730 &pVCpu->cpum.GstCtx.XState.x87.aXMM[PackedArgs.s.iYRegDst].uXmm,
10731 &pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[PackedArgs.s.iYRegDst].uXmm
10732 };
10733 PCRTUINT128U const apuIdc[2] =
10734 {
10735 &pVCpu->cpum.GstCtx.XState.x87.aXMM[PackedArgs.s.iYRegIdc].uXmm,
10736 &pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[PackedArgs.s.iYRegIdc].uXmm
10737 };
10738 PRTUINT128U const apuMsk[2] =
10739 {
10740 &pVCpu->cpum.GstCtx.XState.x87.aXMM[PackedArgs.s.iYRegMsk].uXmm,
10741 &pVCpu->cpum.GstCtx.XState.u.YmmHi.aYmmHi[PackedArgs.s.iYRegMsk].uXmm
10742 };
10743
10744 /*
10745 * Convert the masks to all-0s or all-1s, writing back to the mask
10746 * register so it will have the correct value if subsequent memory
10747 * accesses fault. Note that cMasks can be larger than cValues, in
10748 * the Qword-index, Dword-value instructions `vgatherqps' and
10749 * `vpgatherqd'. Updating the masks for as many masks as *would*
10750 * have been used if the destination register were wide enough --
10751 * is the observed behavior of a Core i7-10700.
10752 */
10753 if (!PackedArgs.s.fValQword)
10754 for (uint32_t i = 0; i < cMasks; i++)
10755 apuMsk[(i >> 2) & 1]->ai32[i & 3] >>= 31; /* Use arithmetic shift right (SAR/ASR) */
10756 else
10757 for (uint32_t i = 0; i < cMasks; i++)
10758 apuMsk[(i >> 1) & 1]->ai64[i & 1] >>= 63; /* Use arithmetic shift right (SAR/ASR) */
10759
10760 /*
10761 * Zero upper bits of mask if VEX128.
10762 */
10763 if (!PackedArgs.s.fVex256)
10764 {
10765 apuMsk[1]->au64[0] = 0;
10766 apuMsk[1]->au64[1] = 0;
10767 }
10768
10769 /*
10770 * Gather the individual values, as masked.
10771 */
10772 for (uint32_t i = 0; i < cValues; i++)
10773 {
10774 /*
10775 * Consult the mask determined above.
10776 */
10777 if ( !PackedArgs.s.fValQword
10778 ? apuMsk[(i >> 2) & 1]->au32[i & 3] != 0
10779 : apuMsk[(i >> 1) & 1]->au64[i & 1] != 0)
10780 {
10781 /*
10782 * Get the index, scale it, add scaled index + offset to the base pointer.
10783 */
10784 int64_t offIndex;
10785 if (!PackedArgs.s.fIdxQword)
10786 offIndex = apuIdc[(i >> 2) & 1]->ai32[i & 3];
10787 else
10788 offIndex = apuIdc[(i >> 1) & 1]->ai64[i & 1];
10789 offIndex <<= PackedArgs.s.iScale;
10790 offIndex += offDisp;
10791
10792 uint64_t u64Addr = u64Base + offIndex;
10793 if (PackedArgs.s.enmEffAddrMode != IEMMODE_64BIT)
10794 u64Addr &= UINT32_MAX;
10795
10796 /*
10797 * Gather it -- fetch this gather-item from guest memory.
10798 */
10799 VBOXSTRICTRC rcStrict;
10800 if (!PackedArgs.s.fValQword)
10801 rcStrict = iemMemFetchDataU32NoAc(pVCpu, &apuDst[(i >> 2) & 1]->au32[i & 3], PackedArgs.s.iEffSeg, u64Addr);
10802 else
10803 rcStrict = iemMemFetchDataU64NoAc(pVCpu, &apuDst[(i >> 1) & 1]->au64[i & 1], PackedArgs.s.iEffSeg, u64Addr);
10804 if (rcStrict != VINF_SUCCESS)
10805 return rcStrict;
10806
10807 /*
10808 * Now that we *didn't* fault, write all-0s to that part of the mask register.
10809 */
10810 if (!PackedArgs.s.fValQword)
10811 apuMsk[(i >> 2) & 1]->au32[i & 3] = 0;
10812 else
10813 apuMsk[(i >> 1) & 1]->au64[i & 1] = 0;
10814 /** @todo How is data breakpoints handled? The intel docs kind of hints they
10815 * may be raised here... */
10816 }
10817 }
10818
10819 /*
10820 * Zero upper bits of destination and mask.
10821 */
10822 if (cbDstWidth != 32)
10823 {
10824 apuDst[1]->au64[0] = 0;
10825 apuDst[1]->au64[1] = 0;
10826 apuMsk[1]->au64[0] = 0;
10827 apuMsk[1]->au64[1] = 0;
10828 if (cbDstWidth == 8)
10829 {
10830 apuDst[0]->au64[1] = 0;
10831 apuMsk[0]->au64[1] = 0;
10832 }
10833 }
10834
10835 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
10836}
10837
10838/** @} */
10839
Note: See TracBrowser for help on using the repository browser.

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