VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/HMSVMAll.cpp@ 68363

Last change on this file since 68363 was 68363, checked in by vboxsync, 7 years ago

VMM/HM: Nested Hw.virt nit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: HMSVMAll.cpp 68363 2017-08-10 09:44:42Z vboxsync $ */
2/** @file
3 * HM SVM (AMD-V) - All contexts.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_HM
23#define VMCPU_INCL_CPUM_GST_CTX
24#include "HMInternal.h"
25#include <VBox/vmm/apic.h>
26#include <VBox/vmm/gim.h>
27#include <VBox/vmm/hm.h>
28#include <VBox/vmm/iem.h>
29#include <VBox/vmm/vm.h>
30#include <VBox/vmm/hm_svm.h>
31
32
33#ifndef IN_RC
34/**
35 * Emulates a simple MOV TPR (CR8) instruction, used for TPR patching on 32-bit
36 * guests. This simply looks up the patch record at EIP and does the required.
37 *
38 * This VMMCALL is used a fallback mechanism when mov to/from cr8 isn't exactly
39 * like how we want it to be (e.g. not followed by shr 4 as is usually done for
40 * TPR). See hmR3ReplaceTprInstr() for the details.
41 *
42 * @returns VBox status code.
43 * @retval VINF_SUCCESS if the access was handled successfully.
44 * @retval VERR_NOT_FOUND if no patch record for this RIP could be found.
45 * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE if the found patch type is invalid.
46 *
47 * @param pVCpu The cross context virtual CPU structure.
48 * @param pCtx Pointer to the guest-CPU context.
49 * @param pfUpdateRipAndRF Whether the guest RIP/EIP has been updated as
50 * part of the TPR patch operation.
51 */
52static int hmSvmEmulateMovTpr(PVMCPU pVCpu, PCPUMCTX pCtx, bool *pfUpdateRipAndRF)
53{
54 Log4(("Emulated VMMCall TPR access replacement at RIP=%RGv\n", pCtx->rip));
55
56 /*
57 * We do this in a loop as we increment the RIP after a successful emulation
58 * and the new RIP may be a patched instruction which needs emulation as well.
59 */
60 bool fUpdateRipAndRF = false;
61 bool fPatchFound = false;
62 PVM pVM = pVCpu->CTX_SUFF(pVM);
63 for (;;)
64 {
65 bool fPending;
66 uint8_t u8Tpr;
67
68 PHMTPRPATCH pPatch = (PHMTPRPATCH)RTAvloU32Get(&pVM->hm.s.PatchTree, (AVLOU32KEY)pCtx->eip);
69 if (!pPatch)
70 break;
71
72 fPatchFound = true;
73 switch (pPatch->enmType)
74 {
75 case HMTPRINSTR_READ:
76 {
77 int rc = APICGetTpr(pVCpu, &u8Tpr, &fPending, NULL /* pu8PendingIrq */);
78 AssertRC(rc);
79
80 rc = DISWriteReg32(CPUMCTX2CORE(pCtx), pPatch->uDstOperand, u8Tpr);
81 AssertRC(rc);
82 pCtx->rip += pPatch->cbOp;
83 pCtx->eflags.Bits.u1RF = 0;
84 fUpdateRipAndRF = true;
85 break;
86 }
87
88 case HMTPRINSTR_WRITE_REG:
89 case HMTPRINSTR_WRITE_IMM:
90 {
91 if (pPatch->enmType == HMTPRINSTR_WRITE_REG)
92 {
93 uint32_t u32Val;
94 int rc = DISFetchReg32(CPUMCTX2CORE(pCtx), pPatch->uSrcOperand, &u32Val);
95 AssertRC(rc);
96 u8Tpr = u32Val;
97 }
98 else
99 u8Tpr = (uint8_t)pPatch->uSrcOperand;
100
101 int rc2 = APICSetTpr(pVCpu, u8Tpr);
102 AssertRC(rc2);
103 HMCPU_CF_SET(pVCpu, HM_CHANGED_SVM_GUEST_APIC_STATE);
104
105 pCtx->rip += pPatch->cbOp;
106 pCtx->eflags.Bits.u1RF = 0;
107 fUpdateRipAndRF = true;
108 break;
109 }
110
111 default:
112 {
113 AssertMsgFailed(("Unexpected patch type %d\n", pPatch->enmType));
114 pVCpu->hm.s.u32HMError = pPatch->enmType;
115 *pfUpdateRipAndRF = fUpdateRipAndRF;
116 return VERR_SVM_UNEXPECTED_PATCH_TYPE;
117 }
118 }
119 }
120
121 *pfUpdateRipAndRF = fUpdateRipAndRF;
122 if (fPatchFound)
123 return VINF_SUCCESS;
124 return VERR_NOT_FOUND;
125}
126#endif /* !IN_RC */
127
128
129/**
130 * Performs the operations necessary that are part of the vmmcall instruction
131 * execution in the guest.
132 *
133 * @returns Strict VBox status code (i.e. informational status codes too).
134 * @retval VINF_SUCCESS on successful handling, no \#UD needs to be thrown,
135 * update RIP and eflags.RF depending on @a pfUpdatedRipAndRF and
136 * continue guest execution.
137 * @retval VINF_GIM_HYPERCALL_CONTINUING continue hypercall without updating
138 * RIP.
139 * @retval VINF_GIM_R3_HYPERCALL re-start the hypercall from ring-3.
140 *
141 * @param pVCpu The cross context virtual CPU structure.
142 * @param pCtx Pointer to the guest-CPU context.
143 * @param pfUpdatedRipAndRF Whether the guest RIP/EIP has been updated as
144 * part of handling the VMMCALL operation.
145 */
146VMM_INT_DECL(VBOXSTRICTRC) HMSvmVmmcall(PVMCPU pVCpu, PCPUMCTX pCtx, bool *pfUpdatedRipAndRF)
147{
148#ifndef IN_RC
149 /*
150 * TPR patched instruction emulation for 32-bit guests.
151 */
152 PVM pVM = pVCpu->CTX_SUFF(pVM);
153 if (pVM->hm.s.fTprPatchingAllowed)
154 {
155 int rc = hmSvmEmulateMovTpr(pVCpu, pCtx, pfUpdatedRipAndRF);
156 if (RT_SUCCESS(rc))
157 return VINF_SUCCESS;
158
159 if (rc != VERR_NOT_FOUND)
160 {
161 Log(("hmSvmExitVmmCall: hmSvmEmulateMovTpr returns %Rrc\n", rc));
162 return rc;
163 }
164 }
165#endif
166
167 /*
168 * Paravirtualized hypercalls.
169 */
170 *pfUpdatedRipAndRF = false;
171 if (pVCpu->hm.s.fHypercallsEnabled)
172 return GIMHypercall(pVCpu, pCtx);
173
174 return VERR_NOT_AVAILABLE;
175}
176
177
178/**
179 * Converts an SVM event type to a TRPM event type.
180 *
181 * @returns The TRPM event type.
182 * @retval TRPM_32BIT_HACK if the specified type of event isn't among the set
183 * of recognized trap types.
184 *
185 * @param pEvent Pointer to the SVM event.
186 */
187VMM_INT_DECL(TRPMEVENT) HMSvmEventToTrpmEventType(PCSVMEVENT pEvent)
188{
189 uint8_t const uType = pEvent->n.u3Type;
190 switch (uType)
191 {
192 case SVM_EVENT_EXTERNAL_IRQ: return TRPM_HARDWARE_INT;
193 case SVM_EVENT_SOFTWARE_INT: return TRPM_SOFTWARE_INT;
194 case SVM_EVENT_EXCEPTION:
195 case SVM_EVENT_NMI: return TRPM_TRAP;
196 default:
197 break;
198 }
199 AssertMsgFailed(("HMSvmEventToTrpmEvent: Invalid pending-event type %#x\n", uType));
200 return TRPM_32BIT_HACK;
201}
202
203
204/**
205 * Gets the MSR permission bitmap byte and bit offset for the specified MSR.
206 *
207 * @returns VBox status code.
208 * @param idMsr The MSR being requested.
209 * @param pbOffMsrpm Where to store the byte offset in the MSR permission
210 * bitmap for @a idMsr.
211 * @param puMsrpmBit Where to store the bit offset starting at the byte
212 * returned in @a pbOffMsrpm.
213 */
214VMM_INT_DECL(int) HMSvmGetMsrpmOffsetAndBit(uint32_t idMsr, uint16_t *pbOffMsrpm, uint32_t *puMsrpmBit)
215{
216 Assert(pbOffMsrpm);
217 Assert(puMsrpmBit);
218
219 /*
220 * MSRPM Layout:
221 * Byte offset MSR range
222 * 0x000 - 0x7ff 0x00000000 - 0x00001fff
223 * 0x800 - 0xfff 0xc0000000 - 0xc0001fff
224 * 0x1000 - 0x17ff 0xc0010000 - 0xc0011fff
225 * 0x1800 - 0x1fff Reserved
226 *
227 * Each MSR is represented by 2 permission bits (read and write).
228 */
229 if (idMsr <= 0x00001fff)
230 {
231 /* Pentium-compatible MSRs. */
232 *pbOffMsrpm = 0;
233 *puMsrpmBit = idMsr << 1;
234 return VINF_SUCCESS;
235 }
236
237 if ( idMsr >= 0xc0000000
238 && idMsr <= 0xc0001fff)
239 {
240 /* AMD Sixth Generation x86 Processor MSRs. */
241 *pbOffMsrpm = 0x800;
242 *puMsrpmBit = (idMsr - 0xc0000000) << 1;
243 return VINF_SUCCESS;
244 }
245
246 if ( idMsr >= 0xc0010000
247 && idMsr <= 0xc0011fff)
248 {
249 /* AMD Seventh and Eighth Generation Processor MSRs. */
250 *pbOffMsrpm = 0x1000;
251 *puMsrpmBit = (idMsr - 0xc0001000) << 1;
252 return VINF_SUCCESS;
253 }
254
255 *pbOffMsrpm = 0;
256 *puMsrpmBit = 0;
257 return VERR_OUT_OF_RANGE;
258}
259
260
261/**
262 * Determines whether an IOIO intercept is active for the nested-guest or not.
263 *
264 * @param pvIoBitmap Pointer to the nested-guest IO bitmap.
265 * @param u16Port The IO port being accessed.
266 * @param enmIoType The type of IO access.
267 * @param cbReg The IO operand size in bytes.
268 * @param cAddrSizeBits The address size bits (for 16, 32 or 64).
269 * @param iEffSeg The effective segment number.
270 * @param fRep Whether this is a repeating IO instruction (REP prefix).
271 * @param fStrIo Whether this is a string IO instruction.
272 * @param pIoExitInfo Pointer to the SVMIOIOEXITINFO struct to be filled.
273 * Optional, can be NULL.
274 */
275VMM_INT_DECL(bool) HMSvmIsIOInterceptActive(void *pvIoBitmap, uint16_t u16Port, SVMIOIOTYPE enmIoType, uint8_t cbReg,
276 uint8_t cAddrSizeBits, uint8_t iEffSeg, bool fRep, bool fStrIo,
277 PSVMIOIOEXITINFO pIoExitInfo)
278{
279 Assert(cAddrSizeBits == 0 || cAddrSizeBits == 16 || cAddrSizeBits == 32 || cAddrSizeBits == 64);
280 Assert(cbReg == 1 || cbReg == 2 || cbReg == 4 || cbReg == 8);
281
282 /*
283 * The IOPM layout:
284 * Each bit represents one 8-bit port. That makes a total of 0..65535 bits or
285 * two 4K pages.
286 *
287 * For IO instructions that access more than a single byte, the permission bits
288 * for all bytes are checked; if any bit is set to 1, the IO access is intercepted.
289 *
290 * Since it's possible to do a 32-bit IO access at port 65534 (accessing 4 bytes),
291 * we need 3 extra bits beyond the second 4K page.
292 */
293 static const uint16_t s_auSizeMasks[] = { 0, 1, 3, 0, 0xf, 0, 0, 0 };
294
295 uint16_t const offIopm = u16Port >> 3;
296 uint16_t const fSizeMask = s_auSizeMasks[(cAddrSizeBits >> SVM_IOIO_OP_SIZE_SHIFT) & 7];
297 uint8_t const cShift = u16Port - (offIopm << 3);
298 uint16_t const fIopmMask = (1 << cShift) | (fSizeMask << cShift);
299
300 uint8_t const *pbIopm = (uint8_t *)pvIoBitmap;
301 Assert(pbIopm);
302 pbIopm += offIopm;
303 uint16_t const u16Iopm = *(uint16_t *)pbIopm;
304 if (u16Iopm & fIopmMask)
305 {
306 if (pIoExitInfo)
307 {
308 static const uint32_t s_auIoOpSize[] =
309 { SVM_IOIO_32_BIT_OP, SVM_IOIO_8_BIT_OP, SVM_IOIO_16_BIT_OP, 0, SVM_IOIO_32_BIT_OP, 0, 0, 0 };
310
311 static const uint32_t s_auIoAddrSize[] =
312 { 0, SVM_IOIO_16_BIT_ADDR, SVM_IOIO_32_BIT_ADDR, 0, SVM_IOIO_64_BIT_ADDR, 0, 0, 0 };
313
314 pIoExitInfo->u = s_auIoOpSize[cbReg & 7];
315 pIoExitInfo->u |= s_auIoAddrSize[(cAddrSizeBits >> 4) & 7];
316 pIoExitInfo->n.u1STR = fStrIo;
317 pIoExitInfo->n.u1REP = fRep;
318 pIoExitInfo->n.u3SEG = iEffSeg & 7;
319 pIoExitInfo->n.u1Type = enmIoType;
320 pIoExitInfo->n.u16Port = u16Port;
321 }
322 return true;
323 }
324
325 /** @todo remove later (for debugging as VirtualBox always traps all IO
326 * intercepts). */
327 AssertMsgFailed(("iemSvmHandleIOIntercept: We expect an IO intercept here!\n"));
328 return false;
329}
330
331
332#ifdef VBOX_WITH_NESTED_HWVIRT
333/**
334 * Notification callback for when a \#VMEXIT happens outside SVM R0 code (e.g.
335 * in IEM).
336 *
337 * @param pVCpu The cross context virtual CPU structure.
338 * @param pVmcbNstGst Pointer to the nested-guest VM control block.
339 *
340 * @sa hmR0SvmVmRunCacheVmcb.
341 */
342VMM_INT_DECL(void) HMSvmNstGstVmExitNotify(PVMCPU pVCpu, PSVMVMCB pVmcbNstGst)
343{
344 /*
345 * Restore the nested-guest VMCB fields which have been modified for executing
346 * the nested-guest under SVM R0.
347 */
348 PSVMNESTEDVMCBCACHE pNstGstVmcbCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
349 if (pNstGstVmcbCache->fValid)
350 {
351 PSVMVMCBCTRL pVmcbNstGstCtrl = &pVmcbNstGst->ctrl;
352 pVmcbNstGstCtrl->u16InterceptRdCRx = pNstGstVmcbCache->u16InterceptRdCRx;
353 pVmcbNstGstCtrl->u16InterceptWrCRx = pNstGstVmcbCache->u16InterceptWrCRx;
354 pVmcbNstGstCtrl->u16InterceptRdCRx = pNstGstVmcbCache->u16InterceptRdCRx;
355 pVmcbNstGstCtrl->u16InterceptWrDRx = pNstGstVmcbCache->u16InterceptWrDRx;
356 pVmcbNstGstCtrl->u32InterceptXcpt = pNstGstVmcbCache->u32InterceptXcpt;
357 pVmcbNstGstCtrl->u64InterceptCtrl = pNstGstVmcbCache->u64InterceptCtrl;
358 pVmcbNstGstCtrl->u64VmcbCleanBits = pNstGstVmcbCache->u64VmcbCleanBits;
359 pVmcbNstGstCtrl->u64IOPMPhysAddr = pNstGstVmcbCache->u64IOPMPhysAddr;
360 pVmcbNstGstCtrl->u64MSRPMPhysAddr = pNstGstVmcbCache->u64MSRPMPhysAddr;
361 pVmcbNstGstCtrl->IntCtrl.n.u1VIntrMasking = pNstGstVmcbCache->fVIntrMasking;
362 pVmcbNstGstCtrl->TLBCtrl = pNstGstVmcbCache->TLBCtrl;
363 pNstGstVmcbCache->fValid = false;
364 }
365 pNstGstVmcbCache->fVmrunEmulatedInR0 = false;
366}
367#endif
368
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