VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IEMAllCImplSvmInstr.cpp@ 100061

Last change on this file since 100061 was 100052, checked in by vboxsync, 18 months ago

VMM/IEM: Refactored the enmCpuMode, uCpl, fBypassHandlers, fDisregardLock and fPendingInstruction* IEMCPU members into a single fExec member and associated IEM_F_XXX flag defines. Added more flags needed for recompiled execution. The fExec value is maintained as code is executed, so it does not need to be recalculated in the instruction loops. bugref:10369

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.3 KB
Line 
1/* $Id: IEMAllCImplSvmInstr.cpp 100052 2023-06-02 14:49:14Z vboxsync $ */
2/** @file
3 * IEM - AMD-V (Secure Virtual Machine) instruction implementation.
4 */
5
6/*
7 * Copyright (C) 2011-2023 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_SVM
33#define VMCPU_INCL_CPUM_GST_CTX
34#include <VBox/vmm/iem.h>
35#include <VBox/vmm/apic.h>
36#include <VBox/vmm/cpum.h>
37#include <VBox/vmm/dbgf.h>
38#include <VBox/vmm/em.h>
39#include <VBox/vmm/hm.h>
40#include <VBox/vmm/pgm.h>
41#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
42# include <VBox/vmm/hm_svm.h>
43#endif
44#include <VBox/vmm/gim.h>
45#include <VBox/vmm/tm.h>
46#include "IEMInternal.h"
47#include <VBox/vmm/vmcc.h>
48#include <VBox/log.h>
49#include <VBox/disopcode-x86-amd64.h> /* for OP_VMMCALL */
50#include <VBox/err.h>
51#include <VBox/param.h>
52#include <iprt/assert.h>
53#include <iprt/string.h>
54#include <iprt/x86.h>
55
56#include "IEMInline.h"
57
58#ifdef VBOX_WITH_NESTED_HWVIRT_SVM /* Almost the whole file. */
59
60
61/*********************************************************************************************************************************
62* Defined Constants And Macros *
63*********************************************************************************************************************************/
64/**
65 * Check the common SVM instruction preconditions.
66 */
67# define IEM_SVM_INSTR_COMMON_CHECKS(a_pVCpu, a_Instr) \
68 do { \
69 if (!CPUMIsGuestSvmEnabled(IEM_GET_CTX(a_pVCpu))) \
70 { \
71 Log((RT_STR(a_Instr) ": EFER.SVME not enabled -> #UD\n")); \
72 return iemRaiseUndefinedOpcode(a_pVCpu); \
73 } \
74 if (IEM_IS_REAL_OR_V86_MODE(a_pVCpu)) \
75 { \
76 Log((RT_STR(a_Instr) ": Real or v8086 mode -> #UD\n")); \
77 return iemRaiseUndefinedOpcode(a_pVCpu); \
78 } \
79 if (IEM_GET_CPL(a_pVCpu) != 0) \
80 { \
81 Log((RT_STR(a_Instr) ": CPL != 0 -> #GP(0)\n")); \
82 return iemRaiseGeneralProtectionFault0(a_pVCpu); \
83 } \
84 } while (0)
85
86
87/**
88 * Converts an IEM exception event type to an SVM event type.
89 *
90 * @returns The SVM event type.
91 * @retval UINT8_MAX if the specified type of event isn't among the set
92 * of recognized IEM event types.
93 *
94 * @param uVector The vector of the event.
95 * @param fIemXcptFlags The IEM exception / interrupt flags.
96 */
97IEM_STATIC uint8_t iemGetSvmEventType(uint32_t uVector, uint32_t fIemXcptFlags)
98{
99 if (fIemXcptFlags & IEM_XCPT_FLAGS_T_CPU_XCPT)
100 {
101 if (uVector != X86_XCPT_NMI)
102 return SVM_EVENT_EXCEPTION;
103 return SVM_EVENT_NMI;
104 }
105
106 /* See AMD spec. Table 15-1. "Guest Exception or Interrupt Types". */
107 if (fIemXcptFlags & (IEM_XCPT_FLAGS_BP_INSTR | IEM_XCPT_FLAGS_ICEBP_INSTR | IEM_XCPT_FLAGS_OF_INSTR))
108 return SVM_EVENT_EXCEPTION;
109
110 if (fIemXcptFlags & IEM_XCPT_FLAGS_T_EXT_INT)
111 return SVM_EVENT_EXTERNAL_IRQ;
112
113 if (fIemXcptFlags & IEM_XCPT_FLAGS_T_SOFT_INT)
114 return SVM_EVENT_SOFTWARE_INT;
115
116 AssertMsgFailed(("iemGetSvmEventType: Invalid IEM xcpt/int. type %#x, uVector=%#x\n", fIemXcptFlags, uVector));
117 return UINT8_MAX;
118}
119
120
121/**
122 * Performs an SVM world-switch (VMRUN, \#VMEXIT) updating PGM and IEM internals.
123 *
124 * @returns Strict VBox status code from PGMChangeMode.
125 * @param pVCpu The cross context virtual CPU structure.
126 * @param cbInstr The length of the current instruction.
127 */
128DECLINLINE(VBOXSTRICTRC) iemSvmWorldSwitch(PVMCPUCC pVCpu, uint8_t cbInstr)
129{
130 /*
131 * Inform PGM about paging mode changes.
132 * We include X86_CR0_PE because PGM doesn't handle paged-real mode yet,
133 * see comment in iemMemPageTranslateAndCheckAccess().
134 */
135 int rc = PGMChangeMode(pVCpu, pVCpu->cpum.GstCtx.cr0 | X86_CR0_PE, pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER,
136 true /* fForce */);
137 AssertRCReturn(rc, rc);
138
139 /* Invalidate IEM TLBs now that we've forced a PGM mode change. */
140 IEMTlbInvalidateAll(pVCpu);
141
142 /* Inform CPUM (recompiler), can later be removed. */
143 CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_ALL);
144
145 /* Re-initialize IEM cache/state after the drastic mode switch. */
146 iemReInitExec(pVCpu, cbInstr);
147 return rc;
148}
149
150
151/**
152 * SVM \#VMEXIT handler.
153 *
154 * @returns Strict VBox status code.
155 * @retval VINF_SVM_VMEXIT when the \#VMEXIT is successful.
156 * @retval VERR_SVM_VMEXIT_FAILED when the \#VMEXIT failed restoring the guest's
157 * "host state" and a shutdown is required.
158 *
159 * @param pVCpu The cross context virtual CPU structure.
160 * @param uExitCode The exit code.
161 * @param uExitInfo1 The exit info. 1 field.
162 * @param uExitInfo2 The exit info. 2 field.
163 */
164VBOXSTRICTRC iemSvmVmexit(PVMCPUCC pVCpu, uint64_t uExitCode, uint64_t uExitInfo1, uint64_t uExitInfo2) RT_NOEXCEPT
165{
166 VBOXSTRICTRC rcStrict;
167 if ( CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu))
168 || uExitCode == SVM_EXIT_INVALID)
169 {
170 Log2(("iemSvmVmexit: CS:RIP=%04x:%08RX64 uExitCode=%#RX64 uExitInfo1=%#RX64 uExitInfo2=%#RX64\n",
171 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, uExitCode, uExitInfo1, uExitInfo2));
172
173 /*
174 * Disable the global-interrupt flag to prevent interrupts during the 'atomic' world switch.
175 */
176 CPUMSetGuestGif(&pVCpu->cpum.GstCtx, false);
177
178 /*
179 * Map the nested-guest VMCB from its location in guest memory.
180 * Write exactly what the CPU does on #VMEXIT thereby preserving most other bits in the
181 * guest's VMCB in memory, see @bugref{7243#c113} and related comment on iemSvmVmrun().
182 */
183 PSVMVMCB pVmcbMem;
184 PGMPAGEMAPLOCK PgLockMem;
185 PSVMVMCBCTRL pVmcbCtrl = &pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.ctrl;
186 rcStrict = iemMemPageMap(pVCpu, pVCpu->cpum.GstCtx.hwvirt.svm.GCPhysVmcb, IEM_ACCESS_DATA_RW, (void **)&pVmcbMem,
187 &PgLockMem);
188 if (rcStrict == VINF_SUCCESS)
189 {
190 /*
191 * Notify HM in case the nested-guest was executed using hardware-assisted SVM (which
192 * would have modified some VMCB state) that might need to be restored on #VMEXIT before
193 * writing the VMCB back to guest memory.
194 */
195 HMNotifySvmNstGstVmexit(pVCpu, IEM_GET_CTX(pVCpu));
196
197 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pVCpu->cpum.GstCtx.es));
198 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pVCpu->cpum.GstCtx.cs));
199 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pVCpu->cpum.GstCtx.ss));
200 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pVCpu->cpum.GstCtx.ds));
201
202 /*
203 * Save the nested-guest state into the VMCB state-save area.
204 */
205 PSVMVMCBSTATESAVE pVmcbMemState = &pVmcbMem->guest;
206 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), pVmcbMemState, ES, es);
207 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), pVmcbMemState, CS, cs);
208 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), pVmcbMemState, SS, ss);
209 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), pVmcbMemState, DS, ds);
210 pVmcbMemState->GDTR.u32Limit = pVCpu->cpum.GstCtx.gdtr.cbGdt;
211 pVmcbMemState->GDTR.u64Base = pVCpu->cpum.GstCtx.gdtr.pGdt;
212 pVmcbMemState->IDTR.u32Limit = pVCpu->cpum.GstCtx.idtr.cbIdt;
213 pVmcbMemState->IDTR.u64Base = pVCpu->cpum.GstCtx.idtr.pIdt;
214 pVmcbMemState->u64EFER = pVCpu->cpum.GstCtx.msrEFER;
215 pVmcbMemState->u64CR4 = pVCpu->cpum.GstCtx.cr4;
216 pVmcbMemState->u64CR3 = pVCpu->cpum.GstCtx.cr3;
217 pVmcbMemState->u64CR2 = pVCpu->cpum.GstCtx.cr2;
218 pVmcbMemState->u64CR0 = pVCpu->cpum.GstCtx.cr0;
219 /** @todo Nested paging. */
220 pVmcbMemState->u64RFlags = pVCpu->cpum.GstCtx.rflags.u;
221 pVmcbMemState->u64RIP = pVCpu->cpum.GstCtx.rip;
222 pVmcbMemState->u64RSP = pVCpu->cpum.GstCtx.rsp;
223 pVmcbMemState->u64RAX = pVCpu->cpum.GstCtx.rax;
224 pVmcbMemState->u64DR7 = pVCpu->cpum.GstCtx.dr[7];
225 pVmcbMemState->u64DR6 = pVCpu->cpum.GstCtx.dr[6];
226 pVmcbMemState->u8CPL = pVCpu->cpum.GstCtx.ss.Attr.n.u2Dpl; /* See comment in CPUMGetGuestCPL(). */
227 Assert(CPUMGetGuestCPL(pVCpu) == pVCpu->cpum.GstCtx.ss.Attr.n.u2Dpl);
228 if (CPUMIsGuestSvmNestedPagingEnabled(pVCpu, IEM_GET_CTX(pVCpu)))
229 pVmcbMemState->u64PAT = pVCpu->cpum.GstCtx.msrPAT;
230
231 /*
232 * Save additional state and intercept information.
233 *
234 * - V_IRQ: Tracked using VMCPU_FF_INTERRUPT_NESTED_GUEST force-flag and updated below.
235 * - V_TPR: Updated by iemCImpl_load_CrX or by the physical CPU for hardware-assisted
236 * SVM execution.
237 * - Interrupt shadow: Tracked using VMCPU_FF_INHIBIT_INTERRUPTS and RIP.
238 */
239 PSVMVMCBCTRL pVmcbMemCtrl = &pVmcbMem->ctrl;
240 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST)) /* V_IRQ. */
241 pVmcbMemCtrl->IntCtrl.n.u1VIrqPending = 0;
242 else
243 {
244 Assert(pVmcbCtrl->IntCtrl.n.u1VIrqPending);
245 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST);
246 }
247
248 pVmcbMemCtrl->IntCtrl.n.u8VTPR = pVmcbCtrl->IntCtrl.n.u8VTPR; /* V_TPR. */
249
250 if (!CPUMIsInInterruptShadowWithUpdate(&pVCpu->cpum.GstCtx)) /* Interrupt shadow. */
251 pVmcbMemCtrl->IntShadow.n.u1IntShadow = 0;
252 else
253 {
254 pVmcbMemCtrl->IntShadow.n.u1IntShadow = 1;
255 LogFlow(("iemSvmVmexit: Interrupt shadow till %#RX64\n", pVCpu->cpum.GstCtx.rip));
256 CPUMClearInterruptShadow(&pVCpu->cpum.GstCtx);
257 }
258
259 /*
260 * Save nRIP, instruction length and byte fields.
261 */
262 pVmcbMemCtrl->u64NextRIP = pVmcbCtrl->u64NextRIP;
263 pVmcbMemCtrl->cbInstrFetched = pVmcbCtrl->cbInstrFetched;
264 memcpy(&pVmcbMemCtrl->abInstr[0], &pVmcbCtrl->abInstr[0], sizeof(pVmcbMemCtrl->abInstr));
265
266 /*
267 * Save exit information.
268 */
269 pVmcbMemCtrl->u64ExitCode = uExitCode;
270 pVmcbMemCtrl->u64ExitInfo1 = uExitInfo1;
271 pVmcbMemCtrl->u64ExitInfo2 = uExitInfo2;
272
273 /*
274 * Update the exit interrupt-information field if this #VMEXIT happened as a result
275 * of delivering an event through IEM.
276 *
277 * Don't update the exit interrupt-information field if the event wasn't being injected
278 * through IEM, as it would have been updated by real hardware if the nested-guest was
279 * executed using hardware-assisted SVM.
280 */
281 {
282 uint8_t uExitIntVector;
283 uint32_t uExitIntErr;
284 uint32_t fExitIntFlags;
285 bool const fRaisingEvent = IEMGetCurrentXcpt(pVCpu, &uExitIntVector, &fExitIntFlags, &uExitIntErr,
286 NULL /* uExitIntCr2 */);
287 if (fRaisingEvent)
288 {
289 pVmcbCtrl->ExitIntInfo.n.u1Valid = 1;
290 pVmcbCtrl->ExitIntInfo.n.u8Vector = uExitIntVector;
291 pVmcbCtrl->ExitIntInfo.n.u3Type = iemGetSvmEventType(uExitIntVector, fExitIntFlags);
292 if (fExitIntFlags & IEM_XCPT_FLAGS_ERR)
293 {
294 pVmcbCtrl->ExitIntInfo.n.u1ErrorCodeValid = true;
295 pVmcbCtrl->ExitIntInfo.n.u32ErrorCode = uExitIntErr;
296 }
297 }
298 }
299
300 /*
301 * Save the exit interrupt-information field.
302 *
303 * We write the whole field including overwriting reserved bits as it was observed on an
304 * AMD Ryzen 5 Pro 1500 that the CPU does not preserve reserved bits in EXITINTINFO.
305 */
306 pVmcbMemCtrl->ExitIntInfo = pVmcbCtrl->ExitIntInfo;
307
308 /*
309 * Clear event injection.
310 */
311 pVmcbMemCtrl->EventInject.n.u1Valid = 0;
312
313 iemMemPageUnmap(pVCpu, pVCpu->cpum.GstCtx.hwvirt.svm.GCPhysVmcb, IEM_ACCESS_DATA_RW, pVmcbMem, &PgLockMem);
314 }
315
316 /*
317 * Prepare for guest's "host mode" by clearing internal processor state bits.
318 *
319 * We don't need to zero out the state-save area, just the controls should be
320 * sufficient because it has the critical bit of indicating whether we're inside
321 * the nested-guest or not.
322 */
323 memset(pVmcbCtrl, 0, sizeof(*pVmcbCtrl));
324 Assert(!CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)));
325
326 /*
327 * Restore the subset of the inhibit flags that were preserved.
328 */
329 pVCpu->cpum.GstCtx.eflags.uBoth |= pVCpu->cpum.GstCtx.hwvirt.fSavedInhibit;
330
331 if (rcStrict == VINF_SUCCESS)
332 {
333 /** @todo Nested paging. */
334 /** @todo ASID. */
335
336 /*
337 * If we are switching to PAE mode host, validate the PDPEs first.
338 * Any invalid PDPEs here causes a VCPU shutdown.
339 */
340 PCSVMHOSTSTATE pHostState = &pVCpu->cpum.GstCtx.hwvirt.svm.HostState;
341 bool const fHostInPaeMode = CPUMIsPaePagingEnabled(pHostState->uCr0, pHostState->uCr4, pHostState->uEferMsr);
342 if (fHostInPaeMode)
343 rcStrict = PGMGstMapPaePdpesAtCr3(pVCpu, pHostState->uCr3);
344 if (RT_SUCCESS(rcStrict))
345 {
346 /*
347 * Reload the host state.
348 */
349 CPUMSvmVmExitRestoreHostState(pVCpu, IEM_GET_CTX(pVCpu));
350
351 /*
352 * Update PGM, IEM and others of a world-switch.
353 */
354 rcStrict = iemSvmWorldSwitch(pVCpu, 0 /*cbInstr - whatever*/);
355 if (rcStrict == VINF_SUCCESS)
356 rcStrict = VINF_SVM_VMEXIT;
357 else if (RT_SUCCESS(rcStrict))
358 {
359 LogFlow(("iemSvmVmexit: Setting passup status from iemSvmWorldSwitch %Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
360 iemSetPassUpStatus(pVCpu, rcStrict);
361 rcStrict = VINF_SVM_VMEXIT;
362 }
363 else
364 LogFlow(("iemSvmVmexit: iemSvmWorldSwitch unexpected failure. rc=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
365 }
366 else
367 {
368 Log(("iemSvmVmexit: PAE PDPEs invalid while restoring host state. rc=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
369 rcStrict = VINF_EM_TRIPLE_FAULT;
370 }
371 }
372 else
373 {
374 AssertMsgFailed(("iemSvmVmexit: Mapping VMCB at %#RGp failed. rc=%Rrc\n", pVCpu->cpum.GstCtx.hwvirt.svm.GCPhysVmcb, VBOXSTRICTRC_VAL(rcStrict)));
375 rcStrict = VINF_EM_TRIPLE_FAULT;
376 }
377 }
378 else
379 {
380 AssertMsgFailed(("iemSvmVmexit: Not in SVM guest mode! uExitCode=%#RX64 uExitInfo1=%#RX64 uExitInfo2=%#RX64\n", uExitCode, uExitInfo1, uExitInfo2));
381 rcStrict = VERR_SVM_IPE_3;
382 }
383
384# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && defined(IN_RING3)
385 /* CLGI/STGI may not have been intercepted and thus not executed in IEM. */
386 if ( HMIsEnabled(pVCpu->CTX_SUFF(pVM))
387 && HMIsSvmVGifActive(pVCpu->CTX_SUFF(pVM)))
388 return EMR3SetExecutionPolicy(pVCpu->CTX_SUFF(pVM)->pUVM, EMEXECPOLICY_IEM_ALL, false);
389# endif
390 return rcStrict;
391}
392
393
394/**
395 * Interface for HM and EM to emulate \#VMEXIT.
396 *
397 * @returns Strict VBox status code.
398 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
399 * @param uExitCode The exit code.
400 * @param uExitInfo1 The exit info. 1 field.
401 * @param uExitInfo2 The exit info. 2 field.
402 * @thread EMT(pVCpu)
403 */
404VMM_INT_DECL(VBOXSTRICTRC) IEMExecSvmVmexit(PVMCPUCC pVCpu, uint64_t uExitCode, uint64_t uExitInfo1, uint64_t uExitInfo2)
405{
406 IEM_CTX_ASSERT(pVCpu, IEM_CPUMCTX_EXTRN_SVM_VMEXIT_MASK);
407 VBOXSTRICTRC rcStrict = iemSvmVmexit(pVCpu, uExitCode, uExitInfo1, uExitInfo2);
408 if (pVCpu->iem.s.cActiveMappings)
409 iemMemRollback(pVCpu);
410 return iemExecStatusCodeFiddling(pVCpu, rcStrict);
411}
412
413
414/**
415 * Performs the operations necessary that are part of the vmrun instruction
416 * execution in the guest.
417 *
418 * @returns Strict VBox status code (i.e. informational status codes too).
419 * @retval VINF_SUCCESS successfully executed VMRUN and entered nested-guest
420 * code execution.
421 * @retval VINF_SVM_VMEXIT when executing VMRUN causes a \#VMEXIT
422 * (SVM_EXIT_INVALID most likely).
423 *
424 * @param pVCpu The cross context virtual CPU structure.
425 * @param cbInstr The length of the VMRUN instruction.
426 * @param GCPhysVmcb Guest physical address of the VMCB to run.
427 */
428static VBOXSTRICTRC iemSvmVmrun(PVMCPUCC pVCpu, uint8_t cbInstr, RTGCPHYS GCPhysVmcb) RT_NOEXCEPT
429{
430 LogFlow(("iemSvmVmrun\n"));
431
432 /*
433 * Cache the physical address of the VMCB for #VMEXIT exceptions.
434 */
435 pVCpu->cpum.GstCtx.hwvirt.svm.GCPhysVmcb = GCPhysVmcb;
436
437 /*
438 * Save the host state.
439 */
440 CPUMSvmVmRunSaveHostState(IEM_GET_CTX(pVCpu), cbInstr);
441
442 /*
443 * Read the guest VMCB.
444 */
445 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
446 int rc = PGMPhysSimpleReadGCPhys(pVM, &pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb, GCPhysVmcb, sizeof(SVMVMCB));
447 if (RT_SUCCESS(rc))
448 {
449 /*
450 * AMD-V seems to preserve reserved fields and only writes back selected, recognized
451 * fields on #VMEXIT. However, not all reserved bits are preserved (e.g, EXITINTINFO)
452 * but in our implementation we try to preserve as much as we possibly can.
453 *
454 * We could read the entire page here and only write back the relevant fields on
455 * #VMEXIT but since our internal VMCB is also being used by HM during hardware-assisted
456 * SVM execution, it creates a potential for a nested-hypervisor to set bits that are
457 * currently reserved but may be recognized as features bits in future CPUs causing
458 * unexpected & undesired results. Hence, we zero out unrecognized fields here as we
459 * typically enter hardware-assisted SVM soon anyway, see @bugref{7243#c113}.
460 */
461 PSVMVMCBCTRL pVmcbCtrl = &pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.ctrl;
462 PSVMVMCBSTATESAVE pVmcbNstGst = &pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.guest;
463
464 RT_ZERO(pVmcbCtrl->u8Reserved0);
465 RT_ZERO(pVmcbCtrl->u8Reserved1);
466 RT_ZERO(pVmcbCtrl->u8Reserved2);
467 RT_ZERO(pVmcbNstGst->u8Reserved0);
468 RT_ZERO(pVmcbNstGst->u8Reserved1);
469 RT_ZERO(pVmcbNstGst->u8Reserved2);
470 RT_ZERO(pVmcbNstGst->u8Reserved3);
471 RT_ZERO(pVmcbNstGst->u8Reserved4);
472 RT_ZERO(pVmcbNstGst->u8Reserved5);
473 pVmcbCtrl->u32Reserved0 = 0;
474 pVmcbCtrl->TLBCtrl.n.u24Reserved = 0;
475 pVmcbCtrl->IntCtrl.n.u6Reserved = 0;
476 pVmcbCtrl->IntCtrl.n.u3Reserved = 0;
477 pVmcbCtrl->IntCtrl.n.u5Reserved = 0;
478 pVmcbCtrl->IntCtrl.n.u24Reserved = 0;
479 pVmcbCtrl->IntShadow.n.u30Reserved = 0;
480 pVmcbCtrl->ExitIntInfo.n.u19Reserved = 0;
481 pVmcbCtrl->NestedPagingCtrl.n.u29Reserved = 0;
482 pVmcbCtrl->EventInject.n.u19Reserved = 0;
483 pVmcbCtrl->LbrVirt.n.u30Reserved = 0;
484
485 /*
486 * Validate guest-state and controls.
487 */
488 /* VMRUN must always be intercepted. */
489 if (!CPUMIsGuestSvmCtrlInterceptSet(pVCpu, IEM_GET_CTX(pVCpu), SVM_CTRL_INTERCEPT_VMRUN))
490 {
491 Log(("iemSvmVmrun: VMRUN instruction not intercepted -> #VMEXIT\n"));
492 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
493 }
494
495 /* Nested paging. */
496 if ( pVmcbCtrl->NestedPagingCtrl.n.u1NestedPaging
497 && !pVM->cpum.ro.GuestFeatures.fSvmNestedPaging)
498 {
499 Log(("iemSvmVmrun: Nested paging not supported -> Disabling\n"));
500 pVmcbCtrl->NestedPagingCtrl.n.u1NestedPaging = 0;
501 }
502
503 /* AVIC. */
504 if ( pVmcbCtrl->IntCtrl.n.u1AvicEnable
505 && !pVM->cpum.ro.GuestFeatures.fSvmAvic)
506 {
507 Log(("iemSvmVmrun: AVIC not supported -> Disabling\n"));
508 pVmcbCtrl->IntCtrl.n.u1AvicEnable = 0;
509 }
510
511 /* Last branch record (LBR) virtualization. */
512 if ( pVmcbCtrl->LbrVirt.n.u1LbrVirt
513 && !pVM->cpum.ro.GuestFeatures.fSvmLbrVirt)
514 {
515 Log(("iemSvmVmrun: LBR virtualization not supported -> Disabling\n"));
516 pVmcbCtrl->LbrVirt.n.u1LbrVirt = 0;
517 }
518
519 /* Virtualized VMSAVE/VMLOAD. */
520 if ( pVmcbCtrl->LbrVirt.n.u1VirtVmsaveVmload
521 && !pVM->cpum.ro.GuestFeatures.fSvmVirtVmsaveVmload)
522 {
523 Log(("iemSvmVmrun: Virtualized VMSAVE/VMLOAD not supported -> Disabling\n"));
524 pVmcbCtrl->LbrVirt.n.u1VirtVmsaveVmload = 0;
525 }
526
527 /* Virtual GIF. */
528 if ( pVmcbCtrl->IntCtrl.n.u1VGifEnable
529 && !pVM->cpum.ro.GuestFeatures.fSvmVGif)
530 {
531 Log(("iemSvmVmrun: Virtual GIF not supported -> Disabling\n"));
532 pVmcbCtrl->IntCtrl.n.u1VGifEnable = 0;
533 }
534
535 /* Guest ASID. */
536 if (!pVmcbCtrl->TLBCtrl.n.u32ASID)
537 {
538 Log(("iemSvmVmrun: Guest ASID is invalid -> #VMEXIT\n"));
539 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
540 }
541
542 /* Guest AVIC. */
543 if ( pVmcbCtrl->IntCtrl.n.u1AvicEnable
544 && !pVM->cpum.ro.GuestFeatures.fSvmAvic)
545 {
546 Log(("iemSvmVmrun: AVIC not supported -> Disabling\n"));
547 pVmcbCtrl->IntCtrl.n.u1AvicEnable = 0;
548 }
549
550 /* Guest Secure Encrypted Virtualization. */
551 if ( ( pVmcbCtrl->NestedPagingCtrl.n.u1Sev
552 || pVmcbCtrl->NestedPagingCtrl.n.u1SevEs)
553 && !pVM->cpum.ro.GuestFeatures.fSvmAvic)
554 {
555 Log(("iemSvmVmrun: SEV not supported -> Disabling\n"));
556 pVmcbCtrl->NestedPagingCtrl.n.u1Sev = 0;
557 pVmcbCtrl->NestedPagingCtrl.n.u1SevEs = 0;
558 }
559
560 /* Flush by ASID. */
561 if ( !pVM->cpum.ro.GuestFeatures.fSvmFlusbByAsid
562 && pVmcbCtrl->TLBCtrl.n.u8TLBFlush != SVM_TLB_FLUSH_NOTHING
563 && pVmcbCtrl->TLBCtrl.n.u8TLBFlush != SVM_TLB_FLUSH_ENTIRE)
564 {
565 Log(("iemSvmVmrun: Flush-by-ASID not supported -> #VMEXIT\n"));
566 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
567 }
568
569 /* IO permission bitmap. */
570 RTGCPHYS const GCPhysIOBitmap = pVmcbCtrl->u64IOPMPhysAddr;
571 if ( (GCPhysIOBitmap & X86_PAGE_4K_OFFSET_MASK)
572 || !PGMPhysIsGCPhysNormal(pVM, GCPhysIOBitmap)
573 || !PGMPhysIsGCPhysNormal(pVM, GCPhysIOBitmap + X86_PAGE_4K_SIZE)
574 || !PGMPhysIsGCPhysNormal(pVM, GCPhysIOBitmap + (X86_PAGE_4K_SIZE << 1)))
575 {
576 Log(("iemSvmVmrun: IO bitmap physaddr invalid. GCPhysIOBitmap=%#RX64 -> #VMEXIT\n", GCPhysIOBitmap));
577 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
578 }
579
580 /* MSR permission bitmap. */
581 RTGCPHYS const GCPhysMsrBitmap = pVmcbCtrl->u64MSRPMPhysAddr;
582 if ( (GCPhysMsrBitmap & X86_PAGE_4K_OFFSET_MASK)
583 || !PGMPhysIsGCPhysNormal(pVM, GCPhysMsrBitmap)
584 || !PGMPhysIsGCPhysNormal(pVM, GCPhysMsrBitmap + X86_PAGE_4K_SIZE))
585 {
586 Log(("iemSvmVmrun: MSR bitmap physaddr invalid. GCPhysMsrBitmap=%#RX64 -> #VMEXIT\n", GCPhysMsrBitmap));
587 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
588 }
589
590 /* CR0. */
591 if ( !(pVmcbNstGst->u64CR0 & X86_CR0_CD)
592 && (pVmcbNstGst->u64CR0 & X86_CR0_NW))
593 {
594 Log(("iemSvmVmrun: CR0 no-write through with cache disabled. CR0=%#RX64 -> #VMEXIT\n", pVmcbNstGst->u64CR0));
595 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
596 }
597 if (pVmcbNstGst->u64CR0 >> 32)
598 {
599 Log(("iemSvmVmrun: CR0 reserved bits set. CR0=%#RX64 -> #VMEXIT\n", pVmcbNstGst->u64CR0));
600 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
601 }
602 /** @todo Implement all reserved bits/illegal combinations for CR3, CR4. */
603
604 /* DR6 and DR7. */
605 if ( pVmcbNstGst->u64DR6 >> 32
606 || pVmcbNstGst->u64DR7 >> 32)
607 {
608 Log(("iemSvmVmrun: DR6 and/or DR7 reserved bits set. DR6=%#RX64 DR7=%#RX64 -> #VMEXIT\n", pVmcbNstGst->u64DR6,
609 pVmcbNstGst->u64DR6));
610 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
611 }
612
613 /*
614 * PAT (Page Attribute Table) MSR.
615 *
616 * The CPU only validates and loads it when nested-paging is enabled.
617 * See AMD spec. "15.25.4 Nested Paging and VMRUN/#VMEXIT".
618 */
619 if ( pVmcbCtrl->NestedPagingCtrl.n.u1NestedPaging
620 && !CPUMIsPatMsrValid(pVmcbNstGst->u64PAT))
621 {
622 Log(("iemSvmVmrun: PAT invalid. u64PAT=%#RX64 -> #VMEXIT\n", pVmcbNstGst->u64PAT));
623 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
624 }
625
626 /*
627 * Copy the IO permission bitmap into the cache.
628 */
629 AssertCompile(sizeof(pVCpu->cpum.GstCtx.hwvirt.svm.abIoBitmap) == SVM_IOPM_PAGES * X86_PAGE_4K_SIZE);
630 rc = PGMPhysSimpleReadGCPhys(pVM, pVCpu->cpum.GstCtx.hwvirt.svm.abIoBitmap, GCPhysIOBitmap,
631 sizeof(pVCpu->cpum.GstCtx.hwvirt.svm.abIoBitmap));
632 if (RT_FAILURE(rc))
633 {
634 Log(("iemSvmVmrun: Failed reading the IO permission bitmap at %#RGp. rc=%Rrc\n", GCPhysIOBitmap, rc));
635 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
636 }
637
638 /*
639 * Copy the MSR permission bitmap into the cache.
640 */
641 AssertCompile(sizeof(pVCpu->cpum.GstCtx.hwvirt.svm.abMsrBitmap) == SVM_MSRPM_PAGES * X86_PAGE_4K_SIZE);
642 rc = PGMPhysSimpleReadGCPhys(pVM, pVCpu->cpum.GstCtx.hwvirt.svm.abMsrBitmap, GCPhysMsrBitmap,
643 sizeof(pVCpu->cpum.GstCtx.hwvirt.svm.abMsrBitmap));
644 if (RT_FAILURE(rc))
645 {
646 Log(("iemSvmVmrun: Failed reading the MSR permission bitmap at %#RGp. rc=%Rrc\n", GCPhysMsrBitmap, rc));
647 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
648 }
649
650 /*
651 * Copy segments from nested-guest VMCB state to the guest-CPU state.
652 *
653 * We do this here as we need to use the CS attributes and it's easier this way
654 * then using the VMCB format selectors. It doesn't really matter where we copy
655 * the state, we restore the guest-CPU context state on the \#VMEXIT anyway.
656 */
657 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), pVmcbNstGst, ES, es);
658 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), pVmcbNstGst, CS, cs);
659 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), pVmcbNstGst, SS, ss);
660 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), pVmcbNstGst, DS, ds);
661
662 /** @todo Segment attribute overrides by VMRUN. */
663
664 /*
665 * CPL adjustments and overrides.
666 *
667 * SS.DPL is apparently the CPU's CPL, see comment in CPUMGetGuestCPL().
668 * We shall thus adjust both CS.DPL and SS.DPL here.
669 */
670 pVCpu->cpum.GstCtx.cs.Attr.n.u2Dpl = pVCpu->cpum.GstCtx.ss.Attr.n.u2Dpl = pVmcbNstGst->u8CPL;
671 if (CPUMIsGuestInV86ModeEx(IEM_GET_CTX(pVCpu)))
672 pVCpu->cpum.GstCtx.cs.Attr.n.u2Dpl = pVCpu->cpum.GstCtx.ss.Attr.n.u2Dpl = 3;
673 if (CPUMIsGuestInRealModeEx(IEM_GET_CTX(pVCpu)))
674 pVCpu->cpum.GstCtx.cs.Attr.n.u2Dpl = pVCpu->cpum.GstCtx.ss.Attr.n.u2Dpl = 0;
675 Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pVCpu->cpum.GstCtx.ss));
676
677 /*
678 * Continue validating guest-state and controls.
679 *
680 * We pass CR0 as 0 to CPUMIsGuestEferMsrWriteValid() below to skip the illegal
681 * EFER.LME bit transition check. We pass the nested-guest's EFER as both the
682 * old and new EFER value to not have any guest EFER bits influence the new
683 * nested-guest EFER.
684 */
685 uint64_t uValidEfer;
686 rc = CPUMIsGuestEferMsrWriteValid(pVM, 0 /* CR0 */, pVmcbNstGst->u64EFER, pVmcbNstGst->u64EFER, &uValidEfer);
687 if (RT_FAILURE(rc))
688 {
689 Log(("iemSvmVmrun: EFER invalid uOldEfer=%#RX64 -> #VMEXIT\n", pVmcbNstGst->u64EFER));
690 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
691 }
692
693 /* Validate paging and CPU mode bits. */
694 bool const fSvm = RT_BOOL(uValidEfer & MSR_K6_EFER_SVME);
695 bool const fLongModeSupported = RT_BOOL(pVM->cpum.ro.GuestFeatures.fLongMode);
696 bool const fLongModeEnabled = RT_BOOL(uValidEfer & MSR_K6_EFER_LME);
697 bool const fPaging = RT_BOOL(pVmcbNstGst->u64CR0 & X86_CR0_PG);
698 bool const fPae = RT_BOOL(pVmcbNstGst->u64CR4 & X86_CR4_PAE);
699 bool const fProtMode = RT_BOOL(pVmcbNstGst->u64CR0 & X86_CR0_PE);
700 bool const fLongModeWithPaging = fLongModeEnabled && fPaging;
701 bool const fLongModeConformCS = pVCpu->cpum.GstCtx.cs.Attr.n.u1Long && pVCpu->cpum.GstCtx.cs.Attr.n.u1DefBig;
702 /* Adjust EFER.LMA (this is normally done by the CPU when system software writes CR0). */
703 if (fLongModeWithPaging)
704 uValidEfer |= MSR_K6_EFER_LMA;
705 bool const fLongModeActiveOrEnabled = RT_BOOL(uValidEfer & (MSR_K6_EFER_LME | MSR_K6_EFER_LMA));
706 if ( !fSvm
707 || (!fLongModeSupported && fLongModeActiveOrEnabled)
708 || (fLongModeWithPaging && !fPae)
709 || (fLongModeWithPaging && !fProtMode)
710 || ( fLongModeEnabled
711 && fPaging
712 && fPae
713 && fLongModeConformCS))
714 {
715 Log(("iemSvmVmrun: EFER invalid. uValidEfer=%#RX64 -> #VMEXIT\n", uValidEfer));
716 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
717 }
718
719 /*
720 * Preserve the required force-flags.
721 *
722 * We only preserve the force-flags that would affect the execution of the
723 * nested-guest (or the guest).
724 *
725 * - VMCPU_FF_BLOCK_NMIS needs to be preserved as it blocks NMI until the
726 * execution of a subsequent IRET instruction in the guest.
727 *
728 * The remaining FFs (e.g. timers) can stay in place so that we will be able to
729 * generate interrupts that should cause #VMEXITs for the nested-guest.
730 *
731 * VMRUN has implicit GIF (Global Interrupt Flag) handling, we don't need to
732 * preserve VMCPU_FF_INHIBIT_INTERRUPTS.
733 */
734 pVCpu->cpum.GstCtx.hwvirt.fSavedInhibit = pVCpu->cpum.GstCtx.eflags.uBoth & CPUMCTX_INHIBIT_NMI;
735 pVCpu->cpum.GstCtx.eflags.uBoth &= ~CPUMCTX_INHIBIT_NMI;
736
737 /*
738 * Pause filter.
739 */
740 if (pVM->cpum.ro.GuestFeatures.fSvmPauseFilter)
741 {
742 pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilter = pVmcbCtrl->u16PauseFilterCount;
743 if (pVM->cpum.ro.GuestFeatures.fSvmPauseFilterThreshold)
744 pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilterThreshold = pVmcbCtrl->u16PauseFilterCount;
745 }
746
747 /*
748 * Interrupt shadow.
749 */
750 if (pVmcbCtrl->IntShadow.n.u1IntShadow)
751 {
752 LogFlow(("iemSvmVmrun: setting interrupt shadow. inhibit PC=%#RX64\n", pVmcbNstGst->u64RIP));
753 /** @todo will this cause trouble if the nested-guest is 64-bit but the guest is 32-bit? */
754 CPUMSetInInterruptShadowEx(&pVCpu->cpum.GstCtx, pVmcbNstGst->u64RIP);
755 }
756
757 /*
758 * TLB flush control.
759 * Currently disabled since it's redundant as we unconditionally flush the TLB
760 * in iemSvmWorldSwitch() below.
761 */
762# if 0
763 /** @todo @bugref{7243}: ASID based PGM TLB flushes. */
764 if ( pVmcbCtrl->TLBCtrl.n.u8TLBFlush == SVM_TLB_FLUSH_ENTIRE
765 || pVmcbCtrl->TLBCtrl.n.u8TLBFlush == SVM_TLB_FLUSH_SINGLE_CONTEXT
766 || pVmcbCtrl->TLBCtrl.n.u8TLBFlush == SVM_TLB_FLUSH_SINGLE_CONTEXT_RETAIN_GLOBALS)
767 PGMFlushTLB(pVCpu, pVmcbNstGst->u64CR3, true /* fGlobal */);
768# endif
769
770 /*
771 * Validate and map PAE PDPEs if the guest will be using PAE paging.
772 * Invalid PAE PDPEs here causes a #VMEXIT.
773 */
774 if ( !pVmcbCtrl->NestedPagingCtrl.n.u1NestedPaging
775 && CPUMIsPaePagingEnabled(pVmcbNstGst->u64CR0, pVmcbNstGst->u64CR4, uValidEfer))
776 {
777 rc = PGMGstMapPaePdpesAtCr3(pVCpu, pVmcbNstGst->u64CR3);
778 if (RT_SUCCESS(rc))
779 { /* likely */ }
780 else
781 {
782 Log(("iemSvmVmrun: PAE PDPEs invalid -> #VMEXIT\n"));
783 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
784 }
785 }
786
787 /*
788 * Copy the remaining guest state from the VMCB to the guest-CPU context.
789 */
790 pVCpu->cpum.GstCtx.gdtr.cbGdt = pVmcbNstGst->GDTR.u32Limit;
791 pVCpu->cpum.GstCtx.gdtr.pGdt = pVmcbNstGst->GDTR.u64Base;
792 pVCpu->cpum.GstCtx.idtr.cbIdt = pVmcbNstGst->IDTR.u32Limit;
793 pVCpu->cpum.GstCtx.idtr.pIdt = pVmcbNstGst->IDTR.u64Base;
794 CPUMSetGuestCR0(pVCpu, pVmcbNstGst->u64CR0);
795 CPUMSetGuestCR4(pVCpu, pVmcbNstGst->u64CR4);
796 pVCpu->cpum.GstCtx.cr3 = pVmcbNstGst->u64CR3;
797 pVCpu->cpum.GstCtx.cr2 = pVmcbNstGst->u64CR2;
798 pVCpu->cpum.GstCtx.dr[6] = pVmcbNstGst->u64DR6;
799 pVCpu->cpum.GstCtx.dr[7] = pVmcbNstGst->u64DR7;
800 pVCpu->cpum.GstCtx.rflags.u = pVmcbNstGst->u64RFlags;
801 pVCpu->cpum.GstCtx.rax = pVmcbNstGst->u64RAX;
802 pVCpu->cpum.GstCtx.rsp = pVmcbNstGst->u64RSP;
803 pVCpu->cpum.GstCtx.rip = pVmcbNstGst->u64RIP;
804 CPUMSetGuestEferMsrNoChecks(pVCpu, pVCpu->cpum.GstCtx.msrEFER, uValidEfer);
805 if (pVmcbCtrl->NestedPagingCtrl.n.u1NestedPaging)
806 pVCpu->cpum.GstCtx.msrPAT = pVmcbNstGst->u64PAT;
807
808 /* Mask DR6, DR7 bits mandatory set/clear bits. */
809 pVCpu->cpum.GstCtx.dr[6] &= ~(X86_DR6_RAZ_MASK | X86_DR6_MBZ_MASK);
810 pVCpu->cpum.GstCtx.dr[6] |= X86_DR6_RA1_MASK;
811 pVCpu->cpum.GstCtx.dr[7] &= ~(X86_DR7_RAZ_MASK | X86_DR7_MBZ_MASK);
812 pVCpu->cpum.GstCtx.dr[7] |= X86_DR7_RA1_MASK;
813
814 /*
815 * Check for pending virtual interrupts.
816 */
817 if (pVmcbCtrl->IntCtrl.n.u1VIrqPending)
818 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST);
819 else
820 Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST));
821
822 /*
823 * Update PGM, IEM and others of a world-switch.
824 */
825 VBOXSTRICTRC rcStrict = iemSvmWorldSwitch(pVCpu, cbInstr);
826 if (rcStrict == VINF_SUCCESS)
827 { /* likely */ }
828 else if (RT_SUCCESS(rcStrict))
829 {
830 LogFlow(("iemSvmVmrun: iemSvmWorldSwitch returned %Rrc, setting passup status\n", VBOXSTRICTRC_VAL(rcStrict)));
831 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
832 }
833 else
834 {
835 LogFlow(("iemSvmVmrun: iemSvmWorldSwitch unexpected failure. rc=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
836 return rcStrict;
837 }
838
839 /*
840 * Set the global-interrupt flag to allow interrupts in the guest.
841 */
842 CPUMSetGuestGif(&pVCpu->cpum.GstCtx, true);
843
844 /*
845 * Event injection.
846 */
847 PCSVMEVENT pEventInject = &pVmcbCtrl->EventInject;
848 pVCpu->cpum.GstCtx.hwvirt.svm.fInterceptEvents = !pEventInject->n.u1Valid;
849 if (pEventInject->n.u1Valid)
850 {
851 uint8_t const uVector = pEventInject->n.u8Vector;
852 TRPMEVENT const enmType = HMSvmEventToTrpmEventType(pEventInject, uVector);
853 uint16_t const uErrorCode = pEventInject->n.u1ErrorCodeValid ? pEventInject->n.u32ErrorCode : 0;
854
855 /* Validate vectors for hardware exceptions, see AMD spec. 15.20 "Event Injection". */
856 if (RT_UNLIKELY(enmType == TRPM_32BIT_HACK))
857 {
858 Log(("iemSvmVmrun: Invalid event type =%#x -> #VMEXIT\n", (uint8_t)pEventInject->n.u3Type));
859 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
860 }
861 if (pEventInject->n.u3Type == SVM_EVENT_EXCEPTION)
862 {
863 if ( uVector == X86_XCPT_NMI
864 || uVector > X86_XCPT_LAST)
865 {
866 Log(("iemSvmVmrun: Invalid vector for hardware exception. uVector=%#x -> #VMEXIT\n", uVector));
867 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
868 }
869 if ( uVector == X86_XCPT_BR
870 && CPUMIsGuestInLongModeEx(IEM_GET_CTX(pVCpu)))
871 {
872 Log(("iemSvmVmrun: Cannot inject #BR when not in long mode -> #VMEXIT\n"));
873 return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
874 }
875 /** @todo any others? */
876 }
877
878 /*
879 * Invalidate the exit interrupt-information field here. This field is fully updated
880 * on #VMEXIT as events other than the one below can also cause intercepts during
881 * their injection (e.g. exceptions).
882 */
883 pVmcbCtrl->ExitIntInfo.n.u1Valid = 0;
884
885 /*
886 * Clear the event injection valid bit here. While the AMD spec. mentions that the CPU
887 * clears this bit from the VMCB unconditionally on #VMEXIT, internally the CPU could be
888 * clearing it at any time, most likely before/after injecting the event. Since VirtualBox
889 * doesn't have any virtual-CPU internal representation of this bit, we clear/update the
890 * VMCB here. This also has the added benefit that we avoid the risk of injecting the event
891 * twice if we fallback to executing the nested-guest using hardware-assisted SVM after
892 * injecting the event through IEM here.
893 */
894 pVmcbCtrl->EventInject.n.u1Valid = 0;
895
896 /** @todo NRIP: Software interrupts can only be pushed properly if we support
897 * NRIP for the nested-guest to calculate the instruction length
898 * below. */
899 LogFlow(("iemSvmVmrun: Injecting event: %04x:%08RX64 vec=%#x type=%d uErr=%u cr2=%#RX64 cr3=%#RX64 efer=%#RX64\n",
900 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, uVector, enmType, uErrorCode, pVCpu->cpum.GstCtx.cr2,
901 pVCpu->cpum.GstCtx.cr3, pVCpu->cpum.GstCtx.msrEFER));
902
903 /*
904 * We shall not inject the event here right away. There may be paging mode related updates
905 * as a result of the world-switch above that are yet to be honored. Instead flag the event
906 * as pending for injection.
907 */
908 TRPMAssertTrap(pVCpu, uVector, enmType);
909 if (pEventInject->n.u1ErrorCodeValid)
910 TRPMSetErrorCode(pVCpu, uErrorCode);
911 if ( enmType == TRPM_TRAP
912 && uVector == X86_XCPT_PF)
913 TRPMSetFaultAddress(pVCpu, pVCpu->cpum.GstCtx.cr2);
914 }
915 else
916 LogFlow(("iemSvmVmrun: Entering nested-guest: %04x:%08RX64 cr0=%#RX64 cr3=%#RX64 cr4=%#RX64 efer=%#RX64 efl=%#x\n",
917 pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.cr0, pVCpu->cpum.GstCtx.cr3,
918 pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER, pVCpu->cpum.GstCtx.eflags.u));
919
920 LogFlow(("iemSvmVmrun: returns %d\n", VBOXSTRICTRC_VAL(rcStrict)));
921
922# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && defined(IN_RING3)
923 /* If CLGI/STGI isn't intercepted we force IEM-only nested-guest execution here. */
924 if ( HMIsEnabled(pVM)
925 && HMIsSvmVGifActive(pVM))
926 return EMR3SetExecutionPolicy(pVCpu->CTX_SUFF(pVM)->pUVM, EMEXECPOLICY_IEM_ALL, true);
927# endif
928
929 return rcStrict;
930 }
931
932 /* Shouldn't really happen as the caller should've validated the physical address already. */
933 Log(("iemSvmVmrun: Failed to read nested-guest VMCB at %#RGp (rc=%Rrc) -> #VMEXIT\n", GCPhysVmcb, rc));
934 return rc;
935}
936
937
938/**
939 * Checks if the event intercepts and performs the \#VMEXIT if the corresponding
940 * intercept is active.
941 *
942 * @returns Strict VBox status code.
943 * @retval VINF_HM_INTERCEPT_NOT_ACTIVE if the intercept is not active or
944 * we're not executing a nested-guest.
945 * @retval VINF_SVM_VMEXIT if the intercept is active and the \#VMEXIT occurred
946 * successfully.
947 * @retval VERR_SVM_VMEXIT_FAILED if the intercept is active and the \#VMEXIT
948 * failed and a shutdown needs to be initiated for the guest.
949 *
950 * @returns VBox strict status code.
951 * @param pVCpu The cross context virtual CPU structure of the calling thread.
952 * @param cbInstr The length of the instruction in bytes triggering the
953 * event.
954 * @param u8Vector The interrupt or exception vector.
955 * @param fFlags The exception flags (see IEM_XCPT_FLAGS_XXX).
956 * @param uErr The error-code associated with the exception.
957 * @param uCr2 The CR2 value in case of a \#PF exception.
958 */
959VBOXSTRICTRC iemHandleSvmEventIntercept(PVMCPUCC pVCpu, uint8_t cbInstr, uint8_t u8Vector, uint32_t fFlags,
960 uint32_t uErr, uint64_t uCr2) RT_NOEXCEPT
961{
962 Assert(CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)));
963
964 /*
965 * Handle SVM exception and software interrupt intercepts, see AMD spec. 15.12 "Exception Intercepts".
966 *
967 * - NMI intercepts have their own exit code and do not cause SVM_EXIT_XCPT_2 #VMEXITs.
968 * - External interrupts and software interrupts (INTn instruction) do not check the exception intercepts
969 * even when they use a vector in the range 0 to 31.
970 * - ICEBP should not trigger #DB intercept, but its own intercept.
971 * - For #PF exceptions, its intercept is checked before CR2 is written by the exception.
972 */
973 /* Check NMI intercept */
974 if ( u8Vector == X86_XCPT_NMI
975 && (fFlags & IEM_XCPT_FLAGS_T_CPU_XCPT)
976 && IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_NMI))
977 {
978 Log2(("iemHandleSvmNstGstEventIntercept: NMI intercept -> #VMEXIT\n"));
979 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_NMI, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
980 }
981
982 /* Check ICEBP intercept. */
983 if ( (fFlags & IEM_XCPT_FLAGS_ICEBP_INSTR)
984 && IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_ICEBP))
985 {
986 Log2(("iemHandleSvmNstGstEventIntercept: ICEBP intercept -> #VMEXIT\n"));
987 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
988 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_ICEBP, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
989 }
990
991 /* Check CPU exception intercepts. */
992 if ( (fFlags & IEM_XCPT_FLAGS_T_CPU_XCPT)
993 && IEM_SVM_IS_XCPT_INTERCEPT_SET(pVCpu, u8Vector))
994 {
995 Assert(u8Vector <= X86_XCPT_LAST);
996 uint64_t const uExitInfo1 = fFlags & IEM_XCPT_FLAGS_ERR ? uErr : 0;
997 uint64_t const uExitInfo2 = fFlags & IEM_XCPT_FLAGS_CR2 ? uCr2 : 0;
998 if ( IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmDecodeAssists
999 && u8Vector == X86_XCPT_PF
1000 && !(uErr & X86_TRAP_PF_ID))
1001 {
1002 PSVMVMCBCTRL pVmcbCtrl = &pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.ctrl;
1003# ifdef IEM_WITH_CODE_TLB
1004 uint8_t const *pbInstrBuf = pVCpu->iem.s.pbInstrBuf;
1005 uint8_t const cbInstrBuf = pVCpu->iem.s.cbInstrBuf;
1006 pVmcbCtrl->cbInstrFetched = RT_MIN(cbInstrBuf, SVM_CTRL_GUEST_INSTR_BYTES_MAX);
1007 if ( pbInstrBuf
1008 && cbInstrBuf > 0)
1009 memcpy(&pVmcbCtrl->abInstr[0], pbInstrBuf, pVmcbCtrl->cbInstrFetched);
1010# else
1011 uint8_t const cbOpcode = pVCpu->iem.s.cbOpcode;
1012 pVmcbCtrl->cbInstrFetched = RT_MIN(cbOpcode, SVM_CTRL_GUEST_INSTR_BYTES_MAX);
1013 if (cbOpcode > 0)
1014 memcpy(&pVmcbCtrl->abInstr[0], &pVCpu->iem.s.abOpcode[0], pVmcbCtrl->cbInstrFetched);
1015# endif
1016 }
1017 if (u8Vector == X86_XCPT_BR)
1018 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
1019 Log2(("iemHandleSvmNstGstEventIntercept: Xcpt intercept u32InterceptXcpt=%#RX32 u8Vector=%#x "
1020 "uExitInfo1=%#RX64 uExitInfo2=%#RX64 -> #VMEXIT\n", pVCpu->cpum.GstCtx.hwvirt.svm.Vmcb.ctrl.u32InterceptXcpt,
1021 u8Vector, uExitInfo1, uExitInfo2));
1022 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_XCPT_0 + u8Vector, uExitInfo1, uExitInfo2);
1023 }
1024
1025 /* Check software interrupt (INTn) intercepts. */
1026 if ( (fFlags & ( IEM_XCPT_FLAGS_T_SOFT_INT
1027 | IEM_XCPT_FLAGS_BP_INSTR
1028 | IEM_XCPT_FLAGS_ICEBP_INSTR
1029 | IEM_XCPT_FLAGS_OF_INSTR)) == IEM_XCPT_FLAGS_T_SOFT_INT
1030 && IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_INTN))
1031 {
1032 uint64_t const uExitInfo1 = IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmDecodeAssists ? u8Vector : 0;
1033 Log2(("iemHandleSvmNstGstEventIntercept: Software INT intercept (u8Vector=%#x) -> #VMEXIT\n", u8Vector));
1034 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
1035 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_SWINT, uExitInfo1, 0 /* uExitInfo2 */);
1036 }
1037
1038 return VINF_SVM_INTERCEPT_NOT_ACTIVE;
1039}
1040
1041
1042/**
1043 * Checks the SVM IO permission bitmap and performs the \#VMEXIT if the
1044 * corresponding intercept is active.
1045 *
1046 * @returns Strict VBox status code.
1047 * @retval VINF_HM_INTERCEPT_NOT_ACTIVE if the intercept is not active or
1048 * we're not executing a nested-guest.
1049 * @retval VINF_SVM_VMEXIT if the intercept is active and the \#VMEXIT occurred
1050 * successfully.
1051 * @retval VERR_SVM_VMEXIT_FAILED if the intercept is active and the \#VMEXIT
1052 * failed and a shutdown needs to be initiated for the guest.
1053 *
1054 * @returns VBox strict status code.
1055 * @param pVCpu The cross context virtual CPU structure of the calling thread.
1056 * @param u16Port The IO port being accessed.
1057 * @param enmIoType The type of IO access.
1058 * @param cbReg The IO operand size in bytes.
1059 * @param cAddrSizeBits The address size bits (for 16, 32 or 64).
1060 * @param iEffSeg The effective segment number.
1061 * @param fRep Whether this is a repeating IO instruction (REP prefix).
1062 * @param fStrIo Whether this is a string IO instruction.
1063 * @param cbInstr The length of the IO instruction in bytes.
1064 */
1065VBOXSTRICTRC iemSvmHandleIOIntercept(PVMCPUCC pVCpu, uint16_t u16Port, SVMIOIOTYPE enmIoType, uint8_t cbReg,
1066 uint8_t cAddrSizeBits, uint8_t iEffSeg, bool fRep, bool fStrIo, uint8_t cbInstr) RT_NOEXCEPT
1067{
1068 Assert(IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT));
1069 Assert(cAddrSizeBits == 16 || cAddrSizeBits == 32 || cAddrSizeBits == 64);
1070 Assert(cbReg == 1 || cbReg == 2 || cbReg == 4 || cbReg == 8);
1071
1072 Log3(("iemSvmHandleIOIntercept: u16Port=%#x (%u)\n", u16Port, u16Port));
1073
1074 SVMIOIOEXITINFO IoExitInfo;
1075 bool const fIntercept = CPUMIsSvmIoInterceptSet(pVCpu->cpum.GstCtx.hwvirt.svm.abMsrBitmap, u16Port, enmIoType, cbReg,
1076 cAddrSizeBits, iEffSeg, fRep, fStrIo, &IoExitInfo);
1077 if (fIntercept)
1078 {
1079 Log3(("iemSvmHandleIOIntercept: u16Port=%#x (%u) -> #VMEXIT\n", u16Port, u16Port));
1080 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
1081 return iemSvmVmexit(pVCpu, SVM_EXIT_IOIO, IoExitInfo.u, pVCpu->cpum.GstCtx.rip + cbInstr);
1082 }
1083
1084 /** @todo remove later (for debugging as VirtualBox always traps all IO
1085 * intercepts). */
1086 AssertMsgFailed(("iemSvmHandleIOIntercept: We expect an IO intercept here!\n"));
1087 return VINF_SVM_INTERCEPT_NOT_ACTIVE;
1088}
1089
1090
1091/**
1092 * Checks the SVM MSR permission bitmap and performs the \#VMEXIT if the
1093 * corresponding intercept is active.
1094 *
1095 * @returns Strict VBox status code.
1096 * @retval VINF_HM_INTERCEPT_NOT_ACTIVE if the MSR permission bitmap does not
1097 * specify interception of the accessed MSR @a idMsr.
1098 * @retval VINF_SVM_VMEXIT if the intercept is active and the \#VMEXIT occurred
1099 * successfully.
1100 * @retval VERR_SVM_VMEXIT_FAILED if the intercept is active and the \#VMEXIT
1101 * failed and a shutdown needs to be initiated for the guest.
1102 *
1103 * @param pVCpu The cross context virtual CPU structure.
1104 * @param idMsr The MSR being accessed in the nested-guest.
1105 * @param fWrite Whether this is an MSR write access, @c false implies an
1106 * MSR read.
1107 * @param cbInstr The length of the MSR read/write instruction in bytes.
1108 */
1109VBOXSTRICTRC iemSvmHandleMsrIntercept(PVMCPUCC pVCpu, uint32_t idMsr, bool fWrite, uint8_t cbInstr) RT_NOEXCEPT
1110{
1111 /*
1112 * Check if any MSRs are being intercepted.
1113 */
1114 Assert(CPUMIsGuestSvmCtrlInterceptSet(pVCpu, IEM_GET_CTX(pVCpu), SVM_CTRL_INTERCEPT_MSR_PROT));
1115 Assert(CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)));
1116
1117 uint64_t const uExitInfo1 = fWrite ? SVM_EXIT1_MSR_WRITE : SVM_EXIT1_MSR_READ;
1118
1119 /*
1120 * Get the byte and bit offset of the permission bits corresponding to the MSR.
1121 */
1122 uint16_t offMsrpm;
1123 uint8_t uMsrpmBit;
1124 int rc = CPUMGetSvmMsrpmOffsetAndBit(idMsr, &offMsrpm, &uMsrpmBit);
1125 if (RT_SUCCESS(rc))
1126 {
1127 Assert(uMsrpmBit == 0 || uMsrpmBit == 2 || uMsrpmBit == 4 || uMsrpmBit == 6);
1128 Assert(offMsrpm < SVM_MSRPM_PAGES << X86_PAGE_4K_SHIFT);
1129 if (fWrite)
1130 ++uMsrpmBit;
1131
1132 /*
1133 * Check if the bit is set, if so, trigger a #VMEXIT.
1134 */
1135 if (pVCpu->cpum.GstCtx.hwvirt.svm.abMsrBitmap[offMsrpm] & RT_BIT(uMsrpmBit))
1136 {
1137 IEM_SVM_UPDATE_NRIP(pVCpu, cbInstr);
1138 return iemSvmVmexit(pVCpu, SVM_EXIT_MSR, uExitInfo1, 0 /* uExitInfo2 */);
1139 }
1140 }
1141 else
1142 {
1143 /*
1144 * This shouldn't happen, but if it does, cause a #VMEXIT and let the "host" (nested hypervisor) deal with it.
1145 */
1146 Log(("iemSvmHandleMsrIntercept: Invalid/out-of-range MSR %#RX32 fWrite=%RTbool -> #VMEXIT\n", idMsr, fWrite));
1147 return iemSvmVmexit(pVCpu, SVM_EXIT_MSR, uExitInfo1, 0 /* uExitInfo2 */);
1148 }
1149 return VINF_SVM_INTERCEPT_NOT_ACTIVE;
1150}
1151
1152
1153
1154/**
1155 * Implements 'VMRUN'.
1156 */
1157IEM_CIMPL_DEF_0(iemCImpl_vmrun)
1158{
1159# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && !defined(IN_RING3)
1160 RT_NOREF2(pVCpu, cbInstr);
1161 return VINF_EM_RAW_EMULATE_INSTR;
1162# else
1163 LogFlow(("iemCImpl_vmrun\n"));
1164 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, vmrun);
1165
1166 /** @todo Check effective address size using address size prefix. */
1167 RTGCPHYS const GCPhysVmcb = IEM_IS_64BIT_CODE(pVCpu) ? pVCpu->cpum.GstCtx.rax : pVCpu->cpum.GstCtx.eax;
1168 if ( (GCPhysVmcb & X86_PAGE_4K_OFFSET_MASK)
1169 || !PGMPhysIsGCPhysNormal(pVCpu->CTX_SUFF(pVM), GCPhysVmcb))
1170 {
1171 Log(("vmrun: VMCB physaddr (%#RGp) not valid -> #GP(0)\n", GCPhysVmcb));
1172 return iemRaiseGeneralProtectionFault0(pVCpu);
1173 }
1174
1175 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_VMRUN))
1176 {
1177 Log(("vmrun: Guest intercept -> #VMEXIT\n"));
1178 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_VMRUN, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1179 }
1180
1181 VBOXSTRICTRC rcStrict = iemSvmVmrun(pVCpu, cbInstr, GCPhysVmcb);
1182 if (rcStrict == VERR_SVM_VMEXIT_FAILED)
1183 {
1184 Assert(!CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu)));
1185 rcStrict = VINF_EM_TRIPLE_FAULT;
1186 }
1187 return rcStrict;
1188# endif
1189}
1190
1191
1192/**
1193 * Interface for HM and EM to emulate the VMRUN instruction.
1194 *
1195 * @returns Strict VBox status code.
1196 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1197 * @param cbInstr The instruction length in bytes.
1198 * @thread EMT(pVCpu)
1199 */
1200VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedVmrun(PVMCPUCC pVCpu, uint8_t cbInstr)
1201{
1202 IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 3);
1203 IEM_CTX_ASSERT(pVCpu, IEM_CPUMCTX_EXTRN_SVM_VMRUN_MASK);
1204
1205 iemInitExec(pVCpu, 0 /*fExecOpts*/);
1206 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_0(iemCImpl_vmrun);
1207 Assert(!pVCpu->iem.s.cActiveMappings);
1208 return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
1209}
1210
1211
1212/**
1213 * Implements 'VMLOAD'.
1214 */
1215IEM_CIMPL_DEF_0(iemCImpl_vmload)
1216{
1217# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && !defined(IN_RING3)
1218 RT_NOREF2(pVCpu, cbInstr);
1219 return VINF_EM_RAW_EMULATE_INSTR;
1220# else
1221 LogFlow(("iemCImpl_vmload\n"));
1222 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, vmload);
1223
1224 /** @todo Check effective address size using address size prefix. */
1225 RTGCPHYS const GCPhysVmcb = IEM_IS_64BIT_CODE(pVCpu) ? pVCpu->cpum.GstCtx.rax : pVCpu->cpum.GstCtx.eax;
1226 if ( (GCPhysVmcb & X86_PAGE_4K_OFFSET_MASK)
1227 || !PGMPhysIsGCPhysNormal(pVCpu->CTX_SUFF(pVM), GCPhysVmcb))
1228 {
1229 Log(("vmload: VMCB physaddr (%#RGp) not valid -> #GP(0)\n", GCPhysVmcb));
1230 return iemRaiseGeneralProtectionFault0(pVCpu);
1231 }
1232
1233 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_VMLOAD))
1234 {
1235 Log(("vmload: Guest intercept -> #VMEXIT\n"));
1236 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_VMLOAD, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1237 }
1238
1239 SVMVMCBSTATESAVE VmcbNstGst;
1240 VBOXSTRICTRC rcStrict = PGMPhysSimpleReadGCPhys(pVCpu->CTX_SUFF(pVM), &VmcbNstGst, GCPhysVmcb + RT_UOFFSETOF(SVMVMCB, guest),
1241 sizeof(SVMVMCBSTATESAVE));
1242 if (rcStrict == VINF_SUCCESS)
1243 {
1244 LogFlow(("vmload: Loading VMCB at %#RGp enmEffAddrMode=%d\n", GCPhysVmcb, pVCpu->iem.s.enmEffAddrMode));
1245 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, FS, fs);
1246 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, GS, gs);
1247 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, TR, tr);
1248 HMSVM_SEG_REG_COPY_FROM_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, LDTR, ldtr);
1249
1250 pVCpu->cpum.GstCtx.msrKERNELGSBASE = VmcbNstGst.u64KernelGSBase;
1251 pVCpu->cpum.GstCtx.msrSTAR = VmcbNstGst.u64STAR;
1252 pVCpu->cpum.GstCtx.msrLSTAR = VmcbNstGst.u64LSTAR;
1253 pVCpu->cpum.GstCtx.msrCSTAR = VmcbNstGst.u64CSTAR;
1254 pVCpu->cpum.GstCtx.msrSFMASK = VmcbNstGst.u64SFMASK;
1255
1256 pVCpu->cpum.GstCtx.SysEnter.cs = VmcbNstGst.u64SysEnterCS;
1257 pVCpu->cpum.GstCtx.SysEnter.esp = VmcbNstGst.u64SysEnterESP;
1258 pVCpu->cpum.GstCtx.SysEnter.eip = VmcbNstGst.u64SysEnterEIP;
1259
1260 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1261 }
1262 return rcStrict;
1263# endif
1264}
1265
1266
1267/**
1268 * Interface for HM and EM to emulate the VMLOAD instruction.
1269 *
1270 * @returns Strict VBox status code.
1271 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1272 * @param cbInstr The instruction length in bytes.
1273 * @thread EMT(pVCpu)
1274 */
1275VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedVmload(PVMCPUCC pVCpu, uint8_t cbInstr)
1276{
1277 IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 3);
1278
1279 iemInitExec(pVCpu, 0 /*fExecOpts*/);
1280 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_0(iemCImpl_vmload);
1281 Assert(!pVCpu->iem.s.cActiveMappings);
1282 return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
1283}
1284
1285
1286/**
1287 * Implements 'VMSAVE'.
1288 */
1289IEM_CIMPL_DEF_0(iemCImpl_vmsave)
1290{
1291# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && !defined(IN_RING3)
1292 RT_NOREF2(pVCpu, cbInstr);
1293 return VINF_EM_RAW_EMULATE_INSTR;
1294# else
1295 LogFlow(("iemCImpl_vmsave\n"));
1296 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, vmsave);
1297
1298 /** @todo Check effective address size using address size prefix. */
1299 RTGCPHYS const GCPhysVmcb = IEM_IS_64BIT_CODE(pVCpu) ? pVCpu->cpum.GstCtx.rax : pVCpu->cpum.GstCtx.eax;
1300 if ( (GCPhysVmcb & X86_PAGE_4K_OFFSET_MASK)
1301 || !PGMPhysIsGCPhysNormal(pVCpu->CTX_SUFF(pVM), GCPhysVmcb))
1302 {
1303 Log(("vmsave: VMCB physaddr (%#RGp) not valid -> #GP(0)\n", GCPhysVmcb));
1304 return iemRaiseGeneralProtectionFault0(pVCpu);
1305 }
1306
1307 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_VMSAVE))
1308 {
1309 Log(("vmsave: Guest intercept -> #VMEXIT\n"));
1310 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_VMSAVE, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1311 }
1312
1313 SVMVMCBSTATESAVE VmcbNstGst;
1314 VBOXSTRICTRC rcStrict = PGMPhysSimpleReadGCPhys(pVCpu->CTX_SUFF(pVM), &VmcbNstGst, GCPhysVmcb + RT_UOFFSETOF(SVMVMCB, guest),
1315 sizeof(SVMVMCBSTATESAVE));
1316 if (rcStrict == VINF_SUCCESS)
1317 {
1318 LogFlow(("vmsave: Saving VMCB at %#RGp enmEffAddrMode=%d\n", GCPhysVmcb, pVCpu->iem.s.enmEffAddrMode));
1319 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_FS | CPUMCTX_EXTRN_GS | CPUMCTX_EXTRN_TR | CPUMCTX_EXTRN_LDTR
1320 | CPUMCTX_EXTRN_KERNEL_GS_BASE | CPUMCTX_EXTRN_SYSCALL_MSRS | CPUMCTX_EXTRN_SYSENTER_MSRS);
1321
1322 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, FS, fs);
1323 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, GS, gs);
1324 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, TR, tr);
1325 HMSVM_SEG_REG_COPY_TO_VMCB(IEM_GET_CTX(pVCpu), &VmcbNstGst, LDTR, ldtr);
1326
1327 VmcbNstGst.u64KernelGSBase = pVCpu->cpum.GstCtx.msrKERNELGSBASE;
1328 VmcbNstGst.u64STAR = pVCpu->cpum.GstCtx.msrSTAR;
1329 VmcbNstGst.u64LSTAR = pVCpu->cpum.GstCtx.msrLSTAR;
1330 VmcbNstGst.u64CSTAR = pVCpu->cpum.GstCtx.msrCSTAR;
1331 VmcbNstGst.u64SFMASK = pVCpu->cpum.GstCtx.msrSFMASK;
1332
1333 VmcbNstGst.u64SysEnterCS = pVCpu->cpum.GstCtx.SysEnter.cs;
1334 VmcbNstGst.u64SysEnterESP = pVCpu->cpum.GstCtx.SysEnter.esp;
1335 VmcbNstGst.u64SysEnterEIP = pVCpu->cpum.GstCtx.SysEnter.eip;
1336
1337 rcStrict = PGMPhysSimpleWriteGCPhys(pVCpu->CTX_SUFF(pVM), GCPhysVmcb + RT_UOFFSETOF(SVMVMCB, guest), &VmcbNstGst,
1338 sizeof(SVMVMCBSTATESAVE));
1339 if (rcStrict == VINF_SUCCESS)
1340 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1341 }
1342 return rcStrict;
1343# endif
1344}
1345
1346
1347/**
1348 * Interface for HM and EM to emulate the VMSAVE instruction.
1349 *
1350 * @returns Strict VBox status code.
1351 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1352 * @param cbInstr The instruction length in bytes.
1353 * @thread EMT(pVCpu)
1354 */
1355VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedVmsave(PVMCPUCC pVCpu, uint8_t cbInstr)
1356{
1357 IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 3);
1358
1359 iemInitExec(pVCpu, 0 /*fExecOpts*/);
1360 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_0(iemCImpl_vmsave);
1361 Assert(!pVCpu->iem.s.cActiveMappings);
1362 return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
1363}
1364
1365
1366/**
1367 * Implements 'CLGI'.
1368 */
1369IEM_CIMPL_DEF_0(iemCImpl_clgi)
1370{
1371# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && !defined(IN_RING3)
1372 RT_NOREF2(pVCpu, cbInstr);
1373 return VINF_EM_RAW_EMULATE_INSTR;
1374# else
1375 LogFlow(("iemCImpl_clgi\n"));
1376 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, clgi);
1377 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_CLGI))
1378 {
1379 Log(("clgi: Guest intercept -> #VMEXIT\n"));
1380 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_CLGI, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1381 }
1382
1383 CPUMSetGuestGif(&pVCpu->cpum.GstCtx, false);
1384
1385# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && defined(IN_RING3)
1386 iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1387 return EMR3SetExecutionPolicy(pVCpu->CTX_SUFF(pVM)->pUVM, EMEXECPOLICY_IEM_ALL, true);
1388# else
1389 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1390# endif
1391# endif
1392}
1393
1394
1395/**
1396 * Interface for HM and EM to emulate the CLGI instruction.
1397 *
1398 * @returns Strict VBox status code.
1399 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1400 * @param cbInstr The instruction length in bytes.
1401 * @thread EMT(pVCpu)
1402 */
1403VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedClgi(PVMCPUCC pVCpu, uint8_t cbInstr)
1404{
1405 IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 3);
1406
1407 iemInitExec(pVCpu, 0 /*fExecOpts*/);
1408 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_0(iemCImpl_clgi);
1409 Assert(!pVCpu->iem.s.cActiveMappings);
1410 return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
1411}
1412
1413
1414/**
1415 * Implements 'STGI'.
1416 */
1417IEM_CIMPL_DEF_0(iemCImpl_stgi)
1418{
1419# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && !defined(IN_RING3)
1420 RT_NOREF2(pVCpu, cbInstr);
1421 return VINF_EM_RAW_EMULATE_INSTR;
1422# else
1423 LogFlow(("iemCImpl_stgi\n"));
1424 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, stgi);
1425 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_STGI))
1426 {
1427 Log2(("stgi: Guest intercept -> #VMEXIT\n"));
1428 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_STGI, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1429 }
1430
1431 CPUMSetGuestGif(&pVCpu->cpum.GstCtx, true);
1432
1433# if defined(VBOX_WITH_NESTED_HWVIRT_ONLY_IN_IEM) && defined(IN_RING3)
1434 iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1435 return EMR3SetExecutionPolicy(pVCpu->CTX_SUFF(pVM)->pUVM, EMEXECPOLICY_IEM_ALL, false);
1436# else
1437 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1438# endif
1439# endif
1440}
1441
1442
1443/**
1444 * Interface for HM and EM to emulate the STGI instruction.
1445 *
1446 * @returns Strict VBox status code.
1447 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1448 * @param cbInstr The instruction length in bytes.
1449 * @thread EMT(pVCpu)
1450 */
1451VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedStgi(PVMCPUCC pVCpu, uint8_t cbInstr)
1452{
1453 IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 3);
1454
1455 iemInitExec(pVCpu, 0 /*fExecOpts*/);
1456 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_0(iemCImpl_stgi);
1457 Assert(!pVCpu->iem.s.cActiveMappings);
1458 return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
1459}
1460
1461
1462/**
1463 * Implements 'INVLPGA'.
1464 */
1465IEM_CIMPL_DEF_0(iemCImpl_invlpga)
1466{
1467 /** @todo Check effective address size using address size prefix. */
1468 RTGCPTR const GCPtrPage = IEM_IS_64BIT_CODE(pVCpu) ? pVCpu->cpum.GstCtx.rax : pVCpu->cpum.GstCtx.eax;
1469 /** @todo PGM needs virtual ASID support. */
1470# if 0
1471 uint32_t const uAsid = pVCpu->cpum.GstCtx.ecx;
1472# endif
1473
1474 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, invlpga);
1475 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_INVLPGA))
1476 {
1477 Log2(("invlpga: Guest intercept (%RGp) -> #VMEXIT\n", GCPtrPage));
1478 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_INVLPGA, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1479 }
1480
1481 PGMInvalidatePage(pVCpu, GCPtrPage);
1482 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1483}
1484
1485
1486/**
1487 * Interface for HM and EM to emulate the INVLPGA instruction.
1488 *
1489 * @returns Strict VBox status code.
1490 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1491 * @param cbInstr The instruction length in bytes.
1492 * @thread EMT(pVCpu)
1493 */
1494VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedInvlpga(PVMCPUCC pVCpu, uint8_t cbInstr)
1495{
1496 IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 3);
1497
1498 iemInitExec(pVCpu, 0 /*fExecOpts*/);
1499 VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_0(iemCImpl_invlpga);
1500 Assert(!pVCpu->iem.s.cActiveMappings);
1501 return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
1502}
1503
1504
1505/**
1506 * Implements 'SKINIT'.
1507 */
1508IEM_CIMPL_DEF_0(iemCImpl_skinit)
1509{
1510 IEM_SVM_INSTR_COMMON_CHECKS(pVCpu, invlpga);
1511
1512 uint32_t uIgnore;
1513 uint32_t fFeaturesECX;
1514 CPUMGetGuestCpuId(pVCpu, 0x80000001, 0 /* iSubLeaf */, -1 /*f64BitMode*/, &uIgnore, &uIgnore, &fFeaturesECX, &uIgnore);
1515 if (!(fFeaturesECX & X86_CPUID_AMD_FEATURE_ECX_SKINIT))
1516 return iemRaiseUndefinedOpcode(pVCpu);
1517
1518 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_SKINIT))
1519 {
1520 Log2(("skinit: Guest intercept -> #VMEXIT\n"));
1521 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_SKINIT, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1522 }
1523
1524 RT_NOREF(cbInstr);
1525 return VERR_IEM_INSTR_NOT_IMPLEMENTED;
1526}
1527
1528
1529/**
1530 * Implements SVM's implementation of PAUSE.
1531 */
1532IEM_CIMPL_DEF_0(iemCImpl_svm_pause)
1533{
1534 bool fCheckIntercept = true;
1535 if (IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmPauseFilter)
1536 {
1537 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_HWVIRT);
1538
1539 /* TSC based pause-filter thresholding. */
1540 if ( IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fSvmPauseFilterThreshold
1541 && pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilterThreshold > 0)
1542 {
1543 uint64_t const uTick = TMCpuTickGet(pVCpu);
1544 if (uTick - pVCpu->cpum.GstCtx.hwvirt.svm.uPrevPauseTick > pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilterThreshold)
1545 pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilter = CPUMGetGuestSvmPauseFilterCount(pVCpu, IEM_GET_CTX(pVCpu));
1546 pVCpu->cpum.GstCtx.hwvirt.svm.uPrevPauseTick = uTick;
1547 }
1548
1549 /* Simple pause-filter counter. */
1550 if (pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilter > 0)
1551 {
1552 --pVCpu->cpum.GstCtx.hwvirt.svm.cPauseFilter;
1553 fCheckIntercept = false;
1554 }
1555 }
1556
1557 if (fCheckIntercept)
1558 IEM_SVM_CHECK_INSTR_INTERCEPT(pVCpu, SVM_CTRL_INTERCEPT_PAUSE, SVM_EXIT_PAUSE, 0, 0, cbInstr);
1559
1560 return iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1561}
1562
1563#endif /* VBOX_WITH_NESTED_HWVIRT_SVM */
1564
1565/**
1566 * Common code for iemCImpl_vmmcall and iemCImpl_vmcall (latter in IEMAllCImplVmxInstr.cpp.h).
1567 */
1568IEM_CIMPL_DEF_1(iemCImpl_Hypercall, uint16_t, uDisOpcode)
1569{
1570 if (EMAreHypercallInstructionsEnabled(pVCpu))
1571 {
1572 NOREF(uDisOpcode);
1573 VBOXSTRICTRC rcStrict = GIMHypercallEx(pVCpu, IEM_GET_CTX(pVCpu), uDisOpcode, cbInstr);
1574 if (RT_SUCCESS(rcStrict))
1575 {
1576 /** @todo finish: Sort out assertion here when iemRegAddToRipAndFinishingClearingRF
1577 * starts returning non-VINF_SUCCESS statuses. */
1578 if (rcStrict == VINF_SUCCESS)
1579 rcStrict = iemRegAddToRipAndFinishingClearingRF(pVCpu, cbInstr);
1580 if ( rcStrict == VINF_SUCCESS
1581 || rcStrict == VINF_GIM_HYPERCALL_CONTINUING)
1582 return VINF_SUCCESS;
1583 AssertMsgReturn(rcStrict == VINF_GIM_R3_HYPERCALL, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)), VERR_IEM_IPE_4);
1584 return rcStrict;
1585 }
1586 AssertMsgReturn( rcStrict == VERR_GIM_HYPERCALL_ACCESS_DENIED
1587 || rcStrict == VERR_GIM_HYPERCALLS_NOT_AVAILABLE
1588 || rcStrict == VERR_GIM_NOT_ENABLED
1589 || rcStrict == VERR_GIM_HYPERCALL_MEMORY_READ_FAILED
1590 || rcStrict == VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED,
1591 ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)), VERR_IEM_IPE_4);
1592
1593 /* Raise #UD on all failures. */
1594 }
1595 return iemRaiseUndefinedOpcode(pVCpu);
1596}
1597
1598
1599/**
1600 * Implements 'VMMCALL'.
1601 */
1602IEM_CIMPL_DEF_0(iemCImpl_vmmcall)
1603{
1604 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_VMMCALL))
1605 {
1606 Log(("vmmcall: Guest intercept -> #VMEXIT\n"));
1607 IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_VMMCALL, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
1608 }
1609
1610 /* This is a little bit more complicated than the VT-x version because HM/SVM may
1611 patch MOV CR8 instructions to speed up APIC.TPR access for 32-bit windows guests. */
1612 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1613 if (VM_IS_HM_ENABLED(pVM))
1614 {
1615 int rc = HMHCMaybeMovTprSvmHypercall(pVM, pVCpu);
1616 if (RT_SUCCESS(rc))
1617 {
1618 Log(("vmmcall: MovTpr\n"));
1619 return VINF_SUCCESS;
1620 }
1621 }
1622
1623 /* Join forces with vmcall. */
1624 return IEM_CIMPL_CALL_1(iemCImpl_Hypercall, OP_VMMCALL);
1625}
1626
Note: See TracBrowser for help on using the repository browser.

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