VirtualBox

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

Last change on this file since 91247 was 88745, checked in by vboxsync, 4 years ago

NEM/win: Improved APIC interrupt delivery (see bugref:9993).

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

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