VirtualBox

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

Last change on this file since 96860 was 96407, checked in by vboxsync, 21 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use