VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/NEMAllNativeTemplate-win.cpp.h@ 92489

Last change on this file since 92489 was 92466, checked in by vboxsync, 3 years ago

VMM/NEM-linux: Some more code. Got far enough to log the BIOS version to the release log. [build fix] bugref:9044

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 241.6 KB
Line 
1/* $Id: NEMAllNativeTemplate-win.cpp.h 92466 2021-11-17 03:53:41Z vboxsync $ */
2/** @file
3 * NEM - Native execution manager, Windows code template ring-0/3.
4 */
5
6/*
7 * Copyright (C) 2018-2020 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* Defined Constants And Macros *
21*********************************************************************************************************************************/
22/** Copy back a segment from hyper-V. */
23#define NEM_WIN_COPY_BACK_SEG(a_Dst, a_Src) \
24 do { \
25 (a_Dst).u64Base = (a_Src).Base; \
26 (a_Dst).u32Limit = (a_Src).Limit; \
27 (a_Dst).ValidSel = (a_Dst).Sel = (a_Src).Selector; \
28 (a_Dst).Attr.u = (a_Src).Attributes; \
29 (a_Dst).fFlags = CPUMSELREG_FLAGS_VALID; \
30 } while (0)
31
32/** @def NEMWIN_ASSERT_MSG_REG_VAL
33 * Asserts the correctness of a register value in a message/context.
34 */
35#if 0
36# define NEMWIN_NEED_GET_REGISTER
37# if defined(IN_RING0) || defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS)
38# define NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, a_Expr, a_Msg) \
39 do { \
40 HV_REGISTER_VALUE TmpVal; \
41 nemHCWinGetRegister(a_pVCpu, a_enmReg, &TmpVal); \
42 AssertMsg(a_Expr, a_Msg); \
43 } while (0)
44# else
45# define NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, a_Expr, a_Msg) \
46 do { \
47 WHV_REGISTER_VALUE TmpVal; \
48 nemR3WinGetRegister(a_pVCpu, a_enmReg, &TmpVal); \
49 AssertMsg(a_Expr, a_Msg); \
50 } while (0)
51# endif
52#else
53# define NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, a_Expr, a_Msg) do { } while (0)
54#endif
55
56/** @def NEMWIN_ASSERT_MSG_REG_VAL
57 * Asserts the correctness of a 64-bit register value in a message/context.
58 */
59#define NEMWIN_ASSERT_MSG_REG_VAL64(a_pVCpu, a_enmReg, a_u64Val) \
60 NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, (a_u64Val) == TmpVal.Reg64, \
61 (#a_u64Val "=%#RX64, expected %#RX64\n", (a_u64Val), TmpVal.Reg64))
62/** @def NEMWIN_ASSERT_MSG_REG_VAL
63 * Asserts the correctness of a segment register value in a message/context.
64 */
65#define NEMWIN_ASSERT_MSG_REG_SEG(a_pVCpu, a_enmReg, a_SReg) \
66 NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, \
67 (a_SReg).Base == TmpVal.Segment.Base \
68 && (a_SReg).Limit == TmpVal.Segment.Limit \
69 && (a_SReg).Selector == TmpVal.Segment.Selector \
70 && (a_SReg).Attributes == TmpVal.Segment.Attributes, \
71 ( #a_SReg "=%#RX16 {%#RX64 LB %#RX32,%#RX16} expected %#RX16 {%#RX64 LB %#RX32,%#RX16}\n", \
72 (a_SReg).Selector, (a_SReg).Base, (a_SReg).Limit, (a_SReg).Attributes, \
73 TmpVal.Segment.Selector, TmpVal.Segment.Base, TmpVal.Segment.Limit, TmpVal.Segment.Attributes))
74
75
76/*********************************************************************************************************************************
77* Global Variables *
78*********************************************************************************************************************************/
79/** NEM_WIN_PAGE_STATE_XXX names. */
80NEM_TMPL_STATIC const char * const g_apszPageStates[4] = { "not-set", "unmapped", "readable", "writable" };
81
82/** HV_INTERCEPT_ACCESS_TYPE names. */
83static const char * const g_apszHvInterceptAccessTypes[4] = { "read", "write", "exec", "!undefined!" };
84
85
86/*********************************************************************************************************************************
87* Internal Functions *
88*********************************************************************************************************************************/
89NEM_TMPL_STATIC int nemHCNativeSetPhysPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst,
90 uint32_t fPageProt, uint8_t *pu2State, bool fBackingChanged);
91
92
93
94#ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
95
96/**
97 * Wrapper around VMMR0_DO_NEM_MAP_PAGES for a single page.
98 *
99 * @returns VBox status code.
100 * @param pVM The cross context VM structure.
101 * @param pVCpu The cross context virtual CPU structure of the caller.
102 * @param GCPhysSrc The source page. Does not need to be page aligned.
103 * @param GCPhysDst The destination page. Same as @a GCPhysSrc except for
104 * when A20 is disabled.
105 * @param fFlags HV_MAP_GPA_XXX.
106 */
107DECLINLINE(int) nemHCWinHypercallMapPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst, uint32_t fFlags)
108{
109#ifdef IN_RING0
110 /** @todo optimize further, caller generally has the physical address. */
111 return nemR0WinMapPages(pVM, pVCpu,
112 GCPhysSrc & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
113 GCPhysDst & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
114 1, fFlags);
115#else
116 pVCpu->nem.s.Hypercall.MapPages.GCPhysSrc = GCPhysSrc & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK;
117 pVCpu->nem.s.Hypercall.MapPages.GCPhysDst = GCPhysDst & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK;
118 pVCpu->nem.s.Hypercall.MapPages.cPages = 1;
119 pVCpu->nem.s.Hypercall.MapPages.fFlags = fFlags;
120 return VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_MAP_PAGES, 0, NULL);
121#endif
122}
123
124
125/**
126 * Wrapper around VMMR0_DO_NEM_UNMAP_PAGES for a single page.
127 *
128 * @returns VBox status code.
129 * @param pVM The cross context VM structure.
130 * @param pVCpu The cross context virtual CPU structure of the caller.
131 * @param GCPhys The page to unmap. Does not need to be page aligned.
132 */
133DECLINLINE(int) nemHCWinHypercallUnmapPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys)
134{
135# ifdef IN_RING0
136 return nemR0WinUnmapPages(pVM, pVCpu, GCPhys & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK, 1);
137# else
138 pVCpu->nem.s.Hypercall.UnmapPages.GCPhys = GCPhys & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK;
139 pVCpu->nem.s.Hypercall.UnmapPages.cPages = 1;
140 return VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_UNMAP_PAGES, 0, NULL);
141# endif
142}
143
144#endif /* NEM_WIN_USE_HYPERCALLS_FOR_PAGES */
145#ifndef IN_RING0
146
147NEM_TMPL_STATIC int nemHCWinCopyStateToHyperV(PVMCC pVM, PVMCPUCC pVCpu)
148{
149# if defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) || defined(NEM_WIN_WITH_RING0_RUNLOOP)
150# if !defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) && defined(NEM_WIN_WITH_RING0_RUNLOOP)
151 if (pVM->nem.s.fUseRing0Runloop)
152# endif
153 {
154 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_EXPORT_STATE, 0, NULL);
155 AssertLogRelRCReturn(rc, rc);
156 return rc;
157 }
158# endif
159# ifndef NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS
160
161 /*
162 * The following is very similar to what nemR0WinExportState() does.
163 */
164 WHV_REGISTER_NAME aenmNames[128];
165 WHV_REGISTER_VALUE aValues[128];
166
167 uint64_t const fWhat = ~pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK);
168 if ( !fWhat
169 && pVCpu->nem.s.fCurrentInterruptWindows == pVCpu->nem.s.fDesiredInterruptWindows)
170 return VINF_SUCCESS;
171 uintptr_t iReg = 0;
172
173# define ADD_REG64(a_enmName, a_uValue) do { \
174 aenmNames[iReg] = (a_enmName); \
175 aValues[iReg].Reg128.High64 = 0; \
176 aValues[iReg].Reg64 = (a_uValue); \
177 iReg++; \
178 } while (0)
179# define ADD_REG128(a_enmName, a_uValueLo, a_uValueHi) do { \
180 aenmNames[iReg] = (a_enmName); \
181 aValues[iReg].Reg128.Low64 = (a_uValueLo); \
182 aValues[iReg].Reg128.High64 = (a_uValueHi); \
183 iReg++; \
184 } while (0)
185
186 /* GPRs */
187 if (fWhat & CPUMCTX_EXTRN_GPRS_MASK)
188 {
189 if (fWhat & CPUMCTX_EXTRN_RAX)
190 ADD_REG64(WHvX64RegisterRax, pVCpu->cpum.GstCtx.rax);
191 if (fWhat & CPUMCTX_EXTRN_RCX)
192 ADD_REG64(WHvX64RegisterRcx, pVCpu->cpum.GstCtx.rcx);
193 if (fWhat & CPUMCTX_EXTRN_RDX)
194 ADD_REG64(WHvX64RegisterRdx, pVCpu->cpum.GstCtx.rdx);
195 if (fWhat & CPUMCTX_EXTRN_RBX)
196 ADD_REG64(WHvX64RegisterRbx, pVCpu->cpum.GstCtx.rbx);
197 if (fWhat & CPUMCTX_EXTRN_RSP)
198 ADD_REG64(WHvX64RegisterRsp, pVCpu->cpum.GstCtx.rsp);
199 if (fWhat & CPUMCTX_EXTRN_RBP)
200 ADD_REG64(WHvX64RegisterRbp, pVCpu->cpum.GstCtx.rbp);
201 if (fWhat & CPUMCTX_EXTRN_RSI)
202 ADD_REG64(WHvX64RegisterRsi, pVCpu->cpum.GstCtx.rsi);
203 if (fWhat & CPUMCTX_EXTRN_RDI)
204 ADD_REG64(WHvX64RegisterRdi, pVCpu->cpum.GstCtx.rdi);
205 if (fWhat & CPUMCTX_EXTRN_R8_R15)
206 {
207 ADD_REG64(WHvX64RegisterR8, pVCpu->cpum.GstCtx.r8);
208 ADD_REG64(WHvX64RegisterR9, pVCpu->cpum.GstCtx.r9);
209 ADD_REG64(WHvX64RegisterR10, pVCpu->cpum.GstCtx.r10);
210 ADD_REG64(WHvX64RegisterR11, pVCpu->cpum.GstCtx.r11);
211 ADD_REG64(WHvX64RegisterR12, pVCpu->cpum.GstCtx.r12);
212 ADD_REG64(WHvX64RegisterR13, pVCpu->cpum.GstCtx.r13);
213 ADD_REG64(WHvX64RegisterR14, pVCpu->cpum.GstCtx.r14);
214 ADD_REG64(WHvX64RegisterR15, pVCpu->cpum.GstCtx.r15);
215 }
216 }
217
218 /* RIP & Flags */
219 if (fWhat & CPUMCTX_EXTRN_RIP)
220 ADD_REG64(WHvX64RegisterRip, pVCpu->cpum.GstCtx.rip);
221 if (fWhat & CPUMCTX_EXTRN_RFLAGS)
222 ADD_REG64(WHvX64RegisterRflags, pVCpu->cpum.GstCtx.rflags.u);
223
224 /* Segments */
225# define ADD_SEG(a_enmName, a_SReg) \
226 do { \
227 aenmNames[iReg] = a_enmName; \
228 aValues[iReg].Segment.Base = (a_SReg).u64Base; \
229 aValues[iReg].Segment.Limit = (a_SReg).u32Limit; \
230 aValues[iReg].Segment.Selector = (a_SReg).Sel; \
231 aValues[iReg].Segment.Attributes = (a_SReg).Attr.u; \
232 iReg++; \
233 } while (0)
234 if (fWhat & CPUMCTX_EXTRN_SREG_MASK)
235 {
236 if (fWhat & CPUMCTX_EXTRN_ES)
237 ADD_SEG(WHvX64RegisterEs, pVCpu->cpum.GstCtx.es);
238 if (fWhat & CPUMCTX_EXTRN_CS)
239 ADD_SEG(WHvX64RegisterCs, pVCpu->cpum.GstCtx.cs);
240 if (fWhat & CPUMCTX_EXTRN_SS)
241 ADD_SEG(WHvX64RegisterSs, pVCpu->cpum.GstCtx.ss);
242 if (fWhat & CPUMCTX_EXTRN_DS)
243 ADD_SEG(WHvX64RegisterDs, pVCpu->cpum.GstCtx.ds);
244 if (fWhat & CPUMCTX_EXTRN_FS)
245 ADD_SEG(WHvX64RegisterFs, pVCpu->cpum.GstCtx.fs);
246 if (fWhat & CPUMCTX_EXTRN_GS)
247 ADD_SEG(WHvX64RegisterGs, pVCpu->cpum.GstCtx.gs);
248 }
249
250 /* Descriptor tables & task segment. */
251 if (fWhat & CPUMCTX_EXTRN_TABLE_MASK)
252 {
253 if (fWhat & CPUMCTX_EXTRN_LDTR)
254 ADD_SEG(WHvX64RegisterLdtr, pVCpu->cpum.GstCtx.ldtr);
255 if (fWhat & CPUMCTX_EXTRN_TR)
256 ADD_SEG(WHvX64RegisterTr, pVCpu->cpum.GstCtx.tr);
257 if (fWhat & CPUMCTX_EXTRN_IDTR)
258 {
259 aenmNames[iReg] = WHvX64RegisterIdtr;
260 aValues[iReg].Table.Limit = pVCpu->cpum.GstCtx.idtr.cbIdt;
261 aValues[iReg].Table.Base = pVCpu->cpum.GstCtx.idtr.pIdt;
262 iReg++;
263 }
264 if (fWhat & CPUMCTX_EXTRN_GDTR)
265 {
266 aenmNames[iReg] = WHvX64RegisterGdtr;
267 aValues[iReg].Table.Limit = pVCpu->cpum.GstCtx.gdtr.cbGdt;
268 aValues[iReg].Table.Base = pVCpu->cpum.GstCtx.gdtr.pGdt;
269 iReg++;
270 }
271 }
272
273 /* Control registers. */
274 if (fWhat & CPUMCTX_EXTRN_CR_MASK)
275 {
276 if (fWhat & CPUMCTX_EXTRN_CR0)
277 ADD_REG64(WHvX64RegisterCr0, pVCpu->cpum.GstCtx.cr0);
278 if (fWhat & CPUMCTX_EXTRN_CR2)
279 ADD_REG64(WHvX64RegisterCr2, pVCpu->cpum.GstCtx.cr2);
280 if (fWhat & CPUMCTX_EXTRN_CR3)
281 ADD_REG64(WHvX64RegisterCr3, pVCpu->cpum.GstCtx.cr3);
282 if (fWhat & CPUMCTX_EXTRN_CR4)
283 ADD_REG64(WHvX64RegisterCr4, pVCpu->cpum.GstCtx.cr4);
284 }
285 if (fWhat & CPUMCTX_EXTRN_APIC_TPR)
286 ADD_REG64(WHvX64RegisterCr8, CPUMGetGuestCR8(pVCpu));
287
288 /* Debug registers. */
289/** @todo fixme. Figure out what the hyper-v version of KVM_SET_GUEST_DEBUG would be. */
290 if (fWhat & CPUMCTX_EXTRN_DR0_DR3)
291 {
292 ADD_REG64(WHvX64RegisterDr0, pVCpu->cpum.GstCtx.dr[0]); // CPUMGetHyperDR0(pVCpu));
293 ADD_REG64(WHvX64RegisterDr1, pVCpu->cpum.GstCtx.dr[1]); // CPUMGetHyperDR1(pVCpu));
294 ADD_REG64(WHvX64RegisterDr2, pVCpu->cpum.GstCtx.dr[2]); // CPUMGetHyperDR2(pVCpu));
295 ADD_REG64(WHvX64RegisterDr3, pVCpu->cpum.GstCtx.dr[3]); // CPUMGetHyperDR3(pVCpu));
296 }
297 if (fWhat & CPUMCTX_EXTRN_DR6)
298 ADD_REG64(WHvX64RegisterDr6, pVCpu->cpum.GstCtx.dr[6]); // CPUMGetHyperDR6(pVCpu));
299 if (fWhat & CPUMCTX_EXTRN_DR7)
300 ADD_REG64(WHvX64RegisterDr7, pVCpu->cpum.GstCtx.dr[7]); // CPUMGetHyperDR7(pVCpu));
301
302 /* Floating point state. */
303 if (fWhat & CPUMCTX_EXTRN_X87)
304 {
305 ADD_REG128(WHvX64RegisterFpMmx0, pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[1]);
306 ADD_REG128(WHvX64RegisterFpMmx1, pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[1]);
307 ADD_REG128(WHvX64RegisterFpMmx2, pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[1]);
308 ADD_REG128(WHvX64RegisterFpMmx3, pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[1]);
309 ADD_REG128(WHvX64RegisterFpMmx4, pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[1]);
310 ADD_REG128(WHvX64RegisterFpMmx5, pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[1]);
311 ADD_REG128(WHvX64RegisterFpMmx6, pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[1]);
312 ADD_REG128(WHvX64RegisterFpMmx7, pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[1]);
313
314 aenmNames[iReg] = WHvX64RegisterFpControlStatus;
315 aValues[iReg].FpControlStatus.FpControl = pVCpu->cpum.GstCtx.XState.x87.FCW;
316 aValues[iReg].FpControlStatus.FpStatus = pVCpu->cpum.GstCtx.XState.x87.FSW;
317 aValues[iReg].FpControlStatus.FpTag = pVCpu->cpum.GstCtx.XState.x87.FTW;
318 aValues[iReg].FpControlStatus.Reserved = pVCpu->cpum.GstCtx.XState.x87.FTW >> 8;
319 aValues[iReg].FpControlStatus.LastFpOp = pVCpu->cpum.GstCtx.XState.x87.FOP;
320 aValues[iReg].FpControlStatus.LastFpRip = (pVCpu->cpum.GstCtx.XState.x87.FPUIP)
321 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.CS << 32)
322 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.Rsrvd1 << 48);
323 iReg++;
324
325 aenmNames[iReg] = WHvX64RegisterXmmControlStatus;
326 aValues[iReg].XmmControlStatus.LastFpRdp = (pVCpu->cpum.GstCtx.XState.x87.FPUDP)
327 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.DS << 32)
328 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.Rsrvd2 << 48);
329 aValues[iReg].XmmControlStatus.XmmStatusControl = pVCpu->cpum.GstCtx.XState.x87.MXCSR;
330 aValues[iReg].XmmControlStatus.XmmStatusControlMask = pVCpu->cpum.GstCtx.XState.x87.MXCSR_MASK; /** @todo ??? (Isn't this an output field?) */
331 iReg++;
332 }
333
334 /* Vector state. */
335 if (fWhat & CPUMCTX_EXTRN_SSE_AVX)
336 {
337 ADD_REG128(WHvX64RegisterXmm0, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Hi);
338 ADD_REG128(WHvX64RegisterXmm1, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Hi);
339 ADD_REG128(WHvX64RegisterXmm2, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Hi);
340 ADD_REG128(WHvX64RegisterXmm3, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Hi);
341 ADD_REG128(WHvX64RegisterXmm4, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Hi);
342 ADD_REG128(WHvX64RegisterXmm5, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Hi);
343 ADD_REG128(WHvX64RegisterXmm6, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Hi);
344 ADD_REG128(WHvX64RegisterXmm7, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Hi);
345 ADD_REG128(WHvX64RegisterXmm8, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Hi);
346 ADD_REG128(WHvX64RegisterXmm9, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Hi);
347 ADD_REG128(WHvX64RegisterXmm10, pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Hi);
348 ADD_REG128(WHvX64RegisterXmm11, pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Hi);
349 ADD_REG128(WHvX64RegisterXmm12, pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Hi);
350 ADD_REG128(WHvX64RegisterXmm13, pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Hi);
351 ADD_REG128(WHvX64RegisterXmm14, pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Hi);
352 ADD_REG128(WHvX64RegisterXmm15, pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Hi);
353 }
354
355 /* MSRs */
356 // WHvX64RegisterTsc - don't touch
357 if (fWhat & CPUMCTX_EXTRN_EFER)
358 ADD_REG64(WHvX64RegisterEfer, pVCpu->cpum.GstCtx.msrEFER);
359 if (fWhat & CPUMCTX_EXTRN_KERNEL_GS_BASE)
360 ADD_REG64(WHvX64RegisterKernelGsBase, pVCpu->cpum.GstCtx.msrKERNELGSBASE);
361 if (fWhat & CPUMCTX_EXTRN_SYSENTER_MSRS)
362 {
363 ADD_REG64(WHvX64RegisterSysenterCs, pVCpu->cpum.GstCtx.SysEnter.cs);
364 ADD_REG64(WHvX64RegisterSysenterEip, pVCpu->cpum.GstCtx.SysEnter.eip);
365 ADD_REG64(WHvX64RegisterSysenterEsp, pVCpu->cpum.GstCtx.SysEnter.esp);
366 }
367 if (fWhat & CPUMCTX_EXTRN_SYSCALL_MSRS)
368 {
369 ADD_REG64(WHvX64RegisterStar, pVCpu->cpum.GstCtx.msrSTAR);
370 ADD_REG64(WHvX64RegisterLstar, pVCpu->cpum.GstCtx.msrLSTAR);
371 ADD_REG64(WHvX64RegisterCstar, pVCpu->cpum.GstCtx.msrCSTAR);
372 ADD_REG64(WHvX64RegisterSfmask, pVCpu->cpum.GstCtx.msrSFMASK);
373 }
374 if (fWhat & (CPUMCTX_EXTRN_TSC_AUX | CPUMCTX_EXTRN_OTHER_MSRS))
375 {
376 PCPUMCTXMSRS const pCtxMsrs = CPUMQueryGuestCtxMsrsPtr(pVCpu);
377 if (fWhat & CPUMCTX_EXTRN_TSC_AUX)
378 ADD_REG64(WHvX64RegisterTscAux, pCtxMsrs->msr.TscAux);
379 if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS)
380 {
381 ADD_REG64(WHvX64RegisterApicBase, APICGetBaseMsrNoCheck(pVCpu));
382 ADD_REG64(WHvX64RegisterPat, pVCpu->cpum.GstCtx.msrPAT);
383#if 0 /** @todo check if WHvX64RegisterMsrMtrrCap works here... */
384 ADD_REG64(WHvX64RegisterMsrMtrrCap, CPUMGetGuestIa32MtrrCap(pVCpu));
385#endif
386 ADD_REG64(WHvX64RegisterMsrMtrrDefType, pCtxMsrs->msr.MtrrDefType);
387 ADD_REG64(WHvX64RegisterMsrMtrrFix64k00000, pCtxMsrs->msr.MtrrFix64K_00000);
388 ADD_REG64(WHvX64RegisterMsrMtrrFix16k80000, pCtxMsrs->msr.MtrrFix16K_80000);
389 ADD_REG64(WHvX64RegisterMsrMtrrFix16kA0000, pCtxMsrs->msr.MtrrFix16K_A0000);
390 ADD_REG64(WHvX64RegisterMsrMtrrFix4kC0000, pCtxMsrs->msr.MtrrFix4K_C0000);
391 ADD_REG64(WHvX64RegisterMsrMtrrFix4kC8000, pCtxMsrs->msr.MtrrFix4K_C8000);
392 ADD_REG64(WHvX64RegisterMsrMtrrFix4kD0000, pCtxMsrs->msr.MtrrFix4K_D0000);
393 ADD_REG64(WHvX64RegisterMsrMtrrFix4kD8000, pCtxMsrs->msr.MtrrFix4K_D8000);
394 ADD_REG64(WHvX64RegisterMsrMtrrFix4kE0000, pCtxMsrs->msr.MtrrFix4K_E0000);
395 ADD_REG64(WHvX64RegisterMsrMtrrFix4kE8000, pCtxMsrs->msr.MtrrFix4K_E8000);
396 ADD_REG64(WHvX64RegisterMsrMtrrFix4kF0000, pCtxMsrs->msr.MtrrFix4K_F0000);
397 ADD_REG64(WHvX64RegisterMsrMtrrFix4kF8000, pCtxMsrs->msr.MtrrFix4K_F8000);
398#if 0 /** @todo these registers aren't available? Might explain something.. .*/
399 const CPUMCPUVENDOR enmCpuVendor = CPUMGetHostCpuVendor(pVM);
400 if (enmCpuVendor != CPUMCPUVENDOR_AMD)
401 {
402 ADD_REG64(HvX64RegisterIa32MiscEnable, pCtxMsrs->msr.MiscEnable);
403 ADD_REG64(HvX64RegisterIa32FeatureControl, CPUMGetGuestIa32FeatureControl(pVCpu));
404 }
405#endif
406 }
407 }
408
409 /* event injection (clear it). */
410 if (fWhat & CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT)
411 ADD_REG64(WHvRegisterPendingInterruption, 0);
412
413 /* Interruptibility state. This can get a little complicated since we get
414 half of the state via HV_X64_VP_EXECUTION_STATE. */
415 if ( (fWhat & (CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI))
416 == (CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI) )
417 {
418 ADD_REG64(WHvRegisterInterruptState, 0);
419 if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)
420 && EMGetInhibitInterruptsPC(pVCpu) == pVCpu->cpum.GstCtx.rip)
421 aValues[iReg - 1].InterruptState.InterruptShadow = 1;
422 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_BLOCK_NMIS))
423 aValues[iReg - 1].InterruptState.NmiMasked = 1;
424 }
425 else if (fWhat & CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT)
426 {
427 if ( pVCpu->nem.s.fLastInterruptShadow
428 || ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)
429 && EMGetInhibitInterruptsPC(pVCpu) == pVCpu->cpum.GstCtx.rip))
430 {
431 ADD_REG64(WHvRegisterInterruptState, 0);
432 if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)
433 && EMGetInhibitInterruptsPC(pVCpu) == pVCpu->cpum.GstCtx.rip)
434 aValues[iReg - 1].InterruptState.InterruptShadow = 1;
435 /** @todo Retrieve NMI state, currently assuming it's zero. (yes this may happen on I/O) */
436 //if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_BLOCK_NMIS))
437 // aValues[iReg - 1].InterruptState.NmiMasked = 1;
438 }
439 }
440 else
441 Assert(!(fWhat & CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI));
442
443 /* Interrupt windows. Always set if active as Hyper-V seems to be forgetful. */
444 uint8_t const fDesiredIntWin = pVCpu->nem.s.fDesiredInterruptWindows;
445 if ( fDesiredIntWin
446 || pVCpu->nem.s.fCurrentInterruptWindows != fDesiredIntWin)
447 {
448 pVCpu->nem.s.fCurrentInterruptWindows = pVCpu->nem.s.fDesiredInterruptWindows;
449 Log8(("Setting WHvX64RegisterDeliverabilityNotifications, fDesiredIntWin=%X\n", fDesiredIntWin));
450 ADD_REG64(WHvX64RegisterDeliverabilityNotifications, fDesiredIntWin);
451 Assert(aValues[iReg - 1].DeliverabilityNotifications.NmiNotification == RT_BOOL(fDesiredIntWin & NEM_WIN_INTW_F_NMI));
452 Assert(aValues[iReg - 1].DeliverabilityNotifications.InterruptNotification == RT_BOOL(fDesiredIntWin & NEM_WIN_INTW_F_REGULAR));
453 Assert(aValues[iReg - 1].DeliverabilityNotifications.InterruptPriority == (unsigned)((fDesiredIntWin & NEM_WIN_INTW_F_PRIO_MASK) >> NEM_WIN_INTW_F_PRIO_SHIFT));
454 }
455
456 /// @todo WHvRegisterPendingEvent
457
458 /*
459 * Set the registers.
460 */
461 Assert(iReg < RT_ELEMENTS(aValues));
462 Assert(iReg < RT_ELEMENTS(aenmNames));
463# ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
464 Log12(("Calling WHvSetVirtualProcessorRegisters(%p, %u, %p, %u, %p)\n",
465 pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, iReg, aValues));
466# endif
467 HRESULT hrc = WHvSetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, iReg, aValues);
468 if (SUCCEEDED(hrc))
469 {
470 pVCpu->cpum.GstCtx.fExtrn |= CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK | CPUMCTX_EXTRN_KEEPER_NEM;
471 return VINF_SUCCESS;
472 }
473 AssertLogRelMsgFailed(("WHvSetVirtualProcessorRegisters(%p, %u,,%u,) -> %Rhrc (Last=%#x/%u)\n",
474 pVM->nem.s.hPartition, pVCpu->idCpu, iReg,
475 hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
476 return VERR_INTERNAL_ERROR;
477
478# undef ADD_REG64
479# undef ADD_REG128
480# undef ADD_SEG
481
482# endif /* !NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS */
483}
484
485
486NEM_TMPL_STATIC int nemHCWinCopyStateFromHyperV(PVMCC pVM, PVMCPUCC pVCpu, uint64_t fWhat)
487{
488# if defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) || defined(NEM_WIN_WITH_RING0_RUNLOOP)
489# if !defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) && defined(NEM_WIN_WITH_RING0_RUNLOOP)
490 if (pVM->nem.s.fUseRing0Runloop)
491# endif
492 {
493 /* See NEMR0ImportState */
494 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_IMPORT_STATE, fWhat, NULL);
495 if (RT_SUCCESS(rc))
496 return rc;
497 if (rc == VERR_NEM_FLUSH_TLB)
498 {
499 rc = PGMFlushTLB(pVCpu, pVCpu->cpum.GstCtx.cr3, true /*fGlobal*/, false /*fPdpesMapped*/);
500 return rc;
501 }
502 AssertLogRelRCReturn(rc, rc);
503 return rc;
504 }
505# endif
506# ifndef NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS
507 WHV_REGISTER_NAME aenmNames[128];
508
509 fWhat &= pVCpu->cpum.GstCtx.fExtrn;
510 uintptr_t iReg = 0;
511
512 /* GPRs */
513 if (fWhat & CPUMCTX_EXTRN_GPRS_MASK)
514 {
515 if (fWhat & CPUMCTX_EXTRN_RAX)
516 aenmNames[iReg++] = WHvX64RegisterRax;
517 if (fWhat & CPUMCTX_EXTRN_RCX)
518 aenmNames[iReg++] = WHvX64RegisterRcx;
519 if (fWhat & CPUMCTX_EXTRN_RDX)
520 aenmNames[iReg++] = WHvX64RegisterRdx;
521 if (fWhat & CPUMCTX_EXTRN_RBX)
522 aenmNames[iReg++] = WHvX64RegisterRbx;
523 if (fWhat & CPUMCTX_EXTRN_RSP)
524 aenmNames[iReg++] = WHvX64RegisterRsp;
525 if (fWhat & CPUMCTX_EXTRN_RBP)
526 aenmNames[iReg++] = WHvX64RegisterRbp;
527 if (fWhat & CPUMCTX_EXTRN_RSI)
528 aenmNames[iReg++] = WHvX64RegisterRsi;
529 if (fWhat & CPUMCTX_EXTRN_RDI)
530 aenmNames[iReg++] = WHvX64RegisterRdi;
531 if (fWhat & CPUMCTX_EXTRN_R8_R15)
532 {
533 aenmNames[iReg++] = WHvX64RegisterR8;
534 aenmNames[iReg++] = WHvX64RegisterR9;
535 aenmNames[iReg++] = WHvX64RegisterR10;
536 aenmNames[iReg++] = WHvX64RegisterR11;
537 aenmNames[iReg++] = WHvX64RegisterR12;
538 aenmNames[iReg++] = WHvX64RegisterR13;
539 aenmNames[iReg++] = WHvX64RegisterR14;
540 aenmNames[iReg++] = WHvX64RegisterR15;
541 }
542 }
543
544 /* RIP & Flags */
545 if (fWhat & CPUMCTX_EXTRN_RIP)
546 aenmNames[iReg++] = WHvX64RegisterRip;
547 if (fWhat & CPUMCTX_EXTRN_RFLAGS)
548 aenmNames[iReg++] = WHvX64RegisterRflags;
549
550 /* Segments */
551 if (fWhat & CPUMCTX_EXTRN_SREG_MASK)
552 {
553 if (fWhat & CPUMCTX_EXTRN_ES)
554 aenmNames[iReg++] = WHvX64RegisterEs;
555 if (fWhat & CPUMCTX_EXTRN_CS)
556 aenmNames[iReg++] = WHvX64RegisterCs;
557 if (fWhat & CPUMCTX_EXTRN_SS)
558 aenmNames[iReg++] = WHvX64RegisterSs;
559 if (fWhat & CPUMCTX_EXTRN_DS)
560 aenmNames[iReg++] = WHvX64RegisterDs;
561 if (fWhat & CPUMCTX_EXTRN_FS)
562 aenmNames[iReg++] = WHvX64RegisterFs;
563 if (fWhat & CPUMCTX_EXTRN_GS)
564 aenmNames[iReg++] = WHvX64RegisterGs;
565 }
566
567 /* Descriptor tables. */
568 if (fWhat & CPUMCTX_EXTRN_TABLE_MASK)
569 {
570 if (fWhat & CPUMCTX_EXTRN_LDTR)
571 aenmNames[iReg++] = WHvX64RegisterLdtr;
572 if (fWhat & CPUMCTX_EXTRN_TR)
573 aenmNames[iReg++] = WHvX64RegisterTr;
574 if (fWhat & CPUMCTX_EXTRN_IDTR)
575 aenmNames[iReg++] = WHvX64RegisterIdtr;
576 if (fWhat & CPUMCTX_EXTRN_GDTR)
577 aenmNames[iReg++] = WHvX64RegisterGdtr;
578 }
579
580 /* Control registers. */
581 if (fWhat & CPUMCTX_EXTRN_CR_MASK)
582 {
583 if (fWhat & CPUMCTX_EXTRN_CR0)
584 aenmNames[iReg++] = WHvX64RegisterCr0;
585 if (fWhat & CPUMCTX_EXTRN_CR2)
586 aenmNames[iReg++] = WHvX64RegisterCr2;
587 if (fWhat & CPUMCTX_EXTRN_CR3)
588 aenmNames[iReg++] = WHvX64RegisterCr3;
589 if (fWhat & CPUMCTX_EXTRN_CR4)
590 aenmNames[iReg++] = WHvX64RegisterCr4;
591 }
592 if (fWhat & CPUMCTX_EXTRN_APIC_TPR)
593 aenmNames[iReg++] = WHvX64RegisterCr8;
594
595 /* Debug registers. */
596 if (fWhat & CPUMCTX_EXTRN_DR7)
597 aenmNames[iReg++] = WHvX64RegisterDr7;
598 if (fWhat & CPUMCTX_EXTRN_DR0_DR3)
599 {
600 if (!(fWhat & CPUMCTX_EXTRN_DR7) && (pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_DR7))
601 {
602 fWhat |= CPUMCTX_EXTRN_DR7;
603 aenmNames[iReg++] = WHvX64RegisterDr7;
604 }
605 aenmNames[iReg++] = WHvX64RegisterDr0;
606 aenmNames[iReg++] = WHvX64RegisterDr1;
607 aenmNames[iReg++] = WHvX64RegisterDr2;
608 aenmNames[iReg++] = WHvX64RegisterDr3;
609 }
610 if (fWhat & CPUMCTX_EXTRN_DR6)
611 aenmNames[iReg++] = WHvX64RegisterDr6;
612
613 /* Floating point state. */
614 if (fWhat & CPUMCTX_EXTRN_X87)
615 {
616 aenmNames[iReg++] = WHvX64RegisterFpMmx0;
617 aenmNames[iReg++] = WHvX64RegisterFpMmx1;
618 aenmNames[iReg++] = WHvX64RegisterFpMmx2;
619 aenmNames[iReg++] = WHvX64RegisterFpMmx3;
620 aenmNames[iReg++] = WHvX64RegisterFpMmx4;
621 aenmNames[iReg++] = WHvX64RegisterFpMmx5;
622 aenmNames[iReg++] = WHvX64RegisterFpMmx6;
623 aenmNames[iReg++] = WHvX64RegisterFpMmx7;
624 aenmNames[iReg++] = WHvX64RegisterFpControlStatus;
625 }
626 if (fWhat & (CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX))
627 aenmNames[iReg++] = WHvX64RegisterXmmControlStatus;
628
629 /* Vector state. */
630 if (fWhat & CPUMCTX_EXTRN_SSE_AVX)
631 {
632 aenmNames[iReg++] = WHvX64RegisterXmm0;
633 aenmNames[iReg++] = WHvX64RegisterXmm1;
634 aenmNames[iReg++] = WHvX64RegisterXmm2;
635 aenmNames[iReg++] = WHvX64RegisterXmm3;
636 aenmNames[iReg++] = WHvX64RegisterXmm4;
637 aenmNames[iReg++] = WHvX64RegisterXmm5;
638 aenmNames[iReg++] = WHvX64RegisterXmm6;
639 aenmNames[iReg++] = WHvX64RegisterXmm7;
640 aenmNames[iReg++] = WHvX64RegisterXmm8;
641 aenmNames[iReg++] = WHvX64RegisterXmm9;
642 aenmNames[iReg++] = WHvX64RegisterXmm10;
643 aenmNames[iReg++] = WHvX64RegisterXmm11;
644 aenmNames[iReg++] = WHvX64RegisterXmm12;
645 aenmNames[iReg++] = WHvX64RegisterXmm13;
646 aenmNames[iReg++] = WHvX64RegisterXmm14;
647 aenmNames[iReg++] = WHvX64RegisterXmm15;
648 }
649
650 /* MSRs */
651 // WHvX64RegisterTsc - don't touch
652 if (fWhat & CPUMCTX_EXTRN_EFER)
653 aenmNames[iReg++] = WHvX64RegisterEfer;
654 if (fWhat & CPUMCTX_EXTRN_KERNEL_GS_BASE)
655 aenmNames[iReg++] = WHvX64RegisterKernelGsBase;
656 if (fWhat & CPUMCTX_EXTRN_SYSENTER_MSRS)
657 {
658 aenmNames[iReg++] = WHvX64RegisterSysenterCs;
659 aenmNames[iReg++] = WHvX64RegisterSysenterEip;
660 aenmNames[iReg++] = WHvX64RegisterSysenterEsp;
661 }
662 if (fWhat & CPUMCTX_EXTRN_SYSCALL_MSRS)
663 {
664 aenmNames[iReg++] = WHvX64RegisterStar;
665 aenmNames[iReg++] = WHvX64RegisterLstar;
666 aenmNames[iReg++] = WHvX64RegisterCstar;
667 aenmNames[iReg++] = WHvX64RegisterSfmask;
668 }
669
670//#ifdef LOG_ENABLED
671// const CPUMCPUVENDOR enmCpuVendor = CPUMGetHostCpuVendor(pVM);
672//#endif
673 if (fWhat & CPUMCTX_EXTRN_TSC_AUX)
674 aenmNames[iReg++] = WHvX64RegisterTscAux;
675 if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS)
676 {
677 aenmNames[iReg++] = WHvX64RegisterApicBase; /// @todo APIC BASE
678 aenmNames[iReg++] = WHvX64RegisterPat;
679#if 0 /*def LOG_ENABLED*/ /** @todo Check if WHvX64RegisterMsrMtrrCap works... */
680 aenmNames[iReg++] = WHvX64RegisterMsrMtrrCap;
681#endif
682 aenmNames[iReg++] = WHvX64RegisterMsrMtrrDefType;
683 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix64k00000;
684 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix16k80000;
685 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix16kA0000;
686 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kC0000;
687 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kC8000;
688 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kD0000;
689 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kD8000;
690 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kE0000;
691 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kE8000;
692 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kF0000;
693 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kF8000;
694 /** @todo look for HvX64RegisterIa32MiscEnable and HvX64RegisterIa32FeatureControl? */
695//#ifdef LOG_ENABLED
696// if (enmCpuVendor != CPUMCPUVENDOR_AMD)
697// aenmNames[iReg++] = HvX64RegisterIa32FeatureControl;
698//#endif
699 }
700
701 /* Interruptibility. */
702 if (fWhat & (CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI))
703 {
704 aenmNames[iReg++] = WHvRegisterInterruptState;
705 aenmNames[iReg++] = WHvX64RegisterRip;
706 }
707
708 /* event injection */
709 aenmNames[iReg++] = WHvRegisterPendingInterruption;
710 aenmNames[iReg++] = WHvRegisterPendingEvent0; /** @todo renamed to WHvRegisterPendingEvent */
711
712 size_t const cRegs = iReg;
713 Assert(cRegs < RT_ELEMENTS(aenmNames));
714
715 /*
716 * Get the registers.
717 */
718 WHV_REGISTER_VALUE aValues[128];
719 RT_ZERO(aValues);
720 Assert(RT_ELEMENTS(aValues) >= cRegs);
721 Assert(RT_ELEMENTS(aenmNames) >= cRegs);
722# ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
723 Log12(("Calling WHvGetVirtualProcessorRegisters(%p, %u, %p, %u, %p)\n",
724 pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, cRegs, aValues));
725# endif
726 HRESULT hrc = WHvGetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, (uint32_t)cRegs, aValues);
727 AssertLogRelMsgReturn(SUCCEEDED(hrc),
728 ("WHvGetVirtualProcessorRegisters(%p, %u,,%u,) -> %Rhrc (Last=%#x/%u)\n",
729 pVM->nem.s.hPartition, pVCpu->idCpu, cRegs, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
730 , VERR_NEM_GET_REGISTERS_FAILED);
731
732 iReg = 0;
733# define GET_REG64(a_DstVar, a_enmName) do { \
734 Assert(aenmNames[iReg] == (a_enmName)); \
735 (a_DstVar) = aValues[iReg].Reg64; \
736 iReg++; \
737 } while (0)
738# define GET_REG64_LOG7(a_DstVar, a_enmName, a_szLogName) do { \
739 Assert(aenmNames[iReg] == (a_enmName)); \
740 if ((a_DstVar) != aValues[iReg].Reg64) \
741 Log7(("NEM/%u: " a_szLogName " changed %RX64 -> %RX64\n", pVCpu->idCpu, (a_DstVar), aValues[iReg].Reg64)); \
742 (a_DstVar) = aValues[iReg].Reg64; \
743 iReg++; \
744 } while (0)
745# define GET_REG128(a_DstVarLo, a_DstVarHi, a_enmName) do { \
746 Assert(aenmNames[iReg] == a_enmName); \
747 (a_DstVarLo) = aValues[iReg].Reg128.Low64; \
748 (a_DstVarHi) = aValues[iReg].Reg128.High64; \
749 iReg++; \
750 } while (0)
751# define GET_SEG(a_SReg, a_enmName) do { \
752 Assert(aenmNames[iReg] == (a_enmName)); \
753 NEM_WIN_COPY_BACK_SEG(a_SReg, aValues[iReg].Segment); \
754 iReg++; \
755 } while (0)
756
757 /* GPRs */
758 if (fWhat & CPUMCTX_EXTRN_GPRS_MASK)
759 {
760 if (fWhat & CPUMCTX_EXTRN_RAX)
761 GET_REG64(pVCpu->cpum.GstCtx.rax, WHvX64RegisterRax);
762 if (fWhat & CPUMCTX_EXTRN_RCX)
763 GET_REG64(pVCpu->cpum.GstCtx.rcx, WHvX64RegisterRcx);
764 if (fWhat & CPUMCTX_EXTRN_RDX)
765 GET_REG64(pVCpu->cpum.GstCtx.rdx, WHvX64RegisterRdx);
766 if (fWhat & CPUMCTX_EXTRN_RBX)
767 GET_REG64(pVCpu->cpum.GstCtx.rbx, WHvX64RegisterRbx);
768 if (fWhat & CPUMCTX_EXTRN_RSP)
769 GET_REG64(pVCpu->cpum.GstCtx.rsp, WHvX64RegisterRsp);
770 if (fWhat & CPUMCTX_EXTRN_RBP)
771 GET_REG64(pVCpu->cpum.GstCtx.rbp, WHvX64RegisterRbp);
772 if (fWhat & CPUMCTX_EXTRN_RSI)
773 GET_REG64(pVCpu->cpum.GstCtx.rsi, WHvX64RegisterRsi);
774 if (fWhat & CPUMCTX_EXTRN_RDI)
775 GET_REG64(pVCpu->cpum.GstCtx.rdi, WHvX64RegisterRdi);
776 if (fWhat & CPUMCTX_EXTRN_R8_R15)
777 {
778 GET_REG64(pVCpu->cpum.GstCtx.r8, WHvX64RegisterR8);
779 GET_REG64(pVCpu->cpum.GstCtx.r9, WHvX64RegisterR9);
780 GET_REG64(pVCpu->cpum.GstCtx.r10, WHvX64RegisterR10);
781 GET_REG64(pVCpu->cpum.GstCtx.r11, WHvX64RegisterR11);
782 GET_REG64(pVCpu->cpum.GstCtx.r12, WHvX64RegisterR12);
783 GET_REG64(pVCpu->cpum.GstCtx.r13, WHvX64RegisterR13);
784 GET_REG64(pVCpu->cpum.GstCtx.r14, WHvX64RegisterR14);
785 GET_REG64(pVCpu->cpum.GstCtx.r15, WHvX64RegisterR15);
786 }
787 }
788
789 /* RIP & Flags */
790 if (fWhat & CPUMCTX_EXTRN_RIP)
791 GET_REG64(pVCpu->cpum.GstCtx.rip, WHvX64RegisterRip);
792 if (fWhat & CPUMCTX_EXTRN_RFLAGS)
793 GET_REG64(pVCpu->cpum.GstCtx.rflags.u, WHvX64RegisterRflags);
794
795 /* Segments */
796 if (fWhat & CPUMCTX_EXTRN_SREG_MASK)
797 {
798 if (fWhat & CPUMCTX_EXTRN_ES)
799 GET_SEG(pVCpu->cpum.GstCtx.es, WHvX64RegisterEs);
800 if (fWhat & CPUMCTX_EXTRN_CS)
801 GET_SEG(pVCpu->cpum.GstCtx.cs, WHvX64RegisterCs);
802 if (fWhat & CPUMCTX_EXTRN_SS)
803 GET_SEG(pVCpu->cpum.GstCtx.ss, WHvX64RegisterSs);
804 if (fWhat & CPUMCTX_EXTRN_DS)
805 GET_SEG(pVCpu->cpum.GstCtx.ds, WHvX64RegisterDs);
806 if (fWhat & CPUMCTX_EXTRN_FS)
807 GET_SEG(pVCpu->cpum.GstCtx.fs, WHvX64RegisterFs);
808 if (fWhat & CPUMCTX_EXTRN_GS)
809 GET_SEG(pVCpu->cpum.GstCtx.gs, WHvX64RegisterGs);
810 }
811
812 /* Descriptor tables and the task segment. */
813 if (fWhat & CPUMCTX_EXTRN_TABLE_MASK)
814 {
815 if (fWhat & CPUMCTX_EXTRN_LDTR)
816 GET_SEG(pVCpu->cpum.GstCtx.ldtr, WHvX64RegisterLdtr);
817
818 if (fWhat & CPUMCTX_EXTRN_TR)
819 {
820 /* AMD-V likes loading TR with in AVAIL state, whereas intel insists on BUSY. So,
821 avoid to trigger sanity assertions around the code, always fix this. */
822 GET_SEG(pVCpu->cpum.GstCtx.tr, WHvX64RegisterTr);
823 switch (pVCpu->cpum.GstCtx.tr.Attr.n.u4Type)
824 {
825 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
826 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
827 break;
828 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
829 pVCpu->cpum.GstCtx.tr.Attr.n.u4Type = X86_SEL_TYPE_SYS_386_TSS_BUSY;
830 break;
831 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
832 pVCpu->cpum.GstCtx.tr.Attr.n.u4Type = X86_SEL_TYPE_SYS_286_TSS_BUSY;
833 break;
834 }
835 }
836 if (fWhat & CPUMCTX_EXTRN_IDTR)
837 {
838 Assert(aenmNames[iReg] == WHvX64RegisterIdtr);
839 pVCpu->cpum.GstCtx.idtr.cbIdt = aValues[iReg].Table.Limit;
840 pVCpu->cpum.GstCtx.idtr.pIdt = aValues[iReg].Table.Base;
841 iReg++;
842 }
843 if (fWhat & CPUMCTX_EXTRN_GDTR)
844 {
845 Assert(aenmNames[iReg] == WHvX64RegisterGdtr);
846 pVCpu->cpum.GstCtx.gdtr.cbGdt = aValues[iReg].Table.Limit;
847 pVCpu->cpum.GstCtx.gdtr.pGdt = aValues[iReg].Table.Base;
848 iReg++;
849 }
850 }
851
852 /* Control registers. */
853 bool fMaybeChangedMode = false;
854 bool fUpdateCr3 = false;
855 if (fWhat & CPUMCTX_EXTRN_CR_MASK)
856 {
857 if (fWhat & CPUMCTX_EXTRN_CR0)
858 {
859 Assert(aenmNames[iReg] == WHvX64RegisterCr0);
860 if (pVCpu->cpum.GstCtx.cr0 != aValues[iReg].Reg64)
861 {
862 CPUMSetGuestCR0(pVCpu, aValues[iReg].Reg64);
863 fMaybeChangedMode = true;
864 }
865 iReg++;
866 }
867 if (fWhat & CPUMCTX_EXTRN_CR2)
868 GET_REG64(pVCpu->cpum.GstCtx.cr2, WHvX64RegisterCr2);
869 if (fWhat & CPUMCTX_EXTRN_CR3)
870 {
871 if (pVCpu->cpum.GstCtx.cr3 != aValues[iReg].Reg64)
872 {
873 CPUMSetGuestCR3(pVCpu, aValues[iReg].Reg64);
874 fUpdateCr3 = true;
875 }
876 iReg++;
877 }
878 if (fWhat & CPUMCTX_EXTRN_CR4)
879 {
880 if (pVCpu->cpum.GstCtx.cr4 != aValues[iReg].Reg64)
881 {
882 CPUMSetGuestCR4(pVCpu, aValues[iReg].Reg64);
883 fMaybeChangedMode = true;
884 }
885 iReg++;
886 }
887 }
888 if (fWhat & CPUMCTX_EXTRN_APIC_TPR)
889 {
890 Assert(aenmNames[iReg] == WHvX64RegisterCr8);
891 APICSetTpr(pVCpu, (uint8_t)aValues[iReg].Reg64 << 4);
892 iReg++;
893 }
894
895 /* Debug registers. */
896 if (fWhat & CPUMCTX_EXTRN_DR7)
897 {
898 Assert(aenmNames[iReg] == WHvX64RegisterDr7);
899 if (pVCpu->cpum.GstCtx.dr[7] != aValues[iReg].Reg64)
900 CPUMSetGuestDR7(pVCpu, aValues[iReg].Reg64);
901 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_DR7; /* Hack alert! Avoids asserting when processing CPUMCTX_EXTRN_DR0_DR3. */
902 iReg++;
903 }
904 if (fWhat & CPUMCTX_EXTRN_DR0_DR3)
905 {
906 Assert(aenmNames[iReg] == WHvX64RegisterDr0);
907 Assert(aenmNames[iReg+3] == WHvX64RegisterDr3);
908 if (pVCpu->cpum.GstCtx.dr[0] != aValues[iReg].Reg64)
909 CPUMSetGuestDR0(pVCpu, aValues[iReg].Reg64);
910 iReg++;
911 if (pVCpu->cpum.GstCtx.dr[1] != aValues[iReg].Reg64)
912 CPUMSetGuestDR1(pVCpu, aValues[iReg].Reg64);
913 iReg++;
914 if (pVCpu->cpum.GstCtx.dr[2] != aValues[iReg].Reg64)
915 CPUMSetGuestDR2(pVCpu, aValues[iReg].Reg64);
916 iReg++;
917 if (pVCpu->cpum.GstCtx.dr[3] != aValues[iReg].Reg64)
918 CPUMSetGuestDR3(pVCpu, aValues[iReg].Reg64);
919 iReg++;
920 }
921 if (fWhat & CPUMCTX_EXTRN_DR6)
922 {
923 Assert(aenmNames[iReg] == WHvX64RegisterDr6);
924 if (pVCpu->cpum.GstCtx.dr[6] != aValues[iReg].Reg64)
925 CPUMSetGuestDR6(pVCpu, aValues[iReg].Reg64);
926 iReg++;
927 }
928
929 /* Floating point state. */
930 if (fWhat & CPUMCTX_EXTRN_X87)
931 {
932 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[1], WHvX64RegisterFpMmx0);
933 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[1], WHvX64RegisterFpMmx1);
934 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[1], WHvX64RegisterFpMmx2);
935 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[1], WHvX64RegisterFpMmx3);
936 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[1], WHvX64RegisterFpMmx4);
937 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[1], WHvX64RegisterFpMmx5);
938 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[1], WHvX64RegisterFpMmx6);
939 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[1], WHvX64RegisterFpMmx7);
940
941 Assert(aenmNames[iReg] == WHvX64RegisterFpControlStatus);
942 pVCpu->cpum.GstCtx.XState.x87.FCW = aValues[iReg].FpControlStatus.FpControl;
943 pVCpu->cpum.GstCtx.XState.x87.FSW = aValues[iReg].FpControlStatus.FpStatus;
944 pVCpu->cpum.GstCtx.XState.x87.FTW = aValues[iReg].FpControlStatus.FpTag
945 /*| (aValues[iReg].FpControlStatus.Reserved << 8)*/;
946 pVCpu->cpum.GstCtx.XState.x87.FOP = aValues[iReg].FpControlStatus.LastFpOp;
947 pVCpu->cpum.GstCtx.XState.x87.FPUIP = (uint32_t)aValues[iReg].FpControlStatus.LastFpRip;
948 pVCpu->cpum.GstCtx.XState.x87.CS = (uint16_t)(aValues[iReg].FpControlStatus.LastFpRip >> 32);
949 pVCpu->cpum.GstCtx.XState.x87.Rsrvd1 = (uint16_t)(aValues[iReg].FpControlStatus.LastFpRip >> 48);
950 iReg++;
951 }
952
953 if (fWhat & (CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX))
954 {
955 Assert(aenmNames[iReg] == WHvX64RegisterXmmControlStatus);
956 if (fWhat & CPUMCTX_EXTRN_X87)
957 {
958 pVCpu->cpum.GstCtx.XState.x87.FPUDP = (uint32_t)aValues[iReg].XmmControlStatus.LastFpRdp;
959 pVCpu->cpum.GstCtx.XState.x87.DS = (uint16_t)(aValues[iReg].XmmControlStatus.LastFpRdp >> 32);
960 pVCpu->cpum.GstCtx.XState.x87.Rsrvd2 = (uint16_t)(aValues[iReg].XmmControlStatus.LastFpRdp >> 48);
961 }
962 pVCpu->cpum.GstCtx.XState.x87.MXCSR = aValues[iReg].XmmControlStatus.XmmStatusControl;
963 pVCpu->cpum.GstCtx.XState.x87.MXCSR_MASK = aValues[iReg].XmmControlStatus.XmmStatusControlMask; /** @todo ??? (Isn't this an output field?) */
964 iReg++;
965 }
966
967 /* Vector state. */
968 if (fWhat & CPUMCTX_EXTRN_SSE_AVX)
969 {
970 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Hi, WHvX64RegisterXmm0);
971 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Hi, WHvX64RegisterXmm1);
972 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Hi, WHvX64RegisterXmm2);
973 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Hi, WHvX64RegisterXmm3);
974 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Hi, WHvX64RegisterXmm4);
975 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Hi, WHvX64RegisterXmm5);
976 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Hi, WHvX64RegisterXmm6);
977 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Hi, WHvX64RegisterXmm7);
978 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Hi, WHvX64RegisterXmm8);
979 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Hi, WHvX64RegisterXmm9);
980 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Hi, WHvX64RegisterXmm10);
981 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Hi, WHvX64RegisterXmm11);
982 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Hi, WHvX64RegisterXmm12);
983 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Hi, WHvX64RegisterXmm13);
984 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Hi, WHvX64RegisterXmm14);
985 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Hi, WHvX64RegisterXmm15);
986 }
987
988 /* MSRs */
989 // WHvX64RegisterTsc - don't touch
990 if (fWhat & CPUMCTX_EXTRN_EFER)
991 {
992 Assert(aenmNames[iReg] == WHvX64RegisterEfer);
993 if (aValues[iReg].Reg64 != pVCpu->cpum.GstCtx.msrEFER)
994 {
995 Log7(("NEM/%u: MSR EFER changed %RX64 -> %RX64\n", pVCpu->idCpu, pVCpu->cpum.GstCtx.msrEFER, aValues[iReg].Reg64));
996 if ((aValues[iReg].Reg64 ^ pVCpu->cpum.GstCtx.msrEFER) & MSR_K6_EFER_NXE)
997 PGMNotifyNxeChanged(pVCpu, RT_BOOL(aValues[iReg].Reg64 & MSR_K6_EFER_NXE));
998 pVCpu->cpum.GstCtx.msrEFER = aValues[iReg].Reg64;
999 fMaybeChangedMode = true;
1000 }
1001 iReg++;
1002 }
1003 if (fWhat & CPUMCTX_EXTRN_KERNEL_GS_BASE)
1004 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrKERNELGSBASE, WHvX64RegisterKernelGsBase, "MSR KERNEL_GS_BASE");
1005 if (fWhat & CPUMCTX_EXTRN_SYSENTER_MSRS)
1006 {
1007 GET_REG64_LOG7(pVCpu->cpum.GstCtx.SysEnter.cs, WHvX64RegisterSysenterCs, "MSR SYSENTER.CS");
1008 GET_REG64_LOG7(pVCpu->cpum.GstCtx.SysEnter.eip, WHvX64RegisterSysenterEip, "MSR SYSENTER.EIP");
1009 GET_REG64_LOG7(pVCpu->cpum.GstCtx.SysEnter.esp, WHvX64RegisterSysenterEsp, "MSR SYSENTER.ESP");
1010 }
1011 if (fWhat & CPUMCTX_EXTRN_SYSCALL_MSRS)
1012 {
1013 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrSTAR, WHvX64RegisterStar, "MSR STAR");
1014 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrLSTAR, WHvX64RegisterLstar, "MSR LSTAR");
1015 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrCSTAR, WHvX64RegisterCstar, "MSR CSTAR");
1016 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrSFMASK, WHvX64RegisterSfmask, "MSR SFMASK");
1017 }
1018 if (fWhat & (CPUMCTX_EXTRN_TSC_AUX | CPUMCTX_EXTRN_OTHER_MSRS))
1019 {
1020 PCPUMCTXMSRS const pCtxMsrs = CPUMQueryGuestCtxMsrsPtr(pVCpu);
1021 if (fWhat & CPUMCTX_EXTRN_TSC_AUX)
1022 GET_REG64_LOG7(pCtxMsrs->msr.TscAux, WHvX64RegisterTscAux, "MSR TSC_AUX");
1023 if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS)
1024 {
1025 Assert(aenmNames[iReg] == WHvX64RegisterApicBase);
1026 const uint64_t uOldBase = APICGetBaseMsrNoCheck(pVCpu);
1027 if (aValues[iReg].Reg64 != uOldBase)
1028 {
1029 Log7(("NEM/%u: MSR APICBase changed %RX64 -> %RX64 (%RX64)\n",
1030 pVCpu->idCpu, uOldBase, aValues[iReg].Reg64, aValues[iReg].Reg64 ^ uOldBase));
1031 int rc2 = APICSetBaseMsr(pVCpu, aValues[iReg].Reg64);
1032 AssertLogRelMsg(rc2 == VINF_SUCCESS, ("%Rrc %RX64\n", rc2, aValues[iReg].Reg64));
1033 }
1034 iReg++;
1035
1036 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrPAT, WHvX64RegisterPat, "MSR PAT");
1037#if 0 /*def LOG_ENABLED*/ /** @todo something's wrong with HvX64RegisterMtrrCap? (AMD) */
1038 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrPAT, WHvX64RegisterMsrMtrrCap);
1039#endif
1040 GET_REG64_LOG7(pCtxMsrs->msr.MtrrDefType, WHvX64RegisterMsrMtrrDefType, "MSR MTRR_DEF_TYPE");
1041 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix64K_00000, WHvX64RegisterMsrMtrrFix64k00000, "MSR MTRR_FIX_64K_00000");
1042 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix16K_80000, WHvX64RegisterMsrMtrrFix16k80000, "MSR MTRR_FIX_16K_80000");
1043 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix16K_A0000, WHvX64RegisterMsrMtrrFix16kA0000, "MSR MTRR_FIX_16K_A0000");
1044 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_C0000, WHvX64RegisterMsrMtrrFix4kC0000, "MSR MTRR_FIX_4K_C0000");
1045 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_C8000, WHvX64RegisterMsrMtrrFix4kC8000, "MSR MTRR_FIX_4K_C8000");
1046 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_D0000, WHvX64RegisterMsrMtrrFix4kD0000, "MSR MTRR_FIX_4K_D0000");
1047 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_D8000, WHvX64RegisterMsrMtrrFix4kD8000, "MSR MTRR_FIX_4K_D8000");
1048 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_E0000, WHvX64RegisterMsrMtrrFix4kE0000, "MSR MTRR_FIX_4K_E0000");
1049 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_E8000, WHvX64RegisterMsrMtrrFix4kE8000, "MSR MTRR_FIX_4K_E8000");
1050 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_F0000, WHvX64RegisterMsrMtrrFix4kF0000, "MSR MTRR_FIX_4K_F0000");
1051 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_F8000, WHvX64RegisterMsrMtrrFix4kF8000, "MSR MTRR_FIX_4K_F8000");
1052 /** @todo look for HvX64RegisterIa32MiscEnable and HvX64RegisterIa32FeatureControl? */
1053 }
1054 }
1055
1056 /* Interruptibility. */
1057 if (fWhat & (CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI))
1058 {
1059 Assert(aenmNames[iReg] == WHvRegisterInterruptState);
1060 Assert(aenmNames[iReg + 1] == WHvX64RegisterRip);
1061
1062 if (!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT))
1063 {
1064 pVCpu->nem.s.fLastInterruptShadow = aValues[iReg].InterruptState.InterruptShadow;
1065 if (aValues[iReg].InterruptState.InterruptShadow)
1066 EMSetInhibitInterruptsPC(pVCpu, aValues[iReg + 1].Reg64);
1067 else
1068 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
1069 }
1070
1071 if (!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI))
1072 {
1073 if (aValues[iReg].InterruptState.NmiMasked)
1074 VMCPU_FF_SET(pVCpu, VMCPU_FF_BLOCK_NMIS);
1075 else
1076 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_BLOCK_NMIS);
1077 }
1078
1079 fWhat |= CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI;
1080 iReg += 2;
1081 }
1082
1083 /* Event injection. */
1084 /// @todo WHvRegisterPendingInterruption
1085 Assert(aenmNames[iReg] == WHvRegisterPendingInterruption);
1086 if (aValues[iReg].PendingInterruption.InterruptionPending)
1087 {
1088 Log7(("PendingInterruption: type=%u vector=%#x errcd=%RTbool/%#x instr-len=%u nested=%u\n",
1089 aValues[iReg].PendingInterruption.InterruptionType, aValues[iReg].PendingInterruption.InterruptionVector,
1090 aValues[iReg].PendingInterruption.DeliverErrorCode, aValues[iReg].PendingInterruption.ErrorCode,
1091 aValues[iReg].PendingInterruption.InstructionLength, aValues[iReg].PendingInterruption.NestedEvent));
1092 AssertMsg((aValues[iReg].PendingInterruption.AsUINT64 & UINT64_C(0xfc00)) == 0,
1093 ("%#RX64\n", aValues[iReg].PendingInterruption.AsUINT64));
1094 }
1095
1096 /// @todo WHvRegisterPendingEvent0 (renamed to WHvRegisterPendingEvent).
1097
1098 /* Almost done, just update extrn flags and maybe change PGM mode. */
1099 pVCpu->cpum.GstCtx.fExtrn &= ~fWhat;
1100 if (!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT))))
1101 pVCpu->cpum.GstCtx.fExtrn = 0;
1102
1103 /* Typical. */
1104 if (!fMaybeChangedMode && !fUpdateCr3)
1105 return VINF_SUCCESS;
1106
1107 /*
1108 * Slow.
1109 */
1110 if (fMaybeChangedMode)
1111 {
1112 int rc = PGMChangeMode(pVCpu, pVCpu->cpum.GstCtx.cr0, pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER);
1113 AssertMsgReturn(rc == VINF_SUCCESS, ("rc=%Rrc\n", rc), RT_FAILURE_NP(rc) ? rc : VERR_NEM_IPE_1);
1114 }
1115
1116 if (fUpdateCr3)
1117 {
1118 int rc = PGMUpdateCR3(pVCpu, pVCpu->cpum.GstCtx.cr3, false /*fPdpesMapped*/);
1119 if (rc == VINF_SUCCESS)
1120 { /* likely */ }
1121 else
1122 AssertMsgFailedReturn(("rc=%Rrc\n", rc), RT_FAILURE_NP(rc) ? rc : VERR_NEM_IPE_2);
1123 }
1124
1125 return VINF_SUCCESS;
1126# endif /* !NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS */
1127}
1128
1129#endif /* !IN_RING0 */
1130
1131
1132/**
1133 * Interface for importing state on demand (used by IEM).
1134 *
1135 * @returns VBox status code.
1136 * @param pVCpu The cross context CPU structure.
1137 * @param fWhat What to import, CPUMCTX_EXTRN_XXX.
1138 */
1139VMM_INT_DECL(int) NEMImportStateOnDemand(PVMCPUCC pVCpu, uint64_t fWhat)
1140{
1141 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnDemand);
1142
1143#ifdef IN_RING0
1144# ifdef NEM_WIN_WITH_RING0_RUNLOOP
1145 return nemR0WinImportState(pVCpu->pGVM, pVCpu, &pVCpu->cpum.GstCtx, fWhat, true /*fCanUpdateCr3*/);
1146# else
1147 RT_NOREF(pVCpu, fWhat);
1148 return VERR_NOT_IMPLEMENTED;
1149# endif
1150#else
1151 return nemHCWinCopyStateFromHyperV(pVCpu->pVMR3, pVCpu, fWhat);
1152#endif
1153}
1154
1155
1156/**
1157 * Query the CPU tick counter and optionally the TSC_AUX MSR value.
1158 *
1159 * @returns VBox status code.
1160 * @param pVCpu The cross context CPU structure.
1161 * @param pcTicks Where to return the CPU tick count.
1162 * @param puAux Where to return the TSC_AUX register value.
1163 */
1164VMM_INT_DECL(int) NEMHCQueryCpuTick(PVMCPUCC pVCpu, uint64_t *pcTicks, uint32_t *puAux)
1165{
1166 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatQueryCpuTick);
1167
1168#ifdef IN_RING3
1169 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1170 VMCPU_ASSERT_EMT_RETURN(pVCpu, VERR_VM_THREAD_NOT_EMT);
1171 AssertReturn(VM_IS_NEM_ENABLED(pVM), VERR_NEM_IPE_9);
1172
1173# if defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) || defined(NEM_WIN_WITH_RING0_RUNLOOP)
1174# if !defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) && defined(NEM_WIN_WITH_RING0_RUNLOOP)
1175 if (pVM->nem.s.fUseRing0Runloop)
1176# endif
1177 {
1178 /* Call ring-0 and get the values. */
1179 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_QUERY_CPU_TICK, 0, NULL);
1180 AssertLogRelRCReturn(rc, rc);
1181 *pcTicks = pVCpu->nem.s.Hypercall.QueryCpuTick.cTicks;
1182 if (puAux)
1183 *puAux = pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_TSC_AUX
1184 ? pVCpu->nem.s.Hypercall.QueryCpuTick.uAux : CPUMGetGuestTscAux(pVCpu);
1185 return VINF_SUCCESS;
1186 }
1187# endif
1188# ifndef NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS
1189 /* Call the offical API. */
1190 WHV_REGISTER_NAME aenmNames[2] = { WHvX64RegisterTsc, WHvX64RegisterTscAux };
1191 WHV_REGISTER_VALUE aValues[2] = { {0, 0}, {0, 0} };
1192 Assert(RT_ELEMENTS(aenmNames) == RT_ELEMENTS(aValues));
1193 HRESULT hrc = WHvGetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, 2, aValues);
1194 AssertLogRelMsgReturn(SUCCEEDED(hrc),
1195 ("WHvGetVirtualProcessorRegisters(%p, %u,{tsc,tsc_aux},2,) -> %Rhrc (Last=%#x/%u)\n",
1196 pVM->nem.s.hPartition, pVCpu->idCpu, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
1197 , VERR_NEM_GET_REGISTERS_FAILED);
1198 *pcTicks = aValues[0].Reg64;
1199 if (puAux)
1200 *pcTicks = pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_TSC_AUX ? aValues[0].Reg64 : CPUMGetGuestTscAux(pVCpu);
1201 return VINF_SUCCESS;
1202# endif /* !NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS */
1203#else /* IN_RING0 */
1204# ifdef NEM_WIN_WITH_RING0_RUNLOOP
1205 int rc = nemR0WinQueryCpuTick(pVCpu->pGVM, pVCpu, pcTicks, puAux);
1206 if (RT_SUCCESS(rc) && puAux && !(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_TSC_AUX))
1207 *puAux = CPUMGetGuestTscAux(pVCpu);
1208 return rc;
1209# else
1210 RT_NOREF(pVCpu, pcTicks, puAux);
1211 return VERR_NOT_IMPLEMENTED;
1212# endif
1213#endif /* IN_RING0 */
1214}
1215
1216
1217/**
1218 * Resumes CPU clock (TSC) on all virtual CPUs.
1219 *
1220 * This is called by TM when the VM is started, restored, resumed or similar.
1221 *
1222 * @returns VBox status code.
1223 * @param pVM The cross context VM structure.
1224 * @param pVCpu The cross context CPU structure of the calling EMT.
1225 * @param uPausedTscValue The TSC value at the time of pausing.
1226 */
1227VMM_INT_DECL(int) NEMHCResumeCpuTickOnAll(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uPausedTscValue)
1228{
1229#ifdef IN_RING0
1230# ifdef NEM_WIN_WITH_RING0_RUNLOOP
1231 return nemR0WinResumeCpuTickOnAll(pVM, pVCpu, uPausedTscValue);
1232# else
1233 RT_NOREF(pVM, pVCpu, uPausedTscValue);
1234 return VERR_NOT_IMPLEMENTED;
1235# endif
1236#else /* IN_RING3 */
1237 VMCPU_ASSERT_EMT_RETURN(pVCpu, VERR_VM_THREAD_NOT_EMT);
1238 AssertReturn(VM_IS_NEM_ENABLED(pVM), VERR_NEM_IPE_9);
1239
1240# if defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) || defined(NEM_WIN_WITH_RING0_RUNLOOP)
1241# if !defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS) && defined(NEM_WIN_WITH_RING0_RUNLOOP)
1242 if (pVM->nem.s.fUseRing0Runloop)
1243# endif
1244 {
1245 /* Call ring-0 and do it all there. */
1246 return VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_RESUME_CPU_TICK_ON_ALL, uPausedTscValue, NULL);
1247 }
1248# endif
1249# ifndef NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS
1250 /*
1251 * Call the offical API to do the job.
1252 */
1253 if (pVM->cCpus > 1)
1254 RTThreadYield(); /* Try decrease the chance that we get rescheduled in the middle. */
1255
1256 /* Start with the first CPU. */
1257 WHV_REGISTER_NAME enmName = WHvX64RegisterTsc;
1258 WHV_REGISTER_VALUE Value = {0, 0};
1259 Value.Reg64 = uPausedTscValue;
1260 uint64_t const uFirstTsc = ASMReadTSC();
1261 HRESULT hrc = WHvSetVirtualProcessorRegisters(pVM->nem.s.hPartition, 0 /*iCpu*/, &enmName, 1, &Value);
1262 AssertLogRelMsgReturn(SUCCEEDED(hrc),
1263 ("WHvSetVirtualProcessorRegisters(%p, 0,{tsc},2,%#RX64) -> %Rhrc (Last=%#x/%u)\n",
1264 pVM->nem.s.hPartition, uPausedTscValue, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
1265 , VERR_NEM_SET_TSC);
1266
1267 /* Do the other CPUs, adjusting for elapsed TSC and keeping finger crossed
1268 that we don't introduce too much drift here. */
1269 for (VMCPUID iCpu = 1; iCpu < pVM->cCpus; iCpu++)
1270 {
1271 Assert(enmName == WHvX64RegisterTsc);
1272 const uint64_t offDelta = (ASMReadTSC() - uFirstTsc);
1273 Value.Reg64 = uPausedTscValue + offDelta;
1274 hrc = WHvSetVirtualProcessorRegisters(pVM->nem.s.hPartition, iCpu, &enmName, 1, &Value);
1275 AssertLogRelMsgReturn(SUCCEEDED(hrc),
1276 ("WHvSetVirtualProcessorRegisters(%p, 0,{tsc},2,%#RX64 + %#RX64) -> %Rhrc (Last=%#x/%u)\n",
1277 pVM->nem.s.hPartition, iCpu, uPausedTscValue, offDelta, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
1278 , VERR_NEM_SET_TSC);
1279 }
1280
1281 return VINF_SUCCESS;
1282# endif /* !NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS */
1283#endif /* IN_RING3 */
1284}
1285
1286#ifdef NEMWIN_NEED_GET_REGISTER
1287# if defined(IN_RING0) || defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS)
1288/** Worker for assertion macro. */
1289NEM_TMPL_STATIC int nemHCWinGetRegister(PVMCPUCC pVCpu, PGVMCPU pGVCpu, uint32_t enmReg, HV_REGISTER_VALUE *pRetValue)
1290{
1291 RT_ZERO(*pRetValue);
1292# ifdef IN_RING3
1293 RT_NOREF(pVCpu, pGVCpu, enmReg);
1294 return VERR_NOT_IMPLEMENTED;
1295# else
1296 NOREF(pVCpu);
1297
1298 /*
1299 * Hypercall parameters.
1300 */
1301 HV_INPUT_GET_VP_REGISTERS *pInput = (HV_INPUT_GET_VP_REGISTERS *)pGVCpu->nem.s.HypercallData.pbPage;
1302 AssertPtrReturn(pInput, VERR_INTERNAL_ERROR_3);
1303 AssertReturn(g_pfnHvlInvokeHypercall, VERR_NEM_MISSING_KERNEL_API);
1304
1305 pInput->PartitionId = pVCpu->pGVM->nemr0.s.idHvPartition;
1306 pInput->VpIndex = pVCpu->idCpu;
1307 pInput->fFlags = 0;
1308 pInput->Names[0] = (HV_REGISTER_NAME)enmReg;
1309
1310 size_t const cbInput = RT_ALIGN_Z(RT_UOFFSETOF(HV_INPUT_GET_VP_REGISTERS, Names[1]), 32);
1311 HV_REGISTER_VALUE *paValues = (HV_REGISTER_VALUE *)((uint8_t *)pInput + cbInput);
1312 RT_BZERO(paValues, sizeof(paValues[0]) * 1);
1313
1314 /*
1315 * Make the hypercall and copy out the value.
1316 */
1317 uint64_t uResult = g_pfnHvlInvokeHypercall(HV_MAKE_CALL_INFO(HvCallGetVpRegisters, 1),
1318 pGVCpu->nem.s.HypercallData.HCPhysPage,
1319 pGVCpu->nem.s.HypercallData.HCPhysPage + cbInput);
1320 AssertLogRelMsgReturn(uResult == HV_MAKE_CALL_REP_RET(1), ("uResult=%RX64 cRegs=%#x\n", uResult, 1),
1321 VERR_NEM_GET_REGISTERS_FAILED);
1322
1323 *pRetValue = paValues[0];
1324 return VINF_SUCCESS;
1325# endif
1326}
1327# else
1328/** Worker for assertion macro. */
1329NEM_TMPL_STATIC int nemR3WinGetRegister(PVMCPUCC a_pVCpu, uint32_t a_enmReg, WHV_REGISTER_VALUE pValue)
1330{
1331 RT_ZERO(*pRetValue);
1332 RT_NOREF(pVCpu, pGVCpu, enmReg);
1333 return VERR_NOT_IMPLEMENTED;
1334}
1335# endif
1336#endif
1337
1338
1339#ifdef LOG_ENABLED
1340/**
1341 * Get the virtual processor running status.
1342 */
1343DECLINLINE(VID_PROCESSOR_STATUS) nemHCWinCpuGetRunningStatus(PVMCPUCC pVCpu)
1344{
1345# ifdef IN_RING0
1346 NOREF(pVCpu);
1347 return VidProcessorStatusUndefined;
1348# else
1349 RTERRVARS Saved;
1350 RTErrVarsSave(&Saved);
1351
1352 /*
1353 * This API is disabled in release builds, it seems. On build 17101 it requires
1354 * the following patch to be enabled (windbg): eb vid+12180 0f 84 98 00 00 00
1355 */
1356 VID_PROCESSOR_STATUS enmCpuStatus = VidProcessorStatusUndefined;
1357 NTSTATUS rcNt = g_pfnVidGetVirtualProcessorRunningStatus(pVCpu->pVMR3->nem.s.hPartitionDevice, pVCpu->idCpu, &enmCpuStatus);
1358 AssertRC(rcNt);
1359
1360 RTErrVarsRestore(&Saved);
1361 return enmCpuStatus;
1362# endif
1363}
1364#endif /* LOG_ENABLED */
1365
1366
1367#if defined(NEM_WIN_USE_OUR_OWN_RUN_API) || defined(NEM_WIN_WITH_RING0_RUNLOOP)
1368# ifdef IN_RING3 /* hopefully not needed in ring-0, as we'd need KTHREADs and KeAlertThread. */
1369/**
1370 * Our own WHvCancelRunVirtualProcessor that can later be moved to ring-0.
1371 *
1372 * This is an experiment only.
1373 *
1374 * @returns VBox status code.
1375 * @param pVM The cross context VM structure.
1376 * @param pVCpu The cross context virtual CPU structure of the
1377 * calling EMT.
1378 */
1379NEM_TMPL_STATIC int nemHCWinCancelRunVirtualProcessor(PVMCC pVM, PVMCPUCC pVCpu)
1380{
1381 /*
1382 * Work the state.
1383 *
1384 * From the looks of things, we should let the EMT call VidStopVirtualProcessor.
1385 * So, we just need to modify the state and kick the EMT if it's waiting on
1386 * messages. For the latter we use QueueUserAPC / KeAlterThread.
1387 */
1388 for (;;)
1389 {
1390 VMCPUSTATE enmState = VMCPU_GET_STATE(pVCpu);
1391 switch (enmState)
1392 {
1393 case VMCPUSTATE_STARTED_EXEC_NEM:
1394 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED, VMCPUSTATE_STARTED_EXEC_NEM))
1395 {
1396 DBGFTRACE_CUSTOM(pVM, "VMCPUSTATE_STARTED_EXEC_NEM -> CANCELED");
1397 Log8(("nemHCWinCancelRunVirtualProcessor: Switched %u to canceled state\n", pVCpu->idCpu));
1398 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatCancelChangedState);
1399 return VINF_SUCCESS;
1400 }
1401 break;
1402
1403 case VMCPUSTATE_STARTED_EXEC_NEM_WAIT:
1404 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED, VMCPUSTATE_STARTED_EXEC_NEM_WAIT))
1405 {
1406 DBGFTRACE_CUSTOM(pVM, "VMCPUSTATE_STARTED_EXEC_NEM_WAIT -> CANCELED");
1407# ifdef IN_RING0
1408 NTSTATUS rcNt = KeAlertThread(??);
1409 DBGFTRACE_CUSTOM(pVM, "KeAlertThread -> %#x", rcNt);
1410# else
1411 NTSTATUS rcNt = NtAlertThread(pVCpu->nem.s.hNativeThreadHandle);
1412 DBGFTRACE_CUSTOM(pVM, "NtAlertThread -> %#x", rcNt);
1413# endif
1414 Log8(("nemHCWinCancelRunVirtualProcessor: Alerted %u: %#x\n", pVCpu->idCpu, rcNt));
1415 Assert(rcNt == STATUS_SUCCESS);
1416 if (NT_SUCCESS(rcNt))
1417 {
1418 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatCancelAlertedThread);
1419 return VINF_SUCCESS;
1420 }
1421 AssertLogRelMsgFailedReturn(("NtAlertThread failed: %#x\n", rcNt), RTErrConvertFromNtStatus(rcNt));
1422 }
1423 break;
1424
1425 default:
1426 return VINF_SUCCESS;
1427 }
1428
1429 ASMNopPause();
1430 RT_NOREF(pVM);
1431 }
1432}
1433# endif /* IN_RING3 */
1434#endif /* NEM_WIN_USE_OUR_OWN_RUN_API || NEM_WIN_WITH_RING0_RUNLOOP */
1435
1436
1437#ifdef LOG_ENABLED
1438/**
1439 * Logs the current CPU state.
1440 */
1441NEM_TMPL_STATIC void nemHCWinLogState(PVMCC pVM, PVMCPUCC pVCpu)
1442{
1443 if (LogIs3Enabled())
1444 {
1445# if 0 // def IN_RING3 - causes lazy state import assertions all over CPUM.
1446 char szRegs[4096];
1447 DBGFR3RegPrintf(pVM->pUVM, pVCpu->idCpu, &szRegs[0], sizeof(szRegs),
1448 "rax=%016VR{rax} rbx=%016VR{rbx} rcx=%016VR{rcx} rdx=%016VR{rdx}\n"
1449 "rsi=%016VR{rsi} rdi=%016VR{rdi} r8 =%016VR{r8} r9 =%016VR{r9}\n"
1450 "r10=%016VR{r10} r11=%016VR{r11} r12=%016VR{r12} r13=%016VR{r13}\n"
1451 "r14=%016VR{r14} r15=%016VR{r15} %VRF{rflags}\n"
1452 "rip=%016VR{rip} rsp=%016VR{rsp} rbp=%016VR{rbp}\n"
1453 "cs={%04VR{cs} base=%016VR{cs_base} limit=%08VR{cs_lim} flags=%04VR{cs_attr}} cr0=%016VR{cr0}\n"
1454 "ds={%04VR{ds} base=%016VR{ds_base} limit=%08VR{ds_lim} flags=%04VR{ds_attr}} cr2=%016VR{cr2}\n"
1455 "es={%04VR{es} base=%016VR{es_base} limit=%08VR{es_lim} flags=%04VR{es_attr}} cr3=%016VR{cr3}\n"
1456 "fs={%04VR{fs} base=%016VR{fs_base} limit=%08VR{fs_lim} flags=%04VR{fs_attr}} cr4=%016VR{cr4}\n"
1457 "gs={%04VR{gs} base=%016VR{gs_base} limit=%08VR{gs_lim} flags=%04VR{gs_attr}} cr8=%016VR{cr8}\n"
1458 "ss={%04VR{ss} base=%016VR{ss_base} limit=%08VR{ss_lim} flags=%04VR{ss_attr}}\n"
1459 "dr0=%016VR{dr0} dr1=%016VR{dr1} dr2=%016VR{dr2} dr3=%016VR{dr3}\n"
1460 "dr6=%016VR{dr6} dr7=%016VR{dr7}\n"
1461 "gdtr=%016VR{gdtr_base}:%04VR{gdtr_lim} idtr=%016VR{idtr_base}:%04VR{idtr_lim} rflags=%08VR{rflags}\n"
1462 "ldtr={%04VR{ldtr} base=%016VR{ldtr_base} limit=%08VR{ldtr_lim} flags=%08VR{ldtr_attr}}\n"
1463 "tr ={%04VR{tr} base=%016VR{tr_base} limit=%08VR{tr_lim} flags=%08VR{tr_attr}}\n"
1464 " sysenter={cs=%04VR{sysenter_cs} eip=%08VR{sysenter_eip} esp=%08VR{sysenter_esp}}\n"
1465 " efer=%016VR{efer}\n"
1466 " pat=%016VR{pat}\n"
1467 " sf_mask=%016VR{sf_mask}\n"
1468 "krnl_gs_base=%016VR{krnl_gs_base}\n"
1469 " lstar=%016VR{lstar}\n"
1470 " star=%016VR{star} cstar=%016VR{cstar}\n"
1471 "fcw=%04VR{fcw} fsw=%04VR{fsw} ftw=%04VR{ftw} mxcsr=%04VR{mxcsr} mxcsr_mask=%04VR{mxcsr_mask}\n"
1472 );
1473
1474 char szInstr[256];
1475 DBGFR3DisasInstrEx(pVM->pUVM, pVCpu->idCpu, 0, 0,
1476 DBGF_DISAS_FLAGS_CURRENT_GUEST | DBGF_DISAS_FLAGS_DEFAULT_MODE,
1477 szInstr, sizeof(szInstr), NULL);
1478 Log3(("%s%s\n", szRegs, szInstr));
1479# else
1480 /** @todo stat logging in ring-0 */
1481 RT_NOREF(pVM, pVCpu);
1482# endif
1483 }
1484}
1485#endif /* LOG_ENABLED */
1486
1487
1488/** Macro used by nemHCWinExecStateToLogStr and nemR3WinExecStateToLogStr. */
1489#define SWITCH_IT(a_szPrefix) \
1490 do \
1491 switch (u)\
1492 { \
1493 case 0x00: return a_szPrefix ""; \
1494 case 0x01: return a_szPrefix ",Pnd"; \
1495 case 0x02: return a_szPrefix ",Dbg"; \
1496 case 0x03: return a_szPrefix ",Pnd,Dbg"; \
1497 case 0x04: return a_szPrefix ",Shw"; \
1498 case 0x05: return a_szPrefix ",Pnd,Shw"; \
1499 case 0x06: return a_szPrefix ",Shw,Dbg"; \
1500 case 0x07: return a_szPrefix ",Pnd,Shw,Dbg"; \
1501 default: AssertFailedReturn("WTF?"); \
1502 } \
1503 while (0)
1504
1505#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
1506/**
1507 * Translates the execution stat bitfield into a short log string, VID version.
1508 *
1509 * @returns Read-only log string.
1510 * @param pMsgHdr The header which state to summarize.
1511 */
1512static const char *nemHCWinExecStateToLogStr(HV_X64_INTERCEPT_MESSAGE_HEADER const *pMsgHdr)
1513{
1514 unsigned u = (unsigned)pMsgHdr->ExecutionState.InterruptionPending
1515 | ((unsigned)pMsgHdr->ExecutionState.DebugActive << 1)
1516 | ((unsigned)pMsgHdr->ExecutionState.InterruptShadow << 2);
1517 if (pMsgHdr->ExecutionState.EferLma)
1518 SWITCH_IT("LM");
1519 else if (pMsgHdr->ExecutionState.Cr0Pe)
1520 SWITCH_IT("PM");
1521 else
1522 SWITCH_IT("RM");
1523}
1524#elif defined(IN_RING3)
1525/**
1526 * Translates the execution stat bitfield into a short log string, WinHv version.
1527 *
1528 * @returns Read-only log string.
1529 * @param pExitCtx The exit context which state to summarize.
1530 */
1531static const char *nemR3WinExecStateToLogStr(WHV_VP_EXIT_CONTEXT const *pExitCtx)
1532{
1533 unsigned u = (unsigned)pExitCtx->ExecutionState.InterruptionPending
1534 | ((unsigned)pExitCtx->ExecutionState.DebugActive << 1)
1535 | ((unsigned)pExitCtx->ExecutionState.InterruptShadow << 2);
1536 if (pExitCtx->ExecutionState.EferLma)
1537 SWITCH_IT("LM");
1538 else if (pExitCtx->ExecutionState.Cr0Pe)
1539 SWITCH_IT("PM");
1540 else
1541 SWITCH_IT("RM");
1542}
1543#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
1544#undef SWITCH_IT
1545
1546
1547#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
1548/**
1549 * Advances the guest RIP and clear EFLAGS.RF, VID version.
1550 *
1551 * This may clear VMCPU_FF_INHIBIT_INTERRUPTS.
1552 *
1553 * @param pVCpu The cross context virtual CPU structure.
1554 * @param pExitCtx The exit context.
1555 * @param cbMinInstr The minimum instruction length, or 1 if not unknown.
1556 */
1557DECLINLINE(void)
1558nemHCWinAdvanceGuestRipAndClearRF(PVMCPUCC pVCpu, HV_X64_INTERCEPT_MESSAGE_HEADER const *pMsgHdr, uint8_t cbMinInstr)
1559{
1560 Assert(!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS)));
1561
1562 /* Advance the RIP. */
1563 Assert(pMsgHdr->InstructionLength >= cbMinInstr); RT_NOREF_PV(cbMinInstr);
1564 pVCpu->cpum.GstCtx.rip += pMsgHdr->InstructionLength;
1565 pVCpu->cpum.GstCtx.rflags.Bits.u1RF = 0;
1566
1567 /* Update interrupt inhibition. */
1568 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
1569 { /* likely */ }
1570 else if (pVCpu->cpum.GstCtx.rip != EMGetInhibitInterruptsPC(pVCpu))
1571 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
1572}
1573#elif defined(IN_RING3)
1574/**
1575 * Advances the guest RIP and clear EFLAGS.RF, WinHv version.
1576 *
1577 * This may clear VMCPU_FF_INHIBIT_INTERRUPTS.
1578 *
1579 * @param pVCpu The cross context virtual CPU structure.
1580 * @param pExitCtx The exit context.
1581 * @param cbMinInstr The minimum instruction length, or 1 if not unknown.
1582 */
1583DECLINLINE(void) nemR3WinAdvanceGuestRipAndClearRF(PVMCPUCC pVCpu, WHV_VP_EXIT_CONTEXT const *pExitCtx, uint8_t cbMinInstr)
1584{
1585 Assert(!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS)));
1586
1587 /* Advance the RIP. */
1588 Assert(pExitCtx->InstructionLength >= cbMinInstr); RT_NOREF_PV(cbMinInstr);
1589 pVCpu->cpum.GstCtx.rip += pExitCtx->InstructionLength;
1590 pVCpu->cpum.GstCtx.rflags.Bits.u1RF = 0;
1591
1592 /* Update interrupt inhibition. */
1593 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
1594 { /* likely */ }
1595 else if (pVCpu->cpum.GstCtx.rip != EMGetInhibitInterruptsPC(pVCpu))
1596 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
1597}
1598#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
1599
1600#if defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) || defined(IN_RING3)
1601
1602NEM_TMPL_STATIC DECLCALLBACK(int)
1603nemHCWinUnmapOnePageCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys, uint8_t *pu2NemState, void *pvUser)
1604{
1605 RT_NOREF_PV(pvUser);
1606# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1607 int rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhys);
1608 AssertRC(rc);
1609 if (RT_SUCCESS(rc))
1610# else
1611 RT_NOREF_PV(pVCpu);
1612 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
1613 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
1614 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
1615 if (SUCCEEDED(hrc))
1616# endif
1617 {
1618 Log5(("NEM GPA unmap all: %RGp (cMappedPages=%u)\n", GCPhys, pVM->nem.s.cMappedPages - 1));
1619 *pu2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
1620 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
1621 }
1622 else
1623 {
1624# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1625 LogRel(("nemHCWinUnmapOnePageCallback: GCPhys=%RGp rc=%Rrc\n", GCPhys, rc));
1626# else
1627 LogRel(("nemHCWinUnmapOnePageCallback: GCPhys=%RGp %s hrc=%Rhrc (%#x) Last=%#x/%u (cMappedPages=%u)\n",
1628 GCPhys, g_apszPageStates[*pu2NemState], hrc, hrc, RTNtLastStatusValue(),
1629 RTNtLastErrorValue(), pVM->nem.s.cMappedPages));
1630# endif
1631 *pu2NemState = NEM_WIN_PAGE_STATE_NOT_SET;
1632 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
1633 }
1634 if (pVM->nem.s.cMappedPages > 0)
1635 ASMAtomicDecU32(&pVM->nem.s.cMappedPages);
1636 return VINF_SUCCESS;
1637}
1638
1639
1640/**
1641 * State to pass between nemHCWinHandleMemoryAccess / nemR3WinWHvHandleMemoryAccess
1642 * and nemHCWinHandleMemoryAccessPageCheckerCallback.
1643 */
1644typedef struct NEMHCWINHMACPCCSTATE
1645{
1646 /** Input: Write access. */
1647 bool fWriteAccess;
1648 /** Output: Set if we did something. */
1649 bool fDidSomething;
1650 /** Output: Set it we should resume. */
1651 bool fCanResume;
1652} NEMHCWINHMACPCCSTATE;
1653
1654/**
1655 * @callback_method_impl{FNPGMPHYSNEMCHECKPAGE,
1656 * Worker for nemR3WinHandleMemoryAccess; pvUser points to a
1657 * NEMHCWINHMACPCCSTATE structure. }
1658 */
1659NEM_TMPL_STATIC DECLCALLBACK(int)
1660nemHCWinHandleMemoryAccessPageCheckerCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys, PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
1661{
1662 NEMHCWINHMACPCCSTATE *pState = (NEMHCWINHMACPCCSTATE *)pvUser;
1663 pState->fDidSomething = false;
1664 pState->fCanResume = false;
1665
1666 /* If A20 is disabled, we may need to make another query on the masked
1667 page to get the correct protection information. */
1668 uint8_t u2State = pInfo->u2NemState;
1669 RTGCPHYS GCPhysSrc;
1670# ifdef NEM_WIN_WITH_A20
1671 if ( pVM->nem.s.fA20Enabled
1672 || !NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
1673# endif
1674 GCPhysSrc = GCPhys;
1675# ifdef NEM_WIN_WITH_A20
1676 else
1677 {
1678 GCPhysSrc = GCPhys & ~(RTGCPHYS)RT_BIT_32(20);
1679 PGMPHYSNEMPAGEINFO Info2;
1680 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhysSrc, pState->fWriteAccess, &Info2, NULL, NULL);
1681 AssertRCReturn(rc, rc);
1682
1683 *pInfo = Info2;
1684 pInfo->u2NemState = u2State;
1685 }
1686# endif
1687
1688 /*
1689 * Consolidate current page state with actual page protection and access type.
1690 * We don't really consider downgrades here, as they shouldn't happen.
1691 */
1692# ifndef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1693 /** @todo Someone at microsoft please explain:
1694 * I'm not sure WTF was going on, but I ended up in a loop if I remapped a
1695 * readonly page as writable (unmap, then map again). Specifically, this was an
1696 * issue with the big VRAM mapping at 0xe0000000 when booing DSL 4.4.1. So, in
1697 * a hope to work around that we no longer pre-map anything, just unmap stuff
1698 * and do it lazily here. And here we will first unmap, restart, and then remap
1699 * with new protection or backing.
1700 */
1701# endif
1702 int rc;
1703 switch (u2State)
1704 {
1705 case NEM_WIN_PAGE_STATE_UNMAPPED:
1706 case NEM_WIN_PAGE_STATE_NOT_SET:
1707 if (pInfo->fNemProt == NEM_PAGE_PROT_NONE)
1708 {
1709 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #1\n", GCPhys));
1710 return VINF_SUCCESS;
1711 }
1712
1713 /* Don't bother remapping it if it's a write request to a non-writable page. */
1714 if ( pState->fWriteAccess
1715 && !(pInfo->fNemProt & NEM_PAGE_PROT_WRITE))
1716 {
1717 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #1w\n", GCPhys));
1718 return VINF_SUCCESS;
1719 }
1720
1721 /* Map the page. */
1722 rc = nemHCNativeSetPhysPage(pVM,
1723 pVCpu,
1724 GCPhysSrc & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
1725 GCPhys & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
1726 pInfo->fNemProt,
1727 &u2State,
1728 true /*fBackingState*/);
1729 pInfo->u2NemState = u2State;
1730 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - synced => %s + %Rrc\n",
1731 GCPhys, g_apszPageStates[u2State], rc));
1732 pState->fDidSomething = true;
1733 pState->fCanResume = true;
1734 return rc;
1735
1736 case NEM_WIN_PAGE_STATE_READABLE:
1737 if ( !(pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
1738 && (pInfo->fNemProt & (NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE)))
1739 {
1740 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #2\n", GCPhys));
1741 return VINF_SUCCESS;
1742 }
1743
1744# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1745 /* Upgrade page to writable. */
1746/** @todo test this*/
1747 if ( (pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
1748 && pState->fWriteAccess)
1749 {
1750 rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhys,
1751 HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE
1752 | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
1753 AssertRC(rc);
1754 if (RT_SUCCESS(rc))
1755 {
1756 STAM_REL_COUNTER_INC(&pVM->nem.s.StatRemapPage);
1757 pInfo->u2NemState = NEM_WIN_PAGE_STATE_WRITABLE;
1758 pState->fDidSomething = true;
1759 pState->fCanResume = true;
1760 Log5(("NEM GPA write-upgrade/exit: %RGp (was %s, cMappedPages=%u)\n",
1761 GCPhys, g_apszPageStates[u2State], pVM->nem.s.cMappedPages));
1762 }
1763 else
1764 STAM_REL_COUNTER_INC(&pVM->nem.s.StatRemapPageFailed);
1765 }
1766 else
1767 {
1768 /* Need to emulate the acces. */
1769 AssertBreak(pInfo->fNemProt != NEM_PAGE_PROT_NONE); /* There should be no downgrades. */
1770 rc = VINF_SUCCESS;
1771 }
1772 return rc;
1773# else
1774 break;
1775# endif
1776
1777 case NEM_WIN_PAGE_STATE_WRITABLE:
1778 if (pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
1779 {
1780 if (pInfo->u2OldNemState == NEM_WIN_PAGE_STATE_WRITABLE)
1781 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #3a\n", GCPhys));
1782 else
1783 {
1784 pState->fCanResume = true;
1785 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #3b (%s -> %s)\n",
1786 GCPhys, g_apszPageStates[pInfo->u2OldNemState], g_apszPageStates[u2State]));
1787 }
1788 return VINF_SUCCESS;
1789 }
1790# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1791 AssertFailed(); /* There should be no downgrades. */
1792# endif
1793 break;
1794
1795 default:
1796 AssertLogRelMsgFailedReturn(("u2State=%#x\n", u2State), VERR_NEM_IPE_4);
1797 }
1798
1799 /*
1800 * Unmap and restart the instruction.
1801 * If this fails, which it does every so often, just unmap everything for now.
1802 */
1803# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1804 rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhys);
1805 AssertRC(rc);
1806 if (RT_SUCCESS(rc))
1807# else
1808 /** @todo figure out whether we mess up the state or if it's WHv. */
1809 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
1810 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
1811 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
1812 if (SUCCEEDED(hrc))
1813# endif
1814 {
1815 pState->fDidSomething = true;
1816 pState->fCanResume = true;
1817 pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
1818 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
1819 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
1820 Log5(("NEM GPA unmapped/exit: %RGp (was %s, cMappedPages=%u)\n", GCPhys, g_apszPageStates[u2State], cMappedPages));
1821 return VINF_SUCCESS;
1822 }
1823 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
1824# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1825 LogRel(("nemHCWinHandleMemoryAccessPageCheckerCallback/unmap: GCPhysDst=%RGp rc=%Rrc\n", GCPhys, rc));
1826 return rc;
1827# elif defined(VBOX_WITH_PGM_NEM_MODE)
1828 LogRel(("nemHCWinHandleMemoryAccessPageCheckerCallback/unmap: GCPhysDst=%RGp %s hrc=%Rhrc (%#x)\n",
1829 GCPhys, g_apszPageStates[u2State], hrc, hrc));
1830 return VERR_NEM_UNMAP_PAGES_FAILED;
1831# else
1832 LogRel(("nemHCWinHandleMemoryAccessPageCheckerCallback/unmap: GCPhysDst=%RGp %s hrc=%Rhrc (%#x) Last=%#x/%u (cMappedPages=%u)\n",
1833 GCPhys, g_apszPageStates[u2State], hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue(),
1834 pVM->nem.s.cMappedPages));
1835
1836 PGMPhysNemEnumPagesByState(pVM, pVCpu, NEM_WIN_PAGE_STATE_READABLE, nemHCWinUnmapOnePageCallback, NULL);
1837 Log(("nemHCWinHandleMemoryAccessPageCheckerCallback: Unmapped all (cMappedPages=%u)\n", pVM->nem.s.cMappedPages));
1838 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapAllPages);
1839
1840 pState->fDidSomething = true;
1841 pState->fCanResume = true;
1842 pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
1843 return VINF_SUCCESS;
1844# endif
1845}
1846
1847#endif /* defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) || defined(IN_RING3) */
1848
1849
1850#if defined(IN_RING0) && defined(NEM_WIN_TEMPLATE_MODE_OWN_RUN_API)
1851/**
1852 * Wrapper around nemR0WinImportState that converts VERR_NEM_FLUSH_TLB
1853 * into informational status codes and logs+asserts statuses.
1854 *
1855 * @returns VBox strict status code.
1856 * @param pGVM The global (ring-0) VM structure.
1857 * @param pGVCpu The global (ring-0) per CPU structure.
1858 * @param fWhat What to import.
1859 * @param pszCaller Who is doing the importing.
1860 */
1861DECLINLINE(VBOXSTRICTRC) nemR0WinImportStateStrict(PGVM pGVM, PGVMCPU pGVCpu, uint64_t fWhat, const char *pszCaller)
1862{
1863 int rc = nemR0WinImportState(pGVM, pGVCpu, &pGVCpu->cpum.GstCtx, fWhat, true /*fCanUpdateCr3*/);
1864 if (RT_SUCCESS(rc))
1865 {
1866 Assert(rc == VINF_SUCCESS);
1867 return VINF_SUCCESS;
1868 }
1869
1870 if (rc == VERR_NEM_FLUSH_TLB)
1871 {
1872 Log4(("%s/%u: nemR0WinImportState -> %Rrc\n", pszCaller, pGVCpu->idCpu, -rc));
1873 return -rc;
1874 }
1875 RT_NOREF(pszCaller);
1876 AssertMsgFailedReturn(("%s/%u: nemR0WinImportState failed: %Rrc\n", pszCaller, pGVCpu->idCpu, rc), rc);
1877}
1878#endif /* IN_RING0 && NEM_WIN_TEMPLATE_MODE_OWN_RUN_API*/
1879
1880#if defined(NEM_WIN_TEMPLATE_MODE_OWN_RUN_API) || defined(IN_RING3)
1881/**
1882 * Wrapper around nemR0WinImportStateStrict and nemHCWinCopyStateFromHyperV.
1883 *
1884 * Unlike the wrapped APIs, this checks whether it's necessary.
1885 *
1886 * @returns VBox strict status code.
1887 * @param pVCpu The cross context per CPU structure.
1888 * @param fWhat What to import.
1889 * @param pszCaller Who is doing the importing.
1890 */
1891DECLINLINE(VBOXSTRICTRC) nemHCWinImportStateIfNeededStrict(PVMCPUCC pVCpu, uint64_t fWhat, const char *pszCaller)
1892{
1893 if (pVCpu->cpum.GstCtx.fExtrn & fWhat)
1894 {
1895# ifdef IN_RING0
1896 return nemR0WinImportStateStrict(pVCpu->pGVM, pVCpu, fWhat, pszCaller);
1897# else
1898 RT_NOREF(pszCaller);
1899 int rc = nemHCWinCopyStateFromHyperV(pVCpu->pVMR3, pVCpu, fWhat);
1900 AssertRCReturn(rc, rc);
1901# endif
1902 }
1903 return VINF_SUCCESS;
1904}
1905#endif /* NEM_WIN_TEMPLATE_MODE_OWN_RUN_API || IN_RING3 */
1906
1907#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
1908/**
1909 * Copies register state from the X64 intercept message header.
1910 *
1911 * ASSUMES no state copied yet.
1912 *
1913 * @param pVCpu The cross context per CPU structure.
1914 * @param pHdr The X64 intercept message header.
1915 * @sa nemR3WinCopyStateFromX64Header
1916 */
1917DECLINLINE(void) nemHCWinCopyStateFromX64Header(PVMCPUCC pVCpu, HV_X64_INTERCEPT_MESSAGE_HEADER const *pHdr)
1918{
1919 Assert( (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT))
1920 == (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT));
1921 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.cs, pHdr->CsSegment);
1922 pVCpu->cpum.GstCtx.rip = pHdr->Rip;
1923 pVCpu->cpum.GstCtx.rflags.u = pHdr->Rflags;
1924
1925 pVCpu->nem.s.fLastInterruptShadow = pHdr->ExecutionState.InterruptShadow;
1926 if (!pHdr->ExecutionState.InterruptShadow)
1927 {
1928 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
1929 { /* likely */ }
1930 else
1931 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
1932 }
1933 else
1934 EMSetInhibitInterruptsPC(pVCpu, pHdr->Rip);
1935
1936 APICSetTpr(pVCpu, pHdr->Cr8 << 4);
1937
1938 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_APIC_TPR);
1939}
1940#elif defined(IN_RING3)
1941/**
1942 * Copies register state from the (common) exit context.
1943 *
1944 * ASSUMES no state copied yet.
1945 *
1946 * @param pVCpu The cross context per CPU structure.
1947 * @param pExitCtx The common exit context.
1948 * @sa nemHCWinCopyStateFromX64Header
1949 */
1950DECLINLINE(void) nemR3WinCopyStateFromX64Header(PVMCPUCC pVCpu, WHV_VP_EXIT_CONTEXT const *pExitCtx)
1951{
1952 Assert( (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT))
1953 == (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT));
1954 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.cs, pExitCtx->Cs);
1955 pVCpu->cpum.GstCtx.rip = pExitCtx->Rip;
1956 pVCpu->cpum.GstCtx.rflags.u = pExitCtx->Rflags;
1957
1958 pVCpu->nem.s.fLastInterruptShadow = pExitCtx->ExecutionState.InterruptShadow;
1959 if (!pExitCtx->ExecutionState.InterruptShadow)
1960 {
1961 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
1962 { /* likely */ }
1963 else
1964 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
1965 }
1966 else
1967 EMSetInhibitInterruptsPC(pVCpu, pExitCtx->Rip);
1968
1969 APICSetTpr(pVCpu, pExitCtx->Cr8 << 4);
1970
1971 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_APIC_TPR);
1972}
1973#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
1974
1975
1976#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
1977/**
1978 * Deals with memory intercept message.
1979 *
1980 * @returns Strict VBox status code.
1981 * @param pVM The cross context VM structure.
1982 * @param pVCpu The cross context per CPU structure.
1983 * @param pMsg The message.
1984 * @sa nemR3WinHandleExitMemory
1985 */
1986NEM_TMPL_STATIC VBOXSTRICTRC
1987nemHCWinHandleMessageMemory(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_MEMORY_INTERCEPT_MESSAGE const *pMsg)
1988{
1989 uint64_t const uHostTsc = ASMReadTSC();
1990 Assert( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_READ
1991 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
1992 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_EXECUTE);
1993
1994 /*
1995 * Whatever we do, we must clear pending event injection upon resume.
1996 */
1997 if (pMsg->Header.ExecutionState.InterruptionPending)
1998 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
1999
2000# if 0 /* Experiment: 20K -> 34K exit/s. */
2001 if ( pMsg->Header.ExecutionState.EferLma
2002 && pMsg->Header.CsSegment.Long
2003 && pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE)
2004 {
2005 if ( pMsg->Header.Rip - (uint64_t)0xf65a < (uint64_t)(0xf662 - 0xf65a)
2006 && pMsg->InstructionBytes[0] == 0x89
2007 && pMsg->InstructionBytes[1] == 0x03)
2008 {
2009 pVCpu->cpum.GstCtx.rip = pMsg->Header.Rip + 2;
2010 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RIP;
2011 AssertMsg(pMsg->Header.InstructionLength == 2, ("%#x\n", pMsg->Header.InstructionLength));
2012 //Log(("%RX64 msg:\n%.80Rhxd\n", pVCpu->cpum.GstCtx.rip, pMsg));
2013 return VINF_SUCCESS;
2014 }
2015 }
2016# endif
2017
2018 /*
2019 * Ask PGM for information about the given GCPhys. We need to check if we're
2020 * out of sync first.
2021 */
2022 NEMHCWINHMACPCCSTATE State = { pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE, false, false };
2023 PGMPHYSNEMPAGEINFO Info;
2024 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, pMsg->GuestPhysicalAddress, State.fWriteAccess, &Info,
2025 nemHCWinHandleMemoryAccessPageCheckerCallback, &State);
2026 if (RT_SUCCESS(rc))
2027 {
2028 if (Info.fNemProt & ( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
2029 ? NEM_PAGE_PROT_WRITE : NEM_PAGE_PROT_READ))
2030 {
2031 if (State.fCanResume)
2032 {
2033 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp (=>%RHp) %s fProt=%u%s%s%s; restarting (%s)\n",
2034 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2035 pMsg->GuestPhysicalAddress, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
2036 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
2037 State.fDidSomething ? "" : " no-change", g_apszHvInterceptAccessTypes[pMsg->Header.InterceptAccessType]));
2038 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_MEMORY_ACCESS),
2039 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, uHostTsc);
2040 return VINF_SUCCESS;
2041 }
2042 }
2043 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp (=>%RHp) %s fProt=%u%s%s%s; emulating (%s)\n",
2044 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2045 pMsg->GuestPhysicalAddress, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
2046 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
2047 State.fDidSomething ? "" : " no-change", g_apszHvInterceptAccessTypes[pMsg->Header.InterceptAccessType]));
2048 }
2049 else
2050 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp rc=%Rrc%s; emulating (%s)\n",
2051 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2052 pMsg->GuestPhysicalAddress, rc, State.fDidSomething ? " modified-backing" : "",
2053 g_apszHvInterceptAccessTypes[pMsg->Header.InterceptAccessType]));
2054
2055 /*
2056 * Emulate the memory access, either access handler or special memory.
2057 */
2058 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
2059 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
2060 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_WRITE)
2061 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_READ),
2062 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, uHostTsc);
2063 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2064 VBOXSTRICTRC rcStrict;
2065# ifdef IN_RING0
2066 rcStrict = nemR0WinImportStateStrict(pVM, pVCpu,
2067 NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES, "MemExit");
2068 if (rcStrict != VINF_SUCCESS)
2069 return rcStrict;
2070# else
2071 rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
2072 AssertRCReturn(rc, rc);
2073# endif
2074
2075 if (pMsg->Reserved1)
2076 Log(("MemExit/Reserved1=%#x\n", pMsg->Reserved1));
2077 if (pMsg->Header.ExecutionState.Reserved0 || pMsg->Header.ExecutionState.Reserved1)
2078 Log(("MemExit/Hdr/State: Reserved0=%#x Reserved1=%#x\n", pMsg->Header.ExecutionState.Reserved0, pMsg->Header.ExecutionState.Reserved1));
2079
2080 if (!pExitRec)
2081 {
2082 //if (pMsg->InstructionByteCount > 0)
2083 // Log4(("InstructionByteCount=%#x %.16Rhxs\n", pMsg->InstructionByteCount, pMsg->InstructionBytes));
2084 if (pMsg->InstructionByteCount > 0)
2085 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pMsg->Header.Rip,
2086 pMsg->InstructionBytes, pMsg->InstructionByteCount);
2087 else
2088 rcStrict = IEMExecOne(pVCpu);
2089 /** @todo do we need to do anything wrt debugging here? */
2090 }
2091 else
2092 {
2093 /* Frequent access or probing. */
2094 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
2095 Log4(("MemExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2096 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2097 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2098 }
2099 return rcStrict;
2100}
2101#elif defined(IN_RING3)
2102/**
2103 * Deals with memory access exits (WHvRunVpExitReasonMemoryAccess).
2104 *
2105 * @returns Strict VBox status code.
2106 * @param pVM The cross context VM structure.
2107 * @param pVCpu The cross context per CPU structure.
2108 * @param pExit The VM exit information to handle.
2109 * @sa nemHCWinHandleMessageMemory
2110 */
2111NEM_TMPL_STATIC VBOXSTRICTRC
2112nemR3WinHandleExitMemory(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2113{
2114 uint64_t const uHostTsc = ASMReadTSC();
2115 Assert(pExit->MemoryAccess.AccessInfo.AccessType != 3);
2116
2117 /*
2118 * Whatever we do, we must clear pending event injection upon resume.
2119 */
2120 if (pExit->VpContext.ExecutionState.InterruptionPending)
2121 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
2122
2123 /*
2124 * Ask PGM for information about the given GCPhys. We need to check if we're
2125 * out of sync first.
2126 */
2127 NEMHCWINHMACPCCSTATE State = { pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite, false, false };
2128 PGMPHYSNEMPAGEINFO Info;
2129 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, pExit->MemoryAccess.Gpa, State.fWriteAccess, &Info,
2130 nemHCWinHandleMemoryAccessPageCheckerCallback, &State);
2131 if (RT_SUCCESS(rc))
2132 {
2133 if (Info.fNemProt & ( pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
2134 ? NEM_PAGE_PROT_WRITE : NEM_PAGE_PROT_READ))
2135 {
2136 if (State.fCanResume)
2137 {
2138 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp (=>%RHp) %s fProt=%u%s%s%s; restarting (%s)\n",
2139 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2140 pExit->MemoryAccess.Gpa, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
2141 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
2142 State.fDidSomething ? "" : " no-change", g_apszHvInterceptAccessTypes[pExit->MemoryAccess.AccessInfo.AccessType]));
2143 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_MEMORY_ACCESS),
2144 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, uHostTsc);
2145 return VINF_SUCCESS;
2146 }
2147 }
2148 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp (=>%RHp) %s fProt=%u%s%s%s; emulating (%s)\n",
2149 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2150 pExit->MemoryAccess.Gpa, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
2151 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
2152 State.fDidSomething ? "" : " no-change", g_apszHvInterceptAccessTypes[pExit->MemoryAccess.AccessInfo.AccessType]));
2153 }
2154 else
2155 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp rc=%Rrc%s; emulating (%s)\n",
2156 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2157 pExit->MemoryAccess.Gpa, rc, State.fDidSomething ? " modified-backing" : "",
2158 g_apszHvInterceptAccessTypes[pExit->MemoryAccess.AccessInfo.AccessType]));
2159
2160 /*
2161 * Emulate the memory access, either access handler or special memory.
2162 */
2163 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
2164 pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
2165 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_WRITE)
2166 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_READ),
2167 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, uHostTsc);
2168 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2169 rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
2170 AssertRCReturn(rc, rc);
2171 if (pExit->VpContext.ExecutionState.Reserved0 || pExit->VpContext.ExecutionState.Reserved1)
2172 Log(("MemExit/Hdr/State: Reserved0=%#x Reserved1=%#x\n", pExit->VpContext.ExecutionState.Reserved0, pExit->VpContext.ExecutionState.Reserved1));
2173
2174 VBOXSTRICTRC rcStrict;
2175 if (!pExitRec)
2176 {
2177 //if (pMsg->InstructionByteCount > 0)
2178 // Log4(("InstructionByteCount=%#x %.16Rhxs\n", pMsg->InstructionByteCount, pMsg->InstructionBytes));
2179 if (pExit->MemoryAccess.InstructionByteCount > 0)
2180 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pExit->VpContext.Rip,
2181 pExit->MemoryAccess.InstructionBytes, pExit->MemoryAccess.InstructionByteCount);
2182 else
2183 rcStrict = IEMExecOne(pVCpu);
2184 /** @todo do we need to do anything wrt debugging here? */
2185 }
2186 else
2187 {
2188 /* Frequent access or probing. */
2189 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
2190 Log4(("MemExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2191 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2192 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2193 }
2194 return rcStrict;
2195}
2196#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
2197
2198
2199#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
2200/**
2201 * Deals with I/O port intercept message.
2202 *
2203 * @returns Strict VBox status code.
2204 * @param pVM The cross context VM structure.
2205 * @param pVCpu The cross context per CPU structure.
2206 * @param pMsg The message.
2207 */
2208NEM_TMPL_STATIC VBOXSTRICTRC
2209nemHCWinHandleMessageIoPort(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_IO_PORT_INTERCEPT_MESSAGE const *pMsg)
2210{
2211 /*
2212 * Assert message sanity.
2213 */
2214 Assert( pMsg->AccessInfo.AccessSize == 1
2215 || pMsg->AccessInfo.AccessSize == 2
2216 || pMsg->AccessInfo.AccessSize == 4);
2217 Assert( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_READ
2218 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE);
2219 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterCs, pMsg->Header.CsSegment);
2220 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRip, pMsg->Header.Rip);
2221 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRflags, pMsg->Header.Rflags);
2222 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterCr8, (uint64_t)pMsg->Header.Cr8);
2223 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRax, pMsg->Rax);
2224 if (pMsg->AccessInfo.StringOp)
2225 {
2226 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterDs, pMsg->DsSegment);
2227 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterEs, pMsg->EsSegment);
2228 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRcx, pMsg->Rcx);
2229 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRsi, pMsg->Rsi);
2230 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRdi, pMsg->Rdi);
2231 }
2232
2233 /*
2234 * Whatever we do, we must clear pending event injection upon resume.
2235 */
2236 if (pMsg->Header.ExecutionState.InterruptionPending)
2237 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
2238
2239 /*
2240 * Add history first to avoid two paths doing EMHistoryExec calls.
2241 */
2242 VBOXSTRICTRC rcStrict;
2243 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
2244 !pMsg->AccessInfo.StringOp
2245 ? ( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
2246 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_WRITE)
2247 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_READ))
2248 : ( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
2249 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_WRITE)
2250 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_READ)),
2251 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
2252 if (!pExitRec)
2253 {
2254 if (!pMsg->AccessInfo.StringOp)
2255 {
2256 /*
2257 * Simple port I/O.
2258 */
2259 static uint32_t const s_fAndMask[8] =
2260 { UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
2261 uint32_t const fAndMask = s_fAndMask[pMsg->AccessInfo.AccessSize];
2262
2263 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2264 if (pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE)
2265 {
2266 rcStrict = IOMIOPortWrite(pVM, pVCpu, pMsg->PortNumber, (uint32_t)pMsg->Rax & fAndMask, pMsg->AccessInfo.AccessSize);
2267 Log4(("IOExit/%u: %04x:%08RX64/%s: OUT %#x, %#x LB %u rcStrict=%Rrc\n",
2268 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2269 pMsg->PortNumber, (uint32_t)pMsg->Rax & fAndMask, pMsg->AccessInfo.AccessSize, VBOXSTRICTRC_VAL(rcStrict) ));
2270 if (IOM_SUCCESS(rcStrict))
2271 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 1);
2272# ifdef IN_RING0
2273 else if ( rcStrict == VINF_IOM_R3_IOPORT_WRITE
2274 && !pVCpu->cpum.GstCtx.rflags.Bits.u1TF
2275 /** @todo check for debug breakpoints */ )
2276 return EMRZSetPendingIoPortWrite(pVCpu, pMsg->PortNumber, pMsg->Header.InstructionLength,
2277 pMsg->AccessInfo.AccessSize, (uint32_t)pMsg->Rax & fAndMask);
2278# endif
2279 else
2280 {
2281 pVCpu->cpum.GstCtx.rax = pMsg->Rax;
2282 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
2283 }
2284 }
2285 else
2286 {
2287 uint32_t uValue = 0;
2288 rcStrict = IOMIOPortRead(pVM, pVCpu, pMsg->PortNumber, &uValue, pMsg->AccessInfo.AccessSize);
2289 Log4(("IOExit/%u: %04x:%08RX64/%s: IN %#x LB %u -> %#x, rcStrict=%Rrc\n",
2290 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2291 pMsg->PortNumber, pMsg->AccessInfo.AccessSize, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
2292 if (IOM_SUCCESS(rcStrict))
2293 {
2294 if (pMsg->AccessInfo.AccessSize != 4)
2295 pVCpu->cpum.GstCtx.rax = (pMsg->Rax & ~(uint64_t)fAndMask) | (uValue & fAndMask);
2296 else
2297 pVCpu->cpum.GstCtx.rax = uValue;
2298 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
2299 Log4(("IOExit/%u: RAX %#RX64 -> %#RX64\n", pVCpu->idCpu, pMsg->Rax, pVCpu->cpum.GstCtx.rax));
2300 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 1);
2301 }
2302 else
2303 {
2304 pVCpu->cpum.GstCtx.rax = pMsg->Rax;
2305 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
2306# ifdef IN_RING0
2307 if ( rcStrict == VINF_IOM_R3_IOPORT_READ
2308 && !pVCpu->cpum.GstCtx.rflags.Bits.u1TF
2309 /** @todo check for debug breakpoints */ )
2310 return EMRZSetPendingIoPortRead(pVCpu, pMsg->PortNumber, pMsg->Header.InstructionLength,
2311 pMsg->AccessInfo.AccessSize);
2312# endif
2313 }
2314 }
2315 }
2316 else
2317 {
2318 /*
2319 * String port I/O.
2320 */
2321 /** @todo Someone at Microsoft please explain how we can get the address mode
2322 * from the IoPortAccess.VpContext. CS.Attributes is only sufficient for
2323 * getting the default mode, it can always be overridden by a prefix. This
2324 * forces us to interpret the instruction from opcodes, which is suboptimal.
2325 * Both AMD-V and VT-x includes the address size in the exit info, at least on
2326 * CPUs that are reasonably new.
2327 *
2328 * Of course, it's possible this is an undocumented and we just need to do some
2329 * experiments to figure out how it's communicated. Alternatively, we can scan
2330 * the opcode bytes for possible evil prefixes.
2331 */
2332 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2333 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
2334 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
2335 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pMsg->DsSegment);
2336 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pMsg->EsSegment);
2337 pVCpu->cpum.GstCtx.rax = pMsg->Rax;
2338 pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
2339 pVCpu->cpum.GstCtx.rdi = pMsg->Rdi;
2340 pVCpu->cpum.GstCtx.rsi = pMsg->Rsi;
2341# ifdef IN_RING0
2342 rcStrict = nemR0WinImportStateStrict(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM, "IOExit");
2343 if (rcStrict != VINF_SUCCESS)
2344 return rcStrict;
2345# else
2346 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
2347 AssertRCReturn(rc, rc);
2348# endif
2349
2350 Log4(("IOExit/%u: %04x:%08RX64/%s: %s%s %#x LB %u (emulating)\n",
2351 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2352 pMsg->AccessInfo.RepPrefix ? "REP " : "",
2353 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE ? "OUTS" : "INS",
2354 pMsg->PortNumber, pMsg->AccessInfo.AccessSize ));
2355 rcStrict = IEMExecOne(pVCpu);
2356 }
2357 if (IOM_SUCCESS(rcStrict))
2358 {
2359 /*
2360 * Do debug checks.
2361 */
2362 if ( pMsg->Header.ExecutionState.DebugActive /** @todo Microsoft: Does DebugActive this only reflect DR7? */
2363 || (pMsg->Header.Rflags & X86_EFL_TF)
2364 || DBGFBpIsHwIoArmed(pVM) )
2365 {
2366 /** @todo Debugging. */
2367 }
2368 }
2369 return rcStrict;
2370 }
2371
2372 /*
2373 * Frequent exit or something needing probing.
2374 * Get state and call EMHistoryExec.
2375 */
2376 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2377 if (!pMsg->AccessInfo.StringOp)
2378 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
2379 else
2380 {
2381 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
2382 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
2383 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pMsg->DsSegment);
2384 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pMsg->EsSegment);
2385 pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
2386 pVCpu->cpum.GstCtx.rdi = pMsg->Rdi;
2387 pVCpu->cpum.GstCtx.rsi = pMsg->Rsi;
2388 }
2389 pVCpu->cpum.GstCtx.rax = pMsg->Rax;
2390
2391# ifdef IN_RING0
2392 rcStrict = nemR0WinImportStateStrict(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM, "IOExit");
2393 if (rcStrict != VINF_SUCCESS)
2394 return rcStrict;
2395# else
2396 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
2397 AssertRCReturn(rc, rc);
2398# endif
2399
2400 Log4(("IOExit/%u: %04x:%08RX64/%s: %s%s%s %#x LB %u -> EMHistoryExec\n",
2401 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2402 pMsg->AccessInfo.RepPrefix ? "REP " : "",
2403 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE ? "OUT" : "IN",
2404 pMsg->AccessInfo.StringOp ? "S" : "",
2405 pMsg->PortNumber, pMsg->AccessInfo.AccessSize));
2406 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
2407 Log4(("IOExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2408 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2409 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2410 return rcStrict;
2411}
2412#elif defined(IN_RING3)
2413/**
2414 * Deals with I/O port access exits (WHvRunVpExitReasonX64IoPortAccess).
2415 *
2416 * @returns Strict VBox status code.
2417 * @param pVM The cross context VM structure.
2418 * @param pVCpu The cross context per CPU structure.
2419 * @param pExit The VM exit information to handle.
2420 * @sa nemHCWinHandleMessageIoPort
2421 */
2422NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitIoPort(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2423{
2424 Assert( pExit->IoPortAccess.AccessInfo.AccessSize == 1
2425 || pExit->IoPortAccess.AccessInfo.AccessSize == 2
2426 || pExit->IoPortAccess.AccessInfo.AccessSize == 4);
2427
2428 /*
2429 * Whatever we do, we must clear pending event injection upon resume.
2430 */
2431 if (pExit->VpContext.ExecutionState.InterruptionPending)
2432 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
2433
2434 /*
2435 * Add history first to avoid two paths doing EMHistoryExec calls.
2436 */
2437 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
2438 !pExit->IoPortAccess.AccessInfo.StringOp
2439 ? ( pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
2440 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_WRITE)
2441 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_READ))
2442 : ( pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
2443 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_WRITE)
2444 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_READ)),
2445 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2446 if (!pExitRec)
2447 {
2448 VBOXSTRICTRC rcStrict;
2449 if (!pExit->IoPortAccess.AccessInfo.StringOp)
2450 {
2451 /*
2452 * Simple port I/O.
2453 */
2454 static uint32_t const s_fAndMask[8] =
2455 { UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
2456 uint32_t const fAndMask = s_fAndMask[pExit->IoPortAccess.AccessInfo.AccessSize];
2457 if (pExit->IoPortAccess.AccessInfo.IsWrite)
2458 {
2459 rcStrict = IOMIOPortWrite(pVM, pVCpu, pExit->IoPortAccess.PortNumber,
2460 (uint32_t)pExit->IoPortAccess.Rax & fAndMask,
2461 pExit->IoPortAccess.AccessInfo.AccessSize);
2462 Log4(("IOExit/%u: %04x:%08RX64/%s: OUT %#x, %#x LB %u rcStrict=%Rrc\n",
2463 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2464 pExit->IoPortAccess.PortNumber, (uint32_t)pExit->IoPortAccess.Rax & fAndMask,
2465 pExit->IoPortAccess.AccessInfo.AccessSize, VBOXSTRICTRC_VAL(rcStrict) ));
2466 if (IOM_SUCCESS(rcStrict))
2467 {
2468 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2469 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 1);
2470 }
2471 }
2472 else
2473 {
2474 uint32_t uValue = 0;
2475 rcStrict = IOMIOPortRead(pVM, pVCpu, pExit->IoPortAccess.PortNumber, &uValue,
2476 pExit->IoPortAccess.AccessInfo.AccessSize);
2477 Log4(("IOExit/%u: %04x:%08RX64/%s: IN %#x LB %u -> %#x, rcStrict=%Rrc\n",
2478 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2479 pExit->IoPortAccess.PortNumber, pExit->IoPortAccess.AccessInfo.AccessSize, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
2480 if (IOM_SUCCESS(rcStrict))
2481 {
2482 if (pExit->IoPortAccess.AccessInfo.AccessSize != 4)
2483 pVCpu->cpum.GstCtx.rax = (pExit->IoPortAccess.Rax & ~(uint64_t)fAndMask) | (uValue & fAndMask);
2484 else
2485 pVCpu->cpum.GstCtx.rax = uValue;
2486 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
2487 Log4(("IOExit/%u: RAX %#RX64 -> %#RX64\n", pVCpu->idCpu, pExit->IoPortAccess.Rax, pVCpu->cpum.GstCtx.rax));
2488 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2489 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 1);
2490 }
2491 }
2492 }
2493 else
2494 {
2495 /*
2496 * String port I/O.
2497 */
2498 /** @todo Someone at Microsoft please explain how we can get the address mode
2499 * from the IoPortAccess.VpContext. CS.Attributes is only sufficient for
2500 * getting the default mode, it can always be overridden by a prefix. This
2501 * forces us to interpret the instruction from opcodes, which is suboptimal.
2502 * Both AMD-V and VT-x includes the address size in the exit info, at least on
2503 * CPUs that are reasonably new.
2504 *
2505 * Of course, it's possible this is an undocumented and we just need to do some
2506 * experiments to figure out how it's communicated. Alternatively, we can scan
2507 * the opcode bytes for possible evil prefixes.
2508 */
2509 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2510 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
2511 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
2512 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pExit->IoPortAccess.Ds);
2513 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pExit->IoPortAccess.Es);
2514 pVCpu->cpum.GstCtx.rax = pExit->IoPortAccess.Rax;
2515 pVCpu->cpum.GstCtx.rcx = pExit->IoPortAccess.Rcx;
2516 pVCpu->cpum.GstCtx.rdi = pExit->IoPortAccess.Rdi;
2517 pVCpu->cpum.GstCtx.rsi = pExit->IoPortAccess.Rsi;
2518 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
2519 AssertRCReturn(rc, rc);
2520
2521 Log4(("IOExit/%u: %04x:%08RX64/%s: %s%s %#x LB %u (emulating)\n",
2522 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2523 pExit->IoPortAccess.AccessInfo.RepPrefix ? "REP " : "",
2524 pExit->IoPortAccess.AccessInfo.IsWrite ? "OUTS" : "INS",
2525 pExit->IoPortAccess.PortNumber, pExit->IoPortAccess.AccessInfo.AccessSize ));
2526 rcStrict = IEMExecOne(pVCpu);
2527 }
2528 if (IOM_SUCCESS(rcStrict))
2529 {
2530 /*
2531 * Do debug checks.
2532 */
2533 if ( pExit->VpContext.ExecutionState.DebugActive /** @todo Microsoft: Does DebugActive this only reflect DR7? */
2534 || (pExit->VpContext.Rflags & X86_EFL_TF)
2535 || DBGFBpIsHwIoArmed(pVM) )
2536 {
2537 /** @todo Debugging. */
2538 }
2539 }
2540 return rcStrict;
2541 }
2542
2543 /*
2544 * Frequent exit or something needing probing.
2545 * Get state and call EMHistoryExec.
2546 */
2547 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2548 if (!pExit->IoPortAccess.AccessInfo.StringOp)
2549 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
2550 else
2551 {
2552 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
2553 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
2554 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pExit->IoPortAccess.Ds);
2555 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pExit->IoPortAccess.Es);
2556 pVCpu->cpum.GstCtx.rcx = pExit->IoPortAccess.Rcx;
2557 pVCpu->cpum.GstCtx.rdi = pExit->IoPortAccess.Rdi;
2558 pVCpu->cpum.GstCtx.rsi = pExit->IoPortAccess.Rsi;
2559 }
2560 pVCpu->cpum.GstCtx.rax = pExit->IoPortAccess.Rax;
2561 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
2562 AssertRCReturn(rc, rc);
2563 Log4(("IOExit/%u: %04x:%08RX64/%s: %s%s%s %#x LB %u -> EMHistoryExec\n",
2564 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2565 pExit->IoPortAccess.AccessInfo.RepPrefix ? "REP " : "",
2566 pExit->IoPortAccess.AccessInfo.IsWrite ? "OUT" : "IN",
2567 pExit->IoPortAccess.AccessInfo.StringOp ? "S" : "",
2568 pExit->IoPortAccess.PortNumber, pExit->IoPortAccess.AccessInfo.AccessSize));
2569 VBOXSTRICTRC rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
2570 Log4(("IOExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2571 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2572 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2573 return rcStrict;
2574}
2575#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
2576
2577
2578#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
2579/**
2580 * Deals with interrupt window message.
2581 *
2582 * @returns Strict VBox status code.
2583 * @param pVM The cross context VM structure.
2584 * @param pVCpu The cross context per CPU structure.
2585 * @param pMsg The message.
2586 * @sa nemR3WinHandleExitInterruptWindow
2587 */
2588NEM_TMPL_STATIC VBOXSTRICTRC
2589nemHCWinHandleMessageInterruptWindow(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_INTERRUPT_WINDOW_MESSAGE const *pMsg)
2590{
2591 /*
2592 * Assert message sanity.
2593 */
2594 Assert( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_EXECUTE
2595 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_READ // READ & WRITE are probably not used here
2596 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE);
2597 AssertMsg(pMsg->Type == HvX64PendingInterrupt || pMsg->Type == HvX64PendingNmi, ("%#x\n", pMsg->Type));
2598
2599 /*
2600 * Just copy the state we've got and handle it in the loop for now.
2601 */
2602 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_INTTERRUPT_WINDOW),
2603 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
2604
2605 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2606 Log4(("IntWinExit/%u: %04x:%08RX64/%s: %u IF=%d InterruptShadow=%d\n",
2607 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2608 pMsg->Type, RT_BOOL(pMsg->Header.Rflags & X86_EFL_IF), pMsg->Header.ExecutionState.InterruptShadow));
2609
2610 /** @todo call nemHCWinHandleInterruptFF */
2611 RT_NOREF(pVM);
2612 return VINF_SUCCESS;
2613}
2614#elif defined(IN_RING3)
2615/**
2616 * Deals with interrupt window exits (WHvRunVpExitReasonX64InterruptWindow).
2617 *
2618 * @returns Strict VBox status code.
2619 * @param pVM The cross context VM structure.
2620 * @param pVCpu The cross context per CPU structure.
2621 * @param pExit The VM exit information to handle.
2622 * @sa nemHCWinHandleMessageInterruptWindow
2623 */
2624NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitInterruptWindow(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2625{
2626 /*
2627 * Assert message sanity.
2628 */
2629 AssertMsg( pExit->InterruptWindow.DeliverableType == WHvX64PendingInterrupt
2630 || pExit->InterruptWindow.DeliverableType == WHvX64PendingNmi,
2631 ("%#x\n", pExit->InterruptWindow.DeliverableType));
2632
2633 /*
2634 * Just copy the state we've got and handle it in the loop for now.
2635 */
2636 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_INTTERRUPT_WINDOW),
2637 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2638
2639 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2640 Log4(("IntWinExit/%u: %04x:%08RX64/%s: %u IF=%d InterruptShadow=%d CR8=%#x\n",
2641 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2642 pExit->InterruptWindow.DeliverableType, RT_BOOL(pExit->VpContext.Rflags & X86_EFL_IF),
2643 pExit->VpContext.ExecutionState.InterruptShadow, pExit->VpContext.Cr8));
2644
2645 /** @todo call nemHCWinHandleInterruptFF */
2646 RT_NOREF(pVM);
2647 return VINF_SUCCESS;
2648}
2649#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
2650
2651
2652#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
2653/**
2654 * Deals with CPUID intercept message.
2655 *
2656 * @returns Strict VBox status code.
2657 * @param pVM The cross context VM structure.
2658 * @param pVCpu The cross context per CPU structure.
2659 * @param pMsg The message.
2660 * @sa nemR3WinHandleExitCpuId
2661 */
2662NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageCpuId(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_CPUID_INTERCEPT_MESSAGE const *pMsg)
2663{
2664 /* Check message register value sanity. */
2665 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterCs, pMsg->Header.CsSegment);
2666 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRip, pMsg->Header.Rip);
2667 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRflags, pMsg->Header.Rflags);
2668 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterCr8, (uint64_t)pMsg->Header.Cr8);
2669 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRax, pMsg->Rax);
2670 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRcx, pMsg->Rcx);
2671 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRdx, pMsg->Rdx);
2672 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRbx, pMsg->Rbx);
2673
2674 /* Do exit history. */
2675 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_CPUID),
2676 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
2677 if (!pExitRec)
2678 {
2679 /*
2680 * Soak up state and execute the instruction.
2681 *
2682 * Note! If this grows slightly more complicated, combine into an IEMExecDecodedCpuId
2683 * function and make everyone use it.
2684 */
2685 /** @todo Combine implementations into IEMExecDecodedCpuId as this will
2686 * only get weirder with nested VT-x and AMD-V support. */
2687 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2688
2689 /* Copy in the low register values (top is always cleared). */
2690 pVCpu->cpum.GstCtx.rax = (uint32_t)pMsg->Rax;
2691 pVCpu->cpum.GstCtx.rcx = (uint32_t)pMsg->Rcx;
2692 pVCpu->cpum.GstCtx.rdx = (uint32_t)pMsg->Rdx;
2693 pVCpu->cpum.GstCtx.rbx = (uint32_t)pMsg->Rbx;
2694 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
2695
2696 /* Get the correct values. */
2697 CPUMGetGuestCpuId(pVCpu, pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx,
2698 &pVCpu->cpum.GstCtx.eax, &pVCpu->cpum.GstCtx.ebx, &pVCpu->cpum.GstCtx.ecx, &pVCpu->cpum.GstCtx.edx);
2699
2700 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: rax=%08RX64 / rcx=%08RX64 / rdx=%08RX64 / rbx=%08RX64 -> %08RX32 / %08RX32 / %08RX32 / %08RX32 (hv: %08RX64 / %08RX64 / %08RX64 / %08RX64)\n",
2701 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2702 pMsg->Rax, pMsg->Rcx, pMsg->Rdx, pMsg->Rbx,
2703 pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx, pVCpu->cpum.GstCtx.edx, pVCpu->cpum.GstCtx.ebx,
2704 pMsg->DefaultResultRax, pMsg->DefaultResultRcx, pMsg->DefaultResultRdx, pMsg->DefaultResultRbx));
2705
2706 /* Move RIP and we're done. */
2707 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 2);
2708
2709 return VINF_SUCCESS;
2710 }
2711
2712 /*
2713 * Frequent exit or something needing probing.
2714 * Get state and call EMHistoryExec.
2715 */
2716 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2717 pVCpu->cpum.GstCtx.rax = pMsg->Rax;
2718 pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
2719 pVCpu->cpum.GstCtx.rdx = pMsg->Rdx;
2720 pVCpu->cpum.GstCtx.rbx = pMsg->Rbx;
2721 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
2722 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: rax=%08RX64 / rcx=%08RX64 / rdx=%08RX64 / rbx=%08RX64 (hv: %08RX64 / %08RX64 / %08RX64 / %08RX64) ==> EMHistoryExec\n",
2723 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2724 pMsg->Rax, pMsg->Rcx, pMsg->Rdx, pMsg->Rbx,
2725 pMsg->DefaultResultRax, pMsg->DefaultResultRcx, pMsg->DefaultResultRdx, pMsg->DefaultResultRbx));
2726# ifdef IN_RING0
2727 VBOXSTRICTRC rcStrict = nemR0WinImportStateStrict(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM, "CpuIdExit");
2728 if (rcStrict != VINF_SUCCESS)
2729 return rcStrict;
2730 RT_NOREF(pVM);
2731# else
2732 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
2733 AssertRCReturn(rc, rc);
2734# endif
2735 VBOXSTRICTRC rcStrictExec = EMHistoryExec(pVCpu, pExitRec, 0);
2736 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2737 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2738 VBOXSTRICTRC_VAL(rcStrictExec), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2739 return rcStrictExec;
2740}
2741#elif defined(IN_RING3)
2742/**
2743 * Deals with CPUID exits (WHvRunVpExitReasonX64Cpuid).
2744 *
2745 * @returns Strict VBox status code.
2746 * @param pVM The cross context VM structure.
2747 * @param pVCpu The cross context per CPU structure.
2748 * @param pExit The VM exit information to handle.
2749 * @sa nemHCWinHandleMessageCpuId
2750 */
2751NEM_TMPL_STATIC VBOXSTRICTRC
2752nemR3WinHandleExitCpuId(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2753{
2754 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_CPUID),
2755 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2756 if (!pExitRec)
2757 {
2758 /*
2759 * Soak up state and execute the instruction.
2760 *
2761 * Note! If this grows slightly more complicated, combine into an IEMExecDecodedCpuId
2762 * function and make everyone use it.
2763 */
2764 /** @todo Combine implementations into IEMExecDecodedCpuId as this will
2765 * only get weirder with nested VT-x and AMD-V support. */
2766 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2767
2768 /* Copy in the low register values (top is always cleared). */
2769 pVCpu->cpum.GstCtx.rax = (uint32_t)pExit->CpuidAccess.Rax;
2770 pVCpu->cpum.GstCtx.rcx = (uint32_t)pExit->CpuidAccess.Rcx;
2771 pVCpu->cpum.GstCtx.rdx = (uint32_t)pExit->CpuidAccess.Rdx;
2772 pVCpu->cpum.GstCtx.rbx = (uint32_t)pExit->CpuidAccess.Rbx;
2773 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
2774
2775 /* Get the correct values. */
2776 CPUMGetGuestCpuId(pVCpu, pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx,
2777 &pVCpu->cpum.GstCtx.eax, &pVCpu->cpum.GstCtx.ebx, &pVCpu->cpum.GstCtx.ecx, &pVCpu->cpum.GstCtx.edx);
2778
2779 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: rax=%08RX64 / rcx=%08RX64 / rdx=%08RX64 / rbx=%08RX64 -> %08RX32 / %08RX32 / %08RX32 / %08RX32 (hv: %08RX64 / %08RX64 / %08RX64 / %08RX64)\n",
2780 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2781 pExit->CpuidAccess.Rax, pExit->CpuidAccess.Rcx, pExit->CpuidAccess.Rdx, pExit->CpuidAccess.Rbx,
2782 pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx, pVCpu->cpum.GstCtx.edx, pVCpu->cpum.GstCtx.ebx,
2783 pExit->CpuidAccess.DefaultResultRax, pExit->CpuidAccess.DefaultResultRcx, pExit->CpuidAccess.DefaultResultRdx, pExit->CpuidAccess.DefaultResultRbx));
2784
2785 /* Move RIP and we're done. */
2786 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 2);
2787
2788 RT_NOREF_PV(pVM);
2789 return VINF_SUCCESS;
2790 }
2791
2792 /*
2793 * Frequent exit or something needing probing.
2794 * Get state and call EMHistoryExec.
2795 */
2796 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2797 pVCpu->cpum.GstCtx.rax = pExit->CpuidAccess.Rax;
2798 pVCpu->cpum.GstCtx.rcx = pExit->CpuidAccess.Rcx;
2799 pVCpu->cpum.GstCtx.rdx = pExit->CpuidAccess.Rdx;
2800 pVCpu->cpum.GstCtx.rbx = pExit->CpuidAccess.Rbx;
2801 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
2802 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: rax=%08RX64 / rcx=%08RX64 / rdx=%08RX64 / rbx=%08RX64 (hv: %08RX64 / %08RX64 / %08RX64 / %08RX64) ==> EMHistoryExec\n",
2803 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2804 pExit->CpuidAccess.Rax, pExit->CpuidAccess.Rcx, pExit->CpuidAccess.Rdx, pExit->CpuidAccess.Rbx,
2805 pExit->CpuidAccess.DefaultResultRax, pExit->CpuidAccess.DefaultResultRcx, pExit->CpuidAccess.DefaultResultRdx, pExit->CpuidAccess.DefaultResultRbx));
2806 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
2807 AssertRCReturn(rc, rc);
2808 VBOXSTRICTRC rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
2809 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2810 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2811 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2812 return rcStrict;
2813}
2814#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
2815
2816
2817#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
2818/**
2819 * Deals with MSR intercept message.
2820 *
2821 * @returns Strict VBox status code.
2822 * @param pVCpu The cross context per CPU structure.
2823 * @param pMsg The message.
2824 * @sa nemR3WinHandleExitMsr
2825 */
2826NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageMsr(PVMCPUCC pVCpu, HV_X64_MSR_INTERCEPT_MESSAGE const *pMsg)
2827{
2828 /*
2829 * A wee bit of sanity first.
2830 */
2831 Assert( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_READ
2832 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE);
2833 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterCs, pMsg->Header.CsSegment);
2834 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRip, pMsg->Header.Rip);
2835 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRflags, pMsg->Header.Rflags);
2836 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterCr8, (uint64_t)pMsg->Header.Cr8);
2837 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRax, pMsg->Rax);
2838 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRdx, pMsg->Rdx);
2839
2840 /*
2841 * Check CPL as that's common to both RDMSR and WRMSR.
2842 */
2843 VBOXSTRICTRC rcStrict;
2844 if (pMsg->Header.ExecutionState.Cpl == 0)
2845 {
2846 /*
2847 * Get all the MSR state. Since we're getting EFER, we also need to
2848 * get CR0, CR4 and CR3.
2849 */
2850 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
2851 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
2852 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_WRITE)
2853 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_READ),
2854 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
2855
2856 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
2857 rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu,
2858 (!pExitRec ? 0 : IEM_CPUMCTX_EXTRN_MUST_MASK)
2859 | CPUMCTX_EXTRN_ALL_MSRS | CPUMCTX_EXTRN_CR0
2860 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4,
2861 "MSRs");
2862 if (rcStrict == VINF_SUCCESS)
2863 {
2864 if (!pExitRec)
2865 {
2866 /*
2867 * Handle writes.
2868 */
2869 if (pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE)
2870 {
2871 rcStrict = CPUMSetGuestMsr(pVCpu, pMsg->MsrNumber, RT_MAKE_U64((uint32_t)pMsg->Rax, (uint32_t)pMsg->Rdx));
2872 Log4(("MsrExit/%u: %04x:%08RX64/%s: WRMSR %08x, %08x:%08x -> %Rrc\n",
2873 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2874 pMsg->MsrNumber, (uint32_t)pMsg->Rax, (uint32_t)pMsg->Rdx, VBOXSTRICTRC_VAL(rcStrict) ));
2875 if (rcStrict == VINF_SUCCESS)
2876 {
2877 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 2);
2878 return VINF_SUCCESS;
2879 }
2880# ifndef IN_RING3
2881 /* move to ring-3 and handle the trap/whatever there, as we want to LogRel this. */
2882 if (rcStrict == VERR_CPUM_RAISE_GP_0)
2883 rcStrict = VINF_CPUM_R3_MSR_WRITE;
2884 return rcStrict;
2885# else
2886 LogRel(("MsrExit/%u: %04x:%08RX64/%s: WRMSR %08x, %08x:%08x -> %Rrc!\n",
2887 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2888 pMsg->MsrNumber, (uint32_t)pMsg->Rax, (uint32_t)pMsg->Rdx, VBOXSTRICTRC_VAL(rcStrict) ));
2889# endif
2890 }
2891 /*
2892 * Handle reads.
2893 */
2894 else
2895 {
2896 uint64_t uValue = 0;
2897 rcStrict = CPUMQueryGuestMsr(pVCpu, pMsg->MsrNumber, &uValue);
2898 Log4(("MsrExit/%u: %04x:%08RX64/%s: RDMSR %08x -> %08RX64 / %Rrc\n",
2899 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2900 pMsg->MsrNumber, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
2901 if (rcStrict == VINF_SUCCESS)
2902 {
2903 pVCpu->cpum.GstCtx.rax = (uint32_t)uValue;
2904 pVCpu->cpum.GstCtx.rdx = uValue >> 32;
2905 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX);
2906 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 2);
2907 return VINF_SUCCESS;
2908 }
2909# ifndef IN_RING3
2910 /* move to ring-3 and handle the trap/whatever there, as we want to LogRel this. */
2911 if (rcStrict == VERR_CPUM_RAISE_GP_0)
2912 rcStrict = VINF_CPUM_R3_MSR_READ;
2913 return rcStrict;
2914# else
2915 LogRel(("MsrExit/%u: %04x:%08RX64/%s: RDMSR %08x -> %08RX64 / %Rrc\n",
2916 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2917 pMsg->MsrNumber, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
2918# endif
2919 }
2920 }
2921 else
2922 {
2923 /*
2924 * Handle frequent exit or something needing probing.
2925 */
2926 Log4(("MsrExit/%u: %04x:%08RX64/%s: %sMSR %#08x\n",
2927 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2928 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE ? "WR" : "RD", pMsg->MsrNumber));
2929 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
2930 Log4(("MsrExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
2931 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2932 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
2933 return rcStrict;
2934 }
2935 }
2936 else
2937 {
2938 LogRel(("MsrExit/%u: %04x:%08RX64/%s: %sMSR %08x -> %Rrc - msr state import\n",
2939 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2940 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE ? "WR" : "RD",
2941 pMsg->MsrNumber, VBOXSTRICTRC_VAL(rcStrict) ));
2942 return rcStrict;
2943 }
2944 }
2945 else if (pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE)
2946 Log4(("MsrExit/%u: %04x:%08RX64/%s: CPL %u -> #GP(0); WRMSR %08x, %08x:%08x\n",
2947 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2948 pMsg->Header.ExecutionState.Cpl, pMsg->MsrNumber, (uint32_t)pMsg->Rax, (uint32_t)pMsg->Rdx ));
2949 else
2950 Log4(("MsrExit/%u: %04x:%08RX64/%s: CPL %u -> #GP(0); RDMSR %08x\n",
2951 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
2952 pMsg->Header.ExecutionState.Cpl, pMsg->MsrNumber));
2953
2954 /*
2955 * If we get down here, we're supposed to #GP(0).
2956 */
2957 rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL_MSRS, "MSR");
2958 if (rcStrict == VINF_SUCCESS)
2959 {
2960 rcStrict = IEMInjectTrap(pVCpu, X86_XCPT_GP, TRPM_TRAP, 0, 0, 0);
2961 if (rcStrict == VINF_IEM_RAISED_XCPT)
2962 rcStrict = VINF_SUCCESS;
2963 else if (rcStrict != VINF_SUCCESS)
2964 Log4(("MsrExit/%u: Injecting #GP(0) failed: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict) ));
2965 }
2966 return rcStrict;
2967}
2968#elif defined(IN_RING3)
2969/**
2970 * Deals with MSR access exits (WHvRunVpExitReasonX64MsrAccess).
2971 *
2972 * @returns Strict VBox status code.
2973 * @param pVM The cross context VM structure.
2974 * @param pVCpu The cross context per CPU structure.
2975 * @param pExit The VM exit information to handle.
2976 * @sa nemHCWinHandleMessageMsr
2977 */
2978NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitMsr(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2979{
2980 /*
2981 * Check CPL as that's common to both RDMSR and WRMSR.
2982 */
2983 VBOXSTRICTRC rcStrict;
2984 if (pExit->VpContext.ExecutionState.Cpl == 0)
2985 {
2986 /*
2987 * Get all the MSR state. Since we're getting EFER, we also need to
2988 * get CR0, CR4 and CR3.
2989 */
2990 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
2991 pExit->MsrAccess.AccessInfo.IsWrite
2992 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_WRITE)
2993 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_READ),
2994 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2995 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2996 rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu,
2997 (!pExitRec ? 0 : IEM_CPUMCTX_EXTRN_MUST_MASK)
2998 | CPUMCTX_EXTRN_ALL_MSRS | CPUMCTX_EXTRN_CR0
2999 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4,
3000 "MSRs");
3001 if (rcStrict == VINF_SUCCESS)
3002 {
3003 if (!pExitRec)
3004 {
3005 /*
3006 * Handle writes.
3007 */
3008 if (pExit->MsrAccess.AccessInfo.IsWrite)
3009 {
3010 rcStrict = CPUMSetGuestMsr(pVCpu, pExit->MsrAccess.MsrNumber,
3011 RT_MAKE_U64((uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx));
3012 Log4(("MsrExit/%u: %04x:%08RX64/%s: WRMSR %08x, %08x:%08x -> %Rrc\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3013 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->MsrAccess.MsrNumber,
3014 (uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx, VBOXSTRICTRC_VAL(rcStrict) ));
3015 if (rcStrict == VINF_SUCCESS)
3016 {
3017 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 2);
3018 return VINF_SUCCESS;
3019 }
3020 LogRel(("MsrExit/%u: %04x:%08RX64/%s: WRMSR %08x, %08x:%08x -> %Rrc!\n", pVCpu->idCpu,
3021 pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
3022 pExit->MsrAccess.MsrNumber, (uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx,
3023 VBOXSTRICTRC_VAL(rcStrict) ));
3024 }
3025 /*
3026 * Handle reads.
3027 */
3028 else
3029 {
3030 uint64_t uValue = 0;
3031 rcStrict = CPUMQueryGuestMsr(pVCpu, pExit->MsrAccess.MsrNumber, &uValue);
3032 Log4(("MsrExit/%u: %04x:%08RX64/%s: RDMSR %08x -> %08RX64 / %Rrc\n", pVCpu->idCpu,
3033 pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
3034 pExit->MsrAccess.MsrNumber, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
3035 if (rcStrict == VINF_SUCCESS)
3036 {
3037 pVCpu->cpum.GstCtx.rax = (uint32_t)uValue;
3038 pVCpu->cpum.GstCtx.rdx = uValue >> 32;
3039 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX);
3040 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 2);
3041 return VINF_SUCCESS;
3042 }
3043 LogRel(("MsrExit/%u: %04x:%08RX64/%s: RDMSR %08x -> %08RX64 / %Rrc\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3044 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->MsrAccess.MsrNumber,
3045 uValue, VBOXSTRICTRC_VAL(rcStrict) ));
3046 }
3047 }
3048 else
3049 {
3050 /*
3051 * Handle frequent exit or something needing probing.
3052 */
3053 Log4(("MsrExit/%u: %04x:%08RX64/%s: %sMSR %#08x\n",
3054 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
3055 pExit->MsrAccess.AccessInfo.IsWrite ? "WR" : "RD", pExit->MsrAccess.MsrNumber));
3056 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
3057 Log4(("MsrExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
3058 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
3059 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
3060 return rcStrict;
3061 }
3062 }
3063 else
3064 {
3065 LogRel(("MsrExit/%u: %04x:%08RX64/%s: %sMSR %08x -> %Rrc - msr state import\n",
3066 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
3067 pExit->MsrAccess.AccessInfo.IsWrite ? "WR" : "RD", pExit->MsrAccess.MsrNumber, VBOXSTRICTRC_VAL(rcStrict) ));
3068 return rcStrict;
3069 }
3070 }
3071 else if (pExit->MsrAccess.AccessInfo.IsWrite)
3072 Log4(("MsrExit/%u: %04x:%08RX64/%s: CPL %u -> #GP(0); WRMSR %08x, %08x:%08x\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3073 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.ExecutionState.Cpl,
3074 pExit->MsrAccess.MsrNumber, (uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx ));
3075 else
3076 Log4(("MsrExit/%u: %04x:%08RX64/%s: CPL %u -> #GP(0); RDMSR %08x\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3077 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.ExecutionState.Cpl,
3078 pExit->MsrAccess.MsrNumber));
3079
3080 /*
3081 * If we get down here, we're supposed to #GP(0).
3082 */
3083 rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL_MSRS, "MSR");
3084 if (rcStrict == VINF_SUCCESS)
3085 {
3086 rcStrict = IEMInjectTrap(pVCpu, X86_XCPT_GP, TRPM_TRAP, 0, 0, 0);
3087 if (rcStrict == VINF_IEM_RAISED_XCPT)
3088 rcStrict = VINF_SUCCESS;
3089 else if (rcStrict != VINF_SUCCESS)
3090 Log4(("MsrExit/%u: Injecting #GP(0) failed: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict) ));
3091 }
3092
3093 RT_NOREF_PV(pVM);
3094 return rcStrict;
3095}
3096#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
3097
3098
3099/**
3100 * Worker for nemHCWinHandleMessageException & nemR3WinHandleExitException that
3101 * checks if the given opcodes are of interest at all.
3102 *
3103 * @returns true if interesting, false if not.
3104 * @param cbOpcodes Number of opcode bytes available.
3105 * @param pbOpcodes The opcode bytes.
3106 * @param f64BitMode Whether we're in 64-bit mode.
3107 */
3108DECLINLINE(bool) nemHcWinIsInterestingUndefinedOpcode(uint8_t cbOpcodes, uint8_t const *pbOpcodes, bool f64BitMode)
3109{
3110 /*
3111 * Currently only interested in VMCALL and VMMCALL.
3112 */
3113 while (cbOpcodes >= 3)
3114 {
3115 switch (pbOpcodes[0])
3116 {
3117 case 0x0f:
3118 switch (pbOpcodes[1])
3119 {
3120 case 0x01:
3121 switch (pbOpcodes[2])
3122 {
3123 case 0xc1: /* 0f 01 c1 VMCALL */
3124 return true;
3125 case 0xd9: /* 0f 01 d9 VMMCALL */
3126 return true;
3127 default:
3128 break;
3129 }
3130 break;
3131 }
3132 break;
3133
3134 default:
3135 return false;
3136
3137 /* prefixes */
3138 case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
3139 case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
3140 if (!f64BitMode)
3141 return false;
3142 RT_FALL_THRU();
3143 case X86_OP_PRF_CS:
3144 case X86_OP_PRF_SS:
3145 case X86_OP_PRF_DS:
3146 case X86_OP_PRF_ES:
3147 case X86_OP_PRF_FS:
3148 case X86_OP_PRF_GS:
3149 case X86_OP_PRF_SIZE_OP:
3150 case X86_OP_PRF_SIZE_ADDR:
3151 case X86_OP_PRF_LOCK:
3152 case X86_OP_PRF_REPZ:
3153 case X86_OP_PRF_REPNZ:
3154 cbOpcodes--;
3155 pbOpcodes++;
3156 continue;
3157 }
3158 break;
3159 }
3160 return false;
3161}
3162
3163
3164#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
3165/**
3166 * Copies state included in a exception intercept message.
3167 *
3168 * @param pVCpu The cross context per CPU structure.
3169 * @param pMsg The message.
3170 * @param fClearXcpt Clear pending exception.
3171 */
3172DECLINLINE(void)
3173nemHCWinCopyStateFromExceptionMessage(PVMCPUCC pVCpu, HV_X64_EXCEPTION_INTERCEPT_MESSAGE const *pMsg, bool fClearXcpt)
3174{
3175 nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
3176 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_GPRS_MASK | CPUMCTX_EXTRN_SS | CPUMCTX_EXTRN_DS
3177 | (fClearXcpt ? CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT : 0) );
3178 pVCpu->cpum.GstCtx.rax = pMsg->Rax;
3179 pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
3180 pVCpu->cpum.GstCtx.rdx = pMsg->Rdx;
3181 pVCpu->cpum.GstCtx.rbx = pMsg->Rbx;
3182 pVCpu->cpum.GstCtx.rsp = pMsg->Rsp;
3183 pVCpu->cpum.GstCtx.rbp = pMsg->Rbp;
3184 pVCpu->cpum.GstCtx.rsi = pMsg->Rsi;
3185 pVCpu->cpum.GstCtx.rdi = pMsg->Rdi;
3186 pVCpu->cpum.GstCtx.r8 = pMsg->R8;
3187 pVCpu->cpum.GstCtx.r9 = pMsg->R9;
3188 pVCpu->cpum.GstCtx.r10 = pMsg->R10;
3189 pVCpu->cpum.GstCtx.r11 = pMsg->R11;
3190 pVCpu->cpum.GstCtx.r12 = pMsg->R12;
3191 pVCpu->cpum.GstCtx.r13 = pMsg->R13;
3192 pVCpu->cpum.GstCtx.r14 = pMsg->R14;
3193 pVCpu->cpum.GstCtx.r15 = pMsg->R15;
3194 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pMsg->DsSegment);
3195 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ss, pMsg->SsSegment);
3196}
3197#elif defined(IN_RING3)
3198/**
3199 * Copies state included in a exception intercept exit.
3200 *
3201 * @param pVCpu The cross context per CPU structure.
3202 * @param pExit The VM exit information.
3203 * @param fClearXcpt Clear pending exception.
3204 */
3205DECLINLINE(void) nemR3WinCopyStateFromExceptionMessage(PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit, bool fClearXcpt)
3206{
3207 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
3208 if (fClearXcpt)
3209 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
3210}
3211#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
3212
3213
3214/**
3215 * Advances the guest RIP by the number of bytes specified in @a cb.
3216 *
3217 * @param pVCpu The cross context virtual CPU structure.
3218 * @param cb RIP increment value in bytes.
3219 */
3220DECLINLINE(void) nemHcWinAdvanceRip(PVMCPUCC pVCpu, uint32_t cb)
3221{
3222 PCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
3223 pCtx->rip += cb;
3224
3225 /* Update interrupt shadow. */
3226 if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)
3227 && pCtx->rip != EMGetInhibitInterruptsPC(pVCpu))
3228 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
3229}
3230
3231
3232/**
3233 * Hacks its way around the lovely mesa driver's backdoor accesses.
3234 *
3235 * @sa hmR0VmxHandleMesaDrvGp
3236 * @sa hmR0SvmHandleMesaDrvGp
3237 */
3238static int nemHcWinHandleMesaDrvGp(PVMCPUCC pVCpu, PCPUMCTX pCtx)
3239{
3240 Assert(!(pCtx->fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_GPRS_MASK)));
3241 RT_NOREF(pCtx);
3242
3243 /* For now we'll just skip the instruction. */
3244 nemHcWinAdvanceRip(pVCpu, 1);
3245 return VINF_SUCCESS;
3246}
3247
3248
3249/**
3250 * Checks if the \#GP'ing instruction is the mesa driver doing it's lovely
3251 * backdoor logging w/o checking what it is running inside.
3252 *
3253 * This recognizes an "IN EAX,DX" instruction executed in flat ring-3, with the
3254 * backdoor port and magic numbers loaded in registers.
3255 *
3256 * @returns true if it is, false if it isn't.
3257 * @sa hmR0VmxIsMesaDrvGp
3258 * @sa hmR0SvmIsMesaDrvGp
3259 */
3260DECLINLINE(bool) nemHcWinIsMesaDrvGp(PVMCPUCC pVCpu, PCPUMCTX pCtx, const uint8_t *pbInsn, uint32_t cbInsn)
3261{
3262 /* #GP(0) is already checked by caller. */
3263
3264 /* Check magic and port. */
3265 Assert(!(pCtx->fExtrn & (CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RAX)));
3266 if (pCtx->dx != UINT32_C(0x5658))
3267 return false;
3268 if (pCtx->rax != UINT32_C(0x564d5868))
3269 return false;
3270
3271 /* Flat ring-3 CS. */
3272 if (CPUMGetGuestCPL(pVCpu) != 3)
3273 return false;
3274 if (pCtx->cs.u64Base != 0)
3275 return false;
3276
3277 /* 0xed: IN eAX,dx */
3278 if (cbInsn < 1) /* Play safe (shouldn't happen). */
3279 {
3280 uint8_t abInstr[1];
3281 int rc = PGMPhysSimpleReadGCPtr(pVCpu, abInstr, pCtx->rip, sizeof(abInstr));
3282 if (RT_FAILURE(rc))
3283 return false;
3284 if (abInstr[0] != 0xed)
3285 return false;
3286 }
3287 else
3288 {
3289 if (pbInsn[0] != 0xed)
3290 return false;
3291 }
3292
3293 return true;
3294}
3295
3296
3297#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
3298/**
3299 * Deals with exception intercept message (HvMessageTypeX64ExceptionIntercept).
3300 *
3301 * @returns Strict VBox status code.
3302 * @param pVCpu The cross context per CPU structure.
3303 * @param pMsg The message.
3304 * @sa nemR3WinHandleExitMsr
3305 */
3306NEM_TMPL_STATIC VBOXSTRICTRC
3307nemHCWinHandleMessageException(PVMCPUCC pVCpu, HV_X64_EXCEPTION_INTERCEPT_MESSAGE const *pMsg)
3308{
3309 /*
3310 * Assert sanity.
3311 */
3312 Assert( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_READ
3313 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
3314 || pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_EXECUTE);
3315 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterCs, pMsg->Header.CsSegment);
3316 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRip, pMsg->Header.Rip);
3317 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRflags, pMsg->Header.Rflags);
3318 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterCr8, (uint64_t)pMsg->Header.Cr8);
3319 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterDs, pMsg->DsSegment);
3320 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterSs, pMsg->SsSegment);
3321 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRax, pMsg->Rax);
3322 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRcx, pMsg->Rcx);
3323 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRdx, pMsg->Rdx);
3324 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRbx, pMsg->Rbx);
3325 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRsp, pMsg->Rsp);
3326 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRbp, pMsg->Rbp);
3327 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRsi, pMsg->Rsi);
3328 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRdi, pMsg->Rdi);
3329 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR8, pMsg->R8);
3330 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR9, pMsg->R9);
3331 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR10, pMsg->R10);
3332 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR11, pMsg->R11);
3333 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR12, pMsg->R12);
3334 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR13, pMsg->R13);
3335 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR14, pMsg->R14);
3336 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterR15, pMsg->R15);
3337
3338 /*
3339 * Get most of the register state since we'll end up making IEM inject the
3340 * event. The exception isn't normally flaged as a pending event, so duh.
3341 *
3342 * Note! We can optimize this later with event injection.
3343 */
3344 Log4(("XcptExit/%u: %04x:%08RX64/%s: %x errcd=%#x parm=%RX64\n",
3345 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header),
3346 pMsg->ExceptionVector, pMsg->ErrorCode, pMsg->ExceptionParameter));
3347 nemHCWinCopyStateFromExceptionMessage(pVCpu, pMsg, true /*fClearXcpt*/);
3348 uint64_t fWhat = NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM;
3349 if (pMsg->ExceptionVector == X86_XCPT_DB)
3350 fWhat |= CPUMCTX_EXTRN_DR0_DR3 | CPUMCTX_EXTRN_DR7 | CPUMCTX_EXTRN_DR6;
3351 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, fWhat, "Xcpt");
3352 if (rcStrict != VINF_SUCCESS)
3353 return rcStrict;
3354
3355 /*
3356 * Handle the intercept.
3357 */
3358 TRPMEVENT enmEvtType = TRPM_TRAP;
3359 switch (pMsg->ExceptionVector)
3360 {
3361 /*
3362 * We get undefined opcodes on VMMCALL(AMD) & VMCALL(Intel) instructions
3363 * and need to turn them over to GIM.
3364 *
3365 * Note! We do not check fGIMTrapXcptUD here ASSUMING that GIM only wants
3366 * #UD for handling non-native hypercall instructions. (IEM will
3367 * decode both and let the GIM provider decide whether to accept it.)
3368 */
3369 case X86_XCPT_UD:
3370 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUd);
3371 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_UD),
3372 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
3373
3374 if (nemHcWinIsInterestingUndefinedOpcode(pMsg->InstructionByteCount, pMsg->InstructionBytes,
3375 pMsg->Header.ExecutionState.EferLma && pMsg->Header.CsSegment.Long ))
3376 {
3377 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pMsg->Header.Rip,
3378 pMsg->InstructionBytes, pMsg->InstructionByteCount);
3379 Log4(("XcptExit/%u: %04x:%08RX64/%s: #UD -> emulated -> %Rrc\n",
3380 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip,
3381 nemHCWinExecStateToLogStr(&pMsg->Header), VBOXSTRICTRC_VAL(rcStrict) ));
3382 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUdHandled);
3383 return rcStrict;
3384 }
3385 Log4(("XcptExit/%u: %04x:%08RX64/%s: #UD [%.*Rhxs] -> re-injected\n", pVCpu->idCpu, pMsg->Header.CsSegment.Selector,
3386 pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header), pMsg->InstructionByteCount, pMsg->InstructionBytes ));
3387 break;
3388
3389 /*
3390 * Workaround the lovely mesa driver assuming that vmsvga means vmware
3391 * hypervisor and tries to log stuff to the host.
3392 */
3393 case X86_XCPT_GP:
3394 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionGp);
3395 /** @todo r=bird: Need workaround in IEM for this, right?
3396 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_GP),
3397 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC()); */
3398 if ( !pVCpu->hm.s.fTrapXcptGpForLovelyMesaDrv
3399 || !nemHcWinIsMesaDrvGp(pVCpu, &pVCpu->cpum.GstCtx, pMsg->InstructionBytes, pMsg->InstructionByteCount))
3400 {
3401# if 1 /** @todo Need to emulate instruction or we get a triple fault when trying to inject the #GP... */
3402 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pMsg->Header.Rip,
3403 pMsg->InstructionBytes, pMsg->InstructionByteCount);
3404 Log4(("XcptExit/%u: %04x:%08RX64/%s: #GP -> emulated -> %Rrc\n",
3405 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip,
3406 nemHCWinExecStateToLogStr(&pMsg->Header), VBOXSTRICTRC_VAL(rcStrict) ));
3407 return rcStrict;
3408# else
3409 break;
3410# endif
3411 }
3412 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionGpMesa);
3413 return nemHcWinHandleMesaDrvGp(pVCpu, &pVCpu->cpum.GstCtx);
3414
3415 /*
3416 * Filter debug exceptions.
3417 */
3418 case X86_XCPT_DB:
3419 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionDb);
3420 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_DB),
3421 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
3422 Log4(("XcptExit/%u: %04x:%08RX64/%s: #DB - TODO\n",
3423 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header) ));
3424 break;
3425
3426 case X86_XCPT_BP:
3427 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionBp);
3428 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_BP),
3429 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
3430 Log4(("XcptExit/%u: %04x:%08RX64/%s: #BP - TODO - %u\n", pVCpu->idCpu, pMsg->Header.CsSegment.Selector,
3431 pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header), pMsg->Header.InstructionLength));
3432 enmEvtType = TRPM_SOFTWARE_INT; /* We're at the INT3 instruction, not after it. */
3433 break;
3434
3435 /* This shouldn't happen. */
3436 default:
3437 AssertLogRelMsgFailedReturn(("ExceptionVector=%#x\n", pMsg->ExceptionVector), VERR_IEM_IPE_6);
3438 }
3439
3440 /*
3441 * Inject it.
3442 */
3443 rcStrict = IEMInjectTrap(pVCpu, pMsg->ExceptionVector, enmEvtType, pMsg->ErrorCode,
3444 pMsg->ExceptionParameter /*??*/, pMsg->Header.InstructionLength);
3445 Log4(("XcptExit/%u: %04x:%08RX64/%s: %#u -> injected -> %Rrc\n",
3446 pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip,
3447 nemHCWinExecStateToLogStr(&pMsg->Header), pMsg->ExceptionVector, VBOXSTRICTRC_VAL(rcStrict) ));
3448 return rcStrict;
3449}
3450#elif defined(IN_RING3)
3451/**
3452 * Deals with MSR access exits (WHvRunVpExitReasonException).
3453 *
3454 * @returns Strict VBox status code.
3455 * @param pVM The cross context VM structure.
3456 * @param pVCpu The cross context per CPU structure.
3457 * @param pExit The VM exit information to handle.
3458 * @sa nemR3WinHandleExitException
3459 */
3460NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitException(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
3461{
3462 /*
3463 * Get most of the register state since we'll end up making IEM inject the
3464 * event. The exception isn't normally flaged as a pending event, so duh.
3465 *
3466 * Note! We can optimize this later with event injection.
3467 */
3468 Log4(("XcptExit/%u: %04x:%08RX64/%s: %x errcd=%#x parm=%RX64\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3469 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpException.ExceptionType,
3470 pExit->VpException.ErrorCode, pExit->VpException.ExceptionParameter ));
3471 nemR3WinCopyStateFromExceptionMessage(pVCpu, pExit, true /*fClearXcpt*/);
3472 uint64_t fWhat = NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM;
3473 if (pExit->VpException.ExceptionType == X86_XCPT_DB)
3474 fWhat |= CPUMCTX_EXTRN_DR0_DR3 | CPUMCTX_EXTRN_DR7 | CPUMCTX_EXTRN_DR6;
3475 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, fWhat, "Xcpt");
3476 if (rcStrict != VINF_SUCCESS)
3477 return rcStrict;
3478
3479 /*
3480 * Handle the intercept.
3481 */
3482 TRPMEVENT enmEvtType = TRPM_TRAP;
3483 switch (pExit->VpException.ExceptionType)
3484 {
3485 /*
3486 * We get undefined opcodes on VMMCALL(AMD) & VMCALL(Intel) instructions
3487 * and need to turn them over to GIM.
3488 *
3489 * Note! We do not check fGIMTrapXcptUD here ASSUMING that GIM only wants
3490 * #UD for handling non-native hypercall instructions. (IEM will
3491 * decode both and let the GIM provider decide whether to accept it.)
3492 */
3493 case X86_XCPT_UD:
3494 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUd);
3495 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_UD),
3496 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
3497 if (nemHcWinIsInterestingUndefinedOpcode(pExit->VpException.InstructionByteCount, pExit->VpException.InstructionBytes,
3498 pExit->VpContext.ExecutionState.EferLma && pExit->VpContext.Cs.Long ))
3499 {
3500 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pExit->VpContext.Rip,
3501 pExit->VpException.InstructionBytes,
3502 pExit->VpException.InstructionByteCount);
3503 Log4(("XcptExit/%u: %04x:%08RX64/%s: #UD -> emulated -> %Rrc\n",
3504 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip,
3505 nemR3WinExecStateToLogStr(&pExit->VpContext), VBOXSTRICTRC_VAL(rcStrict) ));
3506 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUdHandled);
3507 return rcStrict;
3508 }
3509
3510 Log4(("XcptExit/%u: %04x:%08RX64/%s: #UD [%.*Rhxs] -> re-injected\n", pVCpu->idCpu,
3511 pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
3512 pExit->VpException.InstructionByteCount, pExit->VpException.InstructionBytes ));
3513 break;
3514
3515 /*
3516 * Workaround the lovely mesa driver assuming that vmsvga means vmware
3517 * hypervisor and tries to log stuff to the host.
3518 */
3519 case X86_XCPT_GP:
3520 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionGp);
3521 /** @todo r=bird: Need workaround in IEM for this, right?
3522 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_GP),
3523 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC()); */
3524 if ( !pVCpu->nem.s.fTrapXcptGpForLovelyMesaDrv
3525 || !nemHcWinIsMesaDrvGp(pVCpu, &pVCpu->cpum.GstCtx, pExit->VpException.InstructionBytes,
3526 pExit->VpException.InstructionByteCount))
3527 {
3528# if 1 /** @todo Need to emulate instruction or we get a triple fault when trying to inject the #GP... */
3529 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pExit->VpContext.Rip,
3530 pExit->VpException.InstructionBytes,
3531 pExit->VpException.InstructionByteCount);
3532 Log4(("XcptExit/%u: %04x:%08RX64/%s: #GP -> emulated -> %Rrc\n",
3533 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip,
3534 nemR3WinExecStateToLogStr(&pExit->VpContext), VBOXSTRICTRC_VAL(rcStrict) ));
3535 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUdHandled);
3536 return rcStrict;
3537# else
3538 break;
3539# endif
3540 }
3541 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionGpMesa);
3542 return nemHcWinHandleMesaDrvGp(pVCpu, &pVCpu->cpum.GstCtx);
3543
3544 /*
3545 * Filter debug exceptions.
3546 */
3547 case X86_XCPT_DB:
3548 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionDb);
3549 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_DB),
3550 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
3551 Log4(("XcptExit/%u: %04x:%08RX64/%s: #DB - TODO\n",
3552 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext) ));
3553 break;
3554
3555 case X86_XCPT_BP:
3556 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionBp);
3557 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_BP),
3558 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
3559 Log4(("XcptExit/%u: %04x:%08RX64/%s: #BP - TODO - %u\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3560 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.InstructionLength));
3561 enmEvtType = TRPM_SOFTWARE_INT; /* We're at the INT3 instruction, not after it. */
3562 break;
3563
3564 /* This shouldn't happen. */
3565 default:
3566 AssertLogRelMsgFailedReturn(("ExceptionType=%#x\n", pExit->VpException.ExceptionType), VERR_IEM_IPE_6);
3567 }
3568
3569 /*
3570 * Inject it.
3571 */
3572 rcStrict = IEMInjectTrap(pVCpu, pExit->VpException.ExceptionType, enmEvtType, pExit->VpException.ErrorCode,
3573 pExit->VpException.ExceptionParameter /*??*/, pExit->VpContext.InstructionLength);
3574 Log4(("XcptExit/%u: %04x:%08RX64/%s: %#u -> injected -> %Rrc\n",
3575 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip,
3576 nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpException.ExceptionType, VBOXSTRICTRC_VAL(rcStrict) ));
3577
3578 RT_NOREF_PV(pVM);
3579 return rcStrict;
3580}
3581#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
3582
3583
3584#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
3585/**
3586 * Deals with unrecoverable exception (triple fault).
3587 *
3588 * Seen WRMSR 0x201 (IA32_MTRR_PHYSMASK0) writes from grub / debian9 ending up
3589 * here too. So we'll leave it to IEM to decide.
3590 *
3591 * @returns Strict VBox status code.
3592 * @param pVCpu The cross context per CPU structure.
3593 * @param pMsgHdr The message header.
3594 * @sa nemR3WinHandleExitUnrecoverableException
3595 */
3596NEM_TMPL_STATIC VBOXSTRICTRC
3597nemHCWinHandleMessageUnrecoverableException(PVMCPUCC pVCpu, HV_X64_INTERCEPT_MESSAGE_HEADER const *pMsgHdr)
3598{
3599 /* Check message register value sanity. */
3600 NEMWIN_ASSERT_MSG_REG_SEG( pVCpu, HvX64RegisterCs, pMsgHdr->CsSegment);
3601 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRip, pMsgHdr->Rip);
3602 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterRflags, pMsgHdr->Rflags);
3603 NEMWIN_ASSERT_MSG_REG_VAL64(pVCpu, HvX64RegisterCr8, (uint64_t)pMsgHdr->Cr8);
3604
3605# if 0
3606 /*
3607 * Just copy the state we've got and handle it in the loop for now.
3608 */
3609 nemHCWinCopyStateFromX64Header(pVCpu, pMsgHdr);
3610 Log(("TripleExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_EM_TRIPLE_FAULT\n",
3611 pVCpu->idCpu, pMsgHdr->CsSegment.Selector, pMsgHdr->Rip, nemHCWinExecStateToLogStr(&pMsg->Header), pMsgHdr->Rflags));
3612 return VINF_EM_TRIPLE_FAULT;
3613# else
3614 /*
3615 * Let IEM decide whether this is really it.
3616 */
3617 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_UNRECOVERABLE_EXCEPTION),
3618 pMsgHdr->Rip + pMsgHdr->CsSegment.Base, ASMReadTSC());
3619 nemHCWinCopyStateFromX64Header(pVCpu, pMsgHdr);
3620 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL, "TripleExit");
3621 if (rcStrict == VINF_SUCCESS)
3622 {
3623 rcStrict = IEMExecOne(pVCpu);
3624 if (rcStrict == VINF_SUCCESS)
3625 {
3626 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_SUCCESS\n", pVCpu->idCpu, pMsgHdr->CsSegment.Selector,
3627 pMsgHdr->Rip, nemHCWinExecStateToLogStr(pMsgHdr), pMsgHdr->Rflags ));
3628 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT; /* Make sure to reset pending #DB(0). */
3629 return VINF_SUCCESS;
3630 }
3631 if (rcStrict == VINF_EM_TRIPLE_FAULT)
3632 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_EM_TRIPLE_FAULT!\n", pVCpu->idCpu, pMsgHdr->CsSegment.Selector,
3633 pMsgHdr->Rip, nemHCWinExecStateToLogStr(pMsgHdr), pMsgHdr->Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
3634 else
3635 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> %Rrc (IEMExecOne)\n", pVCpu->idCpu, pMsgHdr->CsSegment.Selector,
3636 pMsgHdr->Rip, nemHCWinExecStateToLogStr(pMsgHdr), pMsgHdr->Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
3637 }
3638 else
3639 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> %Rrc (state import)\n", pVCpu->idCpu, pMsgHdr->CsSegment.Selector,
3640 pMsgHdr->Rip, nemHCWinExecStateToLogStr(pMsgHdr), pMsgHdr->Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
3641 return rcStrict;
3642# endif
3643}
3644#elif defined(IN_RING3)
3645/**
3646 * Deals with MSR access exits (WHvRunVpExitReasonUnrecoverableException).
3647 *
3648 * @returns Strict VBox status code.
3649 * @param pVM The cross context VM structure.
3650 * @param pVCpu The cross context per CPU structure.
3651 * @param pExit The VM exit information to handle.
3652 * @sa nemHCWinHandleMessageUnrecoverableException
3653 */
3654NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitUnrecoverableException(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
3655{
3656# if 0
3657 /*
3658 * Just copy the state we've got and handle it in the loop for now.
3659 */
3660 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
3661 Log(("TripleExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_EM_TRIPLE_FAULT\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3662 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags));
3663 RT_NOREF_PV(pVM);
3664 return VINF_EM_TRIPLE_FAULT;
3665# else
3666 /*
3667 * Let IEM decide whether this is really it.
3668 */
3669 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_UNRECOVERABLE_EXCEPTION),
3670 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
3671 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
3672 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL, "TripleExit");
3673 if (rcStrict == VINF_SUCCESS)
3674 {
3675 rcStrict = IEMExecOne(pVCpu);
3676 if (rcStrict == VINF_SUCCESS)
3677 {
3678 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_SUCCESS\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3679 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags));
3680 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT; /* Make sure to reset pending #DB(0). */
3681 return VINF_SUCCESS;
3682 }
3683 if (rcStrict == VINF_EM_TRIPLE_FAULT)
3684 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_EM_TRIPLE_FAULT!\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3685 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
3686 else
3687 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> %Rrc (IEMExecOne)\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3688 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
3689 }
3690 else
3691 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> %Rrc (state import)\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
3692 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
3693 RT_NOREF_PV(pVM);
3694 return rcStrict;
3695# endif
3696
3697}
3698#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
3699
3700
3701#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
3702/**
3703 * Handles messages (VM exits).
3704 *
3705 * @returns Strict VBox status code.
3706 * @param pVM The cross context VM structure.
3707 * @param pVCpu The cross context per CPU structure.
3708 * @param pMappingHeader The message slot mapping.
3709 * @sa nemR3WinHandleExit
3710 */
3711NEM_TMPL_STATIC VBOXSTRICTRC
3712nemHCWinHandleMessage(PVMCC pVM, PVMCPUCC pVCpu, VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader)
3713{
3714 if (pMappingHeader->enmVidMsgType == VidMessageHypervisorMessage)
3715 {
3716 AssertMsg(pMappingHeader->cbMessage == HV_MESSAGE_SIZE, ("%#x\n", pMappingHeader->cbMessage));
3717 HV_MESSAGE const *pMsg = (HV_MESSAGE const *)(pMappingHeader + 1);
3718 switch (pMsg->Header.MessageType)
3719 {
3720 case HvMessageTypeUnmappedGpa:
3721 Assert(pMsg->Header.PayloadSize == RT_UOFFSETOF(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment));
3722 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMemUnmapped);
3723 return nemHCWinHandleMessageMemory(pVM, pVCpu, &pMsg->X64MemoryIntercept);
3724
3725 case HvMessageTypeGpaIntercept:
3726 Assert(pMsg->Header.PayloadSize == RT_UOFFSETOF(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment));
3727 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMemIntercept);
3728 return nemHCWinHandleMessageMemory(pVM, pVCpu, &pMsg->X64MemoryIntercept);
3729
3730 case HvMessageTypeX64IoPortIntercept:
3731 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64IoPortIntercept));
3732 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitPortIo);
3733 return nemHCWinHandleMessageIoPort(pVM, pVCpu, &pMsg->X64IoPortIntercept);
3734
3735 case HvMessageTypeX64Halt:
3736 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitHalt);
3737 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_HALT),
3738 pMsg->X64InterceptHeader.Rip + pMsg->X64InterceptHeader.CsSegment.Base, ASMReadTSC());
3739 Log4(("HaltExit\n"));
3740 return VINF_EM_HALT;
3741
3742 case HvMessageTypeX64InterruptWindow:
3743 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64InterruptWindow));
3744 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitInterruptWindow);
3745 return nemHCWinHandleMessageInterruptWindow(pVM, pVCpu, &pMsg->X64InterruptWindow);
3746
3747 case HvMessageTypeX64CpuidIntercept:
3748 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64CpuIdIntercept));
3749 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitCpuId);
3750 return nemHCWinHandleMessageCpuId(pVM, pVCpu, &pMsg->X64CpuIdIntercept);
3751
3752 case HvMessageTypeX64MsrIntercept:
3753 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64MsrIntercept));
3754 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMsr);
3755 return nemHCWinHandleMessageMsr(pVCpu, &pMsg->X64MsrIntercept);
3756
3757 case HvMessageTypeX64ExceptionIntercept:
3758 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64ExceptionIntercept));
3759 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitException);
3760 return nemHCWinHandleMessageException(pVCpu, &pMsg->X64ExceptionIntercept);
3761
3762 case HvMessageTypeUnrecoverableException:
3763 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64InterceptHeader));
3764 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitUnrecoverable);
3765 return nemHCWinHandleMessageUnrecoverableException(pVCpu, &pMsg->X64InterceptHeader);
3766
3767 case HvMessageTypeInvalidVpRegisterValue:
3768 case HvMessageTypeUnsupportedFeature:
3769 case HvMessageTypeTlbPageSizeMismatch:
3770 LogRel(("Unimplemented msg:\n%.*Rhxd\n", (int)sizeof(*pMsg), pMsg));
3771 AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n%.32Rhxd\n", pMsg->Header.MessageType, pMsg),
3772 VERR_NEM_IPE_3);
3773
3774 case HvMessageTypeX64ApicEoi:
3775 case HvMessageTypeX64LegacyFpError:
3776 case HvMessageTypeX64RegisterIntercept:
3777 case HvMessageTypeApicEoi:
3778 case HvMessageTypeFerrAsserted:
3779 case HvMessageTypeEventLogBufferComplete:
3780 case HvMessageTimerExpired:
3781 LogRel(("Unexpected msg:\n%.*Rhxd\n", (int)sizeof(*pMsg), pMsg));
3782 AssertLogRelMsgFailedReturn(("Unexpected message on CPU #%u: %#x\n", pVCpu->idCpu, pMsg->Header.MessageType),
3783 VERR_NEM_IPE_3);
3784
3785 default:
3786 LogRel(("Unknown msg:\n%.*Rhxd\n", (int)sizeof(*pMsg), pMsg));
3787 AssertLogRelMsgFailedReturn(("Unknown message on CPU #%u: %#x\n", pVCpu->idCpu, pMsg->Header.MessageType),
3788 VERR_NEM_IPE_3);
3789 }
3790 }
3791 else
3792 AssertLogRelMsgFailedReturn(("Unexpected VID message type on CPU #%u: %#x LB %u\n",
3793 pVCpu->idCpu, pMappingHeader->enmVidMsgType, pMappingHeader->cbMessage),
3794 VERR_NEM_IPE_4);
3795}
3796#elif defined(IN_RING3)
3797/**
3798 * Handles VM exits.
3799 *
3800 * @returns Strict VBox status code.
3801 * @param pVM The cross context VM structure.
3802 * @param pVCpu The cross context per CPU structure.
3803 * @param pExit The VM exit information to handle.
3804 * @sa nemHCWinHandleMessage
3805 */
3806NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExit(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
3807{
3808 switch (pExit->ExitReason)
3809 {
3810 case WHvRunVpExitReasonMemoryAccess:
3811 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMemUnmapped);
3812 return nemR3WinHandleExitMemory(pVM, pVCpu, pExit);
3813
3814 case WHvRunVpExitReasonX64IoPortAccess:
3815 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitPortIo);
3816 return nemR3WinHandleExitIoPort(pVM, pVCpu, pExit);
3817
3818 case WHvRunVpExitReasonX64Halt:
3819 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitHalt);
3820 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_HALT),
3821 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
3822 Log4(("HaltExit/%u\n", pVCpu->idCpu));
3823 return VINF_EM_HALT;
3824
3825 case WHvRunVpExitReasonCanceled:
3826 Log4(("CanceledExit/%u\n", pVCpu->idCpu));
3827 return VINF_SUCCESS;
3828
3829 case WHvRunVpExitReasonX64InterruptWindow:
3830 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitInterruptWindow);
3831 return nemR3WinHandleExitInterruptWindow(pVM, pVCpu, pExit);
3832
3833 case WHvRunVpExitReasonX64Cpuid:
3834 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitCpuId);
3835 return nemR3WinHandleExitCpuId(pVM, pVCpu, pExit);
3836
3837 case WHvRunVpExitReasonX64MsrAccess:
3838 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMsr);
3839 return nemR3WinHandleExitMsr(pVM, pVCpu, pExit);
3840
3841 case WHvRunVpExitReasonException:
3842 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitException);
3843 return nemR3WinHandleExitException(pVM, pVCpu, pExit);
3844
3845 case WHvRunVpExitReasonUnrecoverableException:
3846 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitUnrecoverable);
3847 return nemR3WinHandleExitUnrecoverableException(pVM, pVCpu, pExit);
3848
3849 case WHvRunVpExitReasonUnsupportedFeature:
3850 case WHvRunVpExitReasonInvalidVpRegisterValue:
3851 LogRel(("Unimplemented exit:\n%.*Rhxd\n", (int)sizeof(*pExit), pExit));
3852 AssertLogRelMsgFailedReturn(("Unexpected exit on CPU #%u: %#x\n%.32Rhxd\n",
3853 pVCpu->idCpu, pExit->ExitReason, pExit), VERR_NEM_IPE_3);
3854
3855 /* Undesired exits: */
3856 case WHvRunVpExitReasonNone:
3857 default:
3858 LogRel(("Unknown exit:\n%.*Rhxd\n", (int)sizeof(*pExit), pExit));
3859 AssertLogRelMsgFailedReturn(("Unknown exit on CPU #%u: %#x!\n", pVCpu->idCpu, pExit->ExitReason), VERR_NEM_IPE_3);
3860 }
3861}
3862#endif /* IN_RING3 && !NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
3863
3864
3865#if defined(IN_RING0) && defined(NEM_WIN_WITH_RING0_RUNLOOP)
3866/**
3867 * Perform an I/O control operation on the partition handle (VID.SYS),
3868 * restarting on alert-like behaviour.
3869 *
3870 * @returns NT status code.
3871 * @param pGVM The ring-0 VM structure.
3872 * @param pGVCpu The global (ring-0) per CPU structure.
3873 * @param fFlags The wait flags.
3874 * @param cMillies The timeout in milliseconds
3875 */
3876static NTSTATUS nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(PGVM pGVM, PGVMCPU pGVCpu, uint32_t fFlags, uint32_t cMillies)
3877{
3878 pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.iCpu = pGVCpu->idCpu;
3879 pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.fFlags = fFlags;
3880 pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.cMillies = cMillies;
3881 NTSTATUS rcNt = nemR0NtPerformIoControl(pGVM, pGVCpu, pGVM->nemr0.s.IoCtlMessageSlotHandleAndGetNext.uFunction,
3882 &pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext,
3883 pGVM->nemr0.s.IoCtlMessageSlotHandleAndGetNext.cbInput,
3884 NULL, 0);
3885 if (rcNt == STATUS_SUCCESS)
3886 { /* likely */ }
3887 /*
3888 * Generally, if we get down here, we have been interrupted between ACK'ing
3889 * a message and waiting for the next due to a NtAlertThread call. So, we
3890 * should stop ACK'ing the previous message and get on waiting on the next.
3891 * See similar stuff in nemHCWinRunGC().
3892 */
3893 else if ( rcNt == STATUS_TIMEOUT
3894 || rcNt == STATUS_ALERTED /* just in case */
3895 || rcNt == STATUS_KERNEL_APC /* just in case */
3896 || rcNt == STATUS_USER_APC /* just in case */)
3897 {
3898 DBGFTRACE_CUSTOM(pGVCpu->CTX_SUFF(pVM), "IoCtlMessageSlotHandleAndGetNextRestart/1 %#x (f=%#x)", rcNt, fFlags);
3899 STAM_REL_COUNTER_INC(&pGVCpu->nem.s.StatStopCpuPendingAlerts);
3900 Assert(fFlags & VID_MSHAGN_F_GET_NEXT_MESSAGE);
3901
3902 pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.iCpu = pGVCpu->idCpu;
3903 pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.fFlags = fFlags & ~VID_MSHAGN_F_HANDLE_MESSAGE;
3904 pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.cMillies = cMillies;
3905 rcNt = nemR0NtPerformIoControl(pGVM, pGVCpu, pGVM->nemr0.s.IoCtlMessageSlotHandleAndGetNext.uFunction,
3906 &pGVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext,
3907 pGVM->nemr0.s.IoCtlMessageSlotHandleAndGetNext.cbInput,
3908 NULL, 0);
3909 DBGFTRACE_CUSTOM(pGVM, "IoCtlMessageSlotHandleAndGetNextRestart/2 %#x", rcNt);
3910 }
3911 return rcNt;
3912}
3913#endif /* IN_RING0 */
3914
3915
3916#ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
3917/**
3918 * Worker for nemHCWinRunGC that stops the execution on the way out.
3919 *
3920 * The CPU was running the last time we checked, no there are no messages that
3921 * needs being marked handled/whatever. Caller checks this.
3922 *
3923 * @returns rcStrict on success, error status on failure.
3924 * @param pVM The cross context VM structure.
3925 * @param pVCpu The cross context per CPU structure.
3926 * @param rcStrict The nemHCWinRunGC return status. This is a little
3927 * bit unnecessary, except in internal error cases,
3928 * since we won't need to stop the CPU if we took an
3929 * exit.
3930 * @param pMappingHeader The message slot mapping.
3931 */
3932NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinStopCpu(PVMCC pVM, PVMCPUCC pVCpu, VBOXSTRICTRC rcStrict,
3933 VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader)
3934{
3935# ifdef DBGFTRACE_ENABLED
3936 HV_MESSAGE const volatile *pMsgForTrace = (HV_MESSAGE const volatile *)(pMappingHeader + 1);
3937# endif
3938
3939 /*
3940 * Try stopping the processor. If we're lucky we manage to do this before it
3941 * does another VM exit.
3942 */
3943 DBGFTRACE_CUSTOM(pVM, "nemStop#0");
3944# ifdef IN_RING0
3945 pVCpu->nem.s.uIoCtlBuf.idCpu = pVCpu->idCpu;
3946 NTSTATUS rcNt = nemR0NtPerformIoControl(pVM, pVCpu, pVM->nemr0.s.IoCtlStopVirtualProcessor.uFunction,
3947 &pVCpu->nem.s.uIoCtlBuf.idCpu, sizeof(pVCpu->nem.s.uIoCtlBuf.idCpu),
3948 NULL, 0);
3949 if (NT_SUCCESS(rcNt))
3950 {
3951 DBGFTRACE_CUSTOM(pVM, "nemStop#0: okay (%#x)", rcNt);
3952 Log8(("nemHCWinStopCpu: Stopping CPU succeeded (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));
3953 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatStopCpuSuccess);
3954 return rcStrict;
3955 }
3956# else
3957 BOOL fRet = VidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu);
3958 if (fRet)
3959 {
3960 DBGFTRACE_CUSTOM(pVM, "nemStop#0: okay");
3961 Log8(("nemHCWinStopCpu: Stopping CPU succeeded (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));
3962 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatStopCpuSuccess);
3963 return rcStrict;
3964 }
3965# endif
3966
3967 /*
3968 * Dang. The CPU stopped by itself and we got a couple of message to deal with.
3969 */
3970# ifdef IN_RING0
3971 DBGFTRACE_CUSTOM(pVM, "nemStop#0: pending (%#x)", rcNt);
3972 AssertLogRelMsgReturn(rcNt == ERROR_VID_STOP_PENDING, ("rcNt=%#x\n", rcNt),
3973 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
3974# else
3975 DWORD dwErr = RTNtLastErrorValue();
3976 DBGFTRACE_CUSTOM(pVM, "nemStop#0: pending (%#x)", dwErr);
3977 AssertLogRelMsgReturn(dwErr == ERROR_VID_STOP_PENDING, ("dwErr=%#u (%#x)\n", dwErr, dwErr),
3978 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
3979# endif
3980 Log8(("nemHCWinStopCpu: Stopping CPU #%u pending...\n", pVCpu->idCpu));
3981 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatStopCpuPending);
3982
3983 /*
3984 * First message: Exit or similar, sometimes VidMessageStopRequestComplete.
3985 * Note! We can safely ASSUME that rcStrict isn't an important information one.
3986 */
3987# ifdef IN_RING0
3988 rcNt = nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(pVM, pVCpu, VID_MSHAGN_F_GET_NEXT_MESSAGE, 30000 /*ms*/);
3989 DBGFTRACE_CUSTOM(pVM, "nemStop#1: %#x / %#x %#x %#x", rcNt, pMappingHeader->enmVidMsgType, pMappingHeader->cbMessage,
3990 pMsgForTrace->Header.MessageType);
3991 AssertLogRelMsgReturn(rcNt == STATUS_SUCCESS,
3992 ("1st VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %#x\n", rcNt),
3993 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
3994# else
3995 BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
3996 VID_MSHAGN_F_GET_NEXT_MESSAGE, 30000 /*ms*/);
3997 DBGFTRACE_CUSTOM(pVM, "nemStop#1: %d+%#x / %#x %#x %#x", fWait, RTNtLastErrorValue(), pMappingHeader->enmVidMsgType,
3998 pMappingHeader->cbMessage, pMsgForTrace->Header.MessageType);
3999 AssertLogRelMsgReturn(fWait, ("1st VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %u\n", RTNtLastErrorValue()),
4000 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
4001# endif
4002
4003 VID_MESSAGE_TYPE enmVidMsgType = pMappingHeader->enmVidMsgType;
4004 if (enmVidMsgType != VidMessageStopRequestComplete)
4005 {
4006 VBOXSTRICTRC rcStrict2 = nemHCWinHandleMessage(pVM, pVCpu, pMappingHeader);
4007 if (rcStrict2 != VINF_SUCCESS && RT_SUCCESS(rcStrict))
4008 rcStrict = rcStrict2;
4009 DBGFTRACE_CUSTOM(pVM, "nemStop#1: handled %#x -> %d", pMsgForTrace->Header.MessageType, VBOXSTRICTRC_VAL(rcStrict));
4010
4011 /*
4012 * Mark it as handled and get the stop request completed message, then mark
4013 * that as handled too. CPU is back into fully stopped stated then.
4014 */
4015# ifdef IN_RING0
4016 rcNt = nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(pVM, pVCpu,
4017 VID_MSHAGN_F_HANDLE_MESSAGE | VID_MSHAGN_F_GET_NEXT_MESSAGE,
4018 30000 /*ms*/);
4019 DBGFTRACE_CUSTOM(pVM, "nemStop#2: %#x / %#x %#x %#x", rcNt, pMappingHeader->enmVidMsgType, pMappingHeader->cbMessage,
4020 pMsgForTrace->Header.MessageType);
4021 AssertLogRelMsgReturn(rcNt == STATUS_SUCCESS,
4022 ("2nd VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %#x\n", rcNt),
4023 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
4024# else
4025 fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
4026 VID_MSHAGN_F_HANDLE_MESSAGE | VID_MSHAGN_F_GET_NEXT_MESSAGE, 30000 /*ms*/);
4027 DBGFTRACE_CUSTOM(pVM, "nemStop#2: %d+%#x / %#x %#x %#x", fWait, RTNtLastErrorValue(), pMappingHeader->enmVidMsgType,
4028 pMappingHeader->cbMessage, pMsgForTrace->Header.MessageType);
4029 AssertLogRelMsgReturn(fWait, ("2nd VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %u\n", RTNtLastErrorValue()),
4030 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
4031# endif
4032
4033 /* It should be a stop request completed message. */
4034 enmVidMsgType = pMappingHeader->enmVidMsgType;
4035 AssertLogRelMsgReturn(enmVidMsgType == VidMessageStopRequestComplete,
4036 ("Unexpected 2nd message following ERROR_VID_STOP_PENDING: %#x LB %#x\n",
4037 enmVidMsgType, pMappingHeader->cbMessage),
4038 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
4039
4040 /*
4041 * Mark the VidMessageStopRequestComplete message as handled.
4042 */
4043# ifdef IN_RING0
4044 rcNt = nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(pVM, pVCpu, VID_MSHAGN_F_HANDLE_MESSAGE, 30000 /*ms*/);
4045 DBGFTRACE_CUSTOM(pVM, "nemStop#3: %#x / %#x %#x %#x", rcNt, pMappingHeader->enmVidMsgType,
4046 pMsgForTrace->Header.MessageType, pMappingHeader->cbMessage, pMsgForTrace->Header.MessageType);
4047 AssertLogRelMsgReturn(rcNt == STATUS_SUCCESS,
4048 ("3rd VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %#x\n", rcNt),
4049 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
4050# else
4051 fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu, VID_MSHAGN_F_HANDLE_MESSAGE, 30000 /*ms*/);
4052 DBGFTRACE_CUSTOM(pVM, "nemStop#3: %d+%#x / %#x %#x %#x", fWait, RTNtLastErrorValue(), pMappingHeader->enmVidMsgType,
4053 pMsgForTrace->Header.MessageType, pMappingHeader->cbMessage, pMsgForTrace->Header.MessageType);
4054 AssertLogRelMsgReturn(fWait, ("3rd VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %u\n", RTNtLastErrorValue()),
4055 RT_SUCCESS(rcStrict) ? VERR_NEM_IPE_5 : rcStrict);
4056# endif
4057 Log8(("nemHCWinStopCpu: Stopped the CPU (rcStrict=%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict) ));
4058 }
4059 else
4060 {
4061 /** @todo I'm not so sure about this now... */
4062 DBGFTRACE_CUSTOM(pVM, "nemStop#9: %#x %#x %#x", pMappingHeader->enmVidMsgType,
4063 pMappingHeader->cbMessage, pMsgForTrace->Header.MessageType);
4064 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatStopCpuPendingOdd);
4065 Log8(("nemHCWinStopCpu: Stopped the CPU (rcStrict=%Rrc) - 1st VidMessageSlotHandleAndGetNext got VidMessageStopRequestComplete.\n",
4066 VBOXSTRICTRC_VAL(rcStrict) ));
4067 }
4068 return rcStrict;
4069}
4070#endif /* NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
4071
4072#if defined(NEM_WIN_TEMPLATE_MODE_OWN_RUN_API) || defined(IN_RING3)
4073
4074/**
4075 * Deals with pending interrupt related force flags, may inject interrupt.
4076 *
4077 * @returns VBox strict status code.
4078 * @param pVM The cross context VM structure.
4079 * @param pVCpu The cross context per CPU structure.
4080 * @param pfInterruptWindows Where to return interrupt window flags.
4081 */
4082NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleInterruptFF(PVMCC pVM, PVMCPUCC pVCpu, uint8_t *pfInterruptWindows)
4083{
4084 Assert(!TRPMHasTrap(pVCpu));
4085 RT_NOREF_PV(pVM);
4086
4087 /*
4088 * First update APIC. We ASSUME this won't need TPR/CR8.
4089 */
4090 if (VMCPU_FF_TEST_AND_CLEAR(pVCpu, VMCPU_FF_UPDATE_APIC))
4091 {
4092 APICUpdatePendingInterrupts(pVCpu);
4093 if (!VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC
4094 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI))
4095 return VINF_SUCCESS;
4096 }
4097
4098 /*
4099 * We don't currently implement SMIs.
4100 */
4101 AssertReturn(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI), VERR_NEM_IPE_0);
4102
4103 /*
4104 * Check if we've got the minimum of state required for deciding whether we
4105 * can inject interrupts and NMIs. If we don't have it, get all we might require
4106 * for injection via IEM.
4107 */
4108 bool const fPendingNmi = VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI);
4109 uint64_t fNeedExtrn = CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS
4110 | (fPendingNmi ? CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI : 0);
4111 if (pVCpu->cpum.GstCtx.fExtrn & fNeedExtrn)
4112 {
4113 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT, "IntFF");
4114 if (rcStrict != VINF_SUCCESS)
4115 return rcStrict;
4116 }
4117 bool const fInhibitInterrupts = VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)
4118 && EMGetInhibitInterruptsPC(pVCpu) == pVCpu->cpum.GstCtx.rip;
4119
4120 /*
4121 * NMI? Try deliver it first.
4122 */
4123 if (fPendingNmi)
4124 {
4125 if ( !fInhibitInterrupts
4126 && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_BLOCK_NMIS))
4127 {
4128 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT, "NMI");
4129 if (rcStrict == VINF_SUCCESS)
4130 {
4131 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
4132 rcStrict = IEMInjectTrap(pVCpu, X86_XCPT_NMI, TRPM_HARDWARE_INT, 0, 0, 0);
4133 Log8(("Injected NMI on %u (%d)\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
4134 }
4135 return rcStrict;
4136 }
4137 *pfInterruptWindows |= NEM_WIN_INTW_F_NMI;
4138 Log8(("NMI window pending on %u\n", pVCpu->idCpu));
4139 }
4140
4141 /*
4142 * APIC or PIC interrupt?
4143 */
4144 if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC))
4145 {
4146 if ( !fInhibitInterrupts
4147 && pVCpu->cpum.GstCtx.rflags.Bits.u1IF)
4148 {
4149 AssertCompile(NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT & CPUMCTX_EXTRN_APIC_TPR);
4150 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT, "NMI");
4151 if (rcStrict == VINF_SUCCESS)
4152 {
4153 uint8_t bInterrupt;
4154 int rc = PDMGetInterrupt(pVCpu, &bInterrupt);
4155 if (RT_SUCCESS(rc))
4156 {
4157 Log8(("Injecting interrupt %#x on %u: %04x:%08RX64 efl=%#x\n", bInterrupt, pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.eflags));
4158 rcStrict = IEMInjectTrap(pVCpu, bInterrupt, TRPM_HARDWARE_INT, 0, 0, 0);
4159 Log8(("Injected interrupt %#x on %u (%d)\n", bInterrupt, pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
4160 }
4161 else if (rc == VERR_APIC_INTR_MASKED_BY_TPR)
4162 {
4163 *pfInterruptWindows |= ((bInterrupt >> 4) << NEM_WIN_INTW_F_PRIO_SHIFT) | NEM_WIN_INTW_F_REGULAR;
4164 Log8(("VERR_APIC_INTR_MASKED_BY_TPR: *pfInterruptWindows=%#x\n", *pfInterruptWindows));
4165 }
4166 else
4167 Log8(("PDMGetInterrupt failed -> %Rrc\n", rc));
4168 }
4169 return rcStrict;
4170 }
4171
4172 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC) && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC))
4173 {
4174 /* If only an APIC interrupt is pending, we need to know its priority. Otherwise we'll
4175 * likely get pointless deliverability notifications with IF=1 but TPR still too high.
4176 */
4177 bool fPendingIntr = false;
4178 uint8_t bTpr = 0;
4179 uint8_t bPendingIntr = 0;
4180 int rc = APICGetTpr(pVCpu, &bTpr, &fPendingIntr, &bPendingIntr);
4181 AssertRC(rc);
4182 *pfInterruptWindows |= (bPendingIntr >> 4) << NEM_WIN_INTW_F_PRIO_SHIFT;
4183 Log8(("Interrupt window pending on %u: %#x (bTpr=%#x fPendingIntr=%d bPendingIntr=%#x)\n",
4184 pVCpu->idCpu, *pfInterruptWindows, bTpr, fPendingIntr, bPendingIntr));
4185 }
4186 else
4187 {
4188 *pfInterruptWindows |= NEM_WIN_INTW_F_REGULAR;
4189 Log8(("Interrupt window pending on %u: %#x\n", pVCpu->idCpu, *pfInterruptWindows));
4190 }
4191 }
4192
4193 return VINF_SUCCESS;
4194}
4195
4196
4197/**
4198 * Inner NEM runloop for windows.
4199 *
4200 * @returns Strict VBox status code.
4201 * @param pVM The cross context VM structure.
4202 * @param pVCpu The cross context per CPU structure.
4203 */
4204NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinRunGC(PVMCC pVM, PVMCPUCC pVCpu)
4205{
4206 LogFlow(("NEM/%u: %04x:%08RX64 efl=%#08RX64 <=\n", pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags));
4207# ifdef LOG_ENABLED
4208 if (LogIs3Enabled())
4209 nemHCWinLogState(pVM, pVCpu);
4210# endif
4211
4212 /*
4213 * Try switch to NEM runloop state.
4214 */
4215 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED))
4216 { /* likely */ }
4217 else
4218 {
4219 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);
4220 LogFlow(("NEM/%u: returning immediately because canceled\n", pVCpu->idCpu));
4221 return VINF_SUCCESS;
4222 }
4223
4224 /*
4225 * The run loop.
4226 *
4227 * Current approach to state updating to use the sledgehammer and sync
4228 * everything every time. This will be optimized later.
4229 */
4230# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4231 VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader = (VID_MESSAGE_MAPPING_HEADER volatile *)pVCpu->nem.s.pvMsgSlotMapping;
4232# endif
4233 const bool fSingleStepping = DBGFIsStepping(pVCpu);
4234// const uint32_t fCheckVmFFs = !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK
4235// : VM_FF_HP_R0_PRE_HM_STEP_MASK;
4236// const uint32_t fCheckCpuFFs = !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK;
4237 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
4238 for (unsigned iLoop = 0;; iLoop++)
4239 {
4240# if !defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) && !defined(VBOX_WITH_PGM_NEM_MODE)
4241 /*
4242 * Hack alert!
4243 */
4244 uint32_t const cMappedPages = pVM->nem.s.cMappedPages;
4245 if (cMappedPages < pVM->nem.s.cMaxMappedPages)
4246 { /* likely*/ }
4247 else
4248 {
4249 PGMPhysNemEnumPagesByState(pVM, pVCpu, NEM_WIN_PAGE_STATE_READABLE, nemHCWinUnmapOnePageCallback, NULL);
4250 Log(("nemHCWinRunGC: Unmapped all; cMappedPages=%u -> %u\n", cMappedPages, pVM->nem.s.cMappedPages));
4251 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapAllPages);
4252 }
4253# endif
4254
4255 /*
4256 * Pending interrupts or such? Need to check and deal with this prior
4257 * to the state syncing.
4258 */
4259 pVCpu->nem.s.fDesiredInterruptWindows = 0;
4260 if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_UPDATE_APIC | VMCPU_FF_INTERRUPT_PIC
4261 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI))
4262 {
4263# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4264 /* Make sure the CPU isn't executing. */
4265 if (pVCpu->nem.s.fHandleAndGetFlags == VID_MSHAGN_F_GET_NEXT_MESSAGE)
4266 {
4267 pVCpu->nem.s.fHandleAndGetFlags = 0;
4268 rcStrict = nemHCWinStopCpu(pVM, pVCpu, rcStrict, pMappingHeader);
4269 if (rcStrict == VINF_SUCCESS)
4270 { /* likely */ }
4271 else
4272 {
4273 LogFlow(("NEM/%u: breaking: nemHCWinStopCpu -> %Rrc\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
4274 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnStatus);
4275 break;
4276 }
4277 }
4278# endif
4279
4280 /* Try inject interrupt. */
4281 rcStrict = nemHCWinHandleInterruptFF(pVM, pVCpu, &pVCpu->nem.s.fDesiredInterruptWindows);
4282 if (rcStrict == VINF_SUCCESS)
4283 { /* likely */ }
4284 else
4285 {
4286 LogFlow(("NEM/%u: breaking: nemHCWinHandleInterruptFF -> %Rrc\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
4287 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnStatus);
4288 break;
4289 }
4290 }
4291
4292# ifndef NEM_WIN_WITH_A20
4293 /*
4294 * Do not execute in hyper-V if the A20 isn't enabled.
4295 */
4296 if (PGMPhysIsA20Enabled(pVCpu))
4297 { /* likely */ }
4298 else
4299 {
4300 rcStrict = VINF_EM_RESCHEDULE_REM;
4301 LogFlow(("NEM/%u: breaking: A20 disabled\n", pVCpu->idCpu));
4302 break;
4303 }
4304# endif
4305
4306 /*
4307 * Ensure that hyper-V has the whole state.
4308 * (We always update the interrupt windows settings when active as hyper-V seems
4309 * to forget about it after an exit.)
4310 */
4311 if ( (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK))
4312 != (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK)
4313 || ( ( pVCpu->nem.s.fDesiredInterruptWindows
4314 || pVCpu->nem.s.fCurrentInterruptWindows != pVCpu->nem.s.fDesiredInterruptWindows)
4315# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4316 && pVCpu->nem.s.fHandleAndGetFlags != VID_MSHAGN_F_GET_NEXT_MESSAGE /* not running */
4317# endif
4318 )
4319 )
4320 {
4321# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4322 AssertMsg(pVCpu->nem.s.fHandleAndGetFlags != VID_MSHAGN_F_GET_NEXT_MESSAGE /* not running */,
4323 ("%#x fExtrn=%#RX64 (%#RX64) fDesiredInterruptWindows=%d fCurrentInterruptWindows=%#x vs %#x\n",
4324 pVCpu->nem.s.fHandleAndGetFlags, pVCpu->cpum.GstCtx.fExtrn, ~pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK),
4325 pVCpu->nem.s.fDesiredInterruptWindows, pVCpu->nem.s.fCurrentInterruptWindows, pVCpu->nem.s.fDesiredInterruptWindows));
4326# endif
4327# ifdef IN_RING0
4328 int rc2 = nemR0WinExportState(pVM, pVCpu, &pVCpu->cpum.GstCtx);
4329# else
4330 int rc2 = nemHCWinCopyStateToHyperV(pVM, pVCpu);
4331# endif
4332 AssertRCReturn(rc2, rc2);
4333 }
4334
4335 /*
4336 * Poll timers and run for a bit.
4337 *
4338 * With the VID approach (ring-0 or ring-3) we can specify a timeout here,
4339 * so we take the time of the next timer event and uses that as a deadline.
4340 * The rounding heuristics are "tuned" so that rhel5 (1K timer) will boot fine.
4341 */
4342 /** @todo See if we cannot optimize this TMTimerPollGIP by only redoing
4343 * the whole polling job when timers have changed... */
4344 uint64_t offDeltaIgnored;
4345 uint64_t const nsNextTimerEvt = TMTimerPollGIP(pVM, pVCpu, &offDeltaIgnored); NOREF(nsNextTimerEvt);
4346 if ( !VM_FF_IS_ANY_SET(pVM, VM_FF_EMT_RENDEZVOUS | VM_FF_TM_VIRTUAL_SYNC)
4347 && !VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_HM_TO_R3_MASK))
4348 {
4349# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4350 if (pVCpu->nem.s.fHandleAndGetFlags)
4351 { /* Very likely that the CPU does NOT need starting (pending msg, running). */ }
4352 else
4353 {
4354# ifdef IN_RING0
4355 pVCpu->nem.s.uIoCtlBuf.idCpu = pVCpu->idCpu;
4356 NTSTATUS rcNt = nemR0NtPerformIoControl(pVM, pVCpu, pVM->nemr0.s.IoCtlStartVirtualProcessor.uFunction,
4357 &pVCpu->nem.s.uIoCtlBuf.idCpu, sizeof(pVCpu->nem.s.uIoCtlBuf.idCpu),
4358 NULL, 0);
4359 LogFlow(("NEM/%u: IoCtlStartVirtualProcessor -> %#x\n", pVCpu->idCpu, rcNt));
4360 AssertLogRelMsgReturn(NT_SUCCESS(rcNt), ("VidStartVirtualProcessor failed for CPU #%u: %#x\n", pVCpu->idCpu, rcNt),
4361 VERR_NEM_IPE_5);
4362# else
4363 AssertLogRelMsgReturn(g_pfnVidStartVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu),
4364 ("VidStartVirtualProcessor failed for CPU #%u: %u (%#x, rcNt=%#x)\n",
4365 pVCpu->idCpu, RTNtLastErrorValue(), RTNtLastErrorValue(), RTNtLastStatusValue()),
4366 VERR_NEM_IPE_5);
4367# endif
4368 pVCpu->nem.s.fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE;
4369 }
4370# endif /* NEM_WIN_TEMPLATE_MODE_OWN_RUN_API */
4371
4372 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_WAIT, VMCPUSTATE_STARTED_EXEC_NEM))
4373 {
4374# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4375 uint64_t const nsNow = RTTimeNanoTS();
4376 int64_t const cNsNextTimerEvt = nsNow - nsNextTimerEvt;
4377 uint32_t cMsWait;
4378 if (cNsNextTimerEvt < 100000 /* ns */)
4379 cMsWait = 0;
4380 else if ((uint64_t)cNsNextTimerEvt < RT_NS_1SEC)
4381 {
4382 if ((uint32_t)cNsNextTimerEvt < 2*RT_NS_1MS)
4383 cMsWait = 1;
4384 else
4385 cMsWait = ((uint32_t)cNsNextTimerEvt - 100000 /*ns*/) / RT_NS_1MS;
4386 }
4387 else
4388 cMsWait = RT_MS_1SEC;
4389# ifdef IN_RING0
4390 pVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.iCpu = pVCpu->idCpu;
4391 pVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.fFlags = pVCpu->nem.s.fHandleAndGetFlags;
4392 pVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext.cMillies = cMsWait;
4393 NTSTATUS rcNt = nemR0NtPerformIoControl(pVM, pVCpu, pVM->nemr0.s.IoCtlMessageSlotHandleAndGetNext.uFunction,
4394 &pVCpu->nem.s.uIoCtlBuf.MsgSlotHandleAndGetNext,
4395 pVM->nemr0.s.IoCtlMessageSlotHandleAndGetNext.cbInput,
4396 NULL, 0);
4397 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);
4398 if (rcNt == STATUS_SUCCESS)
4399# else
4400 BOOL fRet = VidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
4401 pVCpu->nem.s.fHandleAndGetFlags, cMsWait);
4402 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);
4403 if (fRet)
4404# endif
4405# else
4406# ifdef LOG_ENABLED
4407 if (LogIsFlowEnabled())
4408 {
4409 static const WHV_REGISTER_NAME s_aNames[6] = { WHvX64RegisterCs, WHvX64RegisterRip, WHvX64RegisterRflags,
4410 WHvX64RegisterSs, WHvX64RegisterRsp, WHvX64RegisterCr0 };
4411 WHV_REGISTER_VALUE aRegs[RT_ELEMENTS(s_aNames)] = {0};
4412 WHvGetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, s_aNames, RT_ELEMENTS(s_aNames), aRegs);
4413 LogFlow(("NEM/%u: Entry @ %04x:%08RX64 IF=%d EFL=%#RX64 SS:RSP=%04x:%08RX64 cr0=%RX64\n",
4414 pVCpu->idCpu, aRegs[0].Segment.Selector, aRegs[1].Reg64, RT_BOOL(aRegs[2].Reg64 & X86_EFL_IF),
4415 aRegs[2].Reg64, aRegs[3].Segment.Selector, aRegs[4].Reg64, aRegs[5].Reg64));
4416 }
4417# endif
4418 WHV_RUN_VP_EXIT_CONTEXT ExitReason = {0};
4419 TMNotifyStartOfExecution(pVM, pVCpu);
4420
4421 HRESULT hrc = WHvRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, &ExitReason, sizeof(ExitReason));
4422
4423 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);
4424 TMNotifyEndOfExecution(pVM, pVCpu, ASMReadTSC());
4425# ifdef LOG_ENABLED
4426 LogFlow(("NEM/%u: Exit @ %04X:%08RX64 IF=%d CR8=%#x Reason=%#x\n", pVCpu->idCpu, ExitReason.VpContext.Cs.Selector,
4427 ExitReason.VpContext.Rip, RT_BOOL(ExitReason.VpContext.Rflags & X86_EFL_IF), ExitReason.VpContext.Cr8,
4428 ExitReason.ExitReason));
4429# endif
4430 if (SUCCEEDED(hrc))
4431# endif
4432 {
4433 /*
4434 * Deal with the message.
4435 */
4436# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4437 rcStrict = nemHCWinHandleMessage(pVM, pVCpu, pMappingHeader);
4438 pVCpu->nem.s.fHandleAndGetFlags |= VID_MSHAGN_F_HANDLE_MESSAGE;
4439# else
4440 rcStrict = nemR3WinHandleExit(pVM, pVCpu, &ExitReason);
4441# endif
4442 if (rcStrict == VINF_SUCCESS)
4443 { /* hopefully likely */ }
4444 else
4445 {
4446 LogFlow(("NEM/%u: breaking: nemHCWinHandleMessage -> %Rrc\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
4447 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnStatus);
4448 break;
4449 }
4450 }
4451 else
4452 {
4453# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4454
4455 /* VID.SYS merges STATUS_ALERTED and STATUS_USER_APC into STATUS_TIMEOUT,
4456 so after NtAlertThread we end up here with a STATUS_TIMEOUT. And yeah,
4457 the error code conversion is into WAIT_XXX, i.e. NT status codes. */
4458# ifndef IN_RING0
4459 DWORD rcNt = GetLastError();
4460# endif
4461 LogFlow(("NEM/%u: VidMessageSlotHandleAndGetNext -> %#x\n", pVCpu->idCpu, rcNt));
4462 AssertLogRelMsgReturn( rcNt == STATUS_TIMEOUT
4463 || rcNt == STATUS_ALERTED /* just in case */
4464 || rcNt == STATUS_USER_APC /* ditto */
4465 || rcNt == STATUS_KERNEL_APC /* ditto */
4466 , ("VidMessageSlotHandleAndGetNext failed for CPU #%u: %#x (%u)\n",
4467 pVCpu->idCpu, rcNt, rcNt),
4468 VERR_NEM_IPE_0);
4469 pVCpu->nem.s.fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE;
4470 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatGetMsgTimeout);
4471# else
4472 AssertLogRelMsgFailedReturn(("WHvRunVirtualProcessor failed for CPU #%u: %#x (%u)\n",
4473 pVCpu->idCpu, hrc, GetLastError()),
4474 VERR_NEM_IPE_0);
4475# endif
4476 }
4477
4478 /*
4479 * If no relevant FFs are pending, loop.
4480 */
4481 if ( !VM_FF_IS_ANY_SET( pVM, !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK : VM_FF_HP_R0_PRE_HM_STEP_MASK)
4482 && !VMCPU_FF_IS_ANY_SET(pVCpu, !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK) )
4483 continue;
4484
4485 /** @todo Try handle pending flags, not just return to EM loops. Take care
4486 * not to set important RCs here unless we've handled a message. */
4487 LogFlow(("NEM/%u: breaking: pending FF (%#x / %#RX64)\n",
4488 pVCpu->idCpu, pVM->fGlobalForcedActions, (uint64_t)pVCpu->fLocalForcedActions));
4489 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnFFPost);
4490 }
4491 else
4492 {
4493 LogFlow(("NEM/%u: breaking: canceled %d (pre exec)\n", pVCpu->idCpu, VMCPU_GET_STATE(pVCpu) ));
4494 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnCancel);
4495 }
4496 }
4497 else
4498 {
4499 LogFlow(("NEM/%u: breaking: pending FF (pre exec)\n", pVCpu->idCpu));
4500 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnFFPre);
4501 }
4502 break;
4503 } /* the run loop */
4504
4505
4506 /*
4507 * If the CPU is running, make sure to stop it before we try sync back the
4508 * state and return to EM. We don't sync back the whole state if we can help it.
4509 */
4510# ifdef NEM_WIN_TEMPLATE_MODE_OWN_RUN_API
4511 if (pVCpu->nem.s.fHandleAndGetFlags == VID_MSHAGN_F_GET_NEXT_MESSAGE)
4512 {
4513 pVCpu->nem.s.fHandleAndGetFlags = 0;
4514 rcStrict = nemHCWinStopCpu(pVM, pVCpu, rcStrict, pMappingHeader);
4515 }
4516# endif
4517
4518 if (!VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM))
4519 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);
4520
4521 if (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT)))
4522 {
4523 /* Try anticipate what we might need. */
4524 uint64_t fImport = IEM_CPUMCTX_EXTRN_MUST_MASK | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_NMI;
4525 if ( (rcStrict >= VINF_EM_FIRST && rcStrict <= VINF_EM_LAST)
4526 || RT_FAILURE(rcStrict))
4527 fImport = CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT);
4528# ifdef IN_RING0 /* Ring-3 I/O port access optimizations: */
4529 else if ( rcStrict == VINF_IOM_R3_IOPORT_COMMIT_WRITE
4530 || rcStrict == VINF_EM_PENDING_R3_IOPORT_WRITE)
4531 fImport = CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT;
4532 else if (rcStrict == VINF_EM_PENDING_R3_IOPORT_READ)
4533 fImport = CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_NEM_WIN_INHIBIT_INT;
4534# endif
4535 else if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_INTERRUPT_APIC
4536 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI))
4537 fImport |= IEM_CPUMCTX_EXTRN_XCPT_MASK;
4538
4539 if (pVCpu->cpum.GstCtx.fExtrn & fImport)
4540 {
4541# ifdef IN_RING0
4542 int rc2 = nemR0WinImportState(pVM, pVCpu, &pVCpu->cpum.GstCtx, fImport | CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT,
4543 true /*fCanUpdateCr3*/);
4544 if (RT_SUCCESS(rc2))
4545 pVCpu->cpum.GstCtx.fExtrn &= ~fImport;
4546 else if (rc2 == VERR_NEM_FLUSH_TLB)
4547 {
4548 pVCpu->cpum.GstCtx.fExtrn &= ~fImport;
4549 if (rcStrict == VINF_SUCCESS || rcStrict == -rc2)
4550 rcStrict = -rc2;
4551 else
4552 {
4553 pVCpu->nem.s.rcPending = -rc2;
4554 LogFlow(("NEM/%u: rcPending=%Rrc (rcStrict=%Rrc)\n", pVCpu->idCpu, rc2, VBOXSTRICTRC_VAL(rcStrict) ));
4555 }
4556 }
4557# else
4558 int rc2 = nemHCWinCopyStateFromHyperV(pVM, pVCpu, fImport | CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT);
4559 if (RT_SUCCESS(rc2))
4560 pVCpu->cpum.GstCtx.fExtrn &= ~fImport;
4561# endif
4562 else if (RT_SUCCESS(rcStrict))
4563 rcStrict = rc2;
4564 if (!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT))))
4565 pVCpu->cpum.GstCtx.fExtrn = 0;
4566 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnReturn);
4567 }
4568 else
4569 {
4570 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnReturnSkipped);
4571 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
4572 }
4573 }
4574 else
4575 {
4576 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnReturnSkipped);
4577 pVCpu->cpum.GstCtx.fExtrn = 0;
4578 }
4579
4580 LogFlow(("NEM/%u: %04x:%08RX64 efl=%#08RX64 => %Rrc\n",
4581 pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags, VBOXSTRICTRC_VAL(rcStrict) ));
4582 return rcStrict;
4583}
4584
4585#endif /* defined(NEM_WIN_TEMPLATE_MODE_OWN_RUN_API) || defined(IN_RING3) */
4586#if defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) || defined(IN_RING3)
4587
4588/**
4589 * @callback_method_impl{FNPGMPHYSNEMCHECKPAGE}
4590 */
4591NEM_TMPL_STATIC DECLCALLBACK(int) nemHCWinUnsetForA20CheckerCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys,
4592 PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
4593{
4594 /* We'll just unmap the memory. */
4595 if (pInfo->u2NemState > NEM_WIN_PAGE_STATE_UNMAPPED)
4596 {
4597# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
4598 int rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhys);
4599 AssertRC(rc);
4600 if (RT_SUCCESS(rc))
4601# else
4602 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
4603 if (SUCCEEDED(hrc))
4604# endif
4605 {
4606 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
4607 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4608 Log5(("NEM GPA unmapped/A20: %RGp (was %s, cMappedPages=%u)\n", GCPhys, g_apszPageStates[pInfo->u2NemState], cMappedPages));
4609 pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
4610 }
4611 else
4612 {
4613 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
4614# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
4615 LogRel(("nemHCWinUnsetForA20CheckerCallback/unmap: GCPhys=%RGp rc=%Rrc\n", GCPhys, rc));
4616 return rc;
4617# else
4618 LogRel(("nemHCWinUnsetForA20CheckerCallback/unmap: GCPhys=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
4619 GCPhys, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
4620 return VERR_NEM_IPE_2;
4621# endif
4622 }
4623 }
4624 RT_NOREF(pVCpu, pvUser);
4625 return VINF_SUCCESS;
4626}
4627
4628
4629/**
4630 * Unmaps a page from Hyper-V for the purpose of emulating A20 gate behavior.
4631 *
4632 * @returns The PGMPhysNemQueryPageInfo result.
4633 * @param pVM The cross context VM structure.
4634 * @param pVCpu The cross context virtual CPU structure.
4635 * @param GCPhys The page to unmap.
4636 */
4637NEM_TMPL_STATIC int nemHCWinUnmapPageForA20Gate(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys)
4638{
4639 PGMPHYSNEMPAGEINFO Info;
4640 return PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhys, false /*fMakeWritable*/, &Info,
4641 nemHCWinUnsetForA20CheckerCallback, NULL);
4642}
4643
4644#endif /* defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) || defined(IN_RING3) */
4645
4646void nemHCNativeNotifyHandlerPhysicalRegister(PVMCC pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhys, RTGCPHYS cb)
4647{
4648 Log5(("nemHCNativeNotifyHandlerPhysicalRegister: %RGp LB %RGp enmKind=%d\n", GCPhys, cb, enmKind));
4649 NOREF(pVM); NOREF(enmKind); NOREF(GCPhys); NOREF(cb);
4650}
4651
4652
4653VMM_INT_DECL(void) NEMHCNotifyHandlerPhysicalDeregister(PVMCC pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhys, RTGCPHYS cb,
4654 RTR3PTR pvMemR3, uint8_t *pu2State)
4655{
4656 Log5(("NEMHCNotifyHandlerPhysicalDeregister: %RGp LB %RGp enmKind=%d pvMemR3=%p pu2State=%p (%d)\n",
4657 GCPhys, cb, enmKind, pvMemR3, pu2State, *pu2State));
4658
4659 *pu2State = UINT8_MAX;
4660#if !defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) && defined(VBOX_WITH_PGM_NEM_MODE) && defined(IN_RING3)
4661 if (pvMemR3)
4662 {
4663 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfMapGpaRange, a);
4664 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, pvMemR3, GCPhys, cb,
4665 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute | WHvMapGpaRangeFlagWrite);
4666 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfMapGpaRange, a);
4667 if (SUCCEEDED(hrc))
4668 *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
4669 else
4670 AssertLogRelMsgFailed(("NEMHCNotifyHandlerPhysicalDeregister: WHvMapGpaRange(,%p,%RGp,%RGp,) -> %Rhrc\n",
4671 pvMemR3, GCPhys, cb, hrc));
4672 }
4673 RT_NOREF(enmKind);
4674#else
4675 RT_NOREF(pVM, enmKind, GCPhys, cb, pvMemR3);
4676#endif
4677}
4678
4679
4680void nemHCNativeNotifyHandlerPhysicalModify(PVMCC pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhysOld,
4681 RTGCPHYS GCPhysNew, RTGCPHYS cb, bool fRestoreAsRAM)
4682{
4683 Log5(("nemHCNativeNotifyHandlerPhysicalModify: %RGp LB %RGp -> %RGp enmKind=%d fRestoreAsRAM=%d\n",
4684 GCPhysOld, cb, GCPhysNew, enmKind, fRestoreAsRAM));
4685 NOREF(pVM); NOREF(enmKind); NOREF(GCPhysOld); NOREF(GCPhysNew); NOREF(cb); NOREF(fRestoreAsRAM);
4686}
4687
4688
4689#if defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) || defined(IN_RING3)
4690/**
4691 * Worker that maps pages into Hyper-V.
4692 *
4693 * This is used by the PGM physical page notifications as well as the memory
4694 * access VMEXIT handlers.
4695 *
4696 * @returns VBox status code.
4697 * @param pVM The cross context VM structure.
4698 * @param pVCpu The cross context virtual CPU structure of the
4699 * calling EMT.
4700 * @param GCPhysSrc The source page address.
4701 * @param GCPhysDst The hyper-V destination page. This may differ from
4702 * GCPhysSrc when A20 is disabled.
4703 * @param fPageProt NEM_PAGE_PROT_XXX.
4704 * @param pu2State Our page state (input/output).
4705 * @param fBackingChanged Set if the page backing is being changed.
4706 * @thread EMT(pVCpu)
4707 */
4708NEM_TMPL_STATIC int nemHCNativeSetPhysPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst,
4709 uint32_t fPageProt, uint8_t *pu2State, bool fBackingChanged)
4710{
4711# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
4712 /*
4713 * When using the hypercalls instead of the ring-3 APIs, we don't need to
4714 * unmap memory before modifying it. We still want to track the state though,
4715 * since unmap will fail when called an unmapped page and we don't want to redo
4716 * upgrades/downgrades.
4717 */
4718 uint8_t const u2OldState = *pu2State;
4719 int rc;
4720 if (fPageProt == NEM_PAGE_PROT_NONE)
4721 {
4722 if (u2OldState > NEM_WIN_PAGE_STATE_UNMAPPED)
4723 {
4724 rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhysDst);
4725 if (RT_SUCCESS(rc))
4726 {
4727 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4728 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
4729 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4730 Log5(("NEM GPA unmapped/set: %RGp (was %s, cMappedPages=%u)\n", GCPhysDst, g_apszPageStates[u2OldState], cMappedPages));
4731 }
4732 else
4733 {
4734 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
4735 AssertLogRelMsgFailed(("nemHCNativeSetPhysPage/unmap: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4736 }
4737 }
4738 else
4739 rc = VINF_SUCCESS;
4740 }
4741 else if (fPageProt & NEM_PAGE_PROT_WRITE)
4742 {
4743 if (u2OldState != NEM_WIN_PAGE_STATE_WRITABLE || fBackingChanged)
4744 {
4745 rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhysDst,
4746 HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE
4747 | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
4748 if (RT_SUCCESS(rc))
4749 {
4750 *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
4751 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
4752 uint32_t cMappedPages = u2OldState <= NEM_WIN_PAGE_STATE_UNMAPPED
4753 ? ASMAtomicIncU32(&pVM->nem.s.cMappedPages) : pVM->nem.s.cMappedPages;
4754 Log5(("NEM GPA writable/set: %RGp (was %s, cMappedPages=%u)\n", GCPhysDst, g_apszPageStates[u2OldState], cMappedPages));
4755 NOREF(cMappedPages);
4756 }
4757 else
4758 {
4759 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
4760 AssertLogRelMsgFailed(("nemHCNativeSetPhysPage/writable: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4761 }
4762 }
4763 else
4764 rc = VINF_SUCCESS;
4765 }
4766 else
4767 {
4768 if (u2OldState != NEM_WIN_PAGE_STATE_READABLE || fBackingChanged)
4769 {
4770 rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhysDst,
4771 HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
4772 if (RT_SUCCESS(rc))
4773 {
4774 *pu2State = NEM_WIN_PAGE_STATE_READABLE;
4775 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
4776 uint32_t cMappedPages = u2OldState <= NEM_WIN_PAGE_STATE_UNMAPPED
4777 ? ASMAtomicIncU32(&pVM->nem.s.cMappedPages) : pVM->nem.s.cMappedPages;
4778 Log5(("NEM GPA read+exec/set: %RGp (was %s, cMappedPages=%u)\n", GCPhysDst, g_apszPageStates[u2OldState], cMappedPages));
4779 NOREF(cMappedPages);
4780 }
4781 else
4782 {
4783 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
4784 AssertLogRelMsgFailed(("nemHCNativeSetPhysPage/writable: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4785 }
4786 }
4787 else
4788 rc = VINF_SUCCESS;
4789 }
4790
4791 return VINF_SUCCESS;
4792
4793# else /* !NEM_WIN_USE_HYPERCALLS_FOR_PAGES */
4794 /*
4795 * Looks like we need to unmap a page before we can change the backing
4796 * or even modify the protection. This is going to be *REALLY* efficient.
4797 * PGM lends us two bits to keep track of the state here.
4798 */
4799 RT_NOREF(pVCpu);
4800 uint8_t const u2OldState = *pu2State;
4801 uint8_t const u2NewState = fPageProt & NEM_PAGE_PROT_WRITE ? NEM_WIN_PAGE_STATE_WRITABLE
4802 : fPageProt & NEM_PAGE_PROT_READ ? NEM_WIN_PAGE_STATE_READABLE : NEM_WIN_PAGE_STATE_UNMAPPED;
4803 if ( fBackingChanged
4804 || u2NewState != u2OldState)
4805 {
4806 if (u2OldState > NEM_WIN_PAGE_STATE_UNMAPPED)
4807 {
4808# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
4809 int rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhysDst);
4810 AssertRC(rc);
4811 if (RT_SUCCESS(rc))
4812 {
4813 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4814 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
4815 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4816 if (u2NewState == NEM_WIN_PAGE_STATE_UNMAPPED)
4817 {
4818 Log5(("NEM GPA unmapped/set: %RGp (was %s, cMappedPages=%u)\n",
4819 GCPhysDst, g_apszPageStates[u2OldState], cMappedPages));
4820 return VINF_SUCCESS;
4821 }
4822 }
4823 else
4824 {
4825 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
4826 LogRel(("nemHCNativeSetPhysPage/unmap: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4827 return rc;
4828 }
4829# else
4830 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
4831 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhysDst, X86_PAGE_SIZE);
4832 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
4833 if (SUCCEEDED(hrc))
4834 {
4835 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4836 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
4837 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4838 if (u2NewState == NEM_WIN_PAGE_STATE_UNMAPPED)
4839 {
4840 Log5(("NEM GPA unmapped/set: %RGp (was %s, cMappedPages=%u)\n",
4841 GCPhysDst, g_apszPageStates[u2OldState], cMappedPages));
4842 return VINF_SUCCESS;
4843 }
4844 }
4845 else
4846 {
4847 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
4848 LogRel(("nemHCNativeSetPhysPage/unmap: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
4849 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
4850 return VERR_NEM_INIT_FAILED;
4851 }
4852# endif
4853 }
4854 }
4855
4856 /*
4857 * Writeable mapping?
4858 */
4859 if (fPageProt & NEM_PAGE_PROT_WRITE)
4860 {
4861# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
4862 int rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhysDst,
4863 HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE
4864 | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
4865 AssertRC(rc);
4866 if (RT_SUCCESS(rc))
4867 {
4868 *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
4869 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
4870 uint32_t cMappedPages = ASMAtomicIncU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4871 Log5(("NEM GPA mapped/set: %RGp %s (was %s, cMappedPages=%u)\n",
4872 GCPhysDst, g_apszPageStates[u2NewState], g_apszPageStates[u2OldState], cMappedPages));
4873 return VINF_SUCCESS;
4874 }
4875 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
4876 LogRel(("nemHCNativeSetPhysPage/writable: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4877 return rc;
4878# else
4879 void *pvPage;
4880 int rc = nemR3NativeGCPhys2R3PtrWriteable(pVM, GCPhysSrc, &pvPage);
4881 if (RT_SUCCESS(rc))
4882 {
4883 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, pvPage, GCPhysDst, X86_PAGE_SIZE,
4884 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute | WHvMapGpaRangeFlagWrite);
4885 if (SUCCEEDED(hrc))
4886 {
4887 *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
4888 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
4889 uint32_t cMappedPages = ASMAtomicIncU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4890 Log5(("NEM GPA mapped/set: %RGp %s (was %s, cMappedPages=%u)\n",
4891 GCPhysDst, g_apszPageStates[u2NewState], g_apszPageStates[u2OldState], cMappedPages));
4892 return VINF_SUCCESS;
4893 }
4894 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
4895 LogRel(("nemHCNativeSetPhysPage/writable: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
4896 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
4897 return VERR_NEM_INIT_FAILED;
4898 }
4899 LogRel(("nemHCNativeSetPhysPage/writable: GCPhysSrc=%RGp rc=%Rrc\n", GCPhysSrc, rc));
4900 return rc;
4901# endif
4902 }
4903
4904 if (fPageProt & NEM_PAGE_PROT_READ)
4905 {
4906# ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
4907 int rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhysDst,
4908 HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
4909 AssertRC(rc);
4910 if (RT_SUCCESS(rc))
4911 {
4912 *pu2State = NEM_WIN_PAGE_STATE_READABLE;
4913 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
4914 uint32_t cMappedPages = ASMAtomicIncU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4915 Log5(("NEM GPA mapped/set: %RGp %s (was %s, cMappedPages=%u)\n",
4916 GCPhysDst, g_apszPageStates[u2NewState], g_apszPageStates[u2OldState], cMappedPages));
4917 return VINF_SUCCESS;
4918 }
4919 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
4920 LogRel(("nemHCNativeSetPhysPage/readonly: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4921 return rc;
4922# else
4923 const void *pvPage;
4924 int rc = nemR3NativeGCPhys2R3PtrReadOnly(pVM, GCPhysSrc, &pvPage);
4925 if (RT_SUCCESS(rc))
4926 {
4927 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfMapGpaRangePage, a);
4928 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, (void *)pvPage, GCPhysDst, X86_PAGE_SIZE,
4929 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute);
4930 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfMapGpaRangePage, a);
4931 if (SUCCEEDED(hrc))
4932 {
4933 *pu2State = NEM_WIN_PAGE_STATE_READABLE;
4934 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
4935 uint32_t cMappedPages = ASMAtomicIncU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4936 Log5(("NEM GPA mapped/set: %RGp %s (was %s, cMappedPages=%u)\n",
4937 GCPhysDst, g_apszPageStates[u2NewState], g_apszPageStates[u2OldState], cMappedPages));
4938 return VINF_SUCCESS;
4939 }
4940 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
4941 LogRel(("nemHCNativeSetPhysPage/readonly: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
4942 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
4943 return VERR_NEM_INIT_FAILED;
4944 }
4945 LogRel(("nemHCNativeSetPhysPage/readonly: GCPhysSrc=%RGp rc=%Rrc\n", GCPhysSrc, rc));
4946 return rc;
4947# endif
4948 }
4949
4950 /* We already unmapped it above. */
4951 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4952 return VINF_SUCCESS;
4953# endif /* !NEM_WIN_USE_HYPERCALLS_FOR_PAGES */
4954}
4955#endif /* defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES) || defined(IN_RING3) */
4956
4957
4958NEM_TMPL_STATIC int nemHCJustUnmapPageFromHyperV(PVMCC pVM, RTGCPHYS GCPhysDst, uint8_t *pu2State)
4959{
4960 if (*pu2State <= NEM_WIN_PAGE_STATE_UNMAPPED)
4961 {
4962 Log5(("nemHCJustUnmapPageFromHyperV: %RGp == unmapped\n", GCPhysDst));
4963 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4964 return VINF_SUCCESS;
4965 }
4966
4967#if defined(NEM_WIN_USE_HYPERCALLS_FOR_PAGES)
4968 PVMCPUCC pVCpu = VMMGetCpu(pVM);
4969 int rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhysDst);
4970 AssertRC(rc);
4971 if (RT_SUCCESS(rc))
4972 {
4973 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
4974 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4975 Log5(("NEM GPA unmapped/just: %RGp (was %s, cMappedPages=%u)\n", GCPhysDst, g_apszPageStates[*pu2State], cMappedPages));
4976 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4977 return VINF_SUCCESS;
4978 }
4979 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
4980 LogRel(("nemHCJustUnmapPageFromHyperV/unmap: GCPhysDst=%RGp rc=%Rrc\n", GCPhysDst, rc));
4981 return rc;
4982
4983#elif defined(IN_RING3)
4984 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
4985 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhysDst & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK, X86_PAGE_SIZE);
4986 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
4987 if (SUCCEEDED(hrc))
4988 {
4989 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
4990 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
4991 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
4992 Log5(("nemHCJustUnmapPageFromHyperV: %RGp => unmapped (total %u)\n", GCPhysDst, cMappedPages));
4993 return VINF_SUCCESS;
4994 }
4995 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
4996 LogRel(("nemHCJustUnmapPageFromHyperV(%RGp): failed! hrc=%Rhrc (%#x) Last=%#x/%u\n",
4997 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
4998 return VERR_NEM_IPE_6;
4999#else
5000 RT_NOREF(pVM, GCPhysDst, pu2State);
5001 LogRel(("nemHCJustUnmapPageFromHyperV(%RGp): Why are we here?!?\n", GCPhysDst));
5002 return VERR_NEM_IPE_6;
5003#endif
5004}
5005
5006
5007int nemHCNativeNotifyPhysPageAllocated(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys, uint32_t fPageProt,
5008 PGMPAGETYPE enmType, uint8_t *pu2State)
5009{
5010 Log5(("nemHCNativeNotifyPhysPageAllocated: %RGp HCPhys=%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
5011 GCPhys, HCPhys, fPageProt, enmType, *pu2State));
5012 RT_NOREF_PV(HCPhys); RT_NOREF_PV(enmType);
5013
5014 int rc;
5015#ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
5016 PVMCPUCC pVCpu = VMMGetCpu(pVM);
5017# ifdef NEM_WIN_WITH_A20
5018 if ( pVM->nem.s.fA20Enabled
5019 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
5020# endif
5021 rc = nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
5022# ifdef NEM_WIN_WITH_A20
5023 else
5024 {
5025 /* To keep effort at a minimum, we unmap the HMA page alias and resync it lazily when needed. */
5026 rc = nemHCWinUnmapPageForA20Gate(pVM, pVCpu, GCPhys | RT_BIT_32(20));
5027 if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys) && RT_SUCCESS(rc))
5028 rc = nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
5029
5030 }
5031# endif
5032#else
5033 RT_NOREF_PV(fPageProt);
5034# ifdef NEM_WIN_WITH_A20
5035 if ( pVM->nem.s.fA20Enabled
5036 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
5037# endif
5038 rc = nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
5039# ifdef NEM_WIN_WITH_A20
5040 else if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
5041 rc = nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
5042 else
5043 rc = VINF_SUCCESS; /* ignore since we've got the alias page at this address. */
5044# endif
5045#endif
5046 return rc;
5047}
5048
5049
5050VMM_INT_DECL(void) NEMHCNotifyPhysPageProtChanged(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys, RTR3PTR pvR3, uint32_t fPageProt,
5051 PGMPAGETYPE enmType, uint8_t *pu2State)
5052{
5053 Log5(("NEMHCNotifyPhysPageProtChanged: %RGp HCPhys=%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
5054 GCPhys, HCPhys, fPageProt, enmType, *pu2State));
5055 Assert(VM_IS_NEM_ENABLED(pVM));
5056 RT_NOREF(HCPhys, enmType, pvR3);
5057
5058#ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
5059 PVMCPUCC pVCpu = VMMGetCpu(pVM);
5060# ifdef NEM_WIN_WITH_A20
5061 if ( pVM->nem.s.fA20Enabled
5062 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
5063# endif
5064 nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, false /*fBackingChanged*/);
5065# ifdef NEM_WIN_WITH_A20
5066 else
5067 {
5068 /* To keep effort at a minimum, we unmap the HMA page alias and resync it lazily when needed. */
5069 nemHCWinUnmapPageForA20Gate(pVM, pVCpu, GCPhys | RT_BIT_32(20));
5070 if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
5071 nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, false /*fBackingChanged*/);
5072 }
5073# endif
5074#else
5075 RT_NOREF_PV(fPageProt);
5076# ifdef NEM_WIN_WITH_A20
5077 if ( pVM->nem.s.fA20Enabled
5078 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
5079# endif
5080 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
5081# ifdef NEM_WIN_WITH_A20
5082 else if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
5083 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
5084 /* else: ignore since we've got the alias page at this address. */
5085# endif
5086#endif
5087}
5088
5089
5090VMM_INT_DECL(void) NEMHCNotifyPhysPageChanged(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhysPrev, RTHCPHYS HCPhysNew,
5091 RTR3PTR pvNewR3, uint32_t fPageProt, PGMPAGETYPE enmType, uint8_t *pu2State)
5092{
5093 Log5(("nemHCNativeNotifyPhysPageChanged: %RGp HCPhys=%RHp->%RHp pvNewR3=%p fPageProt=%#x enmType=%d *pu2State=%d\n",
5094 GCPhys, HCPhysPrev, HCPhysNew, pvNewR3, fPageProt, enmType, *pu2State));
5095 Assert(VM_IS_NEM_ENABLED(pVM));
5096 RT_NOREF(HCPhysPrev, HCPhysNew, pvNewR3, enmType);
5097
5098#ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
5099 PVMCPUCC pVCpu = VMMGetCpu(pVM);
5100# ifdef NEM_WIN_WITH_A20
5101 if ( pVM->nem.s.fA20Enabled
5102 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
5103# endif
5104 nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
5105# ifdef NEM_WIN_WITH_A20
5106 else
5107 {
5108 /* To keep effort at a minimum, we unmap the HMA page alias and resync it lazily when needed. */
5109 nemHCWinUnmapPageForA20Gate(pVM, pVCpu, GCPhys | RT_BIT_32(20));
5110 if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
5111 nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
5112 }
5113# endif
5114#else
5115 RT_NOREF_PV(fPageProt);
5116# ifdef NEM_WIN_WITH_A20
5117 if ( pVM->nem.s.fA20Enabled
5118 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
5119# endif
5120 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
5121# ifdef NEM_WIN_WITH_A20
5122 else if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
5123 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
5124 /* else: ignore since we've got the alias page at this address. */
5125# endif
5126#endif
5127}
5128
5129
5130/**
5131 * Returns features supported by the NEM backend.
5132 *
5133 * @returns Flags of features supported by the native NEM backend.
5134 * @param pVM The cross context VM structure.
5135 */
5136VMM_INT_DECL(uint32_t) NEMHCGetFeatures(PVMCC pVM)
5137{
5138 RT_NOREF(pVM);
5139 /** @todo Make use of the WHvGetVirtualProcessorXsaveState/WHvSetVirtualProcessorXsaveState
5140 * interface added in 2019 to enable passthrough of xsave/xrstor (and depending) features to the guest. */
5141 /** @todo Is NEM_FEAT_F_FULL_GST_EXEC always true? */
5142 return NEM_FEAT_F_NESTED_PAGING | NEM_FEAT_F_FULL_GST_EXEC;
5143}
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