VirtualBox

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

Last change on this file since 108195 was 108076, checked in by vboxsync, 3 months ago

VMM/NEM-win: Only export & import IA32_SPEC_CTRL if the host supports it and we're exposing the register to the guest. (AMD regression from r167064.) [fix] jiraref:VBP-947 bugref:10738

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

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