VirtualBox

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

Last change on this file since 106212 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 141.1 KB
Line 
1/* $Id: NEMAllNativeTemplate-win.cpp.h 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * NEM - Native execution manager, Windows code template ring-0/3.
4 */
5
6/*
7 * Copyright (C) 2018-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#ifndef IN_RING3
29# error "This is ring-3 only now"
30#endif
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36/** Copy back a segment from hyper-V. */
37#define NEM_WIN_COPY_BACK_SEG(a_Dst, a_Src) \
38 do { \
39 (a_Dst).u64Base = (a_Src).Base; \
40 (a_Dst).u32Limit = (a_Src).Limit; \
41 (a_Dst).ValidSel = (a_Dst).Sel = (a_Src).Selector; \
42 (a_Dst).Attr.u = (a_Src).Attributes; \
43 (a_Dst).fFlags = CPUMSELREG_FLAGS_VALID; \
44 } while (0)
45
46/** @def NEMWIN_ASSERT_MSG_REG_VAL
47 * Asserts the correctness of a register value in a message/context.
48 */
49#if 0
50# define NEMWIN_NEED_GET_REGISTER
51# define NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, a_Expr, a_Msg) \
52 do { \
53 WHV_REGISTER_VALUE TmpVal; \
54 nemR3WinGetRegister(a_pVCpu, a_enmReg, &TmpVal); \
55 AssertMsg(a_Expr, a_Msg); \
56 } while (0)
57#else
58# define NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, a_Expr, a_Msg) do { } while (0)
59#endif
60
61/** @def NEMWIN_ASSERT_MSG_REG_VAL
62 * Asserts the correctness of a 64-bit register value in a message/context.
63 */
64#define NEMWIN_ASSERT_MSG_REG_VAL64(a_pVCpu, a_enmReg, a_u64Val) \
65 NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, (a_u64Val) == TmpVal.Reg64, \
66 (#a_u64Val "=%#RX64, expected %#RX64\n", (a_u64Val), TmpVal.Reg64))
67/** @def NEMWIN_ASSERT_MSG_REG_VAL
68 * Asserts the correctness of a segment register value in a message/context.
69 */
70#define NEMWIN_ASSERT_MSG_REG_SEG(a_pVCpu, a_enmReg, a_SReg) \
71 NEMWIN_ASSERT_MSG_REG_VAL(a_pVCpu, a_enmReg, \
72 (a_SReg).Base == TmpVal.Segment.Base \
73 && (a_SReg).Limit == TmpVal.Segment.Limit \
74 && (a_SReg).Selector == TmpVal.Segment.Selector \
75 && (a_SReg).Attributes == TmpVal.Segment.Attributes, \
76 ( #a_SReg "=%#RX16 {%#RX64 LB %#RX32,%#RX16} expected %#RX16 {%#RX64 LB %#RX32,%#RX16}\n", \
77 (a_SReg).Selector, (a_SReg).Base, (a_SReg).Limit, (a_SReg).Attributes, \
78 TmpVal.Segment.Selector, TmpVal.Segment.Base, TmpVal.Segment.Limit, TmpVal.Segment.Attributes))
79
80
81#ifndef NTDDI_WIN10_19H1
82# define NTDDI_WIN10_19H1 0x0a000007
83#endif
84
85/** WHvRegisterPendingEvent0 was renamed to WHvRegisterPendingEvent between
86 * SDK 17134 and 18362. */
87#if WDK_NTDDI_VERSION < NTDDI_WIN10_19H1
88# define WHvRegisterPendingEvent WHvRegisterPendingEvent0
89#endif
90
91
92/*********************************************************************************************************************************
93* Global Variables *
94*********************************************************************************************************************************/
95/** NEM_WIN_PAGE_STATE_XXX names. */
96NEM_TMPL_STATIC const char * const g_apszPageStates[4] = { "not-set", "unmapped", "readable", "writable" };
97
98/** HV_INTERCEPT_ACCESS_TYPE names. */
99static const char * const g_apszHvInterceptAccessTypes[4] = { "read", "write", "exec", "!undefined!" };
100
101
102/*********************************************************************************************************************************
103* Internal Functions *
104*********************************************************************************************************************************/
105NEM_TMPL_STATIC int nemHCNativeSetPhysPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst,
106 uint32_t fPageProt, uint8_t *pu2State, bool fBackingChanged);
107
108
109
110NEM_TMPL_STATIC int nemHCWinCopyStateToHyperV(PVMCC pVM, PVMCPUCC pVCpu)
111{
112 /*
113 * The following is very similar to what nemR0WinExportState() does.
114 */
115 WHV_REGISTER_NAME aenmNames[128];
116 WHV_REGISTER_VALUE aValues[128];
117
118 uint64_t const fWhat = ~pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK);
119 if ( !fWhat
120 && pVCpu->nem.s.fCurrentInterruptWindows == pVCpu->nem.s.fDesiredInterruptWindows)
121 return VINF_SUCCESS;
122 uintptr_t iReg = 0;
123
124#define ADD_REG64(a_enmName, a_uValue) do { \
125 aenmNames[iReg] = (a_enmName); \
126 aValues[iReg].Reg128.High64 = 0; \
127 aValues[iReg].Reg64 = (a_uValue); \
128 iReg++; \
129 } while (0)
130#define ADD_REG128(a_enmName, a_uValueLo, a_uValueHi) do { \
131 aenmNames[iReg] = (a_enmName); \
132 aValues[iReg].Reg128.Low64 = (a_uValueLo); \
133 aValues[iReg].Reg128.High64 = (a_uValueHi); \
134 iReg++; \
135 } while (0)
136
137 /* GPRs */
138 if (fWhat & CPUMCTX_EXTRN_GPRS_MASK)
139 {
140 if (fWhat & CPUMCTX_EXTRN_RAX)
141 ADD_REG64(WHvX64RegisterRax, pVCpu->cpum.GstCtx.rax);
142 if (fWhat & CPUMCTX_EXTRN_RCX)
143 ADD_REG64(WHvX64RegisterRcx, pVCpu->cpum.GstCtx.rcx);
144 if (fWhat & CPUMCTX_EXTRN_RDX)
145 ADD_REG64(WHvX64RegisterRdx, pVCpu->cpum.GstCtx.rdx);
146 if (fWhat & CPUMCTX_EXTRN_RBX)
147 ADD_REG64(WHvX64RegisterRbx, pVCpu->cpum.GstCtx.rbx);
148 if (fWhat & CPUMCTX_EXTRN_RSP)
149 ADD_REG64(WHvX64RegisterRsp, pVCpu->cpum.GstCtx.rsp);
150 if (fWhat & CPUMCTX_EXTRN_RBP)
151 ADD_REG64(WHvX64RegisterRbp, pVCpu->cpum.GstCtx.rbp);
152 if (fWhat & CPUMCTX_EXTRN_RSI)
153 ADD_REG64(WHvX64RegisterRsi, pVCpu->cpum.GstCtx.rsi);
154 if (fWhat & CPUMCTX_EXTRN_RDI)
155 ADD_REG64(WHvX64RegisterRdi, pVCpu->cpum.GstCtx.rdi);
156 if (fWhat & CPUMCTX_EXTRN_R8_R15)
157 {
158 ADD_REG64(WHvX64RegisterR8, pVCpu->cpum.GstCtx.r8);
159 ADD_REG64(WHvX64RegisterR9, pVCpu->cpum.GstCtx.r9);
160 ADD_REG64(WHvX64RegisterR10, pVCpu->cpum.GstCtx.r10);
161 ADD_REG64(WHvX64RegisterR11, pVCpu->cpum.GstCtx.r11);
162 ADD_REG64(WHvX64RegisterR12, pVCpu->cpum.GstCtx.r12);
163 ADD_REG64(WHvX64RegisterR13, pVCpu->cpum.GstCtx.r13);
164 ADD_REG64(WHvX64RegisterR14, pVCpu->cpum.GstCtx.r14);
165 ADD_REG64(WHvX64RegisterR15, pVCpu->cpum.GstCtx.r15);
166 }
167 }
168
169 /* RIP & Flags */
170 if (fWhat & CPUMCTX_EXTRN_RIP)
171 ADD_REG64(WHvX64RegisterRip, pVCpu->cpum.GstCtx.rip);
172 if (fWhat & CPUMCTX_EXTRN_RFLAGS)
173 ADD_REG64(WHvX64RegisterRflags, pVCpu->cpum.GstCtx.rflags.u);
174
175 /* Segments */
176#define ADD_SEG(a_enmName, a_SReg) \
177 do { \
178 aenmNames[iReg] = a_enmName; \
179 aValues[iReg].Segment.Base = (a_SReg).u64Base; \
180 aValues[iReg].Segment.Limit = (a_SReg).u32Limit; \
181 aValues[iReg].Segment.Selector = (a_SReg).Sel; \
182 aValues[iReg].Segment.Attributes = (a_SReg).Attr.u; \
183 iReg++; \
184 } while (0)
185 if (fWhat & CPUMCTX_EXTRN_SREG_MASK)
186 {
187 if (fWhat & CPUMCTX_EXTRN_ES)
188 ADD_SEG(WHvX64RegisterEs, pVCpu->cpum.GstCtx.es);
189 if (fWhat & CPUMCTX_EXTRN_CS)
190 ADD_SEG(WHvX64RegisterCs, pVCpu->cpum.GstCtx.cs);
191 if (fWhat & CPUMCTX_EXTRN_SS)
192 ADD_SEG(WHvX64RegisterSs, pVCpu->cpum.GstCtx.ss);
193 if (fWhat & CPUMCTX_EXTRN_DS)
194 ADD_SEG(WHvX64RegisterDs, pVCpu->cpum.GstCtx.ds);
195 if (fWhat & CPUMCTX_EXTRN_FS)
196 ADD_SEG(WHvX64RegisterFs, pVCpu->cpum.GstCtx.fs);
197 if (fWhat & CPUMCTX_EXTRN_GS)
198 ADD_SEG(WHvX64RegisterGs, pVCpu->cpum.GstCtx.gs);
199 }
200
201 /* Descriptor tables & task segment. */
202 if (fWhat & CPUMCTX_EXTRN_TABLE_MASK)
203 {
204 if (fWhat & CPUMCTX_EXTRN_LDTR)
205 ADD_SEG(WHvX64RegisterLdtr, pVCpu->cpum.GstCtx.ldtr);
206 if (fWhat & CPUMCTX_EXTRN_TR)
207 ADD_SEG(WHvX64RegisterTr, pVCpu->cpum.GstCtx.tr);
208 if (fWhat & CPUMCTX_EXTRN_IDTR)
209 {
210 aenmNames[iReg] = WHvX64RegisterIdtr;
211 aValues[iReg].Table.Limit = pVCpu->cpum.GstCtx.idtr.cbIdt;
212 aValues[iReg].Table.Base = pVCpu->cpum.GstCtx.idtr.pIdt;
213 iReg++;
214 }
215 if (fWhat & CPUMCTX_EXTRN_GDTR)
216 {
217 aenmNames[iReg] = WHvX64RegisterGdtr;
218 aValues[iReg].Table.Limit = pVCpu->cpum.GstCtx.gdtr.cbGdt;
219 aValues[iReg].Table.Base = pVCpu->cpum.GstCtx.gdtr.pGdt;
220 iReg++;
221 }
222 }
223
224 /* Control registers. */
225 if (fWhat & CPUMCTX_EXTRN_CR_MASK)
226 {
227 if (fWhat & CPUMCTX_EXTRN_CR0)
228 ADD_REG64(WHvX64RegisterCr0, pVCpu->cpum.GstCtx.cr0);
229 if (fWhat & CPUMCTX_EXTRN_CR2)
230 ADD_REG64(WHvX64RegisterCr2, pVCpu->cpum.GstCtx.cr2);
231 if (fWhat & CPUMCTX_EXTRN_CR3)
232 ADD_REG64(WHvX64RegisterCr3, pVCpu->cpum.GstCtx.cr3);
233 if (fWhat & CPUMCTX_EXTRN_CR4)
234 ADD_REG64(WHvX64RegisterCr4, pVCpu->cpum.GstCtx.cr4);
235 }
236 if (fWhat & CPUMCTX_EXTRN_APIC_TPR)
237 ADD_REG64(WHvX64RegisterCr8, CPUMGetGuestCR8(pVCpu));
238
239 /* Debug registers. */
240/** @todo fixme. Figure out what the hyper-v version of KVM_SET_GUEST_DEBUG would be. */
241 if (fWhat & CPUMCTX_EXTRN_DR0_DR3)
242 {
243 ADD_REG64(WHvX64RegisterDr0, pVCpu->cpum.GstCtx.dr[0]); // CPUMGetHyperDR0(pVCpu));
244 ADD_REG64(WHvX64RegisterDr1, pVCpu->cpum.GstCtx.dr[1]); // CPUMGetHyperDR1(pVCpu));
245 ADD_REG64(WHvX64RegisterDr2, pVCpu->cpum.GstCtx.dr[2]); // CPUMGetHyperDR2(pVCpu));
246 ADD_REG64(WHvX64RegisterDr3, pVCpu->cpum.GstCtx.dr[3]); // CPUMGetHyperDR3(pVCpu));
247 }
248 if (fWhat & CPUMCTX_EXTRN_DR6)
249 ADD_REG64(WHvX64RegisterDr6, pVCpu->cpum.GstCtx.dr[6]); // CPUMGetHyperDR6(pVCpu));
250 if (fWhat & CPUMCTX_EXTRN_DR7)
251 ADD_REG64(WHvX64RegisterDr7, pVCpu->cpum.GstCtx.dr[7]); // CPUMGetHyperDR7(pVCpu));
252
253 /* Floating point state. */
254 if (fWhat & CPUMCTX_EXTRN_X87)
255 {
256 ADD_REG128(WHvX64RegisterFpMmx0, pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[1]);
257 ADD_REG128(WHvX64RegisterFpMmx1, pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[1]);
258 ADD_REG128(WHvX64RegisterFpMmx2, pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[1]);
259 ADD_REG128(WHvX64RegisterFpMmx3, pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[1]);
260 ADD_REG128(WHvX64RegisterFpMmx4, pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[1]);
261 ADD_REG128(WHvX64RegisterFpMmx5, pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[1]);
262 ADD_REG128(WHvX64RegisterFpMmx6, pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[1]);
263 ADD_REG128(WHvX64RegisterFpMmx7, pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[1]);
264
265 aenmNames[iReg] = WHvX64RegisterFpControlStatus;
266 aValues[iReg].FpControlStatus.FpControl = pVCpu->cpum.GstCtx.XState.x87.FCW;
267 aValues[iReg].FpControlStatus.FpStatus = pVCpu->cpum.GstCtx.XState.x87.FSW;
268 aValues[iReg].FpControlStatus.FpTag = pVCpu->cpum.GstCtx.XState.x87.FTW;
269 aValues[iReg].FpControlStatus.Reserved = pVCpu->cpum.GstCtx.XState.x87.FTW >> 8;
270 aValues[iReg].FpControlStatus.LastFpOp = pVCpu->cpum.GstCtx.XState.x87.FOP;
271 aValues[iReg].FpControlStatus.LastFpRip = (pVCpu->cpum.GstCtx.XState.x87.FPUIP)
272 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.CS << 32)
273 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.Rsrvd1 << 48);
274 iReg++;
275
276 aenmNames[iReg] = WHvX64RegisterXmmControlStatus;
277 aValues[iReg].XmmControlStatus.LastFpRdp = (pVCpu->cpum.GstCtx.XState.x87.FPUDP)
278 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.DS << 32)
279 | ((uint64_t)pVCpu->cpum.GstCtx.XState.x87.Rsrvd2 << 48);
280 aValues[iReg].XmmControlStatus.XmmStatusControl = pVCpu->cpum.GstCtx.XState.x87.MXCSR;
281 aValues[iReg].XmmControlStatus.XmmStatusControlMask = pVCpu->cpum.GstCtx.XState.x87.MXCSR_MASK; /** @todo ??? (Isn't this an output field?) */
282 iReg++;
283 }
284
285 /* Vector state. */
286 if (fWhat & CPUMCTX_EXTRN_SSE_AVX)
287 {
288 ADD_REG128(WHvX64RegisterXmm0, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Hi);
289 ADD_REG128(WHvX64RegisterXmm1, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Hi);
290 ADD_REG128(WHvX64RegisterXmm2, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Hi);
291 ADD_REG128(WHvX64RegisterXmm3, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Hi);
292 ADD_REG128(WHvX64RegisterXmm4, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Hi);
293 ADD_REG128(WHvX64RegisterXmm5, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Hi);
294 ADD_REG128(WHvX64RegisterXmm6, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Hi);
295 ADD_REG128(WHvX64RegisterXmm7, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Hi);
296 ADD_REG128(WHvX64RegisterXmm8, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Hi);
297 ADD_REG128(WHvX64RegisterXmm9, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Hi);
298 ADD_REG128(WHvX64RegisterXmm10, pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Hi);
299 ADD_REG128(WHvX64RegisterXmm11, pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Hi);
300 ADD_REG128(WHvX64RegisterXmm12, pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Hi);
301 ADD_REG128(WHvX64RegisterXmm13, pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Hi);
302 ADD_REG128(WHvX64RegisterXmm14, pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Hi);
303 ADD_REG128(WHvX64RegisterXmm15, pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Hi);
304 }
305
306 /* MSRs */
307 // WHvX64RegisterTsc - don't touch
308 if (fWhat & CPUMCTX_EXTRN_EFER)
309 ADD_REG64(WHvX64RegisterEfer, pVCpu->cpum.GstCtx.msrEFER);
310 if (fWhat & CPUMCTX_EXTRN_KERNEL_GS_BASE)
311 ADD_REG64(WHvX64RegisterKernelGsBase, pVCpu->cpum.GstCtx.msrKERNELGSBASE);
312 if (fWhat & CPUMCTX_EXTRN_SYSENTER_MSRS)
313 {
314 ADD_REG64(WHvX64RegisterSysenterCs, pVCpu->cpum.GstCtx.SysEnter.cs);
315 ADD_REG64(WHvX64RegisterSysenterEip, pVCpu->cpum.GstCtx.SysEnter.eip);
316 ADD_REG64(WHvX64RegisterSysenterEsp, pVCpu->cpum.GstCtx.SysEnter.esp);
317 }
318 if (fWhat & CPUMCTX_EXTRN_SYSCALL_MSRS)
319 {
320 ADD_REG64(WHvX64RegisterStar, pVCpu->cpum.GstCtx.msrSTAR);
321 ADD_REG64(WHvX64RegisterLstar, pVCpu->cpum.GstCtx.msrLSTAR);
322 ADD_REG64(WHvX64RegisterCstar, pVCpu->cpum.GstCtx.msrCSTAR);
323 ADD_REG64(WHvX64RegisterSfmask, pVCpu->cpum.GstCtx.msrSFMASK);
324 }
325 if (fWhat & (CPUMCTX_EXTRN_TSC_AUX | CPUMCTX_EXTRN_OTHER_MSRS))
326 {
327 PCPUMCTXMSRS const pCtxMsrs = CPUMQueryGuestCtxMsrsPtr(pVCpu);
328 if (fWhat & CPUMCTX_EXTRN_TSC_AUX)
329 ADD_REG64(WHvX64RegisterTscAux, pCtxMsrs->msr.TscAux);
330 if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS)
331 {
332 ADD_REG64(WHvX64RegisterApicBase, APICGetBaseMsrNoCheck(pVCpu));
333 ADD_REG64(WHvX64RegisterPat, pVCpu->cpum.GstCtx.msrPAT);
334#if 0 /** @todo check if WHvX64RegisterMsrMtrrCap works here... */
335 ADD_REG64(WHvX64RegisterMsrMtrrCap, CPUMGetGuestIa32MtrrCap(pVCpu));
336#endif
337 ADD_REG64(WHvX64RegisterMsrMtrrDefType, pCtxMsrs->msr.MtrrDefType);
338 ADD_REG64(WHvX64RegisterMsrMtrrFix64k00000, pCtxMsrs->msr.MtrrFix64K_00000);
339 ADD_REG64(WHvX64RegisterMsrMtrrFix16k80000, pCtxMsrs->msr.MtrrFix16K_80000);
340 ADD_REG64(WHvX64RegisterMsrMtrrFix16kA0000, pCtxMsrs->msr.MtrrFix16K_A0000);
341 ADD_REG64(WHvX64RegisterMsrMtrrFix4kC0000, pCtxMsrs->msr.MtrrFix4K_C0000);
342 ADD_REG64(WHvX64RegisterMsrMtrrFix4kC8000, pCtxMsrs->msr.MtrrFix4K_C8000);
343 ADD_REG64(WHvX64RegisterMsrMtrrFix4kD0000, pCtxMsrs->msr.MtrrFix4K_D0000);
344 ADD_REG64(WHvX64RegisterMsrMtrrFix4kD8000, pCtxMsrs->msr.MtrrFix4K_D8000);
345 ADD_REG64(WHvX64RegisterMsrMtrrFix4kE0000, pCtxMsrs->msr.MtrrFix4K_E0000);
346 ADD_REG64(WHvX64RegisterMsrMtrrFix4kE8000, pCtxMsrs->msr.MtrrFix4K_E8000);
347 ADD_REG64(WHvX64RegisterMsrMtrrFix4kF0000, pCtxMsrs->msr.MtrrFix4K_F0000);
348 ADD_REG64(WHvX64RegisterMsrMtrrFix4kF8000, pCtxMsrs->msr.MtrrFix4K_F8000);
349#if 0 /** @todo these registers aren't available? Might explain something.. .*/
350 const CPUMCPUVENDOR enmCpuVendor = CPUMGetHostCpuVendor(pVM);
351 if (enmCpuVendor != CPUMCPUVENDOR_AMD)
352 {
353 ADD_REG64(HvX64RegisterIa32MiscEnable, pCtxMsrs->msr.MiscEnable);
354 ADD_REG64(HvX64RegisterIa32FeatureControl, CPUMGetGuestIa32FeatureControl(pVCpu));
355 }
356#endif
357 }
358 }
359
360 /* event injection (clear it). */
361 if (fWhat & CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT)
362 ADD_REG64(WHvRegisterPendingInterruption, 0);
363
364 /* Interruptibility state. This can get a little complicated since we get
365 half of the state via HV_X64_VP_EXECUTION_STATE. */
366 if ( (fWhat & (CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI))
367 == (CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI) )
368 {
369 ADD_REG64(WHvRegisterInterruptState, 0);
370 if (CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx))
371 aValues[iReg - 1].InterruptState.InterruptShadow = 1;
372 aValues[iReg - 1].InterruptState.NmiMasked = CPUMAreInterruptsInhibitedByNmi(&pVCpu->cpum.GstCtx);
373 }
374 else if (fWhat & CPUMCTX_EXTRN_INHIBIT_INT)
375 {
376 if ( pVCpu->nem.s.fLastInterruptShadow
377 || CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx))
378 {
379 ADD_REG64(WHvRegisterInterruptState, 0);
380 if (CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx))
381 aValues[iReg - 1].InterruptState.InterruptShadow = 1;
382 /** @todo Retrieve NMI state, currently assuming it's zero. (yes this may happen on I/O) */
383 //if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_BLOCK_NMIS))
384 // aValues[iReg - 1].InterruptState.NmiMasked = 1;
385 }
386 }
387 else
388 Assert(!(fWhat & CPUMCTX_EXTRN_INHIBIT_NMI));
389
390 /* Interrupt windows. Always set if active as Hyper-V seems to be forgetful. */
391 uint8_t const fDesiredIntWin = pVCpu->nem.s.fDesiredInterruptWindows;
392 if ( fDesiredIntWin
393 || pVCpu->nem.s.fCurrentInterruptWindows != fDesiredIntWin)
394 {
395 pVCpu->nem.s.fCurrentInterruptWindows = pVCpu->nem.s.fDesiredInterruptWindows;
396 Log8(("Setting WHvX64RegisterDeliverabilityNotifications, fDesiredIntWin=%X\n", fDesiredIntWin));
397 ADD_REG64(WHvX64RegisterDeliverabilityNotifications, fDesiredIntWin);
398 Assert(aValues[iReg - 1].DeliverabilityNotifications.NmiNotification == RT_BOOL(fDesiredIntWin & NEM_WIN_INTW_F_NMI));
399 Assert(aValues[iReg - 1].DeliverabilityNotifications.InterruptNotification == RT_BOOL(fDesiredIntWin & NEM_WIN_INTW_F_REGULAR));
400 Assert(aValues[iReg - 1].DeliverabilityNotifications.InterruptPriority == (unsigned)((fDesiredIntWin & NEM_WIN_INTW_F_PRIO_MASK) >> NEM_WIN_INTW_F_PRIO_SHIFT));
401 }
402
403 /// @todo WHvRegisterPendingEvent
404
405#undef ADD_REG64
406#undef ADD_REG128
407#undef ADD_SEG
408
409 /*
410 * Set the registers.
411 */
412 Assert(iReg < RT_ELEMENTS(aValues));
413 Assert(iReg < RT_ELEMENTS(aenmNames));
414#ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
415 Log12(("Calling WHvSetVirtualProcessorRegisters(%p, %u, %p, %u, %p)\n",
416 pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, iReg, aValues));
417#endif
418 HRESULT hrc = WHvSetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, iReg, aValues);
419 if (SUCCEEDED(hrc))
420 {
421 pVCpu->cpum.GstCtx.fExtrn |= CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK | CPUMCTX_EXTRN_KEEPER_NEM;
422 return VINF_SUCCESS;
423 }
424 AssertLogRelMsgFailed(("WHvSetVirtualProcessorRegisters(%p, %u,,%u,) -> %Rhrc (Last=%#x/%u)\n",
425 pVM->nem.s.hPartition, pVCpu->idCpu, iReg,
426 hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
427 return VERR_INTERNAL_ERROR;
428}
429
430
431NEM_TMPL_STATIC int nemHCWinCopyStateFromHyperV(PVMCC pVM, PVMCPUCC pVCpu, uint64_t fWhat)
432{
433 WHV_REGISTER_NAME aenmNames[128];
434
435 fWhat &= pVCpu->cpum.GstCtx.fExtrn;
436 uintptr_t iReg = 0;
437
438 /* GPRs */
439 if (fWhat & CPUMCTX_EXTRN_GPRS_MASK)
440 {
441 if (fWhat & CPUMCTX_EXTRN_RAX)
442 aenmNames[iReg++] = WHvX64RegisterRax;
443 if (fWhat & CPUMCTX_EXTRN_RCX)
444 aenmNames[iReg++] = WHvX64RegisterRcx;
445 if (fWhat & CPUMCTX_EXTRN_RDX)
446 aenmNames[iReg++] = WHvX64RegisterRdx;
447 if (fWhat & CPUMCTX_EXTRN_RBX)
448 aenmNames[iReg++] = WHvX64RegisterRbx;
449 if (fWhat & CPUMCTX_EXTRN_RSP)
450 aenmNames[iReg++] = WHvX64RegisterRsp;
451 if (fWhat & CPUMCTX_EXTRN_RBP)
452 aenmNames[iReg++] = WHvX64RegisterRbp;
453 if (fWhat & CPUMCTX_EXTRN_RSI)
454 aenmNames[iReg++] = WHvX64RegisterRsi;
455 if (fWhat & CPUMCTX_EXTRN_RDI)
456 aenmNames[iReg++] = WHvX64RegisterRdi;
457 if (fWhat & CPUMCTX_EXTRN_R8_R15)
458 {
459 aenmNames[iReg++] = WHvX64RegisterR8;
460 aenmNames[iReg++] = WHvX64RegisterR9;
461 aenmNames[iReg++] = WHvX64RegisterR10;
462 aenmNames[iReg++] = WHvX64RegisterR11;
463 aenmNames[iReg++] = WHvX64RegisterR12;
464 aenmNames[iReg++] = WHvX64RegisterR13;
465 aenmNames[iReg++] = WHvX64RegisterR14;
466 aenmNames[iReg++] = WHvX64RegisterR15;
467 }
468 }
469
470 /* RIP & Flags */
471 if (fWhat & CPUMCTX_EXTRN_RIP)
472 aenmNames[iReg++] = WHvX64RegisterRip;
473 if (fWhat & CPUMCTX_EXTRN_RFLAGS)
474 aenmNames[iReg++] = WHvX64RegisterRflags;
475
476 /* Segments */
477 if (fWhat & CPUMCTX_EXTRN_SREG_MASK)
478 {
479 if (fWhat & CPUMCTX_EXTRN_ES)
480 aenmNames[iReg++] = WHvX64RegisterEs;
481 if (fWhat & CPUMCTX_EXTRN_CS)
482 aenmNames[iReg++] = WHvX64RegisterCs;
483 if (fWhat & CPUMCTX_EXTRN_SS)
484 aenmNames[iReg++] = WHvX64RegisterSs;
485 if (fWhat & CPUMCTX_EXTRN_DS)
486 aenmNames[iReg++] = WHvX64RegisterDs;
487 if (fWhat & CPUMCTX_EXTRN_FS)
488 aenmNames[iReg++] = WHvX64RegisterFs;
489 if (fWhat & CPUMCTX_EXTRN_GS)
490 aenmNames[iReg++] = WHvX64RegisterGs;
491 }
492
493 /* Descriptor tables. */
494 if (fWhat & CPUMCTX_EXTRN_TABLE_MASK)
495 {
496 if (fWhat & CPUMCTX_EXTRN_LDTR)
497 aenmNames[iReg++] = WHvX64RegisterLdtr;
498 if (fWhat & CPUMCTX_EXTRN_TR)
499 aenmNames[iReg++] = WHvX64RegisterTr;
500 if (fWhat & CPUMCTX_EXTRN_IDTR)
501 aenmNames[iReg++] = WHvX64RegisterIdtr;
502 if (fWhat & CPUMCTX_EXTRN_GDTR)
503 aenmNames[iReg++] = WHvX64RegisterGdtr;
504 }
505
506 /* Control registers. */
507 if (fWhat & CPUMCTX_EXTRN_CR_MASK)
508 {
509 if (fWhat & CPUMCTX_EXTRN_CR0)
510 aenmNames[iReg++] = WHvX64RegisterCr0;
511 if (fWhat & CPUMCTX_EXTRN_CR2)
512 aenmNames[iReg++] = WHvX64RegisterCr2;
513 if (fWhat & CPUMCTX_EXTRN_CR3)
514 aenmNames[iReg++] = WHvX64RegisterCr3;
515 if (fWhat & CPUMCTX_EXTRN_CR4)
516 aenmNames[iReg++] = WHvX64RegisterCr4;
517 }
518 if (fWhat & CPUMCTX_EXTRN_APIC_TPR)
519 aenmNames[iReg++] = WHvX64RegisterCr8;
520
521 /* Debug registers. */
522 if (fWhat & CPUMCTX_EXTRN_DR7)
523 aenmNames[iReg++] = WHvX64RegisterDr7;
524 if (fWhat & CPUMCTX_EXTRN_DR0_DR3)
525 {
526 if (!(fWhat & CPUMCTX_EXTRN_DR7) && (pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_DR7))
527 {
528 fWhat |= CPUMCTX_EXTRN_DR7;
529 aenmNames[iReg++] = WHvX64RegisterDr7;
530 }
531 aenmNames[iReg++] = WHvX64RegisterDr0;
532 aenmNames[iReg++] = WHvX64RegisterDr1;
533 aenmNames[iReg++] = WHvX64RegisterDr2;
534 aenmNames[iReg++] = WHvX64RegisterDr3;
535 }
536 if (fWhat & CPUMCTX_EXTRN_DR6)
537 aenmNames[iReg++] = WHvX64RegisterDr6;
538
539 /* Floating point state. */
540 if (fWhat & CPUMCTX_EXTRN_X87)
541 {
542 aenmNames[iReg++] = WHvX64RegisterFpMmx0;
543 aenmNames[iReg++] = WHvX64RegisterFpMmx1;
544 aenmNames[iReg++] = WHvX64RegisterFpMmx2;
545 aenmNames[iReg++] = WHvX64RegisterFpMmx3;
546 aenmNames[iReg++] = WHvX64RegisterFpMmx4;
547 aenmNames[iReg++] = WHvX64RegisterFpMmx5;
548 aenmNames[iReg++] = WHvX64RegisterFpMmx6;
549 aenmNames[iReg++] = WHvX64RegisterFpMmx7;
550 aenmNames[iReg++] = WHvX64RegisterFpControlStatus;
551 }
552 if (fWhat & (CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX))
553 aenmNames[iReg++] = WHvX64RegisterXmmControlStatus;
554
555 /* Vector state. */
556 if (fWhat & CPUMCTX_EXTRN_SSE_AVX)
557 {
558 aenmNames[iReg++] = WHvX64RegisterXmm0;
559 aenmNames[iReg++] = WHvX64RegisterXmm1;
560 aenmNames[iReg++] = WHvX64RegisterXmm2;
561 aenmNames[iReg++] = WHvX64RegisterXmm3;
562 aenmNames[iReg++] = WHvX64RegisterXmm4;
563 aenmNames[iReg++] = WHvX64RegisterXmm5;
564 aenmNames[iReg++] = WHvX64RegisterXmm6;
565 aenmNames[iReg++] = WHvX64RegisterXmm7;
566 aenmNames[iReg++] = WHvX64RegisterXmm8;
567 aenmNames[iReg++] = WHvX64RegisterXmm9;
568 aenmNames[iReg++] = WHvX64RegisterXmm10;
569 aenmNames[iReg++] = WHvX64RegisterXmm11;
570 aenmNames[iReg++] = WHvX64RegisterXmm12;
571 aenmNames[iReg++] = WHvX64RegisterXmm13;
572 aenmNames[iReg++] = WHvX64RegisterXmm14;
573 aenmNames[iReg++] = WHvX64RegisterXmm15;
574 }
575
576 /* MSRs */
577 // WHvX64RegisterTsc - don't touch
578 if (fWhat & CPUMCTX_EXTRN_EFER)
579 aenmNames[iReg++] = WHvX64RegisterEfer;
580 if (fWhat & CPUMCTX_EXTRN_KERNEL_GS_BASE)
581 aenmNames[iReg++] = WHvX64RegisterKernelGsBase;
582 if (fWhat & CPUMCTX_EXTRN_SYSENTER_MSRS)
583 {
584 aenmNames[iReg++] = WHvX64RegisterSysenterCs;
585 aenmNames[iReg++] = WHvX64RegisterSysenterEip;
586 aenmNames[iReg++] = WHvX64RegisterSysenterEsp;
587 }
588 if (fWhat & CPUMCTX_EXTRN_SYSCALL_MSRS)
589 {
590 aenmNames[iReg++] = WHvX64RegisterStar;
591 aenmNames[iReg++] = WHvX64RegisterLstar;
592 aenmNames[iReg++] = WHvX64RegisterCstar;
593 aenmNames[iReg++] = WHvX64RegisterSfmask;
594 }
595
596//#ifdef LOG_ENABLED
597// const CPUMCPUVENDOR enmCpuVendor = CPUMGetHostCpuVendor(pVM);
598//#endif
599 if (fWhat & CPUMCTX_EXTRN_TSC_AUX)
600 aenmNames[iReg++] = WHvX64RegisterTscAux;
601 if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS)
602 {
603 aenmNames[iReg++] = WHvX64RegisterApicBase; /// @todo APIC BASE
604 aenmNames[iReg++] = WHvX64RegisterPat;
605#if 0 /*def LOG_ENABLED*/ /** @todo Check if WHvX64RegisterMsrMtrrCap works... */
606 aenmNames[iReg++] = WHvX64RegisterMsrMtrrCap;
607#endif
608 aenmNames[iReg++] = WHvX64RegisterMsrMtrrDefType;
609 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix64k00000;
610 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix16k80000;
611 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix16kA0000;
612 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kC0000;
613 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kC8000;
614 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kD0000;
615 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kD8000;
616 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kE0000;
617 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kE8000;
618 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kF0000;
619 aenmNames[iReg++] = WHvX64RegisterMsrMtrrFix4kF8000;
620 /** @todo look for HvX64RegisterIa32MiscEnable and HvX64RegisterIa32FeatureControl? */
621//#ifdef LOG_ENABLED
622// if (enmCpuVendor != CPUMCPUVENDOR_AMD)
623// aenmNames[iReg++] = HvX64RegisterIa32FeatureControl;
624//#endif
625 }
626
627 /* Interruptibility. */
628 if (fWhat & (CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI))
629 {
630 aenmNames[iReg++] = WHvRegisterInterruptState;
631 aenmNames[iReg++] = WHvX64RegisterRip;
632 }
633
634 /* event injection */
635 aenmNames[iReg++] = WHvRegisterPendingInterruption;
636 aenmNames[iReg++] = WHvRegisterPendingEvent;
637
638 size_t const cRegs = iReg;
639 Assert(cRegs < RT_ELEMENTS(aenmNames));
640
641 /*
642 * Get the registers.
643 */
644 WHV_REGISTER_VALUE aValues[128];
645 RT_ZERO(aValues);
646 Assert(RT_ELEMENTS(aValues) >= cRegs);
647 Assert(RT_ELEMENTS(aenmNames) >= cRegs);
648#ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
649 Log12(("Calling WHvGetVirtualProcessorRegisters(%p, %u, %p, %u, %p)\n",
650 pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, cRegs, aValues));
651#endif
652 HRESULT hrc = WHvGetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, (uint32_t)cRegs, aValues);
653 AssertLogRelMsgReturn(SUCCEEDED(hrc),
654 ("WHvGetVirtualProcessorRegisters(%p, %u,,%u,) -> %Rhrc (Last=%#x/%u)\n",
655 pVM->nem.s.hPartition, pVCpu->idCpu, cRegs, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
656 , VERR_NEM_GET_REGISTERS_FAILED);
657
658 iReg = 0;
659#define GET_REG64(a_DstVar, a_enmName) do { \
660 Assert(aenmNames[iReg] == (a_enmName)); \
661 (a_DstVar) = aValues[iReg].Reg64; \
662 iReg++; \
663 } while (0)
664#define GET_REG64_LOG7(a_DstVar, a_enmName, a_szLogName) do { \
665 Assert(aenmNames[iReg] == (a_enmName)); \
666 if ((a_DstVar) != aValues[iReg].Reg64) \
667 Log7(("NEM/%u: " a_szLogName " changed %RX64 -> %RX64\n", pVCpu->idCpu, (a_DstVar), aValues[iReg].Reg64)); \
668 (a_DstVar) = aValues[iReg].Reg64; \
669 iReg++; \
670 } while (0)
671#define GET_REG128(a_DstVarLo, a_DstVarHi, a_enmName) do { \
672 Assert(aenmNames[iReg] == a_enmName); \
673 (a_DstVarLo) = aValues[iReg].Reg128.Low64; \
674 (a_DstVarHi) = aValues[iReg].Reg128.High64; \
675 iReg++; \
676 } while (0)
677#define GET_SEG(a_SReg, a_enmName) do { \
678 Assert(aenmNames[iReg] == (a_enmName)); \
679 NEM_WIN_COPY_BACK_SEG(a_SReg, aValues[iReg].Segment); \
680 iReg++; \
681 } while (0)
682
683 /* GPRs */
684 if (fWhat & CPUMCTX_EXTRN_GPRS_MASK)
685 {
686 if (fWhat & CPUMCTX_EXTRN_RAX)
687 GET_REG64(pVCpu->cpum.GstCtx.rax, WHvX64RegisterRax);
688 if (fWhat & CPUMCTX_EXTRN_RCX)
689 GET_REG64(pVCpu->cpum.GstCtx.rcx, WHvX64RegisterRcx);
690 if (fWhat & CPUMCTX_EXTRN_RDX)
691 GET_REG64(pVCpu->cpum.GstCtx.rdx, WHvX64RegisterRdx);
692 if (fWhat & CPUMCTX_EXTRN_RBX)
693 GET_REG64(pVCpu->cpum.GstCtx.rbx, WHvX64RegisterRbx);
694 if (fWhat & CPUMCTX_EXTRN_RSP)
695 GET_REG64(pVCpu->cpum.GstCtx.rsp, WHvX64RegisterRsp);
696 if (fWhat & CPUMCTX_EXTRN_RBP)
697 GET_REG64(pVCpu->cpum.GstCtx.rbp, WHvX64RegisterRbp);
698 if (fWhat & CPUMCTX_EXTRN_RSI)
699 GET_REG64(pVCpu->cpum.GstCtx.rsi, WHvX64RegisterRsi);
700 if (fWhat & CPUMCTX_EXTRN_RDI)
701 GET_REG64(pVCpu->cpum.GstCtx.rdi, WHvX64RegisterRdi);
702 if (fWhat & CPUMCTX_EXTRN_R8_R15)
703 {
704 GET_REG64(pVCpu->cpum.GstCtx.r8, WHvX64RegisterR8);
705 GET_REG64(pVCpu->cpum.GstCtx.r9, WHvX64RegisterR9);
706 GET_REG64(pVCpu->cpum.GstCtx.r10, WHvX64RegisterR10);
707 GET_REG64(pVCpu->cpum.GstCtx.r11, WHvX64RegisterR11);
708 GET_REG64(pVCpu->cpum.GstCtx.r12, WHvX64RegisterR12);
709 GET_REG64(pVCpu->cpum.GstCtx.r13, WHvX64RegisterR13);
710 GET_REG64(pVCpu->cpum.GstCtx.r14, WHvX64RegisterR14);
711 GET_REG64(pVCpu->cpum.GstCtx.r15, WHvX64RegisterR15);
712 }
713 }
714
715 /* RIP & Flags */
716 if (fWhat & CPUMCTX_EXTRN_RIP)
717 GET_REG64(pVCpu->cpum.GstCtx.rip, WHvX64RegisterRip);
718 if (fWhat & CPUMCTX_EXTRN_RFLAGS)
719 GET_REG64(pVCpu->cpum.GstCtx.rflags.u, WHvX64RegisterRflags);
720
721 /* Segments */
722 if (fWhat & CPUMCTX_EXTRN_SREG_MASK)
723 {
724 if (fWhat & CPUMCTX_EXTRN_ES)
725 GET_SEG(pVCpu->cpum.GstCtx.es, WHvX64RegisterEs);
726 if (fWhat & CPUMCTX_EXTRN_CS)
727 GET_SEG(pVCpu->cpum.GstCtx.cs, WHvX64RegisterCs);
728 if (fWhat & CPUMCTX_EXTRN_SS)
729 GET_SEG(pVCpu->cpum.GstCtx.ss, WHvX64RegisterSs);
730 if (fWhat & CPUMCTX_EXTRN_DS)
731 GET_SEG(pVCpu->cpum.GstCtx.ds, WHvX64RegisterDs);
732 if (fWhat & CPUMCTX_EXTRN_FS)
733 GET_SEG(pVCpu->cpum.GstCtx.fs, WHvX64RegisterFs);
734 if (fWhat & CPUMCTX_EXTRN_GS)
735 GET_SEG(pVCpu->cpum.GstCtx.gs, WHvX64RegisterGs);
736 }
737
738 /* Descriptor tables and the task segment. */
739 if (fWhat & CPUMCTX_EXTRN_TABLE_MASK)
740 {
741 if (fWhat & CPUMCTX_EXTRN_LDTR)
742 GET_SEG(pVCpu->cpum.GstCtx.ldtr, WHvX64RegisterLdtr);
743
744 if (fWhat & CPUMCTX_EXTRN_TR)
745 {
746 /* AMD-V likes loading TR with in AVAIL state, whereas intel insists on BUSY. So,
747 avoid to trigger sanity assertions around the code, always fix this. */
748 GET_SEG(pVCpu->cpum.GstCtx.tr, WHvX64RegisterTr);
749 switch (pVCpu->cpum.GstCtx.tr.Attr.n.u4Type)
750 {
751 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
752 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
753 break;
754 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
755 pVCpu->cpum.GstCtx.tr.Attr.n.u4Type = X86_SEL_TYPE_SYS_386_TSS_BUSY;
756 break;
757 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
758 pVCpu->cpum.GstCtx.tr.Attr.n.u4Type = X86_SEL_TYPE_SYS_286_TSS_BUSY;
759 break;
760 }
761 }
762 if (fWhat & CPUMCTX_EXTRN_IDTR)
763 {
764 Assert(aenmNames[iReg] == WHvX64RegisterIdtr);
765 pVCpu->cpum.GstCtx.idtr.cbIdt = aValues[iReg].Table.Limit;
766 pVCpu->cpum.GstCtx.idtr.pIdt = aValues[iReg].Table.Base;
767 iReg++;
768 }
769 if (fWhat & CPUMCTX_EXTRN_GDTR)
770 {
771 Assert(aenmNames[iReg] == WHvX64RegisterGdtr);
772 pVCpu->cpum.GstCtx.gdtr.cbGdt = aValues[iReg].Table.Limit;
773 pVCpu->cpum.GstCtx.gdtr.pGdt = aValues[iReg].Table.Base;
774 iReg++;
775 }
776 }
777
778 /* Control registers. */
779 bool fMaybeChangedMode = false;
780 bool fUpdateCr3 = false;
781 if (fWhat & CPUMCTX_EXTRN_CR_MASK)
782 {
783 if (fWhat & CPUMCTX_EXTRN_CR0)
784 {
785 Assert(aenmNames[iReg] == WHvX64RegisterCr0);
786 if (pVCpu->cpum.GstCtx.cr0 != aValues[iReg].Reg64)
787 {
788 CPUMSetGuestCR0(pVCpu, aValues[iReg].Reg64);
789 fMaybeChangedMode = true;
790 }
791 iReg++;
792 }
793 if (fWhat & CPUMCTX_EXTRN_CR2)
794 GET_REG64(pVCpu->cpum.GstCtx.cr2, WHvX64RegisterCr2);
795 if (fWhat & CPUMCTX_EXTRN_CR3)
796 {
797 if (pVCpu->cpum.GstCtx.cr3 != aValues[iReg].Reg64)
798 {
799 CPUMSetGuestCR3(pVCpu, aValues[iReg].Reg64);
800 fUpdateCr3 = true;
801 }
802 iReg++;
803 }
804 if (fWhat & CPUMCTX_EXTRN_CR4)
805 {
806 if (pVCpu->cpum.GstCtx.cr4 != aValues[iReg].Reg64)
807 {
808 CPUMSetGuestCR4(pVCpu, aValues[iReg].Reg64);
809 fMaybeChangedMode = true;
810 }
811 iReg++;
812 }
813 }
814 if (fWhat & CPUMCTX_EXTRN_APIC_TPR)
815 {
816 Assert(aenmNames[iReg] == WHvX64RegisterCr8);
817 APICSetTpr(pVCpu, (uint8_t)aValues[iReg].Reg64 << 4);
818 iReg++;
819 }
820
821 /* Debug registers. */
822 if (fWhat & CPUMCTX_EXTRN_DR7)
823 {
824 Assert(aenmNames[iReg] == WHvX64RegisterDr7);
825 if (pVCpu->cpum.GstCtx.dr[7] != aValues[iReg].Reg64)
826 CPUMSetGuestDR7(pVCpu, aValues[iReg].Reg64);
827 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_DR7; /* Hack alert! Avoids asserting when processing CPUMCTX_EXTRN_DR0_DR3. */
828 iReg++;
829 }
830 if (fWhat & CPUMCTX_EXTRN_DR0_DR3)
831 {
832 Assert(aenmNames[iReg] == WHvX64RegisterDr0);
833 Assert(aenmNames[iReg+3] == WHvX64RegisterDr3);
834 if (pVCpu->cpum.GstCtx.dr[0] != aValues[iReg].Reg64)
835 CPUMSetGuestDR0(pVCpu, aValues[iReg].Reg64);
836 iReg++;
837 if (pVCpu->cpum.GstCtx.dr[1] != aValues[iReg].Reg64)
838 CPUMSetGuestDR1(pVCpu, aValues[iReg].Reg64);
839 iReg++;
840 if (pVCpu->cpum.GstCtx.dr[2] != aValues[iReg].Reg64)
841 CPUMSetGuestDR2(pVCpu, aValues[iReg].Reg64);
842 iReg++;
843 if (pVCpu->cpum.GstCtx.dr[3] != aValues[iReg].Reg64)
844 CPUMSetGuestDR3(pVCpu, aValues[iReg].Reg64);
845 iReg++;
846 }
847 if (fWhat & CPUMCTX_EXTRN_DR6)
848 {
849 Assert(aenmNames[iReg] == WHvX64RegisterDr6);
850 if (pVCpu->cpum.GstCtx.dr[6] != aValues[iReg].Reg64)
851 CPUMSetGuestDR6(pVCpu, aValues[iReg].Reg64);
852 iReg++;
853 }
854
855 /* Floating point state. */
856 if (fWhat & CPUMCTX_EXTRN_X87)
857 {
858 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[0].au64[1], WHvX64RegisterFpMmx0);
859 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[1].au64[1], WHvX64RegisterFpMmx1);
860 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[2].au64[1], WHvX64RegisterFpMmx2);
861 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[3].au64[1], WHvX64RegisterFpMmx3);
862 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[4].au64[1], WHvX64RegisterFpMmx4);
863 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[5].au64[1], WHvX64RegisterFpMmx5);
864 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[6].au64[1], WHvX64RegisterFpMmx6);
865 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[0], pVCpu->cpum.GstCtx.XState.x87.aRegs[7].au64[1], WHvX64RegisterFpMmx7);
866
867 Assert(aenmNames[iReg] == WHvX64RegisterFpControlStatus);
868 pVCpu->cpum.GstCtx.XState.x87.FCW = aValues[iReg].FpControlStatus.FpControl;
869 pVCpu->cpum.GstCtx.XState.x87.FSW = aValues[iReg].FpControlStatus.FpStatus;
870 pVCpu->cpum.GstCtx.XState.x87.FTW = aValues[iReg].FpControlStatus.FpTag
871 /*| (aValues[iReg].FpControlStatus.Reserved << 8)*/;
872 pVCpu->cpum.GstCtx.XState.x87.FOP = aValues[iReg].FpControlStatus.LastFpOp;
873 pVCpu->cpum.GstCtx.XState.x87.FPUIP = (uint32_t)aValues[iReg].FpControlStatus.LastFpRip;
874 pVCpu->cpum.GstCtx.XState.x87.CS = (uint16_t)(aValues[iReg].FpControlStatus.LastFpRip >> 32);
875 pVCpu->cpum.GstCtx.XState.x87.Rsrvd1 = (uint16_t)(aValues[iReg].FpControlStatus.LastFpRip >> 48);
876 iReg++;
877 }
878
879 if (fWhat & (CPUMCTX_EXTRN_X87 | CPUMCTX_EXTRN_SSE_AVX))
880 {
881 Assert(aenmNames[iReg] == WHvX64RegisterXmmControlStatus);
882 if (fWhat & CPUMCTX_EXTRN_X87)
883 {
884 pVCpu->cpum.GstCtx.XState.x87.FPUDP = (uint32_t)aValues[iReg].XmmControlStatus.LastFpRdp;
885 pVCpu->cpum.GstCtx.XState.x87.DS = (uint16_t)(aValues[iReg].XmmControlStatus.LastFpRdp >> 32);
886 pVCpu->cpum.GstCtx.XState.x87.Rsrvd2 = (uint16_t)(aValues[iReg].XmmControlStatus.LastFpRdp >> 48);
887 }
888 pVCpu->cpum.GstCtx.XState.x87.MXCSR = aValues[iReg].XmmControlStatus.XmmStatusControl;
889 pVCpu->cpum.GstCtx.XState.x87.MXCSR_MASK = aValues[iReg].XmmControlStatus.XmmStatusControlMask; /** @todo ??? (Isn't this an output field?) */
890 iReg++;
891 }
892
893 /* Vector state. */
894 if (fWhat & CPUMCTX_EXTRN_SSE_AVX)
895 {
896 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 0].uXmm.s.Hi, WHvX64RegisterXmm0);
897 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 1].uXmm.s.Hi, WHvX64RegisterXmm1);
898 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 2].uXmm.s.Hi, WHvX64RegisterXmm2);
899 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 3].uXmm.s.Hi, WHvX64RegisterXmm3);
900 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 4].uXmm.s.Hi, WHvX64RegisterXmm4);
901 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 5].uXmm.s.Hi, WHvX64RegisterXmm5);
902 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 6].uXmm.s.Hi, WHvX64RegisterXmm6);
903 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 7].uXmm.s.Hi, WHvX64RegisterXmm7);
904 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 8].uXmm.s.Hi, WHvX64RegisterXmm8);
905 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[ 9].uXmm.s.Hi, WHvX64RegisterXmm9);
906 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[10].uXmm.s.Hi, WHvX64RegisterXmm10);
907 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[11].uXmm.s.Hi, WHvX64RegisterXmm11);
908 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[12].uXmm.s.Hi, WHvX64RegisterXmm12);
909 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[13].uXmm.s.Hi, WHvX64RegisterXmm13);
910 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[14].uXmm.s.Hi, WHvX64RegisterXmm14);
911 GET_REG128(pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Lo, pVCpu->cpum.GstCtx.XState.x87.aXMM[15].uXmm.s.Hi, WHvX64RegisterXmm15);
912 }
913
914 /* MSRs */
915 // WHvX64RegisterTsc - don't touch
916 if (fWhat & CPUMCTX_EXTRN_EFER)
917 {
918 Assert(aenmNames[iReg] == WHvX64RegisterEfer);
919 if (aValues[iReg].Reg64 != pVCpu->cpum.GstCtx.msrEFER)
920 {
921 Log7(("NEM/%u: MSR EFER changed %RX64 -> %RX64\n", pVCpu->idCpu, pVCpu->cpum.GstCtx.msrEFER, aValues[iReg].Reg64));
922 if ((aValues[iReg].Reg64 ^ pVCpu->cpum.GstCtx.msrEFER) & MSR_K6_EFER_NXE)
923 PGMNotifyNxeChanged(pVCpu, RT_BOOL(aValues[iReg].Reg64 & MSR_K6_EFER_NXE));
924 pVCpu->cpum.GstCtx.msrEFER = aValues[iReg].Reg64;
925 fMaybeChangedMode = true;
926 }
927 iReg++;
928 }
929 if (fWhat & CPUMCTX_EXTRN_KERNEL_GS_BASE)
930 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrKERNELGSBASE, WHvX64RegisterKernelGsBase, "MSR KERNEL_GS_BASE");
931 if (fWhat & CPUMCTX_EXTRN_SYSENTER_MSRS)
932 {
933 GET_REG64_LOG7(pVCpu->cpum.GstCtx.SysEnter.cs, WHvX64RegisterSysenterCs, "MSR SYSENTER.CS");
934 GET_REG64_LOG7(pVCpu->cpum.GstCtx.SysEnter.eip, WHvX64RegisterSysenterEip, "MSR SYSENTER.EIP");
935 GET_REG64_LOG7(pVCpu->cpum.GstCtx.SysEnter.esp, WHvX64RegisterSysenterEsp, "MSR SYSENTER.ESP");
936 }
937 if (fWhat & CPUMCTX_EXTRN_SYSCALL_MSRS)
938 {
939 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrSTAR, WHvX64RegisterStar, "MSR STAR");
940 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrLSTAR, WHvX64RegisterLstar, "MSR LSTAR");
941 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrCSTAR, WHvX64RegisterCstar, "MSR CSTAR");
942 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrSFMASK, WHvX64RegisterSfmask, "MSR SFMASK");
943 }
944 if (fWhat & (CPUMCTX_EXTRN_TSC_AUX | CPUMCTX_EXTRN_OTHER_MSRS))
945 {
946 PCPUMCTXMSRS const pCtxMsrs = CPUMQueryGuestCtxMsrsPtr(pVCpu);
947 if (fWhat & CPUMCTX_EXTRN_TSC_AUX)
948 GET_REG64_LOG7(pCtxMsrs->msr.TscAux, WHvX64RegisterTscAux, "MSR TSC_AUX");
949 if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS)
950 {
951 Assert(aenmNames[iReg] == WHvX64RegisterApicBase);
952 const uint64_t uOldBase = APICGetBaseMsrNoCheck(pVCpu);
953 if (aValues[iReg].Reg64 != uOldBase)
954 {
955 Log7(("NEM/%u: MSR APICBase changed %RX64 -> %RX64 (%RX64)\n",
956 pVCpu->idCpu, uOldBase, aValues[iReg].Reg64, aValues[iReg].Reg64 ^ uOldBase));
957 int rc2 = APICSetBaseMsr(pVCpu, aValues[iReg].Reg64);
958 AssertLogRelMsg(rc2 == VINF_SUCCESS, ("%Rrc %RX64\n", rc2, aValues[iReg].Reg64));
959 }
960 iReg++;
961
962 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrPAT, WHvX64RegisterPat, "MSR PAT");
963#if 0 /*def LOG_ENABLED*/ /** @todo something's wrong with HvX64RegisterMtrrCap? (AMD) */
964 GET_REG64_LOG7(pVCpu->cpum.GstCtx.msrPAT, WHvX64RegisterMsrMtrrCap);
965#endif
966 GET_REG64_LOG7(pCtxMsrs->msr.MtrrDefType, WHvX64RegisterMsrMtrrDefType, "MSR MTRR_DEF_TYPE");
967 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix64K_00000, WHvX64RegisterMsrMtrrFix64k00000, "MSR MTRR_FIX_64K_00000");
968 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix16K_80000, WHvX64RegisterMsrMtrrFix16k80000, "MSR MTRR_FIX_16K_80000");
969 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix16K_A0000, WHvX64RegisterMsrMtrrFix16kA0000, "MSR MTRR_FIX_16K_A0000");
970 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_C0000, WHvX64RegisterMsrMtrrFix4kC0000, "MSR MTRR_FIX_4K_C0000");
971 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_C8000, WHvX64RegisterMsrMtrrFix4kC8000, "MSR MTRR_FIX_4K_C8000");
972 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_D0000, WHvX64RegisterMsrMtrrFix4kD0000, "MSR MTRR_FIX_4K_D0000");
973 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_D8000, WHvX64RegisterMsrMtrrFix4kD8000, "MSR MTRR_FIX_4K_D8000");
974 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_E0000, WHvX64RegisterMsrMtrrFix4kE0000, "MSR MTRR_FIX_4K_E0000");
975 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_E8000, WHvX64RegisterMsrMtrrFix4kE8000, "MSR MTRR_FIX_4K_E8000");
976 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_F0000, WHvX64RegisterMsrMtrrFix4kF0000, "MSR MTRR_FIX_4K_F0000");
977 GET_REG64_LOG7(pCtxMsrs->msr.MtrrFix4K_F8000, WHvX64RegisterMsrMtrrFix4kF8000, "MSR MTRR_FIX_4K_F8000");
978 /** @todo look for HvX64RegisterIa32MiscEnable and HvX64RegisterIa32FeatureControl? */
979 }
980 }
981
982 /* Interruptibility. */
983 if (fWhat & (CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI))
984 {
985 Assert(aenmNames[iReg] == WHvRegisterInterruptState);
986 Assert(aenmNames[iReg + 1] == WHvX64RegisterRip);
987
988 if (!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_INHIBIT_INT))
989 pVCpu->nem.s.fLastInterruptShadow = CPUMUpdateInterruptShadowEx(&pVCpu->cpum.GstCtx,
990 aValues[iReg].InterruptState.InterruptShadow,
991 aValues[iReg + 1].Reg64);
992
993 if (!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_INHIBIT_NMI))
994 CPUMUpdateInterruptInhibitingByNmi(&pVCpu->cpum.GstCtx, aValues[iReg].InterruptState.NmiMasked);
995
996 fWhat |= CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI;
997 iReg += 2;
998 }
999
1000 /* Event injection. */
1001 /// @todo WHvRegisterPendingInterruption
1002 Assert(aenmNames[iReg] == WHvRegisterPendingInterruption);
1003 if (aValues[iReg].PendingInterruption.InterruptionPending)
1004 {
1005 Log7(("PendingInterruption: type=%u vector=%#x errcd=%RTbool/%#x instr-len=%u nested=%u\n",
1006 aValues[iReg].PendingInterruption.InterruptionType, aValues[iReg].PendingInterruption.InterruptionVector,
1007 aValues[iReg].PendingInterruption.DeliverErrorCode, aValues[iReg].PendingInterruption.ErrorCode,
1008 aValues[iReg].PendingInterruption.InstructionLength, aValues[iReg].PendingInterruption.NestedEvent));
1009 AssertMsg((aValues[iReg].PendingInterruption.AsUINT64 & UINT64_C(0xfc00)) == 0,
1010 ("%#RX64\n", aValues[iReg].PendingInterruption.AsUINT64));
1011 }
1012
1013 /// @todo WHvRegisterPendingEvent
1014
1015 /* Almost done, just update extrn flags and maybe change PGM mode. */
1016 pVCpu->cpum.GstCtx.fExtrn &= ~fWhat;
1017 if (!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT))))
1018 pVCpu->cpum.GstCtx.fExtrn = 0;
1019
1020 /* Typical. */
1021 if (!fMaybeChangedMode && !fUpdateCr3)
1022 return VINF_SUCCESS;
1023
1024 /*
1025 * Slow.
1026 */
1027 if (fMaybeChangedMode)
1028 {
1029 int rc = PGMChangeMode(pVCpu, pVCpu->cpum.GstCtx.cr0, pVCpu->cpum.GstCtx.cr4, pVCpu->cpum.GstCtx.msrEFER,
1030 false /* fForce */);
1031 AssertMsgReturn(rc == VINF_SUCCESS, ("rc=%Rrc\n", rc), RT_FAILURE_NP(rc) ? rc : VERR_NEM_IPE_1);
1032 }
1033
1034 if (fUpdateCr3)
1035 {
1036 int rc = PGMUpdateCR3(pVCpu, pVCpu->cpum.GstCtx.cr3);
1037 if (rc == VINF_SUCCESS)
1038 { /* likely */ }
1039 else
1040 AssertMsgFailedReturn(("rc=%Rrc\n", rc), RT_FAILURE_NP(rc) ? rc : VERR_NEM_IPE_2);
1041 }
1042
1043 return VINF_SUCCESS;
1044}
1045
1046
1047/**
1048 * Interface for importing state on demand (used by IEM).
1049 *
1050 * @returns VBox status code.
1051 * @param pVCpu The cross context CPU structure.
1052 * @param fWhat What to import, CPUMCTX_EXTRN_XXX.
1053 */
1054VMM_INT_DECL(int) NEMImportStateOnDemand(PVMCPUCC pVCpu, uint64_t fWhat)
1055{
1056 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnDemand);
1057 return nemHCWinCopyStateFromHyperV(pVCpu->pVMR3, pVCpu, fWhat);
1058}
1059
1060
1061/**
1062 * Query the CPU tick counter and optionally the TSC_AUX MSR value.
1063 *
1064 * @returns VBox status code.
1065 * @param pVCpu The cross context CPU structure.
1066 * @param pcTicks Where to return the CPU tick count.
1067 * @param puAux Where to return the TSC_AUX register value.
1068 */
1069VMM_INT_DECL(int) NEMHCQueryCpuTick(PVMCPUCC pVCpu, uint64_t *pcTicks, uint32_t *puAux)
1070{
1071 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatQueryCpuTick);
1072
1073 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1074 VMCPU_ASSERT_EMT_RETURN(pVCpu, VERR_VM_THREAD_NOT_EMT);
1075 AssertReturn(VM_IS_NEM_ENABLED(pVM), VERR_NEM_IPE_9);
1076
1077 /* Call the offical API. */
1078 WHV_REGISTER_NAME aenmNames[2] = { WHvX64RegisterTsc, WHvX64RegisterTscAux };
1079 WHV_REGISTER_VALUE aValues[2] = { { {0, 0} }, { {0, 0} } };
1080 Assert(RT_ELEMENTS(aenmNames) == RT_ELEMENTS(aValues));
1081 HRESULT hrc = WHvGetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, aenmNames, 2, aValues);
1082 AssertLogRelMsgReturn(SUCCEEDED(hrc),
1083 ("WHvGetVirtualProcessorRegisters(%p, %u,{tsc,tsc_aux},2,) -> %Rhrc (Last=%#x/%u)\n",
1084 pVM->nem.s.hPartition, pVCpu->idCpu, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
1085 , VERR_NEM_GET_REGISTERS_FAILED);
1086 *pcTicks = aValues[0].Reg64;
1087 if (puAux)
1088 *puAux = pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_TSC_AUX ? aValues[1].Reg64 : CPUMGetGuestTscAux(pVCpu);
1089 return VINF_SUCCESS;
1090}
1091
1092
1093/**
1094 * Resumes CPU clock (TSC) on all virtual CPUs.
1095 *
1096 * This is called by TM when the VM is started, restored, resumed or similar.
1097 *
1098 * @returns VBox status code.
1099 * @param pVM The cross context VM structure.
1100 * @param pVCpu The cross context CPU structure of the calling EMT.
1101 * @param uPausedTscValue The TSC value at the time of pausing.
1102 */
1103VMM_INT_DECL(int) NEMHCResumeCpuTickOnAll(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uPausedTscValue)
1104{
1105 VMCPU_ASSERT_EMT_RETURN(pVCpu, VERR_VM_THREAD_NOT_EMT);
1106 AssertReturn(VM_IS_NEM_ENABLED(pVM), VERR_NEM_IPE_9);
1107
1108 /*
1109 * Call the offical API to do the job.
1110 */
1111 if (pVM->cCpus > 1)
1112 RTThreadYield(); /* Try decrease the chance that we get rescheduled in the middle. */
1113
1114 /* Start with the first CPU. */
1115 WHV_REGISTER_NAME enmName = WHvX64RegisterTsc;
1116 WHV_REGISTER_VALUE Value = { {0, 0} };
1117 Value.Reg64 = uPausedTscValue;
1118 uint64_t const uFirstTsc = ASMReadTSC();
1119 HRESULT hrc = WHvSetVirtualProcessorRegisters(pVM->nem.s.hPartition, 0 /*iCpu*/, &enmName, 1, &Value);
1120 AssertLogRelMsgReturn(SUCCEEDED(hrc),
1121 ("WHvSetVirtualProcessorRegisters(%p, 0,{tsc},2,%#RX64) -> %Rhrc (Last=%#x/%u)\n",
1122 pVM->nem.s.hPartition, uPausedTscValue, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
1123 , VERR_NEM_SET_TSC);
1124
1125 /* Do the other CPUs, adjusting for elapsed TSC and keeping finger crossed
1126 that we don't introduce too much drift here. */
1127 for (VMCPUID iCpu = 1; iCpu < pVM->cCpus; iCpu++)
1128 {
1129 Assert(enmName == WHvX64RegisterTsc);
1130 const uint64_t offDelta = (ASMReadTSC() - uFirstTsc);
1131 Value.Reg64 = uPausedTscValue + offDelta;
1132 hrc = WHvSetVirtualProcessorRegisters(pVM->nem.s.hPartition, iCpu, &enmName, 1, &Value);
1133 AssertLogRelMsgReturn(SUCCEEDED(hrc),
1134 ("WHvSetVirtualProcessorRegisters(%p, 0,{tsc},2,%#RX64 + %#RX64) -> %Rhrc (Last=%#x/%u)\n",
1135 pVM->nem.s.hPartition, iCpu, uPausedTscValue, offDelta, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
1136 , VERR_NEM_SET_TSC);
1137 }
1138
1139 return VINF_SUCCESS;
1140}
1141
1142#ifdef LOG_ENABLED
1143
1144/**
1145 * Get the virtual processor running status.
1146 */
1147DECLINLINE(VID_PROCESSOR_STATUS) nemHCWinCpuGetRunningStatus(PVMCPUCC pVCpu)
1148{
1149 RTERRVARS Saved;
1150 RTErrVarsSave(&Saved);
1151
1152 /*
1153 * This API is disabled in release builds, it seems. On build 17101 it requires
1154 * the following patch to be enabled (windbg): eb vid+12180 0f 84 98 00 00 00
1155 */
1156 VID_PROCESSOR_STATUS enmCpuStatus = VidProcessorStatusUndefined;
1157 NTSTATUS rcNt = g_pfnVidGetVirtualProcessorRunningStatus(pVCpu->pVMR3->nem.s.hPartitionDevice, pVCpu->idCpu, &enmCpuStatus);
1158 AssertMsg(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
1159
1160 RTErrVarsRestore(&Saved);
1161 return enmCpuStatus;
1162}
1163
1164
1165/**
1166 * Logs the current CPU state.
1167 */
1168NEM_TMPL_STATIC void nemHCWinLogState(PVMCC pVM, PVMCPUCC pVCpu)
1169{
1170 if (LogIs3Enabled())
1171 {
1172# if 0 // def IN_RING3 - causes lazy state import assertions all over CPUM.
1173 char szRegs[4096];
1174 DBGFR3RegPrintf(pVM->pUVM, pVCpu->idCpu, &szRegs[0], sizeof(szRegs),
1175 "rax=%016VR{rax} rbx=%016VR{rbx} rcx=%016VR{rcx} rdx=%016VR{rdx}\n"
1176 "rsi=%016VR{rsi} rdi=%016VR{rdi} r8 =%016VR{r8} r9 =%016VR{r9}\n"
1177 "r10=%016VR{r10} r11=%016VR{r11} r12=%016VR{r12} r13=%016VR{r13}\n"
1178 "r14=%016VR{r14} r15=%016VR{r15} %VRF{rflags}\n"
1179 "rip=%016VR{rip} rsp=%016VR{rsp} rbp=%016VR{rbp}\n"
1180 "cs={%04VR{cs} base=%016VR{cs_base} limit=%08VR{cs_lim} flags=%04VR{cs_attr}} cr0=%016VR{cr0}\n"
1181 "ds={%04VR{ds} base=%016VR{ds_base} limit=%08VR{ds_lim} flags=%04VR{ds_attr}} cr2=%016VR{cr2}\n"
1182 "es={%04VR{es} base=%016VR{es_base} limit=%08VR{es_lim} flags=%04VR{es_attr}} cr3=%016VR{cr3}\n"
1183 "fs={%04VR{fs} base=%016VR{fs_base} limit=%08VR{fs_lim} flags=%04VR{fs_attr}} cr4=%016VR{cr4}\n"
1184 "gs={%04VR{gs} base=%016VR{gs_base} limit=%08VR{gs_lim} flags=%04VR{gs_attr}} cr8=%016VR{cr8}\n"
1185 "ss={%04VR{ss} base=%016VR{ss_base} limit=%08VR{ss_lim} flags=%04VR{ss_attr}}\n"
1186 "dr0=%016VR{dr0} dr1=%016VR{dr1} dr2=%016VR{dr2} dr3=%016VR{dr3}\n"
1187 "dr6=%016VR{dr6} dr7=%016VR{dr7}\n"
1188 "gdtr=%016VR{gdtr_base}:%04VR{gdtr_lim} idtr=%016VR{idtr_base}:%04VR{idtr_lim} rflags=%08VR{rflags}\n"
1189 "ldtr={%04VR{ldtr} base=%016VR{ldtr_base} limit=%08VR{ldtr_lim} flags=%08VR{ldtr_attr}}\n"
1190 "tr ={%04VR{tr} base=%016VR{tr_base} limit=%08VR{tr_lim} flags=%08VR{tr_attr}}\n"
1191 " sysenter={cs=%04VR{sysenter_cs} eip=%08VR{sysenter_eip} esp=%08VR{sysenter_esp}}\n"
1192 " efer=%016VR{efer}\n"
1193 " pat=%016VR{pat}\n"
1194 " sf_mask=%016VR{sf_mask}\n"
1195 "krnl_gs_base=%016VR{krnl_gs_base}\n"
1196 " lstar=%016VR{lstar}\n"
1197 " star=%016VR{star} cstar=%016VR{cstar}\n"
1198 "fcw=%04VR{fcw} fsw=%04VR{fsw} ftw=%04VR{ftw} mxcsr=%04VR{mxcsr} mxcsr_mask=%04VR{mxcsr_mask}\n"
1199 );
1200
1201 char szInstr[256];
1202 DBGFR3DisasInstrEx(pVM->pUVM, pVCpu->idCpu, 0, 0,
1203 DBGF_DISAS_FLAGS_CURRENT_GUEST | DBGF_DISAS_FLAGS_DEFAULT_MODE,
1204 szInstr, sizeof(szInstr), NULL);
1205 Log3(("%s%s\n", szRegs, szInstr));
1206# else
1207 /** @todo stat logging in ring-0 */
1208 RT_NOREF(pVM, pVCpu);
1209# endif
1210 }
1211}
1212
1213#endif /* LOG_ENABLED */
1214
1215/**
1216 * Translates the execution stat bitfield into a short log string, WinHv version.
1217 *
1218 * @returns Read-only log string.
1219 * @param pExitCtx The exit context which state to summarize.
1220 */
1221static const char *nemR3WinExecStateToLogStr(WHV_VP_EXIT_CONTEXT const *pExitCtx)
1222{
1223 unsigned u = (unsigned)pExitCtx->ExecutionState.InterruptionPending
1224 | ((unsigned)pExitCtx->ExecutionState.DebugActive << 1)
1225 | ((unsigned)pExitCtx->ExecutionState.InterruptShadow << 2);
1226#define SWITCH_IT(a_szPrefix) \
1227 do \
1228 switch (u)\
1229 { \
1230 case 0x00: return a_szPrefix ""; \
1231 case 0x01: return a_szPrefix ",Pnd"; \
1232 case 0x02: return a_szPrefix ",Dbg"; \
1233 case 0x03: return a_szPrefix ",Pnd,Dbg"; \
1234 case 0x04: return a_szPrefix ",Shw"; \
1235 case 0x05: return a_szPrefix ",Pnd,Shw"; \
1236 case 0x06: return a_szPrefix ",Shw,Dbg"; \
1237 case 0x07: return a_szPrefix ",Pnd,Shw,Dbg"; \
1238 default: AssertFailedReturn("WTF?"); \
1239 } \
1240 while (0)
1241 if (pExitCtx->ExecutionState.EferLma)
1242 SWITCH_IT("LM");
1243 else if (pExitCtx->ExecutionState.Cr0Pe)
1244 SWITCH_IT("PM");
1245 else
1246 SWITCH_IT("RM");
1247#undef SWITCH_IT
1248}
1249
1250
1251/**
1252 * Advances the guest RIP and clear EFLAGS.RF, WinHv version.
1253 *
1254 * This may clear VMCPU_FF_INHIBIT_INTERRUPTS.
1255 *
1256 * @param pVCpu The cross context virtual CPU structure.
1257 * @param pExitCtx The exit context.
1258 * @param cbMinInstr The minimum instruction length, or 1 if not unknown.
1259 */
1260DECLINLINE(void) nemR3WinAdvanceGuestRipAndClearRF(PVMCPUCC pVCpu, WHV_VP_EXIT_CONTEXT const *pExitCtx, uint8_t cbMinInstr)
1261{
1262 Assert(!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS)));
1263
1264 /* Advance the RIP. */
1265 Assert(pExitCtx->InstructionLength >= cbMinInstr); RT_NOREF_PV(cbMinInstr);
1266 pVCpu->cpum.GstCtx.rip += pExitCtx->InstructionLength;
1267 pVCpu->cpum.GstCtx.rflags.Bits.u1RF = 0;
1268 CPUMClearInterruptShadow(&pVCpu->cpum.GstCtx);
1269}
1270
1271
1272/**
1273 * State to pass between nemHCWinHandleMemoryAccess / nemR3WinWHvHandleMemoryAccess
1274 * and nemHCWinHandleMemoryAccessPageCheckerCallback.
1275 */
1276typedef struct NEMHCWINHMACPCCSTATE
1277{
1278 /** Input: Write access. */
1279 bool fWriteAccess;
1280 /** Output: Set if we did something. */
1281 bool fDidSomething;
1282 /** Output: Set it we should resume. */
1283 bool fCanResume;
1284} NEMHCWINHMACPCCSTATE;
1285
1286/**
1287 * @callback_method_impl{FNPGMPHYSNEMCHECKPAGE,
1288 * Worker for nemR3WinHandleMemoryAccess; pvUser points to a
1289 * NEMHCWINHMACPCCSTATE structure. }
1290 */
1291NEM_TMPL_STATIC DECLCALLBACK(int)
1292nemHCWinHandleMemoryAccessPageCheckerCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys, PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
1293{
1294 NEMHCWINHMACPCCSTATE *pState = (NEMHCWINHMACPCCSTATE *)pvUser;
1295 pState->fDidSomething = false;
1296 pState->fCanResume = false;
1297
1298 /* If A20 is disabled, we may need to make another query on the masked
1299 page to get the correct protection information. */
1300 uint8_t u2State = pInfo->u2NemState;
1301 RTGCPHYS GCPhysSrc;
1302#ifdef NEM_WIN_WITH_A20
1303 if ( pVM->nem.s.fA20Enabled
1304 || !NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
1305#endif
1306 GCPhysSrc = GCPhys;
1307#ifdef NEM_WIN_WITH_A20
1308 else
1309 {
1310 GCPhysSrc = GCPhys & ~(RTGCPHYS)RT_BIT_32(20);
1311 PGMPHYSNEMPAGEINFO Info2;
1312 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhysSrc, pState->fWriteAccess, &Info2, NULL, NULL);
1313 AssertRCReturn(rc, rc);
1314
1315 *pInfo = Info2;
1316 pInfo->u2NemState = u2State;
1317 }
1318#endif
1319
1320 /*
1321 * Consolidate current page state with actual page protection and access type.
1322 * We don't really consider downgrades here, as they shouldn't happen.
1323 */
1324 /** @todo Someone at microsoft please explain:
1325 * I'm not sure WTF was going on, but I ended up in a loop if I remapped a
1326 * readonly page as writable (unmap, then map again). Specifically, this was an
1327 * issue with the big VRAM mapping at 0xe0000000 when booing DSL 4.4.1. So, in
1328 * a hope to work around that we no longer pre-map anything, just unmap stuff
1329 * and do it lazily here. And here we will first unmap, restart, and then remap
1330 * with new protection or backing.
1331 */
1332 int rc;
1333 switch (u2State)
1334 {
1335 case NEM_WIN_PAGE_STATE_UNMAPPED:
1336 case NEM_WIN_PAGE_STATE_NOT_SET:
1337 if (pInfo->fNemProt == NEM_PAGE_PROT_NONE)
1338 {
1339 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #1\n", GCPhys));
1340 return VINF_SUCCESS;
1341 }
1342
1343 /* Don't bother remapping it if it's a write request to a non-writable page. */
1344 if ( pState->fWriteAccess
1345 && !(pInfo->fNemProt & NEM_PAGE_PROT_WRITE))
1346 {
1347 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #1w\n", GCPhys));
1348 return VINF_SUCCESS;
1349 }
1350
1351 /* Map the page. */
1352 rc = nemHCNativeSetPhysPage(pVM,
1353 pVCpu,
1354 GCPhysSrc & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
1355 GCPhys & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
1356 pInfo->fNemProt,
1357 &u2State,
1358 true /*fBackingState*/);
1359 pInfo->u2NemState = u2State;
1360 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - synced => %s + %Rrc\n",
1361 GCPhys, g_apszPageStates[u2State], rc));
1362 pState->fDidSomething = true;
1363 pState->fCanResume = true;
1364 return rc;
1365
1366 case NEM_WIN_PAGE_STATE_READABLE:
1367 if ( !(pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
1368 && (pInfo->fNemProt & (NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE)))
1369 {
1370 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #2\n", GCPhys));
1371 return VINF_SUCCESS;
1372 }
1373
1374 break;
1375
1376 case NEM_WIN_PAGE_STATE_WRITABLE:
1377 if (pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
1378 {
1379 if (pInfo->u2OldNemState == NEM_WIN_PAGE_STATE_WRITABLE)
1380 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #3a\n", GCPhys));
1381 else
1382 {
1383 pState->fCanResume = true;
1384 Log4(("nemHCWinHandleMemoryAccessPageCheckerCallback: %RGp - #3b (%s -> %s)\n",
1385 GCPhys, g_apszPageStates[pInfo->u2OldNemState], g_apszPageStates[u2State]));
1386 }
1387 return VINF_SUCCESS;
1388 }
1389 break;
1390
1391 default:
1392 AssertLogRelMsgFailedReturn(("u2State=%#x\n", u2State), VERR_NEM_IPE_4);
1393 }
1394
1395 /*
1396 * Unmap and restart the instruction.
1397 * If this fails, which it does every so often, just unmap everything for now.
1398 */
1399 /** @todo figure out whether we mess up the state or if it's WHv. */
1400 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
1401 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
1402 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
1403 if (SUCCEEDED(hrc))
1404 {
1405 pState->fDidSomething = true;
1406 pState->fCanResume = true;
1407 pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
1408 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
1409 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
1410 Log5(("NEM GPA unmapped/exit: %RGp (was %s, cMappedPages=%u)\n", GCPhys, g_apszPageStates[u2State], cMappedPages));
1411 return VINF_SUCCESS;
1412 }
1413 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
1414 LogRel(("nemHCWinHandleMemoryAccessPageCheckerCallback/unmap: GCPhysDst=%RGp %s hrc=%Rhrc (%#x)\n",
1415 GCPhys, g_apszPageStates[u2State], hrc, hrc));
1416 return VERR_NEM_UNMAP_PAGES_FAILED;
1417}
1418
1419
1420/**
1421 * Wrapper around nemHCWinCopyStateFromHyperV.
1422 *
1423 * Unlike the wrapped APIs, this checks whether it's necessary.
1424 *
1425 * @returns VBox strict status code.
1426 * @param pVCpu The cross context per CPU structure.
1427 * @param fWhat What to import.
1428 * @param pszCaller Who is doing the importing.
1429 */
1430DECLINLINE(VBOXSTRICTRC) nemHCWinImportStateIfNeededStrict(PVMCPUCC pVCpu, uint64_t fWhat, const char *pszCaller)
1431{
1432 if (pVCpu->cpum.GstCtx.fExtrn & fWhat)
1433 {
1434 RT_NOREF(pszCaller);
1435 int rc = nemHCWinCopyStateFromHyperV(pVCpu->pVMR3, pVCpu, fWhat);
1436 AssertRCReturn(rc, rc);
1437 }
1438 return VINF_SUCCESS;
1439}
1440
1441
1442/**
1443 * Copies register state from the (common) exit context.
1444 *
1445 * ASSUMES no state copied yet.
1446 *
1447 * @param pVCpu The cross context per CPU structure.
1448 * @param pExitCtx The common exit context.
1449 * @sa nemHCWinCopyStateFromX64Header
1450 */
1451DECLINLINE(void) nemR3WinCopyStateFromX64Header(PVMCPUCC pVCpu, WHV_VP_EXIT_CONTEXT const *pExitCtx)
1452{
1453 Assert( (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_INHIBIT_INT))
1454 == (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_INHIBIT_INT));
1455
1456 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.cs, pExitCtx->Cs);
1457 pVCpu->cpum.GstCtx.rip = pExitCtx->Rip;
1458 pVCpu->cpum.GstCtx.rflags.u = pExitCtx->Rflags;
1459 pVCpu->nem.s.fLastInterruptShadow = CPUMUpdateInterruptShadowEx(&pVCpu->cpum.GstCtx,
1460 pExitCtx->ExecutionState.InterruptShadow,
1461 pExitCtx->Rip);
1462 APICSetTpr(pVCpu, pExitCtx->Cr8 << 4);
1463
1464 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_APIC_TPR);
1465}
1466
1467
1468/**
1469 * Deals with memory access exits (WHvRunVpExitReasonMemoryAccess).
1470 *
1471 * @returns Strict VBox status code.
1472 * @param pVM The cross context VM structure.
1473 * @param pVCpu The cross context per CPU structure.
1474 * @param pExit The VM exit information to handle.
1475 * @sa nemHCWinHandleMessageMemory
1476 */
1477NEM_TMPL_STATIC VBOXSTRICTRC
1478nemR3WinHandleExitMemory(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
1479{
1480 uint64_t const uHostTsc = ASMReadTSC();
1481 Assert(pExit->MemoryAccess.AccessInfo.AccessType != 3);
1482
1483 /*
1484 * Whatever we do, we must clear pending event injection upon resume.
1485 */
1486 if (pExit->VpContext.ExecutionState.InterruptionPending)
1487 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
1488
1489 /*
1490 * Ask PGM for information about the given GCPhys. We need to check if we're
1491 * out of sync first.
1492 */
1493 NEMHCWINHMACPCCSTATE State = { pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite, false, false };
1494 PGMPHYSNEMPAGEINFO Info;
1495 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, pExit->MemoryAccess.Gpa, State.fWriteAccess, &Info,
1496 nemHCWinHandleMemoryAccessPageCheckerCallback, &State);
1497 if (RT_SUCCESS(rc))
1498 {
1499 if (Info.fNemProt & ( pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
1500 ? NEM_PAGE_PROT_WRITE : NEM_PAGE_PROT_READ))
1501 {
1502 if (State.fCanResume)
1503 {
1504 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp (=>%RHp) %s fProt=%u%s%s%s; restarting (%s)\n",
1505 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1506 pExit->MemoryAccess.Gpa, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
1507 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
1508 State.fDidSomething ? "" : " no-change", g_apszHvInterceptAccessTypes[pExit->MemoryAccess.AccessInfo.AccessType]));
1509 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_MEMORY_ACCESS),
1510 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, uHostTsc);
1511 return VINF_SUCCESS;
1512 }
1513 }
1514 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp (=>%RHp) %s fProt=%u%s%s%s; emulating (%s)\n",
1515 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1516 pExit->MemoryAccess.Gpa, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
1517 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
1518 State.fDidSomething ? "" : " no-change", g_apszHvInterceptAccessTypes[pExit->MemoryAccess.AccessInfo.AccessType]));
1519 }
1520 else
1521 Log4(("MemExit/%u: %04x:%08RX64/%s: %RGp rc=%Rrc%s; emulating (%s)\n",
1522 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1523 pExit->MemoryAccess.Gpa, rc, State.fDidSomething ? " modified-backing" : "",
1524 g_apszHvInterceptAccessTypes[pExit->MemoryAccess.AccessInfo.AccessType]));
1525
1526 /*
1527 * Emulate the memory access, either access handler or special memory.
1528 */
1529 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
1530 pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
1531 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_WRITE)
1532 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_READ),
1533 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, uHostTsc);
1534 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1535 rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
1536 AssertRCReturn(rc, rc);
1537 if (pExit->VpContext.ExecutionState.Reserved0 || pExit->VpContext.ExecutionState.Reserved1)
1538 Log(("MemExit/Hdr/State: Reserved0=%#x Reserved1=%#x\n", pExit->VpContext.ExecutionState.Reserved0, pExit->VpContext.ExecutionState.Reserved1));
1539
1540 VBOXSTRICTRC rcStrict;
1541 if (!pExitRec)
1542 {
1543 //if (pMsg->InstructionByteCount > 0)
1544 // Log4(("InstructionByteCount=%#x %.16Rhxs\n", pMsg->InstructionByteCount, pMsg->InstructionBytes));
1545 if (pExit->MemoryAccess.InstructionByteCount > 0)
1546 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, pExit->VpContext.Rip, pExit->MemoryAccess.InstructionBytes, pExit->MemoryAccess.InstructionByteCount);
1547 else
1548 rcStrict = IEMExecOne(pVCpu);
1549 /** @todo do we need to do anything wrt debugging here? */
1550 }
1551 else
1552 {
1553 /* Frequent access or probing. */
1554 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
1555 Log4(("MemExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
1556 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1557 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
1558 }
1559 return rcStrict;
1560}
1561
1562
1563/**
1564 * Deals with I/O port access exits (WHvRunVpExitReasonX64IoPortAccess).
1565 *
1566 * @returns Strict VBox status code.
1567 * @param pVM The cross context VM structure.
1568 * @param pVCpu The cross context per CPU structure.
1569 * @param pExit The VM exit information to handle.
1570 * @sa nemHCWinHandleMessageIoPort
1571 */
1572NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitIoPort(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
1573{
1574 Assert( pExit->IoPortAccess.AccessInfo.AccessSize == 1
1575 || pExit->IoPortAccess.AccessInfo.AccessSize == 2
1576 || pExit->IoPortAccess.AccessInfo.AccessSize == 4);
1577
1578 /*
1579 * Whatever we do, we must clear pending event injection upon resume.
1580 */
1581 if (pExit->VpContext.ExecutionState.InterruptionPending)
1582 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
1583
1584 /*
1585 * Add history first to avoid two paths doing EMHistoryExec calls.
1586 */
1587 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
1588 !pExit->IoPortAccess.AccessInfo.StringOp
1589 ? ( pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
1590 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_WRITE)
1591 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_READ))
1592 : ( pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
1593 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_WRITE)
1594 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_READ)),
1595 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
1596 if (!pExitRec)
1597 {
1598 VBOXSTRICTRC rcStrict;
1599 if (!pExit->IoPortAccess.AccessInfo.StringOp)
1600 {
1601 /*
1602 * Simple port I/O.
1603 */
1604 static uint32_t const s_fAndMask[8] =
1605 { UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
1606 uint32_t const fAndMask = s_fAndMask[pExit->IoPortAccess.AccessInfo.AccessSize];
1607 if (pExit->IoPortAccess.AccessInfo.IsWrite)
1608 {
1609 rcStrict = IOMIOPortWrite(pVM, pVCpu, pExit->IoPortAccess.PortNumber,
1610 (uint32_t)pExit->IoPortAccess.Rax & fAndMask,
1611 pExit->IoPortAccess.AccessInfo.AccessSize);
1612 Log4(("IOExit/%u: %04x:%08RX64/%s: OUT %#x, %#x LB %u rcStrict=%Rrc\n",
1613 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1614 pExit->IoPortAccess.PortNumber, (uint32_t)pExit->IoPortAccess.Rax & fAndMask,
1615 pExit->IoPortAccess.AccessInfo.AccessSize, VBOXSTRICTRC_VAL(rcStrict) ));
1616 if (IOM_SUCCESS(rcStrict))
1617 {
1618 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1619 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 1);
1620 }
1621 }
1622 else
1623 {
1624 uint32_t uValue = 0;
1625 rcStrict = IOMIOPortRead(pVM, pVCpu, pExit->IoPortAccess.PortNumber, &uValue,
1626 pExit->IoPortAccess.AccessInfo.AccessSize);
1627 Log4(("IOExit/%u: %04x:%08RX64/%s: IN %#x LB %u -> %#x, rcStrict=%Rrc\n",
1628 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1629 pExit->IoPortAccess.PortNumber, pExit->IoPortAccess.AccessInfo.AccessSize, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
1630 if (IOM_SUCCESS(rcStrict))
1631 {
1632 if (pExit->IoPortAccess.AccessInfo.AccessSize != 4)
1633 pVCpu->cpum.GstCtx.rax = (pExit->IoPortAccess.Rax & ~(uint64_t)fAndMask) | (uValue & fAndMask);
1634 else
1635 pVCpu->cpum.GstCtx.rax = uValue;
1636 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
1637 Log4(("IOExit/%u: RAX %#RX64 -> %#RX64\n", pVCpu->idCpu, pExit->IoPortAccess.Rax, pVCpu->cpum.GstCtx.rax));
1638 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1639 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 1);
1640 }
1641 }
1642 }
1643 else
1644 {
1645 /*
1646 * String port I/O.
1647 */
1648 /** @todo Someone at Microsoft please explain how we can get the address mode
1649 * from the IoPortAccess.VpContext. CS.Attributes is only sufficient for
1650 * getting the default mode, it can always be overridden by a prefix. This
1651 * forces us to interpret the instruction from opcodes, which is suboptimal.
1652 * Both AMD-V and VT-x includes the address size in the exit info, at least on
1653 * CPUs that are reasonably new.
1654 *
1655 * Of course, it's possible this is an undocumented and we just need to do some
1656 * experiments to figure out how it's communicated. Alternatively, we can scan
1657 * the opcode bytes for possible evil prefixes.
1658 */
1659 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1660 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
1661 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
1662 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pExit->IoPortAccess.Ds);
1663 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pExit->IoPortAccess.Es);
1664 pVCpu->cpum.GstCtx.rax = pExit->IoPortAccess.Rax;
1665 pVCpu->cpum.GstCtx.rcx = pExit->IoPortAccess.Rcx;
1666 pVCpu->cpum.GstCtx.rdi = pExit->IoPortAccess.Rdi;
1667 pVCpu->cpum.GstCtx.rsi = pExit->IoPortAccess.Rsi;
1668 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
1669 AssertRCReturn(rc, rc);
1670
1671 Log4(("IOExit/%u: %04x:%08RX64/%s: %s%s %#x LB %u (emulating)\n",
1672 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1673 pExit->IoPortAccess.AccessInfo.RepPrefix ? "REP " : "",
1674 pExit->IoPortAccess.AccessInfo.IsWrite ? "OUTS" : "INS",
1675 pExit->IoPortAccess.PortNumber, pExit->IoPortAccess.AccessInfo.AccessSize ));
1676 rcStrict = IEMExecOne(pVCpu);
1677 }
1678 if (IOM_SUCCESS(rcStrict))
1679 {
1680 /*
1681 * Do debug checks.
1682 */
1683 if ( pExit->VpContext.ExecutionState.DebugActive /** @todo Microsoft: Does DebugActive this only reflect DR7? */
1684 || (pExit->VpContext.Rflags & X86_EFL_TF)
1685 || DBGFBpIsHwIoArmed(pVM) )
1686 {
1687 /** @todo Debugging. */
1688 }
1689 }
1690 return rcStrict;
1691 }
1692
1693 /*
1694 * Frequent exit or something needing probing.
1695 * Get state and call EMHistoryExec.
1696 */
1697 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1698 if (!pExit->IoPortAccess.AccessInfo.StringOp)
1699 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
1700 else
1701 {
1702 pVCpu->cpum.GstCtx.fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
1703 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
1704 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pExit->IoPortAccess.Ds);
1705 NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pExit->IoPortAccess.Es);
1706 pVCpu->cpum.GstCtx.rcx = pExit->IoPortAccess.Rcx;
1707 pVCpu->cpum.GstCtx.rdi = pExit->IoPortAccess.Rdi;
1708 pVCpu->cpum.GstCtx.rsi = pExit->IoPortAccess.Rsi;
1709 }
1710 pVCpu->cpum.GstCtx.rax = pExit->IoPortAccess.Rax;
1711 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
1712 AssertRCReturn(rc, rc);
1713 Log4(("IOExit/%u: %04x:%08RX64/%s: %s%s%s %#x LB %u -> EMHistoryExec\n",
1714 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1715 pExit->IoPortAccess.AccessInfo.RepPrefix ? "REP " : "",
1716 pExit->IoPortAccess.AccessInfo.IsWrite ? "OUT" : "IN",
1717 pExit->IoPortAccess.AccessInfo.StringOp ? "S" : "",
1718 pExit->IoPortAccess.PortNumber, pExit->IoPortAccess.AccessInfo.AccessSize));
1719 VBOXSTRICTRC rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
1720 Log4(("IOExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
1721 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1722 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
1723 return rcStrict;
1724}
1725
1726
1727/**
1728 * Deals with interrupt window exits (WHvRunVpExitReasonX64InterruptWindow).
1729 *
1730 * @returns Strict VBox status code.
1731 * @param pVM The cross context VM structure.
1732 * @param pVCpu The cross context per CPU structure.
1733 * @param pExit The VM exit information to handle.
1734 * @sa nemHCWinHandleMessageInterruptWindow
1735 */
1736NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitInterruptWindow(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
1737{
1738 /*
1739 * Assert message sanity.
1740 */
1741 AssertMsg( pExit->InterruptWindow.DeliverableType == WHvX64PendingInterrupt
1742 || pExit->InterruptWindow.DeliverableType == WHvX64PendingNmi,
1743 ("%#x\n", pExit->InterruptWindow.DeliverableType));
1744
1745 /*
1746 * Just copy the state we've got and handle it in the loop for now.
1747 */
1748 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_INTTERRUPT_WINDOW),
1749 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
1750
1751 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1752 Log4(("IntWinExit/%u: %04x:%08RX64/%s: %u IF=%d InterruptShadow=%d CR8=%#x\n",
1753 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1754 pExit->InterruptWindow.DeliverableType, RT_BOOL(pExit->VpContext.Rflags & X86_EFL_IF),
1755 pExit->VpContext.ExecutionState.InterruptShadow, pExit->VpContext.Cr8));
1756
1757 /** @todo call nemHCWinHandleInterruptFF */
1758 RT_NOREF(pVM);
1759 return VINF_SUCCESS;
1760}
1761
1762
1763/**
1764 * Deals with CPUID exits (WHvRunVpExitReasonX64Cpuid).
1765 *
1766 * @returns Strict VBox status code.
1767 * @param pVM The cross context VM structure.
1768 * @param pVCpu The cross context per CPU structure.
1769 * @param pExit The VM exit information to handle.
1770 * @sa nemHCWinHandleMessageCpuId
1771 */
1772NEM_TMPL_STATIC VBOXSTRICTRC
1773nemR3WinHandleExitCpuId(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
1774{
1775 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_CPUID),
1776 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
1777 if (!pExitRec)
1778 {
1779 /*
1780 * Soak up state and execute the instruction.
1781 */
1782 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1783 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu,
1784 IEM_CPUMCTX_EXTRN_EXEC_DECODED_NO_MEM_MASK
1785 | CPUMCTX_EXTRN_CR3, /* May call PGMChangeMode() requiring cr3 (due to cr0 being imported). */
1786 "CPUID");
1787 if (rcStrict == VINF_SUCCESS)
1788 {
1789 /* Copy in the low register values (top is always cleared). */
1790 pVCpu->cpum.GstCtx.rax = (uint32_t)pExit->CpuidAccess.Rax;
1791 pVCpu->cpum.GstCtx.rcx = (uint32_t)pExit->CpuidAccess.Rcx;
1792 pVCpu->cpum.GstCtx.rdx = (uint32_t)pExit->CpuidAccess.Rdx;
1793 pVCpu->cpum.GstCtx.rbx = (uint32_t)pExit->CpuidAccess.Rbx;
1794 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
1795
1796 /* Execute the decoded instruction. */
1797 rcStrict = IEMExecDecodedCpuid(pVCpu, pExit->VpContext.InstructionLength);
1798
1799 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: rax=%08RX64 / rcx=%08RX64 / rdx=%08RX64 / rbx=%08RX64 -> %08RX32 / %08RX32 / %08RX32 / %08RX32 (hv: %08RX64 / %08RX64 / %08RX64 / %08RX64)\n",
1800 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1801 pExit->CpuidAccess.Rax, pExit->CpuidAccess.Rcx, pExit->CpuidAccess.Rdx, pExit->CpuidAccess.Rbx,
1802 pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx, pVCpu->cpum.GstCtx.edx, pVCpu->cpum.GstCtx.ebx,
1803 pExit->CpuidAccess.DefaultResultRax, pExit->CpuidAccess.DefaultResultRcx, pExit->CpuidAccess.DefaultResultRdx, pExit->CpuidAccess.DefaultResultRbx));
1804 }
1805
1806 RT_NOREF_PV(pVM);
1807 return rcStrict;
1808 }
1809
1810 /*
1811 * Frequent exit or something needing probing.
1812 * Get state and call EMHistoryExec.
1813 */
1814 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1815 pVCpu->cpum.GstCtx.rax = pExit->CpuidAccess.Rax;
1816 pVCpu->cpum.GstCtx.rcx = pExit->CpuidAccess.Rcx;
1817 pVCpu->cpum.GstCtx.rdx = pExit->CpuidAccess.Rdx;
1818 pVCpu->cpum.GstCtx.rbx = pExit->CpuidAccess.Rbx;
1819 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
1820 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: rax=%08RX64 / rcx=%08RX64 / rdx=%08RX64 / rbx=%08RX64 (hv: %08RX64 / %08RX64 / %08RX64 / %08RX64) ==> EMHistoryExec\n",
1821 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1822 pExit->CpuidAccess.Rax, pExit->CpuidAccess.Rcx, pExit->CpuidAccess.Rdx, pExit->CpuidAccess.Rbx,
1823 pExit->CpuidAccess.DefaultResultRax, pExit->CpuidAccess.DefaultResultRcx, pExit->CpuidAccess.DefaultResultRdx, pExit->CpuidAccess.DefaultResultRbx));
1824 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
1825 AssertRCReturn(rc, rc);
1826 VBOXSTRICTRC rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
1827 Log4(("CpuIdExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
1828 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1829 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
1830 return rcStrict;
1831}
1832
1833
1834/**
1835 * Deals with MSR access exits (WHvRunVpExitReasonX64MsrAccess).
1836 *
1837 * @returns Strict VBox status code.
1838 * @param pVM The cross context VM structure.
1839 * @param pVCpu The cross context per CPU structure.
1840 * @param pExit The VM exit information to handle.
1841 * @sa nemHCWinHandleMessageMsr
1842 */
1843NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitMsr(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
1844{
1845 /*
1846 * Check CPL as that's common to both RDMSR and WRMSR.
1847 */
1848 VBOXSTRICTRC rcStrict;
1849 if (pExit->VpContext.ExecutionState.Cpl == 0)
1850 {
1851 /*
1852 * Get all the MSR state. Since we're getting EFER, we also need to
1853 * get CR0, CR4 and CR3.
1854 */
1855 PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
1856 pExit->MsrAccess.AccessInfo.IsWrite
1857 ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_WRITE)
1858 : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_READ),
1859 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
1860 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
1861 rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu,
1862 (!pExitRec ? 0 : IEM_CPUMCTX_EXTRN_MUST_MASK)
1863 | CPUMCTX_EXTRN_ALL_MSRS | CPUMCTX_EXTRN_CR0
1864 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4,
1865 "MSRs");
1866 if (rcStrict == VINF_SUCCESS)
1867 {
1868 if (!pExitRec)
1869 {
1870 /*
1871 * Handle writes.
1872 */
1873 if (pExit->MsrAccess.AccessInfo.IsWrite)
1874 {
1875 rcStrict = CPUMSetGuestMsr(pVCpu, pExit->MsrAccess.MsrNumber,
1876 RT_MAKE_U64((uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx));
1877 Log4(("MsrExit/%u: %04x:%08RX64/%s: WRMSR %08x, %08x:%08x -> %Rrc\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
1878 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->MsrAccess.MsrNumber,
1879 (uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx, VBOXSTRICTRC_VAL(rcStrict) ));
1880 if (rcStrict == VINF_SUCCESS)
1881 {
1882 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 2);
1883 return VINF_SUCCESS;
1884 }
1885 LogRel(("MsrExit/%u: %04x:%08RX64/%s: WRMSR %08x, %08x:%08x -> %Rrc!\n", pVCpu->idCpu,
1886 pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1887 pExit->MsrAccess.MsrNumber, (uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx,
1888 VBOXSTRICTRC_VAL(rcStrict) ));
1889 }
1890 /*
1891 * Handle reads.
1892 */
1893 else
1894 {
1895 uint64_t uValue = 0;
1896 rcStrict = CPUMQueryGuestMsr(pVCpu, pExit->MsrAccess.MsrNumber, &uValue);
1897 Log4(("MsrExit/%u: %04x:%08RX64/%s: RDMSR %08x -> %08RX64 / %Rrc\n", pVCpu->idCpu,
1898 pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1899 pExit->MsrAccess.MsrNumber, uValue, VBOXSTRICTRC_VAL(rcStrict) ));
1900 if (rcStrict == VINF_SUCCESS)
1901 {
1902 pVCpu->cpum.GstCtx.rax = (uint32_t)uValue;
1903 pVCpu->cpum.GstCtx.rdx = uValue >> 32;
1904 pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX);
1905 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, &pExit->VpContext, 2);
1906 return VINF_SUCCESS;
1907 }
1908 LogRel(("MsrExit/%u: %04x:%08RX64/%s: RDMSR %08x -> %08RX64 / %Rrc\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
1909 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->MsrAccess.MsrNumber,
1910 uValue, VBOXSTRICTRC_VAL(rcStrict) ));
1911 }
1912 }
1913 else
1914 {
1915 /*
1916 * Handle frequent exit or something needing probing.
1917 */
1918 Log4(("MsrExit/%u: %04x:%08RX64/%s: %sMSR %#08x\n",
1919 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1920 pExit->MsrAccess.AccessInfo.IsWrite ? "WR" : "RD", pExit->MsrAccess.MsrNumber));
1921 rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
1922 Log4(("MsrExit/%u: %04x:%08RX64/%s: EMHistoryExec -> %Rrc + %04x:%08RX64\n",
1923 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1924 VBOXSTRICTRC_VAL(rcStrict), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip));
1925 return rcStrict;
1926 }
1927 }
1928 else
1929 {
1930 LogRel(("MsrExit/%u: %04x:%08RX64/%s: %sMSR %08x -> %Rrc - msr state import\n",
1931 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
1932 pExit->MsrAccess.AccessInfo.IsWrite ? "WR" : "RD", pExit->MsrAccess.MsrNumber, VBOXSTRICTRC_VAL(rcStrict) ));
1933 return rcStrict;
1934 }
1935 }
1936 else if (pExit->MsrAccess.AccessInfo.IsWrite)
1937 Log4(("MsrExit/%u: %04x:%08RX64/%s: CPL %u -> #GP(0); WRMSR %08x, %08x:%08x\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
1938 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.ExecutionState.Cpl,
1939 pExit->MsrAccess.MsrNumber, (uint32_t)pExit->MsrAccess.Rax, (uint32_t)pExit->MsrAccess.Rdx ));
1940 else
1941 Log4(("MsrExit/%u: %04x:%08RX64/%s: CPL %u -> #GP(0); RDMSR %08x\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
1942 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.ExecutionState.Cpl,
1943 pExit->MsrAccess.MsrNumber));
1944
1945 /*
1946 * If we get down here, we're supposed to #GP(0).
1947 */
1948 rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL_MSRS, "MSR");
1949 if (rcStrict == VINF_SUCCESS)
1950 {
1951 rcStrict = IEMInjectTrap(pVCpu, X86_XCPT_GP, TRPM_TRAP, 0, 0, 0);
1952 if (rcStrict == VINF_IEM_RAISED_XCPT)
1953 rcStrict = VINF_SUCCESS;
1954 else if (rcStrict != VINF_SUCCESS)
1955 Log4(("MsrExit/%u: Injecting #GP(0) failed: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict) ));
1956 }
1957
1958 RT_NOREF_PV(pVM);
1959 return rcStrict;
1960}
1961
1962
1963/**
1964 * Worker for nemHCWinHandleMessageException & nemR3WinHandleExitException that
1965 * checks if the given opcodes are of interest at all.
1966 *
1967 * @returns true if interesting, false if not.
1968 * @param cbOpcodes Number of opcode bytes available.
1969 * @param pbOpcodes The opcode bytes.
1970 * @param f64BitMode Whether we're in 64-bit mode.
1971 */
1972DECLINLINE(bool) nemHcWinIsInterestingUndefinedOpcode(uint8_t cbOpcodes, uint8_t const *pbOpcodes, bool f64BitMode)
1973{
1974 /*
1975 * Currently only interested in VMCALL and VMMCALL.
1976 */
1977 while (cbOpcodes >= 3)
1978 {
1979 switch (pbOpcodes[0])
1980 {
1981 case 0x0f:
1982 switch (pbOpcodes[1])
1983 {
1984 case 0x01:
1985 switch (pbOpcodes[2])
1986 {
1987 case 0xc1: /* 0f 01 c1 VMCALL */
1988 return true;
1989 case 0xd9: /* 0f 01 d9 VMMCALL */
1990 return true;
1991 default:
1992 break;
1993 }
1994 break;
1995 }
1996 break;
1997
1998 default:
1999 return false;
2000
2001 /* prefixes */
2002 case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
2003 case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
2004 if (!f64BitMode)
2005 return false;
2006 RT_FALL_THRU();
2007 case X86_OP_PRF_CS:
2008 case X86_OP_PRF_SS:
2009 case X86_OP_PRF_DS:
2010 case X86_OP_PRF_ES:
2011 case X86_OP_PRF_FS:
2012 case X86_OP_PRF_GS:
2013 case X86_OP_PRF_SIZE_OP:
2014 case X86_OP_PRF_SIZE_ADDR:
2015 case X86_OP_PRF_LOCK:
2016 case X86_OP_PRF_REPZ:
2017 case X86_OP_PRF_REPNZ:
2018 cbOpcodes--;
2019 pbOpcodes++;
2020 continue;
2021 }
2022 break;
2023 }
2024 return false;
2025}
2026
2027
2028/**
2029 * Copies state included in a exception intercept exit.
2030 *
2031 * @param pVCpu The cross context per CPU structure.
2032 * @param pExit The VM exit information.
2033 * @param fClearXcpt Clear pending exception.
2034 */
2035DECLINLINE(void) nemR3WinCopyStateFromExceptionMessage(PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit, bool fClearXcpt)
2036{
2037 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2038 if (fClearXcpt)
2039 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
2040}
2041
2042
2043/**
2044 * Advances the guest RIP by the number of bytes specified in @a cb.
2045 *
2046 * @param pVCpu The cross context virtual CPU structure.
2047 * @param cb RIP increment value in bytes.
2048 */
2049DECLINLINE(void) nemHcWinAdvanceRip(PVMCPUCC pVCpu, uint32_t cb)
2050{
2051 PCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
2052 pCtx->rip += cb;
2053 /** @todo Why not clear RF too? */
2054 CPUMClearInterruptShadow(&pVCpu->cpum.GstCtx);
2055}
2056
2057
2058/**
2059 * Hacks its way around the lovely mesa driver's backdoor accesses.
2060 *
2061 * @sa hmR0VmxHandleMesaDrvGp
2062 * @sa hmR0SvmHandleMesaDrvGp
2063 */
2064static int nemHcWinHandleMesaDrvGp(PVMCPUCC pVCpu, PCPUMCTX pCtx)
2065{
2066 Assert(!(pCtx->fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_CS | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_GPRS_MASK)));
2067 RT_NOREF(pCtx);
2068
2069 /* For now we'll just skip the instruction. */
2070 nemHcWinAdvanceRip(pVCpu, 1);
2071 return VINF_SUCCESS;
2072}
2073
2074
2075/**
2076 * Checks if the \#GP'ing instruction is the mesa driver doing it's lovely
2077 * backdoor logging w/o checking what it is running inside.
2078 *
2079 * This recognizes an "IN EAX,DX" instruction executed in flat ring-3, with the
2080 * backdoor port and magic numbers loaded in registers.
2081 *
2082 * @returns true if it is, false if it isn't.
2083 * @sa hmR0VmxIsMesaDrvGp
2084 * @sa hmR0SvmIsMesaDrvGp
2085 */
2086DECLINLINE(bool) nemHcWinIsMesaDrvGp(PVMCPUCC pVCpu, PCPUMCTX pCtx, const uint8_t *pbInsn, uint32_t cbInsn)
2087{
2088 /* #GP(0) is already checked by caller. */
2089
2090 /* Check magic and port. */
2091 Assert(!(pCtx->fExtrn & (CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RAX)));
2092 if (pCtx->dx != UINT32_C(0x5658))
2093 return false;
2094 if (pCtx->rax != UINT32_C(0x564d5868))
2095 return false;
2096
2097 /* Flat ring-3 CS. */
2098 if (CPUMGetGuestCPL(pVCpu) != 3)
2099 return false;
2100 if (pCtx->cs.u64Base != 0)
2101 return false;
2102
2103 /* 0xed: IN eAX,dx */
2104 if (cbInsn < 1) /* Play safe (shouldn't happen). */
2105 {
2106 uint8_t abInstr[1];
2107 int rc = PGMPhysSimpleReadGCPtr(pVCpu, abInstr, pCtx->rip, sizeof(abInstr));
2108 if (RT_FAILURE(rc))
2109 return false;
2110 if (abInstr[0] != 0xed)
2111 return false;
2112 }
2113 else
2114 {
2115 if (pbInsn[0] != 0xed)
2116 return false;
2117 }
2118
2119 return true;
2120}
2121
2122
2123/**
2124 * Deals with MSR access exits (WHvRunVpExitReasonException).
2125 *
2126 * @returns Strict VBox status code.
2127 * @param pVM The cross context VM structure.
2128 * @param pVCpu The cross context per CPU structure.
2129 * @param pExit The VM exit information to handle.
2130 * @sa nemR3WinHandleExitException
2131 */
2132NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitException(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2133{
2134 /*
2135 * Get most of the register state since we'll end up making IEM inject the
2136 * event. The exception isn't normally flaged as a pending event, so duh.
2137 *
2138 * Note! We can optimize this later with event injection.
2139 */
2140 Log4(("XcptExit/%u: %04x:%08RX64/%s: %x errcd=%#x parm=%RX64\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2141 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpException.ExceptionType,
2142 pExit->VpException.ErrorCode, pExit->VpException.ExceptionParameter ));
2143 nemR3WinCopyStateFromExceptionMessage(pVCpu, pExit, true /*fClearXcpt*/);
2144 uint64_t fWhat = NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM;
2145 if (pExit->VpException.ExceptionType == X86_XCPT_DB)
2146 fWhat |= CPUMCTX_EXTRN_DR0_DR3 | CPUMCTX_EXTRN_DR7 | CPUMCTX_EXTRN_DR6;
2147 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, fWhat, "Xcpt");
2148 if (rcStrict != VINF_SUCCESS)
2149 return rcStrict;
2150
2151 /*
2152 * Handle the intercept.
2153 */
2154 TRPMEVENT enmEvtType = TRPM_TRAP;
2155 switch (pExit->VpException.ExceptionType)
2156 {
2157 /*
2158 * We get undefined opcodes on VMMCALL(AMD) & VMCALL(Intel) instructions
2159 * and need to turn them over to GIM.
2160 *
2161 * Note! We do not check fGIMTrapXcptUD here ASSUMING that GIM only wants
2162 * #UD for handling non-native hypercall instructions. (IEM will
2163 * decode both and let the GIM provider decide whether to accept it.)
2164 */
2165 case X86_XCPT_UD:
2166 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUd);
2167 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_UD),
2168 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2169 if (nemHcWinIsInterestingUndefinedOpcode(pExit->VpException.InstructionByteCount, pExit->VpException.InstructionBytes,
2170 pExit->VpContext.ExecutionState.EferLma && pExit->VpContext.Cs.Long ))
2171 {
2172 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, pExit->VpContext.Rip,
2173 pExit->VpException.InstructionBytes,
2174 pExit->VpException.InstructionByteCount);
2175 Log4(("XcptExit/%u: %04x:%08RX64/%s: #UD -> emulated -> %Rrc\n",
2176 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip,
2177 nemR3WinExecStateToLogStr(&pExit->VpContext), VBOXSTRICTRC_VAL(rcStrict) ));
2178 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUdHandled);
2179 return rcStrict;
2180 }
2181
2182 Log4(("XcptExit/%u: %04x:%08RX64/%s: #UD [%.*Rhxs] -> re-injected\n", pVCpu->idCpu,
2183 pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext),
2184 pExit->VpException.InstructionByteCount, pExit->VpException.InstructionBytes ));
2185 break;
2186
2187 /*
2188 * Workaround the lovely mesa driver assuming that vmsvga means vmware
2189 * hypervisor and tries to log stuff to the host.
2190 */
2191 case X86_XCPT_GP:
2192 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionGp);
2193 /** @todo r=bird: Need workaround in IEM for this, right?
2194 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_GP),
2195 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC()); */
2196 if ( !pVCpu->nem.s.fTrapXcptGpForLovelyMesaDrv
2197 || !nemHcWinIsMesaDrvGp(pVCpu, &pVCpu->cpum.GstCtx, pExit->VpException.InstructionBytes,
2198 pExit->VpException.InstructionByteCount))
2199 {
2200#if 1 /** @todo Need to emulate instruction or we get a triple fault when trying to inject the \#GP... */
2201 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, pExit->VpContext.Rip,
2202 pExit->VpException.InstructionBytes,
2203 pExit->VpException.InstructionByteCount);
2204 Log4(("XcptExit/%u: %04x:%08RX64/%s: #GP -> emulated -> %Rrc\n",
2205 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip,
2206 nemR3WinExecStateToLogStr(&pExit->VpContext), VBOXSTRICTRC_VAL(rcStrict) ));
2207 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUdHandled);
2208 return rcStrict;
2209#else
2210 break;
2211#endif
2212 }
2213 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionGpMesa);
2214 return nemHcWinHandleMesaDrvGp(pVCpu, &pVCpu->cpum.GstCtx);
2215
2216 /*
2217 * Filter debug exceptions.
2218 */
2219 case X86_XCPT_DB:
2220 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionDb);
2221 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_DB),
2222 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2223 Log4(("XcptExit/%u: %04x:%08RX64/%s: #DB - TODO\n",
2224 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext) ));
2225 break;
2226
2227 case X86_XCPT_BP:
2228 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionBp);
2229 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_BP),
2230 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2231 Log4(("XcptExit/%u: %04x:%08RX64/%s: #BP - TODO - %u\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2232 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.InstructionLength));
2233 enmEvtType = TRPM_SOFTWARE_INT; /* We're at the INT3 instruction, not after it. */
2234 break;
2235
2236 /* This shouldn't happen. */
2237 default:
2238 AssertLogRelMsgFailedReturn(("ExceptionType=%#x\n", pExit->VpException.ExceptionType), VERR_IEM_IPE_6);
2239 }
2240
2241 /*
2242 * Inject it.
2243 */
2244 rcStrict = IEMInjectTrap(pVCpu, pExit->VpException.ExceptionType, enmEvtType, pExit->VpException.ErrorCode,
2245 pExit->VpException.ExceptionParameter /*??*/, pExit->VpContext.InstructionLength);
2246 Log4(("XcptExit/%u: %04x:%08RX64/%s: %#u -> injected -> %Rrc\n",
2247 pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip,
2248 nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpException.ExceptionType, VBOXSTRICTRC_VAL(rcStrict) ));
2249
2250 RT_NOREF_PV(pVM);
2251 return rcStrict;
2252}
2253
2254
2255/**
2256 * Deals with MSR access exits (WHvRunVpExitReasonUnrecoverableException).
2257 *
2258 * @returns Strict VBox status code.
2259 * @param pVM The cross context VM structure.
2260 * @param pVCpu The cross context per CPU structure.
2261 * @param pExit The VM exit information to handle.
2262 * @sa nemHCWinHandleMessageUnrecoverableException
2263 */
2264NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExitUnrecoverableException(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2265{
2266#if 0
2267 /*
2268 * Just copy the state we've got and handle it in the loop for now.
2269 */
2270 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2271 Log(("TripleExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_EM_TRIPLE_FAULT\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2272 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags));
2273 RT_NOREF_PV(pVM);
2274 return VINF_EM_TRIPLE_FAULT;
2275#else
2276 /*
2277 * Let IEM decide whether this is really it.
2278 */
2279 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_UNRECOVERABLE_EXCEPTION),
2280 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2281 nemR3WinCopyStateFromX64Header(pVCpu, &pExit->VpContext);
2282 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL, "TripleExit");
2283 if (rcStrict == VINF_SUCCESS)
2284 {
2285 rcStrict = IEMExecOne(pVCpu);
2286 if (rcStrict == VINF_SUCCESS)
2287 {
2288 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_SUCCESS\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2289 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags));
2290 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT; /* Make sure to reset pending #DB(0). */
2291 return VINF_SUCCESS;
2292 }
2293 if (rcStrict == VINF_EM_TRIPLE_FAULT)
2294 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> VINF_EM_TRIPLE_FAULT!\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2295 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
2296 else
2297 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> %Rrc (IEMExecOne)\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2298 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
2299 }
2300 else
2301 Log(("UnrecovExit/%u: %04x:%08RX64/%s: RFL=%#RX64 -> %Rrc (state import)\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
2302 pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.Rflags, VBOXSTRICTRC_VAL(rcStrict) ));
2303 RT_NOREF_PV(pVM);
2304 return rcStrict;
2305#endif
2306}
2307
2308
2309/**
2310 * Handles VM exits.
2311 *
2312 * @returns Strict VBox status code.
2313 * @param pVM The cross context VM structure.
2314 * @param pVCpu The cross context per CPU structure.
2315 * @param pExit The VM exit information to handle.
2316 * @sa nemHCWinHandleMessage
2317 */
2318NEM_TMPL_STATIC VBOXSTRICTRC nemR3WinHandleExit(PVMCC pVM, PVMCPUCC pVCpu, WHV_RUN_VP_EXIT_CONTEXT const *pExit)
2319{
2320 switch (pExit->ExitReason)
2321 {
2322 case WHvRunVpExitReasonMemoryAccess:
2323 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMemUnmapped);
2324 return nemR3WinHandleExitMemory(pVM, pVCpu, pExit);
2325
2326 case WHvRunVpExitReasonX64IoPortAccess:
2327 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitPortIo);
2328 return nemR3WinHandleExitIoPort(pVM, pVCpu, pExit);
2329
2330 case WHvRunVpExitReasonX64Halt:
2331 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitHalt);
2332 EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_HALT),
2333 pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
2334 Log4(("HaltExit/%u\n", pVCpu->idCpu));
2335 return VINF_EM_HALT;
2336
2337 case WHvRunVpExitReasonCanceled:
2338 Log4(("CanceledExit/%u\n", pVCpu->idCpu));
2339 return VINF_SUCCESS;
2340
2341 case WHvRunVpExitReasonX64InterruptWindow:
2342 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitInterruptWindow);
2343 return nemR3WinHandleExitInterruptWindow(pVM, pVCpu, pExit);
2344
2345 case WHvRunVpExitReasonX64Cpuid:
2346 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitCpuId);
2347 return nemR3WinHandleExitCpuId(pVM, pVCpu, pExit);
2348
2349 case WHvRunVpExitReasonX64MsrAccess:
2350 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitMsr);
2351 return nemR3WinHandleExitMsr(pVM, pVCpu, pExit);
2352
2353 case WHvRunVpExitReasonException:
2354 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitException);
2355 return nemR3WinHandleExitException(pVM, pVCpu, pExit);
2356
2357 case WHvRunVpExitReasonUnrecoverableException:
2358 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitUnrecoverable);
2359 return nemR3WinHandleExitUnrecoverableException(pVM, pVCpu, pExit);
2360
2361 case WHvRunVpExitReasonUnsupportedFeature:
2362 case WHvRunVpExitReasonInvalidVpRegisterValue:
2363 LogRel(("Unimplemented exit:\n%.*Rhxd\n", (int)sizeof(*pExit), pExit));
2364 AssertLogRelMsgFailedReturn(("Unexpected exit on CPU #%u: %#x\n%.32Rhxd\n",
2365 pVCpu->idCpu, pExit->ExitReason, pExit), VERR_NEM_IPE_3);
2366
2367 /* Undesired exits: */
2368 case WHvRunVpExitReasonNone:
2369 default:
2370 LogRel(("Unknown exit:\n%.*Rhxd\n", (int)sizeof(*pExit), pExit));
2371 AssertLogRelMsgFailedReturn(("Unknown exit on CPU #%u: %#x!\n", pVCpu->idCpu, pExit->ExitReason), VERR_NEM_IPE_3);
2372 }
2373}
2374
2375
2376/**
2377 * Deals with pending interrupt related force flags, may inject interrupt.
2378 *
2379 * @returns VBox strict status code.
2380 * @param pVM The cross context VM structure.
2381 * @param pVCpu The cross context per CPU structure.
2382 * @param pfInterruptWindows Where to return interrupt window flags.
2383 */
2384NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleInterruptFF(PVMCC pVM, PVMCPUCC pVCpu, uint8_t *pfInterruptWindows)
2385{
2386 Assert(!TRPMHasTrap(pVCpu));
2387 RT_NOREF_PV(pVM);
2388
2389 /*
2390 * First update APIC. We ASSUME this won't need TPR/CR8.
2391 */
2392 if (VMCPU_FF_TEST_AND_CLEAR(pVCpu, VMCPU_FF_UPDATE_APIC))
2393 {
2394 APICUpdatePendingInterrupts(pVCpu);
2395 if (!VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC
2396 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI))
2397 return VINF_SUCCESS;
2398 }
2399
2400 /*
2401 * We don't currently implement SMIs.
2402 */
2403 AssertReturn(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI), VERR_NEM_IPE_0);
2404
2405 /*
2406 * Check if we've got the minimum of state required for deciding whether we
2407 * can inject interrupts and NMIs. If we don't have it, get all we might require
2408 * for injection via IEM.
2409 */
2410 bool const fPendingNmi = VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI);
2411 uint64_t fNeedExtrn = CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS
2412 | (fPendingNmi ? CPUMCTX_EXTRN_INHIBIT_NMI : 0);
2413 if (pVCpu->cpum.GstCtx.fExtrn & fNeedExtrn)
2414 {
2415 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT, "IntFF");
2416 if (rcStrict != VINF_SUCCESS)
2417 return rcStrict;
2418 }
2419
2420 /*
2421 * NMI? Try deliver it first.
2422 */
2423 if (fPendingNmi)
2424 {
2425 if ( !CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx)
2426 && !CPUMAreInterruptsInhibitedByNmi(&pVCpu->cpum.GstCtx))
2427 {
2428 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT, "NMI");
2429 if (rcStrict == VINF_SUCCESS)
2430 {
2431 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
2432 rcStrict = IEMInjectTrap(pVCpu, X86_XCPT_NMI, TRPM_HARDWARE_INT, 0, 0, 0);
2433 Log8(("Injected NMI on %u (%d)\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
2434 }
2435 return rcStrict;
2436 }
2437 *pfInterruptWindows |= NEM_WIN_INTW_F_NMI;
2438 Log8(("NMI window pending on %u\n", pVCpu->idCpu));
2439 }
2440
2441 /*
2442 * APIC or PIC interrupt?
2443 */
2444 if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC))
2445 {
2446 /** @todo check NMI inhibiting here too! */
2447 if ( !CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx)
2448 && pVCpu->cpum.GstCtx.rflags.Bits.u1IF)
2449 {
2450 AssertCompile(NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT & CPUMCTX_EXTRN_APIC_TPR);
2451 VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT, "NMI");
2452 if (rcStrict == VINF_SUCCESS)
2453 {
2454 uint8_t bInterrupt;
2455 int rc = PDMGetInterrupt(pVCpu, &bInterrupt);
2456 if (RT_SUCCESS(rc))
2457 {
2458 Log8(("Injecting interrupt %#x on %u: %04x:%08RX64 efl=%#x\n", bInterrupt, pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.eflags.u));
2459 rcStrict = IEMInjectTrap(pVCpu, bInterrupt, TRPM_HARDWARE_INT, 0, 0, 0);
2460 Log8(("Injected interrupt %#x on %u (%d)\n", bInterrupt, pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
2461 }
2462 else if (rc == VERR_APIC_INTR_MASKED_BY_TPR)
2463 {
2464 *pfInterruptWindows |= ((bInterrupt >> 4) << NEM_WIN_INTW_F_PRIO_SHIFT) | NEM_WIN_INTW_F_REGULAR;
2465 Log8(("VERR_APIC_INTR_MASKED_BY_TPR: *pfInterruptWindows=%#x\n", *pfInterruptWindows));
2466 }
2467 else
2468 Log8(("PDMGetInterrupt failed -> %Rrc\n", rc));
2469 }
2470 return rcStrict;
2471 }
2472
2473 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC) && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC))
2474 {
2475 /* If only an APIC interrupt is pending, we need to know its priority. Otherwise we'll
2476 * likely get pointless deliverability notifications with IF=1 but TPR still too high.
2477 */
2478 bool fPendingIntr = false;
2479 uint8_t bTpr = 0;
2480 uint8_t bPendingIntr = 0;
2481 int rc = APICGetTpr(pVCpu, &bTpr, &fPendingIntr, &bPendingIntr);
2482 AssertRC(rc);
2483 *pfInterruptWindows |= ((bPendingIntr >> 4) << NEM_WIN_INTW_F_PRIO_SHIFT) | NEM_WIN_INTW_F_REGULAR;
2484 Log8(("Interrupt window pending on %u: %#x (bTpr=%#x fPendingIntr=%d bPendingIntr=%#x)\n",
2485 pVCpu->idCpu, *pfInterruptWindows, bTpr, fPendingIntr, bPendingIntr));
2486 }
2487 else
2488 {
2489 *pfInterruptWindows |= NEM_WIN_INTW_F_REGULAR;
2490 Log8(("Interrupt window pending on %u: %#x\n", pVCpu->idCpu, *pfInterruptWindows));
2491 }
2492 }
2493
2494 return VINF_SUCCESS;
2495}
2496
2497
2498/**
2499 * Inner NEM runloop for windows.
2500 *
2501 * @returns Strict VBox status code.
2502 * @param pVM The cross context VM structure.
2503 * @param pVCpu The cross context per CPU structure.
2504 */
2505NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinRunGC(PVMCC pVM, PVMCPUCC pVCpu)
2506{
2507 LogFlow(("NEM/%u: %04x:%08RX64 efl=%#08RX64 <=\n", pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags.u));
2508#ifdef LOG_ENABLED
2509 if (LogIs3Enabled())
2510 nemHCWinLogState(pVM, pVCpu);
2511#endif
2512
2513 /*
2514 * Try switch to NEM runloop state.
2515 */
2516 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED))
2517 { /* likely */ }
2518 else
2519 {
2520 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);
2521 LogFlow(("NEM/%u: returning immediately because canceled\n", pVCpu->idCpu));
2522 return VINF_SUCCESS;
2523 }
2524
2525 /*
2526 * The run loop.
2527 *
2528 * Current approach to state updating to use the sledgehammer and sync
2529 * everything every time. This will be optimized later.
2530 */
2531 const bool fSingleStepping = DBGFIsStepping(pVCpu);
2532// const uint32_t fCheckVmFFs = !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK
2533// : VM_FF_HP_R0_PRE_HM_STEP_MASK;
2534// const uint32_t fCheckCpuFFs = !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK;
2535 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2536 for (unsigned iLoop = 0;; iLoop++)
2537 {
2538 /*
2539 * Pending interrupts or such? Need to check and deal with this prior
2540 * to the state syncing.
2541 */
2542 pVCpu->nem.s.fDesiredInterruptWindows = 0;
2543 if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_UPDATE_APIC | VMCPU_FF_INTERRUPT_PIC
2544 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI))
2545 {
2546 /* Try inject interrupt. */
2547 rcStrict = nemHCWinHandleInterruptFF(pVM, pVCpu, &pVCpu->nem.s.fDesiredInterruptWindows);
2548 if (rcStrict == VINF_SUCCESS)
2549 { /* likely */ }
2550 else
2551 {
2552 LogFlow(("NEM/%u: breaking: nemHCWinHandleInterruptFF -> %Rrc\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
2553 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnStatus);
2554 break;
2555 }
2556 }
2557
2558#ifndef NEM_WIN_WITH_A20
2559 /*
2560 * Do not execute in hyper-V if the A20 isn't enabled.
2561 */
2562 if (PGMPhysIsA20Enabled(pVCpu))
2563 { /* likely */ }
2564 else
2565 {
2566 rcStrict = VINF_EM_RESCHEDULE_REM;
2567 LogFlow(("NEM/%u: breaking: A20 disabled\n", pVCpu->idCpu));
2568 break;
2569 }
2570#endif
2571
2572 /*
2573 * Ensure that hyper-V has the whole state.
2574 * (We always update the interrupt windows settings when active as hyper-V seems
2575 * to forget about it after an exit.)
2576 */
2577 if ( (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK))
2578 != (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK)
2579 || ( ( pVCpu->nem.s.fDesiredInterruptWindows
2580 || pVCpu->nem.s.fCurrentInterruptWindows != pVCpu->nem.s.fDesiredInterruptWindows) ) )
2581 {
2582 int rc2 = nemHCWinCopyStateToHyperV(pVM, pVCpu);
2583 AssertRCReturn(rc2, rc2);
2584 }
2585
2586 /*
2587 * Poll timers and run for a bit.
2588 *
2589 * With the VID approach (ring-0 or ring-3) we can specify a timeout here,
2590 * so we take the time of the next timer event and uses that as a deadline.
2591 * The rounding heuristics are "tuned" so that rhel5 (1K timer) will boot fine.
2592 */
2593 /** @todo See if we cannot optimize this TMTimerPollGIP by only redoing
2594 * the whole polling job when timers have changed... */
2595 uint64_t offDeltaIgnored;
2596 uint64_t const nsNextTimerEvt = TMTimerPollGIP(pVM, pVCpu, &offDeltaIgnored); NOREF(nsNextTimerEvt);
2597 if ( !VM_FF_IS_ANY_SET(pVM, VM_FF_EMT_RENDEZVOUS | VM_FF_TM_VIRTUAL_SYNC)
2598 && !VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_HM_TO_R3_MASK))
2599 {
2600 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_WAIT, VMCPUSTATE_STARTED_EXEC_NEM))
2601 {
2602#ifdef LOG_ENABLED
2603 if (LogIsFlowEnabled())
2604 {
2605 static const WHV_REGISTER_NAME s_aNames[6] = { WHvX64RegisterCs, WHvX64RegisterRip, WHvX64RegisterRflags,
2606 WHvX64RegisterSs, WHvX64RegisterRsp, WHvX64RegisterCr0 };
2607 WHV_REGISTER_VALUE aRegs[RT_ELEMENTS(s_aNames)] = { {{0, 0} } };
2608 WHvGetVirtualProcessorRegisters(pVM->nem.s.hPartition, pVCpu->idCpu, s_aNames, RT_ELEMENTS(s_aNames), aRegs);
2609 LogFlow(("NEM/%u: Entry @ %04x:%08RX64 IF=%d EFL=%#RX64 SS:RSP=%04x:%08RX64 cr0=%RX64\n",
2610 pVCpu->idCpu, aRegs[0].Segment.Selector, aRegs[1].Reg64, RT_BOOL(aRegs[2].Reg64 & X86_EFL_IF),
2611 aRegs[2].Reg64, aRegs[3].Segment.Selector, aRegs[4].Reg64, aRegs[5].Reg64));
2612 }
2613#endif
2614 WHV_RUN_VP_EXIT_CONTEXT ExitReason = {0};
2615 TMNotifyStartOfExecution(pVM, pVCpu);
2616
2617 HRESULT hrc = WHvRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, &ExitReason, sizeof(ExitReason));
2618
2619 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);
2620 TMNotifyEndOfExecution(pVM, pVCpu, ASMReadTSC());
2621#ifdef LOG_ENABLED
2622 LogFlow(("NEM/%u: Exit @ %04X:%08RX64 IF=%d CR8=%#x Reason=%#x\n", pVCpu->idCpu, ExitReason.VpContext.Cs.Selector,
2623 ExitReason.VpContext.Rip, RT_BOOL(ExitReason.VpContext.Rflags & X86_EFL_IF), ExitReason.VpContext.Cr8,
2624 ExitReason.ExitReason));
2625#endif
2626 if (SUCCEEDED(hrc))
2627 {
2628 /*
2629 * Deal with the message.
2630 */
2631 rcStrict = nemR3WinHandleExit(pVM, pVCpu, &ExitReason);
2632 if (rcStrict == VINF_SUCCESS)
2633 { /* hopefully likely */ }
2634 else
2635 {
2636 LogFlow(("NEM/%u: breaking: nemHCWinHandleMessage -> %Rrc\n", pVCpu->idCpu, VBOXSTRICTRC_VAL(rcStrict) ));
2637 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnStatus);
2638 break;
2639 }
2640 }
2641 else
2642 AssertLogRelMsgFailedReturn(("WHvRunVirtualProcessor failed for CPU #%u: %#x (%u)\n",
2643 pVCpu->idCpu, hrc, GetLastError()),
2644 VERR_NEM_IPE_0);
2645
2646 /*
2647 * If no relevant FFs are pending, loop.
2648 */
2649 if ( !VM_FF_IS_ANY_SET( pVM, !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK : VM_FF_HP_R0_PRE_HM_STEP_MASK)
2650 && !VMCPU_FF_IS_ANY_SET(pVCpu, !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK) )
2651 continue;
2652
2653 /** @todo Try handle pending flags, not just return to EM loops. Take care
2654 * not to set important RCs here unless we've handled a message. */
2655 LogFlow(("NEM/%u: breaking: pending FF (%#x / %#RX64)\n",
2656 pVCpu->idCpu, pVM->fGlobalForcedActions, (uint64_t)pVCpu->fLocalForcedActions));
2657 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnFFPost);
2658 }
2659 else
2660 {
2661 LogFlow(("NEM/%u: breaking: canceled %d (pre exec)\n", pVCpu->idCpu, VMCPU_GET_STATE(pVCpu) ));
2662 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnCancel);
2663 }
2664 }
2665 else
2666 {
2667 LogFlow(("NEM/%u: breaking: pending FF (pre exec)\n", pVCpu->idCpu));
2668 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatBreakOnFFPre);
2669 }
2670 break;
2671 } /* the run loop */
2672
2673
2674 /*
2675 * If the CPU is running, make sure to stop it before we try sync back the
2676 * state and return to EM. We don't sync back the whole state if we can help it.
2677 */
2678 if (!VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM))
2679 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);
2680
2681 if (pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT)))
2682 {
2683 /* Try anticipate what we might need. */
2684 uint64_t fImport = IEM_CPUMCTX_EXTRN_MUST_MASK | CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI;
2685 if ( (rcStrict >= VINF_EM_FIRST && rcStrict <= VINF_EM_LAST)
2686 || RT_FAILURE(rcStrict))
2687 fImport = CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT);
2688 else if (VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_INTERRUPT_APIC
2689 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI))
2690 fImport |= IEM_CPUMCTX_EXTRN_XCPT_MASK;
2691
2692 if (pVCpu->cpum.GstCtx.fExtrn & fImport)
2693 {
2694 int rc2 = nemHCWinCopyStateFromHyperV(pVM, pVCpu, fImport | CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT);
2695 if (RT_SUCCESS(rc2))
2696 pVCpu->cpum.GstCtx.fExtrn &= ~fImport;
2697 else if (RT_SUCCESS(rcStrict))
2698 rcStrict = rc2;
2699 if (!(pVCpu->cpum.GstCtx.fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT))))
2700 pVCpu->cpum.GstCtx.fExtrn = 0;
2701 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnReturn);
2702 }
2703 else
2704 {
2705 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnReturnSkipped);
2706 pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
2707 }
2708 }
2709 else
2710 {
2711 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatImportOnReturnSkipped);
2712 pVCpu->cpum.GstCtx.fExtrn = 0;
2713 }
2714
2715 LogFlow(("NEM/%u: %04x:%08RX64 efl=%#08RX64 => %Rrc\n", pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel,
2716 pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.rflags.u, VBOXSTRICTRC_VAL(rcStrict) ));
2717 return rcStrict;
2718}
2719
2720
2721/**
2722 * @callback_method_impl{FNPGMPHYSNEMCHECKPAGE}
2723 */
2724NEM_TMPL_STATIC DECLCALLBACK(int) nemHCWinUnsetForA20CheckerCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys,
2725 PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
2726{
2727 /* We'll just unmap the memory. */
2728 if (pInfo->u2NemState > NEM_WIN_PAGE_STATE_UNMAPPED)
2729 {
2730 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
2731 if (SUCCEEDED(hrc))
2732 {
2733 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
2734 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
2735 Log5(("NEM GPA unmapped/A20: %RGp (was %s, cMappedPages=%u)\n", GCPhys, g_apszPageStates[pInfo->u2NemState], cMappedPages));
2736 pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
2737 }
2738 else
2739 {
2740 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
2741 LogRel(("nemHCWinUnsetForA20CheckerCallback/unmap: GCPhys=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
2742 GCPhys, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2743 return VERR_NEM_IPE_2;
2744 }
2745 }
2746 RT_NOREF(pVCpu, pvUser);
2747 return VINF_SUCCESS;
2748}
2749
2750
2751/**
2752 * Unmaps a page from Hyper-V for the purpose of emulating A20 gate behavior.
2753 *
2754 * @returns The PGMPhysNemQueryPageInfo result.
2755 * @param pVM The cross context VM structure.
2756 * @param pVCpu The cross context virtual CPU structure.
2757 * @param GCPhys The page to unmap.
2758 */
2759NEM_TMPL_STATIC int nemHCWinUnmapPageForA20Gate(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys)
2760{
2761 PGMPHYSNEMPAGEINFO Info;
2762 return PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhys, false /*fMakeWritable*/, &Info,
2763 nemHCWinUnsetForA20CheckerCallback, NULL);
2764}
2765
2766
2767void nemHCNativeNotifyHandlerPhysicalRegister(PVMCC pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhys, RTGCPHYS cb)
2768{
2769 Log5(("nemHCNativeNotifyHandlerPhysicalRegister: %RGp LB %RGp enmKind=%d\n", GCPhys, cb, enmKind));
2770 NOREF(pVM); NOREF(enmKind); NOREF(GCPhys); NOREF(cb);
2771}
2772
2773
2774VMM_INT_DECL(void) NEMHCNotifyHandlerPhysicalDeregister(PVMCC pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhys, RTGCPHYS cb,
2775 RTR3PTR pvMemR3, uint8_t *pu2State)
2776{
2777 Log5(("NEMHCNotifyHandlerPhysicalDeregister: %RGp LB %RGp enmKind=%d pvMemR3=%p pu2State=%p (%d)\n",
2778 GCPhys, cb, enmKind, pvMemR3, pu2State, *pu2State));
2779
2780 *pu2State = UINT8_MAX;
2781 if (pvMemR3)
2782 {
2783 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfMapGpaRange, a);
2784 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, pvMemR3, GCPhys, cb,
2785 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute | WHvMapGpaRangeFlagWrite);
2786 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfMapGpaRange, a);
2787 if (SUCCEEDED(hrc))
2788 *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
2789 else
2790 AssertLogRelMsgFailed(("NEMHCNotifyHandlerPhysicalDeregister: WHvMapGpaRange(,%p,%RGp,%RGp,) -> %Rhrc\n",
2791 pvMemR3, GCPhys, cb, hrc));
2792 }
2793 RT_NOREF(enmKind);
2794}
2795
2796
2797void nemHCNativeNotifyHandlerPhysicalModify(PVMCC pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhysOld,
2798 RTGCPHYS GCPhysNew, RTGCPHYS cb, bool fRestoreAsRAM)
2799{
2800 Log5(("nemHCNativeNotifyHandlerPhysicalModify: %RGp LB %RGp -> %RGp enmKind=%d fRestoreAsRAM=%d\n",
2801 GCPhysOld, cb, GCPhysNew, enmKind, fRestoreAsRAM));
2802 NOREF(pVM); NOREF(enmKind); NOREF(GCPhysOld); NOREF(GCPhysNew); NOREF(cb); NOREF(fRestoreAsRAM);
2803}
2804
2805
2806/**
2807 * Worker that maps pages into Hyper-V.
2808 *
2809 * This is used by the PGM physical page notifications as well as the memory
2810 * access VMEXIT handlers.
2811 *
2812 * @returns VBox status code.
2813 * @param pVM The cross context VM structure.
2814 * @param pVCpu The cross context virtual CPU structure of the
2815 * calling EMT.
2816 * @param GCPhysSrc The source page address.
2817 * @param GCPhysDst The hyper-V destination page. This may differ from
2818 * GCPhysSrc when A20 is disabled.
2819 * @param fPageProt NEM_PAGE_PROT_XXX.
2820 * @param pu2State Our page state (input/output).
2821 * @param fBackingChanged Set if the page backing is being changed.
2822 * @thread EMT(pVCpu)
2823 */
2824NEM_TMPL_STATIC int nemHCNativeSetPhysPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst,
2825 uint32_t fPageProt, uint8_t *pu2State, bool fBackingChanged)
2826{
2827 /*
2828 * Looks like we need to unmap a page before we can change the backing
2829 * or even modify the protection. This is going to be *REALLY* efficient.
2830 * PGM lends us two bits to keep track of the state here.
2831 */
2832 RT_NOREF(pVCpu);
2833 uint8_t const u2OldState = *pu2State;
2834 uint8_t const u2NewState = fPageProt & NEM_PAGE_PROT_WRITE ? NEM_WIN_PAGE_STATE_WRITABLE
2835 : fPageProt & NEM_PAGE_PROT_READ ? NEM_WIN_PAGE_STATE_READABLE : NEM_WIN_PAGE_STATE_UNMAPPED;
2836 if ( fBackingChanged
2837 || u2NewState != u2OldState)
2838 {
2839 if (u2OldState > NEM_WIN_PAGE_STATE_UNMAPPED)
2840 {
2841 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
2842 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhysDst, X86_PAGE_SIZE);
2843 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
2844 if (SUCCEEDED(hrc))
2845 {
2846 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
2847 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
2848 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
2849 if (u2NewState == NEM_WIN_PAGE_STATE_UNMAPPED)
2850 {
2851 Log5(("NEM GPA unmapped/set: %RGp (was %s, cMappedPages=%u)\n",
2852 GCPhysDst, g_apszPageStates[u2OldState], cMappedPages));
2853 return VINF_SUCCESS;
2854 }
2855 }
2856 else
2857 {
2858 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
2859 LogRel(("nemHCNativeSetPhysPage/unmap: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
2860 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2861 return VERR_NEM_INIT_FAILED;
2862 }
2863 }
2864 }
2865
2866 /*
2867 * Writeable mapping?
2868 */
2869 if (fPageProt & NEM_PAGE_PROT_WRITE)
2870 {
2871 void *pvPage;
2872 int rc = nemR3NativeGCPhys2R3PtrWriteable(pVM, GCPhysSrc, &pvPage);
2873 if (RT_SUCCESS(rc))
2874 {
2875 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, pvPage, GCPhysDst, X86_PAGE_SIZE,
2876 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute | WHvMapGpaRangeFlagWrite);
2877 if (SUCCEEDED(hrc))
2878 {
2879 *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
2880 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
2881 uint32_t cMappedPages = ASMAtomicIncU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
2882 Log5(("NEM GPA mapped/set: %RGp %s (was %s, cMappedPages=%u)\n",
2883 GCPhysDst, g_apszPageStates[u2NewState], g_apszPageStates[u2OldState], cMappedPages));
2884 return VINF_SUCCESS;
2885 }
2886 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
2887 LogRel(("nemHCNativeSetPhysPage/writable: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
2888 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2889 return VERR_NEM_INIT_FAILED;
2890 }
2891 LogRel(("nemHCNativeSetPhysPage/writable: GCPhysSrc=%RGp rc=%Rrc\n", GCPhysSrc, rc));
2892 return rc;
2893 }
2894
2895 if (fPageProt & NEM_PAGE_PROT_READ)
2896 {
2897 const void *pvPage;
2898 int rc = nemR3NativeGCPhys2R3PtrReadOnly(pVM, GCPhysSrc, &pvPage);
2899 if (RT_SUCCESS(rc))
2900 {
2901 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfMapGpaRangePage, a);
2902 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, (void *)pvPage, GCPhysDst, X86_PAGE_SIZE,
2903 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute);
2904 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfMapGpaRangePage, a);
2905 if (SUCCEEDED(hrc))
2906 {
2907 *pu2State = NEM_WIN_PAGE_STATE_READABLE;
2908 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPage);
2909 uint32_t cMappedPages = ASMAtomicIncU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
2910 Log5(("NEM GPA mapped/set: %RGp %s (was %s, cMappedPages=%u)\n",
2911 GCPhysDst, g_apszPageStates[u2NewState], g_apszPageStates[u2OldState], cMappedPages));
2912 return VINF_SUCCESS;
2913 }
2914 STAM_REL_COUNTER_INC(&pVM->nem.s.StatMapPageFailed);
2915 LogRel(("nemHCNativeSetPhysPage/readonly: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
2916 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2917 return VERR_NEM_INIT_FAILED;
2918 }
2919 LogRel(("nemHCNativeSetPhysPage/readonly: GCPhysSrc=%RGp rc=%Rrc\n", GCPhysSrc, rc));
2920 return rc;
2921 }
2922
2923 /* We already unmapped it above. */
2924 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
2925 return VINF_SUCCESS;
2926}
2927
2928
2929NEM_TMPL_STATIC int nemHCJustUnmapPageFromHyperV(PVMCC pVM, RTGCPHYS GCPhysDst, uint8_t *pu2State)
2930{
2931 if (*pu2State <= NEM_WIN_PAGE_STATE_UNMAPPED)
2932 {
2933 Log5(("nemHCJustUnmapPageFromHyperV: %RGp == unmapped\n", GCPhysDst));
2934 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
2935 return VINF_SUCCESS;
2936 }
2937
2938 STAM_REL_PROFILE_START(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
2939 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhysDst & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK, X86_PAGE_SIZE);
2940 STAM_REL_PROFILE_STOP(&pVM->nem.s.StatProfUnmapGpaRangePage, a);
2941 if (SUCCEEDED(hrc))
2942 {
2943 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPage);
2944 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
2945 *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
2946 Log5(("nemHCJustUnmapPageFromHyperV: %RGp => unmapped (total %u)\n", GCPhysDst, cMappedPages));
2947 return VINF_SUCCESS;
2948 }
2949 STAM_REL_COUNTER_INC(&pVM->nem.s.StatUnmapPageFailed);
2950 LogRel(("nemHCJustUnmapPageFromHyperV(%RGp): failed! hrc=%Rhrc (%#x) Last=%#x/%u\n",
2951 GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2952 return VERR_NEM_IPE_6;
2953}
2954
2955
2956int nemHCNativeNotifyPhysPageAllocated(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys, uint32_t fPageProt,
2957 PGMPAGETYPE enmType, uint8_t *pu2State)
2958{
2959 Log5(("nemHCNativeNotifyPhysPageAllocated: %RGp HCPhys=%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
2960 GCPhys, HCPhys, fPageProt, enmType, *pu2State));
2961 RT_NOREF_PV(HCPhys); RT_NOREF_PV(enmType);
2962
2963 int rc;
2964 RT_NOREF_PV(fPageProt);
2965#ifdef NEM_WIN_WITH_A20
2966 if ( pVM->nem.s.fA20Enabled
2967 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
2968#endif
2969 rc = nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
2970#ifdef NEM_WIN_WITH_A20
2971 else if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
2972 rc = nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
2973 else
2974 rc = VINF_SUCCESS; /* ignore since we've got the alias page at this address. */
2975#endif
2976 return rc;
2977}
2978
2979
2980VMM_INT_DECL(void) NEMHCNotifyPhysPageProtChanged(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys, RTR3PTR pvR3, uint32_t fPageProt,
2981 PGMPAGETYPE enmType, uint8_t *pu2State)
2982{
2983 Log5(("NEMHCNotifyPhysPageProtChanged: %RGp HCPhys=%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
2984 GCPhys, HCPhys, fPageProt, enmType, *pu2State));
2985 Assert(VM_IS_NEM_ENABLED(pVM));
2986 RT_NOREF(HCPhys, enmType, pvR3);
2987
2988 RT_NOREF_PV(fPageProt);
2989#ifdef NEM_WIN_WITH_A20
2990 if ( pVM->nem.s.fA20Enabled
2991 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
2992#endif
2993 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
2994#ifdef NEM_WIN_WITH_A20
2995 else if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
2996 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
2997 /* else: ignore since we've got the alias page at this address. */
2998#endif
2999}
3000
3001
3002VMM_INT_DECL(void) NEMHCNotifyPhysPageChanged(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhysPrev, RTHCPHYS HCPhysNew,
3003 RTR3PTR pvNewR3, uint32_t fPageProt, PGMPAGETYPE enmType, uint8_t *pu2State)
3004{
3005 Log5(("nemHCNativeNotifyPhysPageChanged: %RGp HCPhys=%RHp->%RHp pvNewR3=%p fPageProt=%#x enmType=%d *pu2State=%d\n",
3006 GCPhys, HCPhysPrev, HCPhysNew, pvNewR3, fPageProt, enmType, *pu2State));
3007 Assert(VM_IS_NEM_ENABLED(pVM));
3008 RT_NOREF(HCPhysPrev, HCPhysNew, pvNewR3, enmType);
3009
3010 RT_NOREF_PV(fPageProt);
3011#ifdef NEM_WIN_WITH_A20
3012 if ( pVM->nem.s.fA20Enabled
3013 || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
3014#endif
3015 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
3016#ifdef NEM_WIN_WITH_A20
3017 else if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
3018 nemHCJustUnmapPageFromHyperV(pVM, GCPhys, pu2State);
3019 /* else: ignore since we've got the alias page at this address. */
3020#endif
3021}
3022
3023
3024/**
3025 * Returns features supported by the NEM backend.
3026 *
3027 * @returns Flags of features supported by the native NEM backend.
3028 * @param pVM The cross context VM structure.
3029 */
3030VMM_INT_DECL(uint32_t) NEMHCGetFeatures(PVMCC pVM)
3031{
3032 RT_NOREF(pVM);
3033 /** @todo Make use of the WHvGetVirtualProcessorXsaveState/WHvSetVirtualProcessorXsaveState
3034 * interface added in 2019 to enable passthrough of xsave/xrstor (and depending) features to the guest. */
3035 /** @todo Is NEM_FEAT_F_FULL_GST_EXEC always true? */
3036 return NEM_FEAT_F_NESTED_PAGING | NEM_FEAT_F_FULL_GST_EXEC;
3037}
3038
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