VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp@ 71289

Last change on this file since 71289 was 71289, checked in by vboxsync, 7 years ago

NEM: Complain about A20 too. bugref:9044

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 110.9 KB
Line 
1/* $Id: NEMR3Native-win.cpp 71289 2018-03-09 13:37:04Z vboxsync $ */
2/** @file
3 * NEM - Native execution manager, native ring-3 Windows backend.
4 *
5 * Log group 2: Exit logging.
6 * Log group 3: Log context on exit.
7 * Log group 5: Ring-3 memory management
8 * Log group 6: Ring-0 memory management
9 * Log group 12: API intercepts.
10 */
11
12/*
13 * Copyright (C) 2018 Oracle Corporation
14 *
15 * This file is part of VirtualBox Open Source Edition (OSE), as
16 * available from http://www.virtualbox.org. This file is free software;
17 * you can redistribute it and/or modify it under the terms of the GNU
18 * General Public License (GPL) as published by the Free Software
19 * Foundation, in version 2 as it comes in the "COPYING" file of the
20 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22 */
23
24
25/*********************************************************************************************************************************
26* Header Files *
27*********************************************************************************************************************************/
28#define LOG_GROUP LOG_GROUP_NEM
29#include <iprt/nt/nt-and-windows.h>
30#include <iprt/nt/hyperv.h>
31#include <iprt/nt/vid.h>
32#include <WinHvPlatform.h>
33
34#ifndef _WIN32_WINNT_WIN10
35# error "Missing _WIN32_WINNT_WIN10"
36#endif
37#ifndef _WIN32_WINNT_WIN10_RS1 /* Missing define, causing trouble for us. */
38# define _WIN32_WINNT_WIN10_RS1 (_WIN32_WINNT_WIN10 + 1)
39#endif
40#include <sysinfoapi.h>
41#include <debugapi.h>
42#include <errhandlingapi.h>
43#include <fileapi.h>
44#include <winerror.h> /* no api header for this. */
45
46#include <VBox/vmm/nem.h>
47#include <VBox/vmm/iem.h>
48#include <VBox/vmm/em.h>
49#include <VBox/vmm/apic.h>
50#include "NEMInternal.h"
51#include <VBox/vmm/vm.h>
52
53#include <iprt/ldr.h>
54#include <iprt/path.h>
55#include <iprt/string.h>
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61#ifdef LOG_ENABLED
62# define NEM_WIN_INTERCEPT_NT_IO_CTLS
63#endif
64
65/** VID I/O control detection: Fake partition handle input. */
66#define NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE ((HANDLE)(uintptr_t)38479125)
67/** VID I/O control detection: Fake partition ID return. */
68#define NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_ID UINT64_C(0xfa1e000042424242)
69/** VID I/O control detection: Fake CPU index input. */
70#define NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX UINT32_C(42)
71/** VID I/O control detection: Fake timeout input. */
72#define NEM_WIN_IOCTL_DETECTOR_FAKE_TIMEOUT UINT32_C(0x00080286)
73
74
75/*********************************************************************************************************************************
76* Global Variables *
77*********************************************************************************************************************************/
78/** @name APIs imported from WinHvPlatform.dll
79 * @{ */
80static decltype(WHvGetCapability) * g_pfnWHvGetCapability;
81static decltype(WHvCreatePartition) * g_pfnWHvCreatePartition;
82static decltype(WHvSetupPartition) * g_pfnWHvSetupPartition;
83static decltype(WHvDeletePartition) * g_pfnWHvDeletePartition;
84static decltype(WHvGetPartitionProperty) * g_pfnWHvGetPartitionProperty;
85static decltype(WHvSetPartitionProperty) * g_pfnWHvSetPartitionProperty;
86static decltype(WHvMapGpaRange) * g_pfnWHvMapGpaRange;
87static decltype(WHvUnmapGpaRange) * g_pfnWHvUnmapGpaRange;
88static decltype(WHvTranslateGva) * g_pfnWHvTranslateGva;
89#ifndef NEM_WIN_USE_OUR_OWN_RUN_API
90static decltype(WHvCreateVirtualProcessor) * g_pfnWHvCreateVirtualProcessor;
91static decltype(WHvDeleteVirtualProcessor) * g_pfnWHvDeleteVirtualProcessor;
92static decltype(WHvRunVirtualProcessor) * g_pfnWHvRunVirtualProcessor;
93static decltype(WHvGetRunExitContextSize) * g_pfnWHvGetRunExitContextSize;
94static decltype(WHvCancelRunVirtualProcessor) * g_pfnWHvCancelRunVirtualProcessor;
95static decltype(WHvGetVirtualProcessorRegisters) * g_pfnWHvGetVirtualProcessorRegisters;
96static decltype(WHvSetVirtualProcessorRegisters) * g_pfnWHvSetVirtualProcessorRegisters;
97#endif
98/** @} */
99
100/** @name APIs imported from Vid.dll
101 * @{ */
102static decltype(VidGetHvPartitionId) *g_pfnVidGetHvPartitionId;
103static decltype(VidStartVirtualProcessor) *g_pfnVidStartVirtualProcessor;
104static decltype(VidStopVirtualProcessor) *g_pfnVidStopVirtualProcessor;
105static decltype(VidMessageSlotMap) *g_pfnVidMessageSlotMap;
106static decltype(VidMessageSlotHandleAndGetNext) *g_pfnVidMessageSlotHandleAndGetNext;
107#ifdef LOG_ENABLED
108static decltype(VidGetVirtualProcessorState) *g_pfnVidGetVirtualProcessorState;
109static decltype(VidSetVirtualProcessorState) *g_pfnVidSetVirtualProcessorState;
110static decltype(VidGetVirtualProcessorRunningStatus) *g_pfnVidGetVirtualProcessorRunningStatus;
111#endif
112/** @} */
113
114
115/**
116 * Import instructions.
117 */
118static const struct
119{
120 uint8_t idxDll; /**< 0 for WinHvPlatform.dll, 1 for vid.dll. */
121 bool fOptional; /**< Set if import is optional. */
122 PFNRT *ppfn; /**< The function pointer variable. */
123 const char *pszName; /**< The function name. */
124} g_aImports[] =
125{
126#define NEM_WIN_IMPORT(a_idxDll, a_fOptional, a_Name) { (a_idxDll), (a_fOptional), (PFNRT *)&RT_CONCAT(g_pfn,a_Name), #a_Name }
127 NEM_WIN_IMPORT(0, false, WHvGetCapability),
128 NEM_WIN_IMPORT(0, false, WHvCreatePartition),
129 NEM_WIN_IMPORT(0, false, WHvSetupPartition),
130 NEM_WIN_IMPORT(0, false, WHvDeletePartition),
131 NEM_WIN_IMPORT(0, false, WHvGetPartitionProperty),
132 NEM_WIN_IMPORT(0, false, WHvSetPartitionProperty),
133 NEM_WIN_IMPORT(0, false, WHvMapGpaRange),
134 NEM_WIN_IMPORT(0, false, WHvUnmapGpaRange),
135 NEM_WIN_IMPORT(0, false, WHvTranslateGva),
136#ifndef NEM_WIN_USE_OUR_OWN_RUN_API
137 NEM_WIN_IMPORT(0, false, WHvCreateVirtualProcessor),
138 NEM_WIN_IMPORT(0, false, WHvDeleteVirtualProcessor),
139 NEM_WIN_IMPORT(0, false, WHvRunVirtualProcessor),
140 NEM_WIN_IMPORT(0, false, WHvCancelRunVirtualProcessor),
141 NEM_WIN_IMPORT(0, false, WHvGetRunExitContextSize),
142 NEM_WIN_IMPORT(0, false, WHvGetVirtualProcessorRegisters),
143 NEM_WIN_IMPORT(0, false, WHvSetVirtualProcessorRegisters),
144#endif
145 NEM_WIN_IMPORT(1, false, VidGetHvPartitionId),
146 NEM_WIN_IMPORT(1, false, VidMessageSlotMap),
147 NEM_WIN_IMPORT(1, false, VidMessageSlotHandleAndGetNext),
148 NEM_WIN_IMPORT(1, false, VidStartVirtualProcessor),
149 NEM_WIN_IMPORT(1, false, VidStopVirtualProcessor),
150#ifdef LOG_ENABLED
151 NEM_WIN_IMPORT(1, false, VidGetVirtualProcessorState),
152 NEM_WIN_IMPORT(1, false, VidSetVirtualProcessorState),
153 NEM_WIN_IMPORT(1, false, VidGetVirtualProcessorRunningStatus),
154#endif
155#undef NEM_WIN_IMPORT
156};
157
158
159/** The real NtDeviceIoControlFile API in NTDLL. */
160static decltype(NtDeviceIoControlFile) *g_pfnNtDeviceIoControlFile;
161/** Pointer to the NtDeviceIoControlFile import table entry. */
162static decltype(NtDeviceIoControlFile) **g_ppfnVidNtDeviceIoControlFile;
163/** Info about the VidGetHvPartitionId I/O control interface. */
164static NEMWINIOCTL g_IoCtlGetHvPartitionId;
165/** Info about the VidStartVirtualProcessor I/O control interface. */
166static NEMWINIOCTL g_IoCtlStartVirtualProcessor;
167/** Info about the VidStopVirtualProcessor I/O control interface. */
168static NEMWINIOCTL g_IoCtlStopVirtualProcessor;
169/** Info about the VidMessageSlotHandleAndGetNext I/O control interface. */
170static NEMWINIOCTL g_IoCtlMessageSlotHandleAndGetNext;
171#ifdef LOG_ENABLED
172/** Info about the VidMessageSlotMap I/O control interface - for logging. */
173static NEMWINIOCTL g_IoCtlMessageSlotMap;
174/* Info about the VidGetVirtualProcessorState I/O control interface - for logging. */
175static NEMWINIOCTL g_IoCtlGetVirtualProcessorState;
176/* Info about the VidSetVirtualProcessorState I/O control interface - for logging. */
177static NEMWINIOCTL g_IoCtlSetVirtualProcessorState;
178/** Pointer to what nemR3WinIoctlDetector_ForLogging should fill in. */
179static NEMWINIOCTL *g_pIoCtlDetectForLogging;
180#endif
181
182#ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
183/** Mapping slot for CPU #0.
184 * @{ */
185static VID_MESSAGE_MAPPING_HEADER *g_pMsgSlotMapping = NULL;
186static const HV_MESSAGE_HEADER *g_pHvMsgHdr;
187static const HV_X64_INTERCEPT_MESSAGE_HEADER *g_pX64MsgHdr;
188/** @} */
189#endif
190
191
192/*
193 * Let the preprocessor alias the APIs to import variables for better autocompletion.
194 */
195#ifndef IN_SLICKEDIT
196# define WHvGetCapability g_pfnWHvGetCapability
197# define WHvCreatePartition g_pfnWHvCreatePartition
198# define WHvSetupPartition g_pfnWHvSetupPartition
199# define WHvDeletePartition g_pfnWHvDeletePartition
200# define WHvGetPartitionProperty g_pfnWHvGetPartitionProperty
201# define WHvSetPartitionProperty g_pfnWHvSetPartitionProperty
202# define WHvMapGpaRange g_pfnWHvMapGpaRange
203# define WHvUnmapGpaRange g_pfnWHvUnmapGpaRange
204# define WHvTranslateGva g_pfnWHvTranslateGva
205# define WHvCreateVirtualProcessor g_pfnWHvCreateVirtualProcessor
206# define WHvDeleteVirtualProcessor g_pfnWHvDeleteVirtualProcessor
207# define WHvRunVirtualProcessor g_pfnWHvRunVirtualProcessor
208# define WHvGetRunExitContextSize g_pfnWHvGetRunExitContextSize
209# define WHvCancelRunVirtualProcessor g_pfnWHvCancelRunVirtualProcessor
210# define WHvGetVirtualProcessorRegisters g_pfnWHvGetVirtualProcessorRegisters
211# define WHvSetVirtualProcessorRegisters g_pfnWHvSetVirtualProcessorRegisters
212
213# define VidMessageSlotHandleAndGetNext g_pfnVidMessageSlotHandleAndGetNext
214# define VidStartVirtualProcessor g_pfnVidStartVirtualProcessor
215# define VidStopVirtualProcessor g_pfnVidStopVirtualProcessor
216
217#endif
218
219/** WHV_MEMORY_ACCESS_TYPE names */
220static const char * const g_apszWHvMemAccesstypes[4] = { "read", "write", "exec", "!undefined!" };
221
222
223/*********************************************************************************************************************************
224* Internal Functions *
225*********************************************************************************************************************************/
226
227/*
228 * Instantate the code we share with ring-0.
229 */
230#include "../VMMAll/NEMAllNativeTemplate-win.cpp.h"
231
232
233
234#ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
235/**
236 * Wrapper that logs the call from VID.DLL.
237 *
238 * This is very handy for figuring out why an API call fails.
239 */
240static NTSTATUS WINAPI
241nemR3WinLogWrapper_NtDeviceIoControlFile(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
242 PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
243 PVOID pvOutput, ULONG cbOutput)
244{
245
246 char szFunction[32];
247 const char *pszFunction;
248 if (uFunction == g_IoCtlMessageSlotHandleAndGetNext.uFunction)
249 pszFunction = "VidMessageSlotHandleAndGetNext";
250 else if (uFunction == g_IoCtlStartVirtualProcessor.uFunction)
251 pszFunction = "VidStartVirtualProcessor";
252 else if (uFunction == g_IoCtlStopVirtualProcessor.uFunction)
253 pszFunction = "VidStopVirtualProcessor";
254 else if (uFunction == g_IoCtlMessageSlotMap.uFunction)
255 pszFunction = "VidMessageSlotMap";
256 else if (uFunction == g_IoCtlGetVirtualProcessorState.uFunction)
257 pszFunction = "VidGetVirtualProcessorState";
258 else if (uFunction == g_IoCtlSetVirtualProcessorState.uFunction)
259 pszFunction = "VidSetVirtualProcessorState";
260 else
261 {
262 RTStrPrintf(szFunction, sizeof(szFunction), "%#x", uFunction);
263 pszFunction = szFunction;
264 }
265
266 if (cbInput > 0 && pvInput)
267 Log12(("VID!NtDeviceIoControlFile: %s/input: %.*Rhxs\n", pszFunction, RT_MIN(cbInput, 32), pvInput));
268 NTSTATUS rcNt = g_pfnNtDeviceIoControlFile(hFile, hEvt, pfnApcCallback, pvApcCtx, pIos, uFunction,
269 pvInput, cbInput, pvOutput, cbOutput);
270 if (!hEvt && !pfnApcCallback && !pvApcCtx)
271 Log12(("VID!NtDeviceIoControlFile: hFile=%#zx pIos=%p->{s:%#x, i:%#zx} uFunction=%s Input=%p LB %#x Output=%p LB %#x) -> %#x; Caller=%p\n",
272 hFile, pIos, pIos->Status, pIos->Information, pszFunction, pvInput, cbInput, pvOutput, cbOutput, rcNt, ASMReturnAddress()));
273 else
274 Log12(("VID!NtDeviceIoControlFile: hFile=%#zx hEvt=%#zx Apc=%p/%p pIos=%p->{s:%#x, i:%#zx} uFunction=%s Input=%p LB %#x Output=%p LB %#x) -> %#x; Caller=%p\n",
275 hFile, hEvt, pfnApcCallback, pvApcCtx, pIos, pIos->Status, pIos->Information, pszFunction,
276 pvInput, cbInput, pvOutput, cbOutput, rcNt, ASMReturnAddress()));
277 if (cbOutput > 0 && pvOutput)
278 {
279 Log12(("VID!NtDeviceIoControlFile: %s/output: %.*Rhxs\n", pszFunction, RT_MIN(cbOutput, 32), pvOutput));
280 if (uFunction == 0x2210cc && g_pMsgSlotMapping == NULL && cbOutput >= sizeof(void *))
281 {
282 g_pMsgSlotMapping = *(VID_MESSAGE_MAPPING_HEADER **)pvOutput;
283 g_pHvMsgHdr = (const HV_MESSAGE_HEADER *)(g_pMsgSlotMapping + 1);
284 g_pX64MsgHdr = (const HV_X64_INTERCEPT_MESSAGE_HEADER *)(g_pHvMsgHdr + 1);
285 Log12(("VID!NtDeviceIoControlFile: Message slot mapping: %p\n", g_pMsgSlotMapping));
286 }
287 }
288 if ( g_pMsgSlotMapping
289 && ( uFunction == g_IoCtlMessageSlotHandleAndGetNext.uFunction
290 || uFunction == g_IoCtlStopVirtualProcessor.uFunction
291 || uFunction == g_IoCtlMessageSlotMap.uFunction
292 ))
293 Log12(("VID!NtDeviceIoControlFile: enmVidMsgType=%#x cb=%#x msg=%#x payload=%u cs:rip=%04x:%08RX64 (%s)\n",
294 g_pMsgSlotMapping->enmVidMsgType, g_pMsgSlotMapping->cbMessage,
295 g_pHvMsgHdr->MessageType, g_pHvMsgHdr->PayloadSize,
296 g_pX64MsgHdr->CsSegment.Selector, g_pX64MsgHdr->Rip, pszFunction));
297
298 return rcNt;
299}
300#endif /* NEM_WIN_INTERCEPT_NT_IO_CTLS */
301
302
303/**
304 * Patches the call table of VID.DLL so we can intercept NtDeviceIoControlFile.
305 *
306 * This is for used to figure out the I/O control codes and in logging builds
307 * for logging API calls that WinHvPlatform.dll does.
308 *
309 * @returns VBox status code.
310 * @param hLdrModVid The VID module handle.
311 * @param pErrInfo Where to return additional error information.
312 */
313static int nemR3WinInitVidIntercepts(RTLDRMOD hLdrModVid, PRTERRINFO pErrInfo)
314{
315 /*
316 * Locate the real API.
317 */
318 g_pfnNtDeviceIoControlFile = (decltype(NtDeviceIoControlFile) *)RTLdrGetSystemSymbol("NTDLL.DLL", "NtDeviceIoControlFile");
319 AssertReturn(g_pfnNtDeviceIoControlFile != NULL,
320 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Failed to resolve NtDeviceIoControlFile from NTDLL.DLL"));
321
322 /*
323 * Locate the PE header and get what we need from it.
324 */
325 uint8_t const *pbImage = (uint8_t const *)RTLdrGetNativeHandle(hLdrModVid);
326 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
327 AssertReturn(pMzHdr->e_magic == IMAGE_DOS_SIGNATURE,
328 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL mapping doesn't start with MZ signature: %#x", pMzHdr->e_magic));
329 IMAGE_NT_HEADERS const *pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
330 AssertReturn(pNtHdrs->Signature == IMAGE_NT_SIGNATURE,
331 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL has invalid PE signaturre: %#x @%#x",
332 pNtHdrs->Signature, pMzHdr->e_lfanew));
333
334 uint32_t const cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
335 IMAGE_DATA_DIRECTORY const ImportDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
336
337 /*
338 * Walk the import descriptor table looking for NTDLL.DLL.
339 */
340 AssertReturn( ImportDir.Size > 0
341 && ImportDir.Size < cbImage,
342 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad import directory size: %#x", ImportDir.Size));
343 AssertReturn( ImportDir.VirtualAddress > 0
344 && ImportDir.VirtualAddress <= cbImage - ImportDir.Size,
345 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad import directory RVA: %#x", ImportDir.VirtualAddress));
346
347 for (PIMAGE_IMPORT_DESCRIPTOR pImps = (PIMAGE_IMPORT_DESCRIPTOR)&pbImage[ImportDir.VirtualAddress];
348 pImps->Name != 0 && pImps->FirstThunk != 0;
349 pImps++)
350 {
351 AssertReturn(pImps->Name < cbImage,
352 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad import directory entry name: %#x", pImps->Name));
353 const char *pszModName = (const char *)&pbImage[pImps->Name];
354 if (RTStrICmpAscii(pszModName, "ntdll.dll"))
355 continue;
356 AssertReturn(pImps->FirstThunk < cbImage,
357 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad FirstThunk: %#x", pImps->FirstThunk));
358 AssertReturn(pImps->OriginalFirstThunk < cbImage,
359 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad FirstThunk: %#x", pImps->FirstThunk));
360
361 /*
362 * Walk the thunks table(s) looking for NtDeviceIoControlFile.
363 */
364 PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)&pbImage[pImps->FirstThunk]; /* update this. */
365 PIMAGE_THUNK_DATA pThunk = pImps->OriginalFirstThunk == 0 /* read from this. */
366 ? (PIMAGE_THUNK_DATA)&pbImage[pImps->FirstThunk]
367 : (PIMAGE_THUNK_DATA)&pbImage[pImps->OriginalFirstThunk];
368 while (pThunk->u1.Ordinal != 0)
369 {
370 if (!(pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32))
371 {
372 AssertReturn(pThunk->u1.Ordinal > 0 && pThunk->u1.Ordinal < cbImage,
373 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad FirstThunk: %#x", pImps->FirstThunk));
374
375 const char *pszSymbol = (const char *)&pbImage[(uintptr_t)pThunk->u1.AddressOfData + 2];
376 if (strcmp(pszSymbol, "NtDeviceIoControlFile") == 0)
377 {
378 DWORD fOldProt = PAGE_READONLY;
379 VirtualProtect(&pFirstThunk->u1.Function, sizeof(uintptr_t), PAGE_EXECUTE_READWRITE, &fOldProt);
380 g_ppfnVidNtDeviceIoControlFile = (decltype(NtDeviceIoControlFile) **)&pFirstThunk->u1.Function;
381 /* Don't restore the protection here, so we modify the NtDeviceIoControlFile pointer later. */
382 }
383 }
384
385 pThunk++;
386 pFirstThunk++;
387 }
388 }
389
390 if (*g_ppfnVidNtDeviceIoControlFile)
391 {
392#ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
393 *g_ppfnVidNtDeviceIoControlFile = nemR3WinLogWrapper_NtDeviceIoControlFile;
394#endif
395 return VINF_SUCCESS;
396 }
397 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Failed to patch NtDeviceIoControlFile import in VID.DLL!");
398}
399
400
401/**
402 * Worker for nemR3NativeInit that probes and load the native API.
403 *
404 * @returns VBox status code.
405 * @param fForced Whether the HMForced flag is set and we should
406 * fail if we cannot initialize.
407 * @param pErrInfo Where to always return error info.
408 */
409static int nemR3WinInitProbeAndLoad(bool fForced, PRTERRINFO pErrInfo)
410{
411 /*
412 * Check that the DLL files we need are present, but without loading them.
413 * We'd like to avoid loading them unnecessarily.
414 */
415 WCHAR wszPath[MAX_PATH + 64];
416 UINT cwcPath = GetSystemDirectoryW(wszPath, MAX_PATH);
417 if (cwcPath >= MAX_PATH || cwcPath < 2)
418 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "GetSystemDirectoryW failed (%#x / %u)", cwcPath, GetLastError());
419
420 if (wszPath[cwcPath - 1] != '\\' || wszPath[cwcPath - 1] != '/')
421 wszPath[cwcPath++] = '\\';
422 RTUtf16CopyAscii(&wszPath[cwcPath], RT_ELEMENTS(wszPath) - cwcPath, "WinHvPlatform.dll");
423 if (GetFileAttributesW(wszPath) == INVALID_FILE_ATTRIBUTES)
424 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "The native API dll was not found (%ls)", wszPath);
425
426 /*
427 * Check that we're in a VM and that the hypervisor identifies itself as Hyper-V.
428 */
429 if (!ASMHasCpuId())
430 return RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "No CPUID support");
431 if (!ASMIsValidStdRange(ASMCpuId_EAX(0)))
432 return RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "No CPUID leaf #1");
433 if (!(ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_HVP))
434 return RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Not in a hypervisor partition (HVP=0)");
435
436 uint32_t cMaxHyperLeaf = 0;
437 uint32_t uEbx = 0;
438 uint32_t uEcx = 0;
439 uint32_t uEdx = 0;
440 ASMCpuIdExSlow(0x40000000, 0, 0, 0, &cMaxHyperLeaf, &uEbx, &uEcx, &uEdx);
441 if (!ASMIsValidHypervisorRange(cMaxHyperLeaf))
442 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Invalid hypervisor CPUID range (%#x %#x %#x %#x)",
443 cMaxHyperLeaf, uEbx, uEcx, uEdx);
444 if ( uEbx != UINT32_C(0x7263694d) /* Micr */
445 || uEcx != UINT32_C(0x666f736f) /* osof */
446 || uEdx != UINT32_C(0x76482074) /* t Hv */)
447 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE,
448 "Not Hyper-V CPUID signature: %#x %#x %#x (expected %#x %#x %#x)",
449 uEbx, uEcx, uEdx, UINT32_C(0x7263694d), UINT32_C(0x666f736f), UINT32_C(0x76482074));
450 if (cMaxHyperLeaf < UINT32_C(0x40000005))
451 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Too narrow hypervisor CPUID range (%#x)", cMaxHyperLeaf);
452
453 /** @todo would be great if we could recognize a root partition from the
454 * CPUID info, but I currently don't dare do that. */
455
456 /*
457 * Now try load the DLLs and resolve the APIs.
458 */
459 static const char * const s_apszDllNames[2] = { "WinHvPlatform.dll", "vid.dll" };
460 RTLDRMOD ahMods[2] = { NIL_RTLDRMOD, NIL_RTLDRMOD };
461 int rc = VINF_SUCCESS;
462 for (unsigned i = 0; i < RT_ELEMENTS(s_apszDllNames); i++)
463 {
464 int rc2 = RTLdrLoadSystem(s_apszDllNames[i], true /*fNoUnload*/, &ahMods[i]);
465 if (RT_FAILURE(rc2))
466 {
467 if (!RTErrInfoIsSet(pErrInfo))
468 RTErrInfoSetF(pErrInfo, rc2, "Failed to load API DLL: %s: %Rrc", s_apszDllNames[i], rc2);
469 else
470 RTErrInfoAddF(pErrInfo, rc2, "; %s: %Rrc", s_apszDllNames[i], rc2);
471 ahMods[i] = NIL_RTLDRMOD;
472 rc = VERR_NEM_INIT_FAILED;
473 }
474 }
475 if (RT_SUCCESS(rc))
476 rc = nemR3WinInitVidIntercepts(ahMods[1], pErrInfo);
477 if (RT_SUCCESS(rc))
478 {
479 for (unsigned i = 0; i < RT_ELEMENTS(g_aImports); i++)
480 {
481 int rc2 = RTLdrGetSymbol(ahMods[g_aImports[i].idxDll], g_aImports[i].pszName, (void **)g_aImports[i].ppfn);
482 if (RT_FAILURE(rc2))
483 {
484 *g_aImports[i].ppfn = NULL;
485
486 LogRel(("NEM: %s: Failed to import %s!%s: %Rrc",
487 g_aImports[i].fOptional ? "info" : fForced ? "fatal" : "error",
488 s_apszDllNames[g_aImports[i].idxDll], g_aImports[i].pszName, rc2));
489 if (!g_aImports[i].fOptional)
490 {
491 if (RTErrInfoIsSet(pErrInfo))
492 RTErrInfoAddF(pErrInfo, rc2, ", %s!%s",
493 s_apszDllNames[g_aImports[i].idxDll], g_aImports[i].pszName);
494 else
495 rc = RTErrInfoSetF(pErrInfo, rc2, "Failed to import: %s!%s",
496 s_apszDllNames[g_aImports[i].idxDll], g_aImports[i].pszName);
497 Assert(RT_FAILURE(rc));
498 }
499 }
500 }
501 if (RT_SUCCESS(rc))
502 {
503 Assert(!RTErrInfoIsSet(pErrInfo));
504 }
505 }
506
507 for (unsigned i = 0; i < RT_ELEMENTS(ahMods); i++)
508 RTLdrClose(ahMods[i]);
509 return rc;
510}
511
512
513/**
514 * Worker for nemR3NativeInit that gets the hypervisor capabilities.
515 *
516 * @returns VBox status code.
517 * @param pVM The cross context VM structure.
518 * @param pErrInfo Where to always return error info.
519 */
520static int nemR3WinInitCheckCapabilities(PVM pVM, PRTERRINFO pErrInfo)
521{
522#define NEM_LOG_REL_CAP_EX(a_szField, a_szFmt, a_Value) LogRel(("NEM: %-38s= " a_szFmt "\n", a_szField, a_Value))
523#define NEM_LOG_REL_CAP_SUB_EX(a_szField, a_szFmt, a_Value) LogRel(("NEM: %36s: " a_szFmt "\n", a_szField, a_Value))
524#define NEM_LOG_REL_CAP_SUB(a_szField, a_Value) NEM_LOG_REL_CAP_SUB_EX(a_szField, "%d", a_Value)
525
526 /*
527 * Is the hypervisor present with the desired capability?
528 *
529 * In build 17083 this translates into:
530 * - CPUID[0x00000001].HVP is set
531 * - CPUID[0x40000000] == "Microsoft Hv"
532 * - CPUID[0x40000001].eax == "Hv#1"
533 * - CPUID[0x40000003].ebx[12] is set.
534 * - VidGetExoPartitionProperty(INVALID_HANDLE_VALUE, 0x60000, &Ignored) returns
535 * a non-zero value.
536 */
537 /**
538 * @todo Someone at Microsoft please explain weird API design:
539 * 1. Pointless CapabilityCode duplication int the output;
540 * 2. No output size.
541 */
542 WHV_CAPABILITY Caps;
543 RT_ZERO(Caps);
544 SetLastError(0);
545 HRESULT hrc = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &Caps, sizeof(Caps));
546 DWORD rcWin = GetLastError();
547 if (FAILED(hrc))
548 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
549 "WHvGetCapability/WHvCapabilityCodeHypervisorPresent failed: %Rhrc (Last=%#x/%u)",
550 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
551 if (!Caps.HypervisorPresent)
552 {
553 if (!RTPathExists(RTPATH_NT_PASSTHRU_PREFIX "Device\\VidExo"))
554 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE,
555 "WHvCapabilityCodeHypervisorPresent is FALSE! Make sure you have enabled the 'Windows Hypervisor Platform' feature.");
556 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "WHvCapabilityCodeHypervisorPresent is FALSE! (%u)", rcWin);
557 }
558 LogRel(("NEM: WHvCapabilityCodeHypervisorPresent is TRUE, so this might work...\n"));
559
560
561 /*
562 * Check what extended VM exits are supported.
563 */
564 RT_ZERO(Caps);
565 hrc = WHvGetCapability(WHvCapabilityCodeExtendedVmExits, &Caps, sizeof(Caps));
566 if (FAILED(hrc))
567 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
568 "WHvGetCapability/WHvCapabilityCodeExtendedVmExits failed: %Rhrc (Last=%#x/%u)",
569 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
570 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeExtendedVmExits", "%'#018RX64", Caps.ExtendedVmExits.AsUINT64);
571 pVM->nem.s.fExtendedMsrExit = RT_BOOL(Caps.ExtendedVmExits.X64MsrExit);
572 pVM->nem.s.fExtendedCpuIdExit = RT_BOOL(Caps.ExtendedVmExits.X64CpuidExit);
573 pVM->nem.s.fExtendedXcptExit = RT_BOOL(Caps.ExtendedVmExits.ExceptionExit);
574 NEM_LOG_REL_CAP_SUB("fExtendedMsrExit", pVM->nem.s.fExtendedMsrExit);
575 NEM_LOG_REL_CAP_SUB("fExtendedCpuIdExit", pVM->nem.s.fExtendedCpuIdExit);
576 NEM_LOG_REL_CAP_SUB("fExtendedXcptExit", pVM->nem.s.fExtendedXcptExit);
577 if (Caps.ExtendedVmExits.AsUINT64 & ~(uint64_t)7)
578 LogRel(("NEM: Warning! Unknown VM exit definitions: %#RX64\n", Caps.ExtendedVmExits.AsUINT64));
579 /** @todo RECHECK: WHV_EXTENDED_VM_EXITS typedef. */
580
581 /*
582 * Check features in case they end up defining any.
583 */
584 RT_ZERO(Caps);
585 hrc = WHvGetCapability(WHvCapabilityCodeFeatures, &Caps, sizeof(Caps));
586 if (FAILED(hrc))
587 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
588 "WHvGetCapability/WHvCapabilityCodeFeatures failed: %Rhrc (Last=%#x/%u)",
589 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
590 if (Caps.Features.AsUINT64 & ~(uint64_t)0)
591 LogRel(("NEM: Warning! Unknown feature definitions: %#RX64\n", Caps.Features.AsUINT64));
592 /** @todo RECHECK: WHV_CAPABILITY_FEATURES typedef. */
593
594 /*
595 * Check that the CPU vendor is supported.
596 */
597 RT_ZERO(Caps);
598 hrc = WHvGetCapability(WHvCapabilityCodeProcessorVendor, &Caps, sizeof(Caps));
599 if (FAILED(hrc))
600 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
601 "WHvGetCapability/WHvCapabilityCodeProcessorVendor failed: %Rhrc (Last=%#x/%u)",
602 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
603 switch (Caps.ProcessorVendor)
604 {
605 /** @todo RECHECK: WHV_PROCESSOR_VENDOR typedef. */
606 case WHvProcessorVendorIntel:
607 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d - Intel", Caps.ProcessorVendor);
608 pVM->nem.s.enmCpuVendor = CPUMCPUVENDOR_INTEL;
609 break;
610 case WHvProcessorVendorAmd:
611 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d - AMD", Caps.ProcessorVendor);
612 pVM->nem.s.enmCpuVendor = CPUMCPUVENDOR_AMD;
613 break;
614 default:
615 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d", Caps.ProcessorVendor);
616 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Unknown processor vendor: %d", Caps.ProcessorVendor);
617 }
618
619 /*
620 * CPU features, guessing these are virtual CPU features?
621 */
622 RT_ZERO(Caps);
623 hrc = WHvGetCapability(WHvCapabilityCodeProcessorFeatures, &Caps, sizeof(Caps));
624 if (FAILED(hrc))
625 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
626 "WHvGetCapability/WHvCapabilityCodeProcessorFeatures failed: %Rhrc (Last=%#x/%u)",
627 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
628 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorFeatures", "%'#018RX64", Caps.ProcessorFeatures.AsUINT64);
629#define NEM_LOG_REL_CPU_FEATURE(a_Field) NEM_LOG_REL_CAP_SUB(#a_Field, Caps.ProcessorFeatures.a_Field)
630 NEM_LOG_REL_CPU_FEATURE(Sse3Support);
631 NEM_LOG_REL_CPU_FEATURE(LahfSahfSupport);
632 NEM_LOG_REL_CPU_FEATURE(Ssse3Support);
633 NEM_LOG_REL_CPU_FEATURE(Sse4_1Support);
634 NEM_LOG_REL_CPU_FEATURE(Sse4_2Support);
635 NEM_LOG_REL_CPU_FEATURE(Sse4aSupport);
636 NEM_LOG_REL_CPU_FEATURE(XopSupport);
637 NEM_LOG_REL_CPU_FEATURE(PopCntSupport);
638 NEM_LOG_REL_CPU_FEATURE(Cmpxchg16bSupport);
639 NEM_LOG_REL_CPU_FEATURE(Altmovcr8Support);
640 NEM_LOG_REL_CPU_FEATURE(LzcntSupport);
641 NEM_LOG_REL_CPU_FEATURE(MisAlignSseSupport);
642 NEM_LOG_REL_CPU_FEATURE(MmxExtSupport);
643 NEM_LOG_REL_CPU_FEATURE(Amd3DNowSupport);
644 NEM_LOG_REL_CPU_FEATURE(ExtendedAmd3DNowSupport);
645 NEM_LOG_REL_CPU_FEATURE(Page1GbSupport);
646 NEM_LOG_REL_CPU_FEATURE(AesSupport);
647 NEM_LOG_REL_CPU_FEATURE(PclmulqdqSupport);
648 NEM_LOG_REL_CPU_FEATURE(PcidSupport);
649 NEM_LOG_REL_CPU_FEATURE(Fma4Support);
650 NEM_LOG_REL_CPU_FEATURE(F16CSupport);
651 NEM_LOG_REL_CPU_FEATURE(RdRandSupport);
652 NEM_LOG_REL_CPU_FEATURE(RdWrFsGsSupport);
653 NEM_LOG_REL_CPU_FEATURE(SmepSupport);
654 NEM_LOG_REL_CPU_FEATURE(EnhancedFastStringSupport);
655 NEM_LOG_REL_CPU_FEATURE(Bmi1Support);
656 NEM_LOG_REL_CPU_FEATURE(Bmi2Support);
657 /* two reserved bits here, see below */
658 NEM_LOG_REL_CPU_FEATURE(MovbeSupport);
659 NEM_LOG_REL_CPU_FEATURE(Npiep1Support);
660 NEM_LOG_REL_CPU_FEATURE(DepX87FPUSaveSupport);
661 NEM_LOG_REL_CPU_FEATURE(RdSeedSupport);
662 NEM_LOG_REL_CPU_FEATURE(AdxSupport);
663 NEM_LOG_REL_CPU_FEATURE(IntelPrefetchSupport);
664 NEM_LOG_REL_CPU_FEATURE(SmapSupport);
665 NEM_LOG_REL_CPU_FEATURE(HleSupport);
666 NEM_LOG_REL_CPU_FEATURE(RtmSupport);
667 NEM_LOG_REL_CPU_FEATURE(RdtscpSupport);
668 NEM_LOG_REL_CPU_FEATURE(ClflushoptSupport);
669 NEM_LOG_REL_CPU_FEATURE(ClwbSupport);
670 NEM_LOG_REL_CPU_FEATURE(ShaSupport);
671 NEM_LOG_REL_CPU_FEATURE(X87PointersSavedSupport);
672#undef NEM_LOG_REL_CPU_FEATURE
673 if (Caps.ProcessorFeatures.AsUINT64 & (~(RT_BIT_64(43) - 1) | RT_BIT_64(27) | RT_BIT_64(28)))
674 LogRel(("NEM: Warning! Unknown CPU features: %#RX64\n", Caps.ProcessorFeatures.AsUINT64));
675 pVM->nem.s.uCpuFeatures.u64 = Caps.ProcessorFeatures.AsUINT64;
676 /** @todo RECHECK: WHV_PROCESSOR_FEATURES typedef. */
677
678 /*
679 * The cache line flush size.
680 */
681 RT_ZERO(Caps);
682 hrc = WHvGetCapability(WHvCapabilityCodeProcessorClFlushSize, &Caps, sizeof(Caps));
683 if (FAILED(hrc))
684 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
685 "WHvGetCapability/WHvCapabilityCodeProcessorClFlushSize failed: %Rhrc (Last=%#x/%u)",
686 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
687 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorClFlushSize", "2^%u", Caps.ProcessorClFlushSize);
688 if (Caps.ProcessorClFlushSize < 8 && Caps.ProcessorClFlushSize > 9)
689 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Unsupported cache line flush size: %u", Caps.ProcessorClFlushSize);
690 pVM->nem.s.cCacheLineFlushShift = Caps.ProcessorClFlushSize;
691
692 /*
693 * See if they've added more properties that we're not aware of.
694 */
695 /** @todo RECHECK: WHV_CAPABILITY_CODE typedef. */
696 if (!IsDebuggerPresent()) /* Too noisy when in debugger, so skip. */
697 {
698 static const struct
699 {
700 uint32_t iMin, iMax; } s_aUnknowns[] =
701 {
702 { 0x0003, 0x000f },
703 { 0x1003, 0x100f },
704 { 0x2000, 0x200f },
705 { 0x3000, 0x300f },
706 { 0x4000, 0x400f },
707 };
708 for (uint32_t j = 0; j < RT_ELEMENTS(s_aUnknowns); j++)
709 for (uint32_t i = s_aUnknowns[j].iMin; i <= s_aUnknowns[j].iMax; i++)
710 {
711 RT_ZERO(Caps);
712 hrc = WHvGetCapability((WHV_CAPABILITY_CODE)i, &Caps, sizeof(Caps));
713 if (SUCCEEDED(hrc))
714 LogRel(("NEM: Warning! Unknown capability %#x returning: %.*Rhxs\n", i, sizeof(Caps), &Caps));
715 }
716 }
717
718#undef NEM_LOG_REL_CAP_EX
719#undef NEM_LOG_REL_CAP_SUB_EX
720#undef NEM_LOG_REL_CAP_SUB
721 return VINF_SUCCESS;
722}
723
724
725/**
726 * Used to fill in g_IoCtlGetHvPartitionId.
727 */
728static NTSTATUS WINAPI
729nemR3WinIoctlDetector_GetHvPartitionId(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
730 PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
731 PVOID pvOutput, ULONG cbOutput)
732{
733 AssertLogRelMsgReturn(hFile == NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, ("hFile=%p\n", hFile), STATUS_INVALID_PARAMETER_1);
734 RT_NOREF(hEvt); RT_NOREF(pfnApcCallback); RT_NOREF(pvApcCtx);
735 AssertLogRelMsgReturn(RT_VALID_PTR(pIos), ("pIos=%p\n", pIos), STATUS_INVALID_PARAMETER_5);
736 AssertLogRelMsgReturn(cbInput == 0, ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_8);
737 RT_NOREF(pvInput);
738
739 AssertLogRelMsgReturn(RT_VALID_PTR(pvOutput), ("pvOutput=%p\n", pvOutput), STATUS_INVALID_PARAMETER_9);
740 AssertLogRelMsgReturn(cbOutput == sizeof(HV_PARTITION_ID), ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_10);
741 *(HV_PARTITION_ID *)pvOutput = NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_ID;
742
743 g_IoCtlGetHvPartitionId.cbInput = cbInput;
744 g_IoCtlGetHvPartitionId.cbOutput = cbOutput;
745 g_IoCtlGetHvPartitionId.uFunction = uFunction;
746
747 return STATUS_SUCCESS;
748}
749
750
751/**
752 * Used to fill in g_IoCtlStartVirtualProcessor.
753 */
754static NTSTATUS WINAPI
755nemR3WinIoctlDetector_StartVirtualProcessor(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
756 PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
757 PVOID pvOutput, ULONG cbOutput)
758{
759 AssertLogRelMsgReturn(hFile == NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, ("hFile=%p\n", hFile), STATUS_INVALID_PARAMETER_1);
760 RT_NOREF(hEvt); RT_NOREF(pfnApcCallback); RT_NOREF(pvApcCtx);
761 AssertLogRelMsgReturn(RT_VALID_PTR(pIos), ("pIos=%p\n", pIos), STATUS_INVALID_PARAMETER_5);
762 AssertLogRelMsgReturn(cbInput == sizeof(HV_VP_INDEX), ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_8);
763 AssertLogRelMsgReturn(RT_VALID_PTR(pvInput), ("pvInput=%p\n", pvInput), STATUS_INVALID_PARAMETER_9);
764 AssertLogRelMsgReturn(*(HV_VP_INDEX *)pvInput == NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX,
765 ("*piCpu=%u\n", *(HV_VP_INDEX *)pvInput), STATUS_INVALID_PARAMETER_9);
766 AssertLogRelMsgReturn(cbOutput == 0, ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_10);
767 RT_NOREF(pvOutput);
768
769 g_IoCtlStartVirtualProcessor.cbInput = cbInput;
770 g_IoCtlStartVirtualProcessor.cbOutput = cbOutput;
771 g_IoCtlStartVirtualProcessor.uFunction = uFunction;
772
773 return STATUS_SUCCESS;
774}
775
776
777/**
778 * Used to fill in g_IoCtlStartVirtualProcessor.
779 */
780static NTSTATUS WINAPI
781nemR3WinIoctlDetector_StopVirtualProcessor(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
782 PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
783 PVOID pvOutput, ULONG cbOutput)
784{
785 AssertLogRelMsgReturn(hFile == NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, ("hFile=%p\n", hFile), STATUS_INVALID_PARAMETER_1);
786 RT_NOREF(hEvt); RT_NOREF(pfnApcCallback); RT_NOREF(pvApcCtx);
787 AssertLogRelMsgReturn(RT_VALID_PTR(pIos), ("pIos=%p\n", pIos), STATUS_INVALID_PARAMETER_5);
788 AssertLogRelMsgReturn(cbInput == sizeof(HV_VP_INDEX), ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_8);
789 AssertLogRelMsgReturn(RT_VALID_PTR(pvInput), ("pvInput=%p\n", pvInput), STATUS_INVALID_PARAMETER_9);
790 AssertLogRelMsgReturn(*(HV_VP_INDEX *)pvInput == NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX,
791 ("*piCpu=%u\n", *(HV_VP_INDEX *)pvInput), STATUS_INVALID_PARAMETER_9);
792 AssertLogRelMsgReturn(cbOutput == 0, ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_10);
793 RT_NOREF(pvOutput);
794
795 g_IoCtlStopVirtualProcessor.cbInput = cbInput;
796 g_IoCtlStopVirtualProcessor.cbOutput = cbOutput;
797 g_IoCtlStopVirtualProcessor.uFunction = uFunction;
798
799 return STATUS_SUCCESS;
800}
801
802
803/**
804 * Used to fill in g_IoCtlMessageSlotHandleAndGetNext
805 */
806static NTSTATUS WINAPI
807nemR3WinIoctlDetector_MessageSlotHandleAndGetNext(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
808 PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
809 PVOID pvOutput, ULONG cbOutput)
810{
811 AssertLogRelMsgReturn(hFile == NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, ("hFile=%p\n", hFile), STATUS_INVALID_PARAMETER_1);
812 RT_NOREF(hEvt); RT_NOREF(pfnApcCallback); RT_NOREF(pvApcCtx);
813 AssertLogRelMsgReturn(RT_VALID_PTR(pIos), ("pIos=%p\n", pIos), STATUS_INVALID_PARAMETER_5);
814
815 AssertLogRelMsgReturn(cbInput == sizeof(VID_IOCTL_INPUT_MESSAGE_SLOT_HANDLE_AND_GET_NEXT), ("cbInput=%#x\n", cbInput),
816 STATUS_INVALID_PARAMETER_8);
817 AssertLogRelMsgReturn(RT_VALID_PTR(pvInput), ("pvInput=%p\n", pvInput), STATUS_INVALID_PARAMETER_9);
818 PCVID_IOCTL_INPUT_MESSAGE_SLOT_HANDLE_AND_GET_NEXT pVidIn = (PCVID_IOCTL_INPUT_MESSAGE_SLOT_HANDLE_AND_GET_NEXT)pvInput;
819 AssertLogRelMsgReturn( pVidIn->iCpu == NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX
820 && pVidIn->fFlags == VID_MSHAGN_F_HANDLE_MESSAGE
821 && pVidIn->cMillies == NEM_WIN_IOCTL_DETECTOR_FAKE_TIMEOUT,
822 ("iCpu=%u fFlags=%#x cMillies=%#x\n", pVidIn->iCpu, pVidIn->fFlags, pVidIn->cMillies),
823 STATUS_INVALID_PARAMETER_9);
824 AssertLogRelMsgReturn(cbOutput == 0, ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_10);
825 RT_NOREF(pvOutput);
826
827 g_IoCtlMessageSlotHandleAndGetNext.cbInput = cbInput;
828 g_IoCtlMessageSlotHandleAndGetNext.cbOutput = cbOutput;
829 g_IoCtlMessageSlotHandleAndGetNext.uFunction = uFunction;
830
831 return STATUS_SUCCESS;
832}
833
834
835#ifdef LOG_ENABLED
836/**
837 * Used to fill in what g_pIoCtlDetectForLogging points to.
838 */
839static NTSTATUS WINAPI nemR3WinIoctlDetector_ForLogging(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
840 PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
841 PVOID pvOutput, ULONG cbOutput)
842{
843 RT_NOREF(hFile, hEvt, pfnApcCallback, pvApcCtx, pIos, pvInput, pvOutput);
844
845 g_pIoCtlDetectForLogging->cbInput = cbInput;
846 g_pIoCtlDetectForLogging->cbOutput = cbOutput;
847 g_pIoCtlDetectForLogging->uFunction = uFunction;
848
849 return STATUS_SUCCESS;
850}
851#endif
852
853
854/**
855 * Worker for nemR3NativeInit that detect I/O control function numbers for VID.
856 *
857 * We use the function numbers directly in ring-0 and to name functions when
858 * logging NtDeviceIoControlFile calls.
859 *
860 * @note We could alternatively do this by disassembling the respective
861 * functions, but hooking NtDeviceIoControlFile and making fake calls
862 * more easily provides the desired information.
863 *
864 * @returns VBox status code.
865 * @param pVM The cross context VM structure. Will set I/O
866 * control info members.
867 * @param pErrInfo Where to always return error info.
868 */
869static int nemR3WinInitDiscoverIoControlProperties(PVM pVM, PRTERRINFO pErrInfo)
870{
871 /*
872 * Probe the I/O control information for select VID APIs so we can use
873 * them directly from ring-0 and better log them.
874 *
875 */
876 decltype(NtDeviceIoControlFile) * const pfnOrg = *g_ppfnVidNtDeviceIoControlFile;
877
878 /* VidGetHvPartitionId */
879 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_GetHvPartitionId;
880 HV_PARTITION_ID idHvPartition = HV_PARTITION_ID_INVALID;
881 BOOL fRet = g_pfnVidGetHvPartitionId(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, &idHvPartition);
882 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
883 AssertReturn(fRet && idHvPartition == NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_ID && g_IoCtlGetHvPartitionId.uFunction != 0,
884 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
885 "Problem figuring out VidGetHvPartitionId: fRet=%u idHvPartition=%#x dwErr=%u",
886 fRet, idHvPartition, GetLastError()) );
887 LogRel(("NEM: VidGetHvPartitionId -> fun:%#x in:%#x out:%#x\n",
888 g_IoCtlGetHvPartitionId.uFunction, g_IoCtlGetHvPartitionId.cbInput, g_IoCtlGetHvPartitionId.cbOutput));
889
890 /* VidStartVirtualProcessor */
891 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_StartVirtualProcessor;
892 fRet = g_pfnVidStartVirtualProcessor(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX);
893 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
894 AssertReturn(fRet && g_IoCtlStartVirtualProcessor.uFunction != 0,
895 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
896 "Problem figuring out VidStartVirtualProcessor: fRet=%u dwErr=%u",
897 fRet, GetLastError()) );
898 LogRel(("NEM: VidStartVirtualProcessor -> fun:%#x in:%#x out:%#x\n", g_IoCtlStartVirtualProcessor.uFunction,
899 g_IoCtlStartVirtualProcessor.cbInput, g_IoCtlStartVirtualProcessor.cbOutput));
900
901 /* VidStopVirtualProcessor */
902 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_StopVirtualProcessor;
903 fRet = g_pfnVidStopVirtualProcessor(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX);
904 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
905 AssertReturn(fRet && g_IoCtlStopVirtualProcessor.uFunction != 0,
906 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
907 "Problem figuring out VidStopVirtualProcessor: fRet=%u dwErr=%u",
908 fRet, GetLastError()) );
909 LogRel(("NEM: VidStopVirtualProcessor -> fun:%#x in:%#x out:%#x\n", g_IoCtlStopVirtualProcessor.uFunction,
910 g_IoCtlStopVirtualProcessor.cbInput, g_IoCtlStopVirtualProcessor.cbOutput));
911
912 /* VidMessageSlotHandleAndGetNext */
913 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_MessageSlotHandleAndGetNext;
914 fRet = g_pfnVidMessageSlotHandleAndGetNext(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE,
915 NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX, VID_MSHAGN_F_HANDLE_MESSAGE,
916 NEM_WIN_IOCTL_DETECTOR_FAKE_TIMEOUT);
917 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
918 AssertReturn(fRet && g_IoCtlMessageSlotHandleAndGetNext.uFunction != 0,
919 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
920 "Problem figuring out VidMessageSlotHandleAndGetNext: fRet=%u dwErr=%u",
921 fRet, GetLastError()) );
922 LogRel(("NEM: VidMessageSlotHandleAndGetNext -> fun:%#x in:%#x out:%#x\n",
923 g_IoCtlMessageSlotHandleAndGetNext.uFunction, g_IoCtlMessageSlotHandleAndGetNext.cbInput,
924 g_IoCtlMessageSlotHandleAndGetNext.cbOutput));
925
926#ifdef LOG_ENABLED
927 /* The following are only for logging: */
928 union
929 {
930 VID_MAPPED_MESSAGE_SLOT MapSlot;
931 HV_REGISTER_NAME Name;
932 HV_REGISTER_VALUE Value;
933 } uBuf;
934
935 /* VidMessageSlotMap */
936 g_pIoCtlDetectForLogging = &g_IoCtlMessageSlotMap;
937 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_ForLogging;
938 fRet = g_pfnVidMessageSlotMap(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, &uBuf.MapSlot, NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX);
939 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
940 Assert(fRet);
941 LogRel(("NEM: VidMessageSlotMap -> fun:%#x in:%#x out:%#x\n", g_pIoCtlDetectForLogging->uFunction,
942 g_pIoCtlDetectForLogging->cbInput, g_pIoCtlDetectForLogging->cbOutput));
943
944 /* VidGetVirtualProcessorState */
945 uBuf.Name = HvRegisterExplicitSuspend;
946 g_pIoCtlDetectForLogging = &g_IoCtlGetVirtualProcessorState;
947 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_ForLogging;
948 fRet = g_pfnVidGetVirtualProcessorState(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX,
949 &uBuf.Name, 1, &uBuf.Value);
950 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
951 Assert(fRet);
952 LogRel(("NEM: VidGetVirtualProcessorState -> fun:%#x in:%#x out:%#x\n", g_pIoCtlDetectForLogging->uFunction,
953 g_pIoCtlDetectForLogging->cbInput, g_pIoCtlDetectForLogging->cbOutput));
954
955 /* VidSetVirtualProcessorState */
956 uBuf.Name = HvRegisterExplicitSuspend;
957 g_pIoCtlDetectForLogging = &g_IoCtlSetVirtualProcessorState;
958 *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_ForLogging;
959 fRet = g_pfnVidSetVirtualProcessorState(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX,
960 &uBuf.Name, 1, &uBuf.Value);
961 *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
962 Assert(fRet);
963 LogRel(("NEM: VidSetVirtualProcessorState -> fun:%#x in:%#x out:%#x\n", g_pIoCtlDetectForLogging->uFunction,
964 g_pIoCtlDetectForLogging->cbInput, g_pIoCtlDetectForLogging->cbOutput));
965
966 g_pIoCtlDetectForLogging = NULL;
967#endif
968
969 /* Done. */
970 pVM->nem.s.IoCtlGetHvPartitionId = g_IoCtlGetHvPartitionId;
971 pVM->nem.s.IoCtlStartVirtualProcessor = g_IoCtlStartVirtualProcessor;
972 pVM->nem.s.IoCtlStopVirtualProcessor = g_IoCtlStopVirtualProcessor;
973 pVM->nem.s.IoCtlMessageSlotHandleAndGetNext = g_IoCtlMessageSlotHandleAndGetNext;
974 return VINF_SUCCESS;
975}
976
977
978/**
979 * Creates and sets up a Hyper-V (exo) partition.
980 *
981 * @returns VBox status code.
982 * @param pVM The cross context VM structure.
983 * @param pErrInfo Where to always return error info.
984 */
985static int nemR3WinInitCreatePartition(PVM pVM, PRTERRINFO pErrInfo)
986{
987 AssertReturn(!pVM->nem.s.hPartition, RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER, "Wrong initalization order"));
988 AssertReturn(!pVM->nem.s.hPartitionDevice, RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER, "Wrong initalization order"));
989
990 /*
991 * Create the partition.
992 */
993 WHV_PARTITION_HANDLE hPartition;
994 HRESULT hrc = WHvCreatePartition(&hPartition);
995 if (FAILED(hrc))
996 return RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "WHvCreatePartition failed with %Rhrc (Last=%#x/%u)",
997 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
998
999 int rc;
1000
1001 /*
1002 * Set partition properties, most importantly the CPU count.
1003 */
1004 /**
1005 * @todo Someone at Microsoft please explain another weird API:
1006 * - Why this API doesn't take the WHV_PARTITION_PROPERTY_CODE value as an
1007 * argument rather than as part of the struct. That is so weird if you've
1008 * used any other NT or windows API, including WHvGetCapability().
1009 * - Why use PVOID when WHV_PARTITION_PROPERTY is what's expected. We
1010 * technically only need 9 bytes for setting/getting
1011 * WHVPartitionPropertyCodeProcessorClFlushSize, but the API insists on 16. */
1012 WHV_PARTITION_PROPERTY Property;
1013 RT_ZERO(Property);
1014 Property.PropertyCode = WHvPartitionPropertyCodeProcessorCount;
1015 Property.ProcessorCount = pVM->cCpus;
1016 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
1017 if (SUCCEEDED(hrc))
1018 {
1019 RT_ZERO(Property);
1020 Property.PropertyCode = WHvPartitionPropertyCodeExtendedVmExits;
1021 Property.ExtendedVmExits.X64CpuidExit = pVM->nem.s.fExtendedCpuIdExit;
1022 Property.ExtendedVmExits.X64MsrExit = pVM->nem.s.fExtendedMsrExit;
1023 Property.ExtendedVmExits.ExceptionExit = pVM->nem.s.fExtendedXcptExit;
1024 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
1025 if (SUCCEEDED(hrc))
1026 {
1027 /*
1028 * We'll continue setup in nemR3NativeInitAfterCPUM.
1029 */
1030 pVM->nem.s.fCreatedEmts = false;
1031 pVM->nem.s.hPartition = hPartition;
1032 LogRel(("NEM: Created partition %p.\n", hPartition));
1033 return VINF_SUCCESS;
1034 }
1035
1036 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED,
1037 "Failed setting WHvPartitionPropertyCodeExtendedVmExits to %'#RX64: %Rhrc",
1038 Property.ExtendedVmExits.AsUINT64, hrc);
1039 }
1040 else
1041 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED,
1042 "Failed setting WHvPartitionPropertyCodeProcessorCount to %u: %Rhrc (Last=%#x/%u)",
1043 pVM->cCpus, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
1044 WHvDeletePartition(hPartition);
1045
1046 Assert(!pVM->nem.s.hPartitionDevice);
1047 Assert(!pVM->nem.s.hPartition);
1048 return rc;
1049}
1050
1051
1052/**
1053 * Try initialize the native API.
1054 *
1055 * This may only do part of the job, more can be done in
1056 * nemR3NativeInitAfterCPUM() and nemR3NativeInitCompleted().
1057 *
1058 * @returns VBox status code.
1059 * @param pVM The cross context VM structure.
1060 * @param fFallback Whether we're in fallback mode or use-NEM mode. In
1061 * the latter we'll fail if we cannot initialize.
1062 * @param fForced Whether the HMForced flag is set and we should
1063 * fail if we cannot initialize.
1064 */
1065int nemR3NativeInit(PVM pVM, bool fFallback, bool fForced)
1066{
1067 /*
1068 * Error state.
1069 * The error message will be non-empty on failure and 'rc' will be set too.
1070 */
1071 RTERRINFOSTATIC ErrInfo;
1072 PRTERRINFO pErrInfo = RTErrInfoInitStatic(&ErrInfo);
1073 int rc = nemR3WinInitProbeAndLoad(fForced, pErrInfo);
1074 if (RT_SUCCESS(rc))
1075 {
1076 /*
1077 * Check the capabilties of the hypervisor, starting with whether it's present.
1078 */
1079 rc = nemR3WinInitCheckCapabilities(pVM, pErrInfo);
1080 if (RT_SUCCESS(rc))
1081 {
1082 /*
1083 * Discover the VID I/O control function numbers we need.
1084 */
1085 rc = nemR3WinInitDiscoverIoControlProperties(pVM, pErrInfo);
1086 if (RT_SUCCESS(rc))
1087 {
1088 /*
1089 * Check out our ring-0 capabilities.
1090 */
1091 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, 0 /*idCpu*/, VMMR0_DO_NEM_INIT_VM, 0, NULL);
1092 if (RT_SUCCESS(rc))
1093 {
1094 /*
1095 * Create and initialize a partition.
1096 */
1097 rc = nemR3WinInitCreatePartition(pVM, pErrInfo);
1098 if (RT_SUCCESS(rc))
1099 {
1100 VM_SET_MAIN_EXECUTION_ENGINE(pVM, VM_EXEC_ENGINE_NATIVE_API);
1101 Log(("NEM: Marked active!\n"));
1102 }
1103 }
1104 }
1105 }
1106 }
1107
1108 /*
1109 * We only fail if in forced mode, otherwise just log the complaint and return.
1110 */
1111 Assert(pVM->bMainExecutionEngine == VM_EXEC_ENGINE_NATIVE_API || RTErrInfoIsSet(pErrInfo));
1112 if ( (fForced || !fFallback)
1113 && pVM->bMainExecutionEngine != VM_EXEC_ENGINE_NATIVE_API)
1114 return VMSetError(pVM, RT_SUCCESS_NP(rc) ? VERR_NEM_NOT_AVAILABLE : rc, RT_SRC_POS, "%s", pErrInfo->pszMsg);
1115
1116 if (RTErrInfoIsSet(pErrInfo))
1117 LogRel(("NEM: Not available: %s\n", pErrInfo->pszMsg));
1118 return VINF_SUCCESS;
1119}
1120
1121
1122/**
1123 * This is called after CPUMR3Init is done.
1124 *
1125 * @returns VBox status code.
1126 * @param pVM The VM handle..
1127 */
1128int nemR3NativeInitAfterCPUM(PVM pVM)
1129{
1130 /*
1131 * Validate sanity.
1132 */
1133 WHV_PARTITION_HANDLE hPartition = pVM->nem.s.hPartition;
1134 AssertReturn(hPartition != NULL, VERR_WRONG_ORDER);
1135 AssertReturn(!pVM->nem.s.hPartitionDevice, VERR_WRONG_ORDER);
1136 AssertReturn(!pVM->nem.s.fCreatedEmts, VERR_WRONG_ORDER);
1137 AssertReturn(pVM->bMainExecutionEngine == VM_EXEC_ENGINE_NATIVE_API, VERR_WRONG_ORDER);
1138
1139 /*
1140 * Continue setting up the partition now that we've got most of the CPUID feature stuff.
1141 */
1142
1143 /* Not sure if we really need to set the vendor. */
1144 WHV_PARTITION_PROPERTY Property;
1145 RT_ZERO(Property);
1146 Property.PropertyCode = WHvPartitionPropertyCodeProcessorVendor;
1147 Property.ProcessorVendor = pVM->nem.s.enmCpuVendor == CPUMCPUVENDOR_AMD ? WHvProcessorVendorAmd
1148 : WHvProcessorVendorIntel;
1149 HRESULT hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
1150 if (FAILED(hrc))
1151 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1152 "Failed to set WHvPartitionPropertyCodeProcessorVendor to %u: %Rhrc (Last=%#x/%u)",
1153 Property.ProcessorVendor, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
1154
1155 /* Not sure if we really need to set the cache line flush size. */
1156 RT_ZERO(Property);
1157 Property.PropertyCode = WHvPartitionPropertyCodeProcessorClFlushSize;
1158 Property.ProcessorClFlushSize = pVM->nem.s.cCacheLineFlushShift;
1159 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
1160 if (FAILED(hrc))
1161 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1162 "Failed to set WHvPartitionPropertyCodeProcessorClFlushSize to %u: %Rhrc (Last=%#x/%u)",
1163 pVM->nem.s.cCacheLineFlushShift, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
1164
1165 /*
1166 * Sync CPU features with CPUM.
1167 */
1168 /** @todo sync CPU features with CPUM. */
1169
1170 /* Set the partition property. */
1171 RT_ZERO(Property);
1172 Property.PropertyCode = WHvPartitionPropertyCodeProcessorFeatures;
1173 Property.ProcessorFeatures.AsUINT64 = pVM->nem.s.uCpuFeatures.u64;
1174 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
1175 if (FAILED(hrc))
1176 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1177 "Failed to set WHvPartitionPropertyCodeProcessorFeatures to %'#RX64: %Rhrc (Last=%#x/%u)",
1178 pVM->nem.s.uCpuFeatures.u64, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
1179
1180 /*
1181 * Set up the partition and create EMTs.
1182 *
1183 * Seems like this is where the partition is actually instantiated and we get
1184 * a handle to it.
1185 */
1186 hrc = WHvSetupPartition(hPartition);
1187 if (FAILED(hrc))
1188 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1189 "Call to WHvSetupPartition failed: %Rhrc (Last=%#x/%u)",
1190 hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
1191
1192 /* Get the handle. */
1193 HANDLE hPartitionDevice;
1194 __try
1195 {
1196 hPartitionDevice = ((HANDLE *)hPartition)[1];
1197 }
1198 __except(EXCEPTION_EXECUTE_HANDLER)
1199 {
1200 hrc = GetExceptionCode();
1201 hPartitionDevice = NULL;
1202 }
1203 if ( hPartitionDevice == NULL
1204 || hPartitionDevice == (HANDLE)(intptr_t)-1)
1205 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1206 "Failed to get device handle for partition %p: %Rhrc", hPartition, hrc);
1207
1208 HV_PARTITION_ID idHvPartition = HV_PARTITION_ID_INVALID;
1209 if (!g_pfnVidGetHvPartitionId(hPartitionDevice, &idHvPartition))
1210 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1211 "Failed to get device handle and/or partition ID for %p (hPartitionDevice=%p, Last=%#x/%u)",
1212 hPartition, hPartitionDevice, RTNtLastStatusValue(), RTNtLastErrorValue());
1213 pVM->nem.s.hPartitionDevice = hPartitionDevice;
1214 pVM->nem.s.idHvPartition = idHvPartition;
1215
1216 /*
1217 * Setup the EMTs.
1218 */
1219 VMCPUID iCpu;
1220 for (iCpu = 0; iCpu < pVM->cCpus; iCpu++)
1221 {
1222 PVMCPU pVCpu = &pVM->aCpus[iCpu];
1223
1224 pVCpu->nem.s.hNativeThreadHandle = (RTR3PTR)RTThreadGetNativeHandle(VMR3GetThreadHandle(pVCpu->pUVCpu));
1225 Assert((HANDLE)pVCpu->nem.s.hNativeThreadHandle != INVALID_HANDLE_VALUE);
1226
1227#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
1228 VID_MAPPED_MESSAGE_SLOT MappedMsgSlot = { NULL, UINT32_MAX, UINT32_MAX };
1229 if (g_pfnVidMessageSlotMap(hPartitionDevice, &MappedMsgSlot, iCpu))
1230 {
1231 AssertLogRelMsg(MappedMsgSlot.iCpu == iCpu && MappedMsgSlot.uParentAdvisory == UINT32_MAX,
1232 ("%#x %#x (iCpu=%#x)\n", MappedMsgSlot.iCpu, MappedMsgSlot.uParentAdvisory, iCpu));
1233 pVCpu->nem.s.pvMsgSlotMapping = MappedMsgSlot.pMsgBlock;
1234 }
1235 else
1236 {
1237 NTSTATUS const rcNtLast = RTNtLastStatusValue();
1238 DWORD const dwErrLast = RTNtLastErrorValue();
1239 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1240 "Call to WHvSetupPartition failed: %Rhrc (Last=%#x/%u)", hrc, rcNtLast, dwErrLast);
1241 }
1242#else
1243 hrc = WHvCreateVirtualProcessor(hPartition, iCpu, 0 /*fFlags*/);
1244 if (FAILED(hrc))
1245 {
1246 NTSTATUS const rcNtLast = RTNtLastStatusValue();
1247 DWORD const dwErrLast = RTNtLastErrorValue();
1248 while (iCpu-- > 0)
1249 {
1250 HRESULT hrc2 = WHvDeleteVirtualProcessor(hPartition, iCpu);
1251 AssertLogRelMsg(SUCCEEDED(hrc2), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc (Last=%#x/%u)\n",
1252 hPartition, iCpu, hrc2, RTNtLastStatusValue(),
1253 RTNtLastErrorValue()));
1254 }
1255 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
1256 "Call to WHvSetupPartition failed: %Rhrc (Last=%#x/%u)", hrc, rcNtLast, dwErrLast);
1257 }
1258#endif /* !NEM_WIN_USE_OUR_OWN_RUN_API */
1259 }
1260 pVM->nem.s.fCreatedEmts = true;
1261
1262 /*
1263 * Do some more ring-0 initialization now that we've got the partition handle.
1264 */
1265 int rc = VMMR3CallR0Emt(pVM, &pVM->aCpus[0], VMMR0_DO_NEM_INIT_VM_PART_2, 0, NULL);
1266 if (RT_SUCCESS(rc))
1267 {
1268 LogRel(("NEM: Successfully set up partition (device handle %p, partition ID %#llx)\n", hPartitionDevice, idHvPartition));
1269 return VINF_SUCCESS;
1270 }
1271 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Call to NEMR0InitVMPart2 failed: %Rrc", rc);
1272}
1273
1274
1275int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat)
1276{
1277 NOREF(pVM); NOREF(enmWhat);
1278 return VINF_SUCCESS;
1279}
1280
1281
1282int nemR3NativeTerm(PVM pVM)
1283{
1284 /*
1285 * Delete the partition.
1286 */
1287 WHV_PARTITION_HANDLE hPartition = pVM->nem.s.hPartition;
1288 pVM->nem.s.hPartition = NULL;
1289 pVM->nem.s.hPartitionDevice = NULL;
1290 if (hPartition != NULL)
1291 {
1292 VMCPUID iCpu = pVM->nem.s.fCreatedEmts ? pVM->cCpus : 0;
1293 LogRel(("NEM: Destroying partition %p with its %u VCpus...\n", hPartition, iCpu));
1294 while (iCpu-- > 0)
1295 {
1296#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
1297 pVM->aCpus[iCpu].nem.s.pvMsgSlotMapping = NULL;
1298#else
1299 HRESULT hrc = WHvDeleteVirtualProcessor(hPartition, iCpu);
1300 AssertLogRelMsg(SUCCEEDED(hrc), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc (Last=%#x/%u)\n",
1301 hPartition, iCpu, hrc, RTNtLastStatusValue(),
1302 RTNtLastErrorValue()));
1303#endif
1304 }
1305 WHvDeletePartition(hPartition);
1306 }
1307 pVM->nem.s.fCreatedEmts = false;
1308 return VINF_SUCCESS;
1309}
1310
1311
1312/**
1313 * VM reset notification.
1314 *
1315 * @param pVM The cross context VM structure.
1316 */
1317void nemR3NativeReset(PVM pVM)
1318{
1319 /* Unfix the A20 gate. */
1320 pVM->nem.s.fA20Fixed = false;
1321}
1322
1323
1324/**
1325 * Reset CPU due to INIT IPI or hot (un)plugging.
1326 *
1327 * @param pVCpu The cross context virtual CPU structure of the CPU being
1328 * reset.
1329 * @param fInitIpi Whether this is the INIT IPI or hot (un)plugging case.
1330 */
1331void nemR3NativeResetCpu(PVMCPU pVCpu, bool fInitIpi)
1332{
1333 /* Lock the A20 gate if INIT IPI, make sure it's enabled. */
1334 if (fInitIpi && pVCpu->idCpu > 0)
1335 {
1336 PVM pVM = pVCpu->CTX_SUFF(pVM);
1337 if (!pVM->nem.s.fA20Enabled)
1338 nemR3NativeNotifySetA20(pVCpu, true);
1339 pVM->nem.s.fA20Enabled = true;
1340 pVM->nem.s.fA20Fixed = true;
1341 }
1342}
1343
1344#ifndef NEM_WIN_USE_OUR_OWN_RUN_API
1345
1346# ifdef LOG_ENABLED
1347/**
1348 * Log the full details of an exit reason.
1349 *
1350 * @param pExitReason The exit reason to log.
1351 */
1352static void nemR3WinLogWHvExitReason(WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1353{
1354 bool fExitCtx = false;
1355 bool fExitInstr = false;
1356 switch (pExitReason->ExitReason)
1357 {
1358 case WHvRunVpExitReasonMemoryAccess:
1359 Log2(("Exit: Memory access: GCPhys=%RGp GCVirt=%RGv %s %s %s\n",
1360 pExitReason->MemoryAccess.Gpa, pExitReason->MemoryAccess.Gva,
1361 g_apszWHvMemAccesstypes[pExitReason->MemoryAccess.AccessInfo.AccessType],
1362 pExitReason->MemoryAccess.AccessInfo.GpaUnmapped ? "unmapped" : "mapped",
1363 pExitReason->MemoryAccess.AccessInfo.GvaValid ? "" : "invalid-gc-virt"));
1364 AssertMsg(!(pExitReason->MemoryAccess.AccessInfo.AsUINT32 & ~UINT32_C(0xf)),
1365 ("MemoryAccess.AccessInfo=%#x\n", pExitReason->MemoryAccess.AccessInfo.AsUINT32));
1366 fExitCtx = fExitInstr = true;
1367 break;
1368
1369 case WHvRunVpExitReasonX64IoPortAccess:
1370 Log2(("Exit: I/O port access: IoPort=%#x LB %u %s%s%s rax=%#RX64 rcx=%#RX64 rsi=%#RX64 rdi=%#RX64\n",
1371 pExitReason->IoPortAccess.PortNumber,
1372 pExitReason->IoPortAccess.AccessInfo.AccessSize,
1373 pExitReason->IoPortAccess.AccessInfo.IsWrite ? "out" : "in",
1374 pExitReason->IoPortAccess.AccessInfo.StringOp ? " string" : "",
1375 pExitReason->IoPortAccess.AccessInfo.RepPrefix ? " rep" : "",
1376 pExitReason->IoPortAccess.Rax,
1377 pExitReason->IoPortAccess.Rcx,
1378 pExitReason->IoPortAccess.Rsi,
1379 pExitReason->IoPortAccess.Rdi));
1380 Log2(("Exit: + ds=%#x:{%#RX64 LB %#RX32, %#x} es=%#x:{%#RX64 LB %#RX32, %#x}\n",
1381 pExitReason->IoPortAccess.Ds.Selector,
1382 pExitReason->IoPortAccess.Ds.Base,
1383 pExitReason->IoPortAccess.Ds.Limit,
1384 pExitReason->IoPortAccess.Ds.Attributes,
1385 pExitReason->IoPortAccess.Es.Selector,
1386 pExitReason->IoPortAccess.Es.Base,
1387 pExitReason->IoPortAccess.Es.Limit,
1388 pExitReason->IoPortAccess.Es.Attributes ));
1389
1390 AssertMsg( pExitReason->IoPortAccess.AccessInfo.AccessSize == 1
1391 || pExitReason->IoPortAccess.AccessInfo.AccessSize == 2
1392 || pExitReason->IoPortAccess.AccessInfo.AccessSize == 4,
1393 ("IoPortAccess.AccessInfo.AccessSize=%d\n", pExitReason->IoPortAccess.AccessInfo.AccessSize));
1394 AssertMsg(!(pExitReason->IoPortAccess.AccessInfo.AsUINT32 & ~UINT32_C(0x3f)),
1395 ("IoPortAccess.AccessInfo=%#x\n", pExitReason->IoPortAccess.AccessInfo.AsUINT32));
1396 fExitCtx = fExitInstr = true;
1397 break;
1398
1399# if 0
1400 case WHvRunVpExitReasonUnrecoverableException:
1401 case WHvRunVpExitReasonInvalidVpRegisterValue:
1402 case WHvRunVpExitReasonUnsupportedFeature:
1403 case WHvRunVpExitReasonX64InterruptWindow:
1404 case WHvRunVpExitReasonX64Halt:
1405 case WHvRunVpExitReasonX64MsrAccess:
1406 case WHvRunVpExitReasonX64Cpuid:
1407 case WHvRunVpExitReasonException:
1408 case WHvRunVpExitReasonCanceled:
1409 case WHvRunVpExitReasonAlerted:
1410 WHV_X64_MSR_ACCESS_CONTEXT MsrAccess;
1411 WHV_X64_CPUID_ACCESS_CONTEXT CpuidAccess;
1412 WHV_VP_EXCEPTION_CONTEXT VpException;
1413 WHV_X64_INTERRUPTION_DELIVERABLE_CONTEXT InterruptWindow;
1414 WHV_UNRECOVERABLE_EXCEPTION_CONTEXT UnrecoverableException;
1415 WHV_X64_UNSUPPORTED_FEATURE_CONTEXT UnsupportedFeature;
1416 WHV_RUN_VP_CANCELED_CONTEXT CancelReason;
1417# endif
1418
1419 case WHvRunVpExitReasonNone:
1420 Log2(("Exit: No reason\n"));
1421 AssertFailed();
1422 break;
1423
1424 default:
1425 Log(("Exit: %#x\n", pExitReason->ExitReason));
1426 break;
1427 }
1428
1429 /*
1430 * Context and maybe instruction details.
1431 */
1432 if (fExitCtx)
1433 {
1434 const WHV_VP_EXIT_CONTEXT *pVpCtx = &pExitReason->IoPortAccess.VpContext;
1435 Log2(("Exit: + CS:RIP=%04x:%08RX64 RFLAGS=%06RX64 cbInstr=%u CS={%RX64 L %#RX32, %#x}\n",
1436 pVpCtx->Cs.Selector,
1437 pVpCtx->Rip,
1438 pVpCtx->Rflags,
1439 pVpCtx->InstructionLength,
1440 pVpCtx->Cs.Base, pVpCtx->Cs.Limit, pVpCtx->Cs.Attributes));
1441 Log2(("Exit: + cpl=%d CR0.PE=%d CR0.AM=%d EFER.LMA=%d DebugActive=%d InterruptionPending=%d InterruptShadow=%d\n",
1442 pVpCtx->ExecutionState.Cpl,
1443 pVpCtx->ExecutionState.Cr0Pe,
1444 pVpCtx->ExecutionState.Cr0Am,
1445 pVpCtx->ExecutionState.EferLma,
1446 pVpCtx->ExecutionState.DebugActive,
1447 pVpCtx->ExecutionState.InterruptionPending,
1448 pVpCtx->ExecutionState.InterruptShadow));
1449 AssertMsg(!(pVpCtx->ExecutionState.AsUINT16 & ~UINT16_C(0x107f)),
1450 ("ExecutionState.AsUINT16=%#x\n", pVpCtx->ExecutionState.AsUINT16));
1451
1452 /** @todo Someone at Microsoft please explain why the InstructionBytes fields
1453 * are 16 bytes long, when 15 would've been sufficent and saved 3-7 bytes of
1454 * alignment padding? Intel max length is 15, so is this sSome ARM stuff?
1455 * Aren't ARM
1456 * instructions max 32-bit wide? Confused. */
1457 if (fExitInstr && pExitReason->IoPortAccess.InstructionByteCount > 0)
1458 Log2(("Exit: + Instruction %.*Rhxs\n",
1459 pExitReason->IoPortAccess.InstructionByteCount, pExitReason->IoPortAccess.InstructionBytes));
1460 }
1461}
1462# endif /* LOG_ENABLED */
1463
1464
1465/**
1466 * Advances the guest RIP and clear EFLAGS.RF.
1467 *
1468 * This may clear VMCPU_FF_INHIBIT_INTERRUPTS.
1469 *
1470 * @param pVCpu The cross context virtual CPU structure.
1471 * @param pCtx The CPU context to update.
1472 * @param pExitCtx The exit context.
1473 */
1474DECLINLINE(void) nemR3WinAdvanceGuestRipAndClearRF(PVMCPU pVCpu, PCPUMCTX pCtx, WHV_VP_EXIT_CONTEXT const *pExitCtx)
1475{
1476 /* Advance the RIP. */
1477 Assert(pExitCtx->InstructionLength > 0 && pExitCtx->InstructionLength < 16);
1478 pCtx->rip += pExitCtx->InstructionLength;
1479 pCtx->rflags.Bits.u1RF = 0;
1480
1481 /* Update interrupt inhibition. */
1482 if (!VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
1483 { /* likely */ }
1484 else if (pCtx->rip != EMGetInhibitInterruptsPC(pVCpu))
1485 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
1486}
1487
1488
1489static VBOXSTRICTRC nemR3WinWHvHandleHalt(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
1490{
1491 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx);
1492 LogFlow(("nemR3WinWHvHandleHalt\n"));
1493 return VINF_EM_HALT;
1494}
1495
1496
1497# ifndef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1498/**
1499 * @callback_method_impl{FNPGMPHYSNEMENUMCALLBACK,
1500 * Hack to unmap all pages when/before we run into quota (WHv only).}
1501 */
1502static DECLCALLBACK(int) nemR3WinWHvUnmapOnePageCallback(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, uint8_t *pu2NemState, void *pvUser)
1503{
1504 RT_NOREF_PV(pvUser);
1505 RT_NOREF_PV(pVCpu);
1506 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
1507 if (SUCCEEDED(hrc))
1508 {
1509 Log5(("NEM GPA unmap all: %RGp (cMappedPages=%u)\n", GCPhys, pVM->nem.s.cMappedPages - 1));
1510 *pu2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
1511 }
1512 else
1513 {
1514 LogRel(("nemR3WinWHvUnmapOnePageCallback: GCPhys=%RGp %s hrc=%Rhrc (%#x) Last=%#x/%u (cMappedPages=%u)\n",
1515 GCPhys, g_apszPageStates[*pu2NemState], hrc, hrc, RTNtLastStatusValue(),
1516 RTNtLastErrorValue(), pVM->nem.s.cMappedPages));
1517 *pu2NemState = NEM_WIN_PAGE_STATE_NOT_SET;
1518 }
1519 if (pVM->nem.s.cMappedPages > 0)
1520 ASMAtomicDecU32(&pVM->nem.s.cMappedPages);
1521 return VINF_SUCCESS;
1522}
1523# endif /* !NEM_WIN_USE_HYPERCALLS_FOR_PAGES */
1524
1525
1526/**
1527 * Handles an memory access VMEXIT.
1528 *
1529 * This can be triggered by a number of things.
1530 *
1531 * @returns Strict VBox status code.
1532 * @param pVM The cross context VM structure.
1533 * @param pVCpu The cross context virtual CPU structure.
1534 * @param pCtx The CPU context to update.
1535 * @param pMemCtx The exit reason information.
1536 */
1537static VBOXSTRICTRC nemR3WinWHvHandleMemoryAccess(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_MEMORY_ACCESS_CONTEXT const *pMemCtx)
1538{
1539 /*
1540 * Ask PGM for information about the given GCPhys. We need to check if we're
1541 * out of sync first.
1542 */
1543 NEMHCWINHMACPCCSTATE State = { pMemCtx->AccessInfo.AccessType == WHvMemoryAccessWrite, false, false };
1544 PGMPHYSNEMPAGEINFO Info;
1545 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, pMemCtx->Gpa, State.fWriteAccess, &Info,
1546 nemHCWinHandleMemoryAccessPageCheckerCallback, &State);
1547 if (RT_SUCCESS(rc))
1548 {
1549 if (Info.fNemProt & (pMemCtx->AccessInfo.AccessType == WHvMemoryAccessWrite ? NEM_PAGE_PROT_WRITE : NEM_PAGE_PROT_READ))
1550 {
1551 if (State.fCanResume)
1552 {
1553 Log4(("MemExit: %RGp (=>%RHp) %s fProt=%u%s%s%s; restarting (%s)\n",
1554 pMemCtx->Gpa, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
1555 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
1556 State.fDidSomething ? "" : " no-change", g_apszWHvMemAccesstypes[pMemCtx->AccessInfo.AccessType]));
1557 return VINF_SUCCESS;
1558 }
1559 }
1560 Log4(("MemExit: %RGp (=>%RHp) %s fProt=%u%s%s%s; emulating (%s)\n",
1561 pMemCtx->Gpa, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt,
1562 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "",
1563 State.fDidSomething ? "" : " no-change", g_apszWHvMemAccesstypes[pMemCtx->AccessInfo.AccessType]));
1564 }
1565 else
1566 Log4(("MemExit: %RGp rc=%Rrc%s; emulating (%s)\n", pMemCtx->Gpa, rc,
1567 State.fDidSomething ? " modified-backing" : "", g_apszWHvMemAccesstypes[pMemCtx->AccessInfo.AccessType]));
1568
1569 /*
1570 * Emulate the memory access, either access handler or special memory.
1571 */
1572 rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
1573 AssertRCReturn(rc, rc);
1574
1575 VBOXSTRICTRC rcStrict;
1576 if (pMemCtx->InstructionByteCount > 0)
1577 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(pCtx), pMemCtx->VpContext.Rip,
1578 pMemCtx->InstructionBytes, pMemCtx->InstructionByteCount);
1579 else
1580 rcStrict = IEMExecOne(pVCpu);
1581 /** @todo do we need to do anything wrt debugging here? */
1582 return rcStrict;
1583}
1584
1585
1586/**
1587 * Handles an I/O port access VMEXIT.
1588 *
1589 * We ASSUME that the hypervisor has don't I/O port access control.
1590 *
1591 * @returns Strict VBox status code.
1592 * @param pVM The cross context VM structure.
1593 * @param pVCpu The cross context virtual CPU structure.
1594 * @param pCtx The CPU context to update.
1595 * @param pIoPortCtx The exit reason information.
1596 */
1597static VBOXSTRICTRC nemR3WinWHvHandleIoPortAccess(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx,
1598 WHV_X64_IO_PORT_ACCESS_CONTEXT const *pIoPortCtx)
1599{
1600 Assert( pIoPortCtx->AccessInfo.AccessSize == 1
1601 || pIoPortCtx->AccessInfo.AccessSize == 2
1602 || pIoPortCtx->AccessInfo.AccessSize == 4);
1603
1604 VBOXSTRICTRC rcStrict;
1605 if (!pIoPortCtx->AccessInfo.StringOp)
1606 {
1607 /*
1608 * Simple port I/O.
1609 */
1610 Assert(pCtx->rax == pIoPortCtx->Rax);
1611
1612 static uint32_t const s_fAndMask[8] =
1613 { UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
1614 uint32_t const fAndMask = s_fAndMask[pIoPortCtx->AccessInfo.AccessSize];
1615 if (pIoPortCtx->AccessInfo.IsWrite)
1616 {
1617 rcStrict = IOMIOPortWrite(pVM, pVCpu, pIoPortCtx->PortNumber, (uint32_t)pIoPortCtx->Rax & fAndMask,
1618 pIoPortCtx->AccessInfo.AccessSize);
1619 if (IOM_SUCCESS(rcStrict))
1620 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, pCtx, &pIoPortCtx->VpContext);
1621 }
1622 else
1623 {
1624 uint32_t uValue = 0;
1625 rcStrict = IOMIOPortRead(pVM, pVCpu, pIoPortCtx->PortNumber, &uValue,
1626 pIoPortCtx->AccessInfo.AccessSize);
1627 if (IOM_SUCCESS(rcStrict))
1628 {
1629 pCtx->eax = (pCtx->eax & ~fAndMask) | (uValue & fAndMask);
1630 nemR3WinAdvanceGuestRipAndClearRF(pVCpu, pCtx, &pIoPortCtx->VpContext);
1631 }
1632 }
1633 }
1634 else
1635 {
1636 /*
1637 * String port I/O.
1638 */
1639 /** @todo Someone at Microsoft please explain how we can get the address mode
1640 * from the IoPortAccess.VpContext. CS.Attributes is only sufficient for
1641 * getting the default mode, it can always be overridden by a prefix. This
1642 * forces us to interpret the instruction from opcodes, which is suboptimal.
1643 * Both AMD-V and VT-x includes the address size in the exit info, at least on
1644 * CPUs that are reasonably new. */
1645# if 0 // requires sledgehammer
1646 Assert( pIoPortCtx->Ds.Base == pCtx->ds.u64Base
1647 && pIoPortCtx->Ds.Limit == pCtx->ds.u32Limit
1648 && pIoPortCtx->Ds.Selector == pCtx->ds.Sel);
1649 Assert( pIoPortCtx->Es.Base == pCtx->es.u64Base
1650 && pIoPortCtx->Es.Limit == pCtx->es.u32Limit
1651 && pIoPortCtx->Es.Selector == pCtx->es.Sel);
1652 Assert(pIoPortCtx->Rdi == pCtx->rdi);
1653 Assert(pIoPortCtx->Rsi == pCtx->rsi);
1654 Assert(pIoPortCtx->Rcx == pCtx->rcx);
1655 Assert(pIoPortCtx->Rcx == pCtx->rcx);
1656# endif
1657
1658 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM);
1659 AssertRCReturn(rc, rc);
1660
1661 rcStrict = IEMExecOne(pVCpu);
1662 }
1663 if (IOM_SUCCESS(rcStrict))
1664 {
1665 /*
1666 * Do debug checks.
1667 */
1668 if ( pIoPortCtx->VpContext.ExecutionState.DebugActive /** @todo Microsoft: Does DebugActive this only reflext DR7? */
1669 || (pIoPortCtx->VpContext.Rflags & X86_EFL_TF)
1670 || DBGFBpIsHwIoArmed(pVM) )
1671 {
1672 /** @todo Debugging. */
1673 }
1674 }
1675 return rcStrict;
1676}
1677
1678
1679static VBOXSTRICTRC nemR3WinWHvHandleInterruptWindow(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1680{
1681 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1682 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1683}
1684
1685
1686static VBOXSTRICTRC nemR3WinWHvHandleMsrAccess(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1687{
1688 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1689 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1690}
1691
1692
1693static VBOXSTRICTRC nemR3WinWHvHandleCpuId(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1694{
1695 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1696 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1697}
1698
1699
1700static VBOXSTRICTRC nemR3WinWHvHandleException(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1701{
1702 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1703 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1704}
1705
1706
1707static VBOXSTRICTRC nemR3WinWHvHandleUD(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1708{
1709 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1710 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1711}
1712
1713
1714static VBOXSTRICTRC nemR3WinWHvHandleTripleFault(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1715{
1716 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1717 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1718}
1719
1720
1721static VBOXSTRICTRC nemR3WinWHvHandleInvalidState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, WHV_RUN_VP_EXIT_CONTEXT const *pExitReason)
1722{
1723 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx); NOREF(pExitReason);
1724 AssertLogRelFailedReturn(VERR_NOT_IMPLEMENTED);
1725}
1726
1727
1728VBOXSTRICTRC nemR3WinWHvRunGC(PVM pVM, PVMCPU pVCpu)
1729{
1730# ifdef LOG_ENABLED
1731 if (LogIs3Enabled())
1732 {
1733 Log3(("nemR3NativeRunGC: Entering #%u\n", pVCpu->idCpu));
1734 nemHCWinLogState(pVM, pVCpu);
1735 }
1736# endif
1737
1738 /*
1739 * The run loop.
1740 */
1741 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
1742 const bool fSingleStepping = false; /** @todo get this from somewhere. */
1743 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
1744 for (unsigned iLoop = 0;;iLoop++)
1745 {
1746 /*
1747 * Copy the state.
1748 */
1749 int rc2 = nemHCWinCopyStateToHyperV(pVM, pVCpu, pCtx);
1750 AssertRCBreakStmt(rc2, rcStrict = rc2);
1751
1752 /*
1753 * Run a bit.
1754 */
1755 WHV_RUN_VP_EXIT_CONTEXT ExitReason;
1756 RT_ZERO(ExitReason);
1757 if ( !VM_FF_IS_PENDING(pVM, VM_FF_EMT_RENDEZVOUS | VM_FF_TM_VIRTUAL_SYNC)
1758 && !VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_HM_TO_R3_MASK))
1759 {
1760 Log8(("Calling WHvRunVirtualProcessor\n"));
1761 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED);
1762 HRESULT hrc = WHvRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, &ExitReason, sizeof(ExitReason));
1763 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM);
1764 AssertLogRelMsgBreakStmt(SUCCEEDED(hrc),
1765 ("WHvRunVirtualProcessor(%p, %u,,) -> %Rhrc (Last=%#x/%u)\n", pVM->nem.s.hPartition, pVCpu->idCpu,
1766 hrc, RTNtLastStatusValue(), RTNtLastErrorValue()),
1767 rcStrict = VERR_INTERNAL_ERROR);
1768 Log2(("WHvRunVirtualProcessor -> %#x; exit code %#x (%d) (cpu status %u)\n",
1769 hrc, ExitReason.ExitReason, ExitReason.ExitReason, nemHCWinCpuGetRunningStatus(pVCpu) ));
1770 }
1771 else
1772 {
1773 LogFlow(("nemR3NativeRunGC: returning: pending FF (pre exec)\n"));
1774 break;
1775 }
1776
1777# if 0 /* sledgehammer approach */
1778 /*
1779 * Copy back the state.
1780 */
1781 rc2 = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, UINT64_MAX);
1782 AssertRCBreakStmt(rc2, rcStrict = rc2);
1783# endif
1784
1785# ifdef LOG_ENABLED
1786 /*
1787 * Do some logging.
1788 */
1789 if (LogIs2Enabled())
1790 nemR3WinLogWHvExitReason(&ExitReason);
1791 if (LogIs3Enabled())
1792 nemHCWinLogState(pVM, pVCpu);
1793# endif
1794
1795# if 0 //def VBOX_STRICT - requires sledgehammer
1796 /* Assert that the VpContext field makes sense. */
1797 switch (ExitReason.ExitReason)
1798 {
1799 case WHvRunVpExitReasonMemoryAccess:
1800 case WHvRunVpExitReasonX64IoPortAccess:
1801 case WHvRunVpExitReasonX64MsrAccess:
1802 case WHvRunVpExitReasonX64Cpuid:
1803 case WHvRunVpExitReasonException:
1804 case WHvRunVpExitReasonUnrecoverableException:
1805 Assert( ExitReason.IoPortAccess.VpContext.InstructionLength > 0
1806 || ( ExitReason.ExitReason == WHvRunVpExitReasonMemoryAccess
1807 && ExitReason.MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessExecute));
1808 Assert(ExitReason.IoPortAccess.VpContext.InstructionLength < 16);
1809 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Cpl == CPUMGetGuestCPL(pVCpu));
1810 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Cr0Pe == RT_BOOL(pCtx->cr0 & X86_CR0_PE));
1811 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Cr0Am == RT_BOOL(pCtx->cr0 & X86_CR0_AM));
1812 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.EferLma == RT_BOOL(pCtx->msrEFER & MSR_K6_EFER_LMA));
1813 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.DebugActive == RT_BOOL(pCtx->dr[7] & X86_DR7_ENABLED_MASK));
1814 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Reserved0 == 0);
1815 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Reserved1 == 0);
1816 Assert(ExitReason.IoPortAccess.VpContext.Rip == pCtx->rip);
1817 Assert(ExitReason.IoPortAccess.VpContext.Rflags == pCtx->rflags.u);
1818 Assert( ExitReason.IoPortAccess.VpContext.Cs.Base == pCtx->cs.u64Base
1819 && ExitReason.IoPortAccess.VpContext.Cs.Limit == pCtx->cs.u32Limit
1820 && ExitReason.IoPortAccess.VpContext.Cs.Selector == pCtx->cs.Sel);
1821 break;
1822 default: break; /* shut up compiler. */
1823 }
1824# endif
1825
1826 /*
1827 * Deal with the exit.
1828 */
1829 switch (ExitReason.ExitReason)
1830 {
1831 /* Frequent exits: */
1832 case WHvRunVpExitReasonCanceled:
1833 case WHvRunVpExitReasonAlerted:
1834 rcStrict = VINF_SUCCESS;
1835 break;
1836
1837 case WHvRunVpExitReasonX64Halt:
1838 rcStrict = nemR3WinWHvHandleHalt(pVM, pVCpu, pCtx);
1839 break;
1840
1841 case WHvRunVpExitReasonMemoryAccess:
1842 rcStrict = nemR3WinWHvHandleMemoryAccess(pVM, pVCpu, pCtx, &ExitReason.MemoryAccess);
1843 break;
1844
1845 case WHvRunVpExitReasonX64IoPortAccess:
1846 rcStrict = nemR3WinWHvHandleIoPortAccess(pVM, pVCpu, pCtx, &ExitReason.IoPortAccess);
1847 break;
1848
1849 case WHvRunVpExitReasonX64InterruptWindow:
1850 rcStrict = nemR3WinWHvHandleInterruptWindow(pVM, pVCpu, pCtx, &ExitReason);
1851 break;
1852
1853 case WHvRunVpExitReasonX64MsrAccess: /* needs configuring */
1854 rcStrict = nemR3WinWHvHandleMsrAccess(pVM, pVCpu, pCtx, &ExitReason);
1855 break;
1856
1857 case WHvRunVpExitReasonX64Cpuid: /* needs configuring */
1858 rcStrict = nemR3WinWHvHandleCpuId(pVM, pVCpu, pCtx, &ExitReason);
1859 break;
1860
1861 case WHvRunVpExitReasonException: /* needs configuring */
1862 rcStrict = nemR3WinWHvHandleException(pVM, pVCpu, pCtx, &ExitReason);
1863 break;
1864
1865 /* Unlikely exits: */
1866 case WHvRunVpExitReasonUnsupportedFeature:
1867 rcStrict = nemR3WinWHvHandleUD(pVM, pVCpu, pCtx, &ExitReason);
1868 break;
1869
1870 case WHvRunVpExitReasonUnrecoverableException:
1871 rcStrict = nemR3WinWHvHandleTripleFault(pVM, pVCpu, pCtx, &ExitReason);
1872 break;
1873
1874 case WHvRunVpExitReasonInvalidVpRegisterValue:
1875 rcStrict = nemR3WinWHvHandleInvalidState(pVM, pVCpu, pCtx, &ExitReason);
1876 break;
1877
1878 /* Undesired exits: */
1879 case WHvRunVpExitReasonNone:
1880 default:
1881 AssertLogRelMsgFailed(("Unknown ExitReason: %#x\n", ExitReason.ExitReason));
1882 rcStrict = VERR_INTERNAL_ERROR_3;
1883 break;
1884 }
1885 if (rcStrict != VINF_SUCCESS)
1886 {
1887 LogFlow(("nemR3NativeRunGC: returning: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
1888 break;
1889 }
1890
1891# ifndef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
1892 /* Hack alert! */
1893 uint32_t const cMappedPages = pVM->nem.s.cMappedPages;
1894 if (cMappedPages < 4000)
1895 { /* likely */ }
1896 else
1897 {
1898 PGMPhysNemEnumPagesByState(pVM, pVCpu, NEM_WIN_PAGE_STATE_READABLE, nemR3WinWHvUnmapOnePageCallback, NULL);
1899 Log(("nemR3NativeRunGC: Unmapped all; cMappedPages=%u -> %u\n", cMappedPages, pVM->nem.s.cMappedPages));
1900 }
1901# endif
1902
1903 /* If any FF is pending, return to the EM loops. That's okay for the
1904 current sledgehammer approach. */
1905 if ( VM_FF_IS_PENDING( pVM, !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK : VM_FF_HP_R0_PRE_HM_STEP_MASK)
1906 || VMCPU_FF_IS_PENDING(pVCpu, !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK) )
1907 {
1908 LogFlow(("nemR3NativeRunGC: returning: pending FF (%#x / %#x)\n", pVM->fGlobalForcedActions, pVCpu->fLocalForcedActions));
1909 break;
1910 }
1911 }
1912
1913
1914 /*
1915 * Copy back the state before returning.
1916 */
1917 if (pCtx->fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT)))
1918 {
1919 int rc2 = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK);
1920 if (RT_SUCCESS(rc2))
1921 pCtx->fExtrn = 0;
1922 else if (RT_SUCCESS(rcStrict))
1923 rcStrict = rc2;
1924 }
1925 else
1926 pCtx->fExtrn = 0;
1927
1928 return rcStrict;
1929}
1930
1931#endif /* !NEM_WIN_USE_OUR_OWN_RUN_API */
1932
1933
1934VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu)
1935{
1936#ifndef NEM_WIN_USE_OUR_OWN_RUN_API
1937 return nemR3WinWHvRunGC(pVM, pVCpu);
1938#elif 0
1939 return nemHCWinRunGC(pVM, pVCpu, NULL /*pGVM*/, NULL /*pGVCpu*/);
1940#else
1941 VBOXSTRICTRC rcStrict = VMMR3CallR0EmtFast(pVM, pVCpu, VMMR0_DO_NEM_RUN);
1942 if (RT_SUCCESS(rcStrict))
1943 {
1944 /* We deal wtih VINF_NEM_CHANGE_PGM_MODE and VINF_NEM_FLUSH_TLB here, since we're running
1945 the risk of getting these while we already got another RC (I/O ports). */
1946 VBOXSTRICTRC rcPgmPending = pVCpu->nem.s.rcPgmPending;
1947 pVCpu->nem.s.rcPgmPending = VINF_SUCCESS;
1948 if ( rcStrict == VINF_NEM_CHANGE_PGM_MODE
1949 || rcStrict == VINF_PGM_CHANGE_MODE
1950 || rcPgmPending == VINF_NEM_CHANGE_PGM_MODE )
1951 {
1952 LogFlow(("nemR3NativeRunGC: calling PGMChangeMode...\n"));
1953 int rc = PGMChangeMode(pVCpu, CPUMGetGuestCR0(pVCpu), CPUMGetGuestCR4(pVCpu), CPUMGetGuestEFER(pVCpu));
1954 AssertRCReturn(rc, rc);
1955 if (rcStrict == VINF_NEM_CHANGE_PGM_MODE || rcStrict == VINF_NEM_FLUSH_TLB)
1956 rcStrict = VINF_SUCCESS;
1957 }
1958 else if (rcStrict == VINF_NEM_FLUSH_TLB || rcPgmPending == VINF_NEM_FLUSH_TLB)
1959 {
1960 LogFlow(("nemR3NativeRunGC: calling PGMFlushTLB...\n"));
1961 int rc = PGMFlushTLB(pVCpu, CPUMGetGuestCR3(pVCpu), true);
1962 AssertRCReturn(rc, rc);
1963 if (rcStrict == VINF_NEM_FLUSH_TLB || rcStrict == VINF_NEM_CHANGE_PGM_MODE)
1964 rcStrict = VINF_SUCCESS;
1965 }
1966 else
1967 AssertMsg(rcPgmPending == VINF_SUCCESS, ("rcPgmPending=%Rrc\n", VBOXSTRICTRC_VAL(rcPgmPending) ));
1968 }
1969 return rcStrict;
1970#endif
1971}
1972
1973
1974bool nemR3NativeCanExecuteGuest(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
1975{
1976 NOREF(pVM); NOREF(pVCpu); NOREF(pCtx);
1977 return true;
1978}
1979
1980
1981bool nemR3NativeSetSingleInstruction(PVM pVM, PVMCPU pVCpu, bool fEnable)
1982{
1983 NOREF(pVM); NOREF(pVCpu); NOREF(fEnable);
1984 return false;
1985}
1986
1987
1988/**
1989 * Forced flag notification call from VMEmt.h.
1990 *
1991 * This is only called when pVCpu is in the VMCPUSTATE_STARTED_EXEC_NEM state.
1992 *
1993 * @param pVM The cross context VM structure.
1994 * @param pVCpu The cross context virtual CPU structure of the CPU
1995 * to be notified.
1996 * @param fFlags Notification flags, VMNOTIFYFF_FLAGS_XXX.
1997 */
1998void nemR3NativeNotifyFF(PVM pVM, PVMCPU pVCpu, uint32_t fFlags)
1999{
2000#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
2001 nemHCWinCancelRunVirtualProcessor(pVM, pVCpu);
2002#else
2003 Log8(("nemR3NativeNotifyFF: canceling %u\n", pVCpu->idCpu));
2004 HRESULT hrc = WHvCancelRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, 0);
2005 AssertMsg(SUCCEEDED(hrc), ("WHvCancelRunVirtualProcessor -> hrc=%Rhrc\n", hrc));
2006 RT_NOREF_PV(hrc);
2007#endif
2008 RT_NOREF_PV(fFlags);
2009}
2010
2011
2012DECLINLINE(int) nemR3NativeGCPhys2R3PtrReadOnly(PVM pVM, RTGCPHYS GCPhys, const void **ppv)
2013{
2014 PGMPAGEMAPLOCK Lock;
2015 int rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, GCPhys, ppv, &Lock);
2016 if (RT_SUCCESS(rc))
2017 PGMPhysReleasePageMappingLock(pVM, &Lock);
2018 return rc;
2019}
2020
2021
2022DECLINLINE(int) nemR3NativeGCPhys2R3PtrWriteable(PVM pVM, RTGCPHYS GCPhys, void **ppv)
2023{
2024 PGMPAGEMAPLOCK Lock;
2025 int rc = PGMPhysGCPhys2CCPtr(pVM, GCPhys, ppv, &Lock);
2026 if (RT_SUCCESS(rc))
2027 PGMPhysReleasePageMappingLock(pVM, &Lock);
2028 return rc;
2029}
2030
2031
2032int nemR3NativeNotifyPhysRamRegister(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb)
2033{
2034 Log5(("nemR3NativeNotifyPhysRamRegister: %RGp LB %RGp\n", GCPhys, cb));
2035 NOREF(pVM); NOREF(GCPhys); NOREF(cb);
2036 return VINF_SUCCESS;
2037}
2038
2039
2040int nemR3NativeNotifyPhysMmioExMap(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, uint32_t fFlags, void *pvMmio2)
2041{
2042 Log5(("nemR3NativeNotifyPhysMmioExMap: %RGp LB %RGp fFlags=%#x pvMmio2=%p\n", GCPhys, cb, fFlags, pvMmio2));
2043 NOREF(pVM); NOREF(GCPhys); NOREF(cb); NOREF(fFlags); NOREF(pvMmio2);
2044 return VINF_SUCCESS;
2045}
2046
2047
2048int nemR3NativeNotifyPhysMmioExUnmap(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, uint32_t fFlags)
2049{
2050 Log5(("nemR3NativeNotifyPhysMmioExUnmap: %RGp LB %RGp fFlags=%#x\n", GCPhys, cb, fFlags));
2051 NOREF(pVM); NOREF(GCPhys); NOREF(cb); NOREF(fFlags);
2052 return VINF_SUCCESS;
2053}
2054
2055
2056/**
2057 * Called early during ROM registration, right after the pages have been
2058 * allocated and the RAM range updated.
2059 *
2060 * This will be succeeded by a number of NEMHCNotifyPhysPageProtChanged() calls
2061 * and finally a NEMR3NotifyPhysRomRegisterEarly().
2062 *
2063 * @returns VBox status code
2064 * @param pVM The cross context VM structure.
2065 * @param GCPhys The ROM address (page aligned).
2066 * @param cb The size (page aligned).
2067 * @param fFlags NEM_NOTIFY_PHYS_ROM_F_XXX.
2068 */
2069int nemR3NativeNotifyPhysRomRegisterEarly(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, uint32_t fFlags)
2070{
2071 Log5(("nemR3NativeNotifyPhysRomRegisterEarly: %RGp LB %RGp fFlags=%#x\n", GCPhys, cb, fFlags));
2072#if 0 /* Let's not do this after all. We'll protection change notifications for each page and if not we'll map them lazily. */
2073 RTGCPHYS const cPages = cb >> X86_PAGE_SHIFT;
2074 for (RTGCPHYS iPage = 0; iPage < cPages; iPage++, GCPhys += X86_PAGE_SIZE)
2075 {
2076 const void *pvPage;
2077 int rc = nemR3NativeGCPhys2R3PtrReadOnly(pVM, GCPhys, &pvPage);
2078 if (RT_SUCCESS(rc))
2079 {
2080 HRESULT hrc = WHvMapGpaRange(pVM->nem.s.hPartition, (void *)pvPage, GCPhys, X86_PAGE_SIZE,
2081 WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute);
2082 if (SUCCEEDED(hrc))
2083 { /* likely */ }
2084 else
2085 {
2086 LogRel(("nemR3NativeNotifyPhysRomRegisterEarly: GCPhys=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
2087 GCPhys, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2088 return VERR_NEM_INIT_FAILED;
2089 }
2090 }
2091 else
2092 {
2093 LogRel(("nemR3NativeNotifyPhysRomRegisterEarly: GCPhys=%RGp rc=%Rrc\n", GCPhys, rc));
2094 return rc;
2095 }
2096 }
2097#else
2098 NOREF(pVM); NOREF(GCPhys); NOREF(cb);
2099#endif
2100 RT_NOREF_PV(fFlags);
2101 return VINF_SUCCESS;
2102}
2103
2104
2105/**
2106 * Called after the ROM range has been fully completed.
2107 *
2108 * This will be preceeded by a NEMR3NotifyPhysRomRegisterEarly() call as well a
2109 * number of NEMHCNotifyPhysPageProtChanged calls.
2110 *
2111 * @returns VBox status code
2112 * @param pVM The cross context VM structure.
2113 * @param GCPhys The ROM address (page aligned).
2114 * @param cb The size (page aligned).
2115 * @param fFlags NEM_NOTIFY_PHYS_ROM_F_XXX.
2116 */
2117int nemR3NativeNotifyPhysRomRegisterLate(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, uint32_t fFlags)
2118{
2119 Log5(("nemR3NativeNotifyPhysRomRegisterLate: %RGp LB %RGp fFlags=%#x\n", GCPhys, cb, fFlags));
2120 NOREF(pVM); NOREF(GCPhys); NOREF(cb); NOREF(fFlags);
2121 return VINF_SUCCESS;
2122}
2123
2124
2125/**
2126 * @callback_method_impl{FNPGMPHYSNEMCHECKPAGE}
2127 */
2128static DECLCALLBACK(int) nemR3WinUnsetForA20CheckerCallback(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys,
2129 PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
2130{
2131 /* We'll just unmap the memory. */
2132 if (pInfo->u2NemState > NEM_WIN_PAGE_STATE_UNMAPPED)
2133 {
2134#ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
2135 int rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhys);
2136 AssertRC(rc);
2137 if (RT_SUCCESS(rc))
2138#else
2139 HRESULT hrc = WHvUnmapGpaRange(pVM->nem.s.hPartition, GCPhys, X86_PAGE_SIZE);
2140 if (SUCCEEDED(hrc))
2141#endif
2142 {
2143 uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
2144 Log5(("NEM GPA unmapped/A20: %RGp (was %s, cMappedPages=%u)\n", GCPhys, g_apszPageStates[pInfo->u2NemState], cMappedPages));
2145 pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
2146 }
2147 else
2148 {
2149#ifdef NEM_WIN_USE_HYPERCALLS_FOR_PAGES
2150 LogRel(("nemR3WinUnsetForA20CheckerCallback/unmap: GCPhys=%RGp rc=%Rrc\n", GCPhys, rc));
2151 return rc;
2152#else
2153 LogRel(("nemR3WinUnsetForA20CheckerCallback/unmap: GCPhys=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
2154 GCPhys, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
2155 return VERR_INTERNAL_ERROR_2;
2156#endif
2157 }
2158 }
2159 RT_NOREF(pVCpu, pvUser);
2160 return VINF_SUCCESS;
2161}
2162
2163
2164/**
2165 * Unmaps a page from Hyper-V for the purpose of emulating A20 gate behavior.
2166 *
2167 * @returns The PGMPhysNemQueryPageInfo result.
2168 * @param pVM The cross context VM structure.
2169 * @param pVCpu The cross context virtual CPU structure.
2170 * @param GCPhys The page to unmap.
2171 */
2172static int nemR3WinUnmapPageForA20Gate(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
2173{
2174 PGMPHYSNEMPAGEINFO Info;
2175 return PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhys, false /*fMakeWritable*/, &Info,
2176 nemR3WinUnsetForA20CheckerCallback, NULL);
2177}
2178
2179
2180/**
2181 * Called when the A20 state changes.
2182 *
2183 * Hyper-V doesn't seem to offer a simple way of implementing the A20 line
2184 * features of PCs. So, we do a very minimal emulation of the HMA to make DOS
2185 * happy.
2186 *
2187 * @param pVCpu The CPU the A20 state changed on.
2188 * @param fEnabled Whether it was enabled (true) or disabled.
2189 */
2190void nemR3NativeNotifySetA20(PVMCPU pVCpu, bool fEnabled)
2191{
2192 Log(("nemR3NativeNotifySetA20: fEnabled=%RTbool\n", fEnabled));
2193 PVM pVM = pVCpu->CTX_SUFF(pVM);
2194 if (!pVM->nem.s.fA20Fixed)
2195 {
2196 pVM->nem.s.fA20Enabled = fEnabled;
2197 for (RTGCPHYS GCPhys = _1M; GCPhys < _1M + _64K; GCPhys += X86_PAGE_SIZE)
2198 nemR3WinUnmapPageForA20Gate(pVM, pVCpu, GCPhys);
2199 }
2200}
2201
2202
2203/** @page pg_nem_win NEM/win - Native Execution Manager, Windows.
2204 *
2205 * On Windows the Hyper-V root partition (dom0 in zen terminology) does not have
2206 * nested VT-x or AMD-V capabilities. For a while raw-mode worked inside it,
2207 * but for a while now we've been getting \#GP when trying to modify CR4 in the
2208 * world switcher. So, when Hyper-V is active on Windows we have little choice
2209 * but to use Hyper-V to run our VMs.
2210 *
2211 *
2212 * @section sub_nem_win_whv The WinHvPlatform API
2213 *
2214 * Since Windows 10 build 17083 there is a documented API for managing Hyper-V
2215 * VMs, header file WinHvPlatform.h and implementation in WinHvPlatform.dll.
2216 * This interface is a wrapper around the undocumented Virtualization
2217 * Infrastructure Driver (VID) API - VID.DLL and VID.SYS. The wrapper is
2218 * written in C++, namespaced, early versions (at least) was using standard C++
2219 * container templates in several places.
2220 *
2221 * When creating a VM using WHvCreatePartition, it will only create the
2222 * WinHvPlatform structures for it, to which you get an abstract pointer. The
2223 * VID API that actually creates the partition is first engaged when you call
2224 * WHvSetupPartition after first setting a lot of properties using
2225 * WHvSetPartitionProperty. Since the VID API is just a very thin wrapper
2226 * around CreateFile and NtDeviceIoControlFile, it returns an actual HANDLE for
2227 * the partition WinHvPlatform. We fish this HANDLE out of the WinHvPlatform
2228 * partition structures because we need to talk directly to VID for reasons
2229 * we'll get to in a bit. (Btw. we could also intercept the CreateFileW or
2230 * NtDeviceIoControlFile calls from VID.DLL to get the HANDLE should fishing in
2231 * the partition structures become difficult.)
2232 *
2233 * The WinHvPlatform API requires us to both set the number of guest CPUs before
2234 * setting up the partition and call WHvCreateVirtualProcessor for each of them.
2235 * The CPU creation function boils down to a VidMessageSlotMap call that sets up
2236 * and maps a message buffer into ring-3 for async communication with hyper-V
2237 * and/or the VID.SYS thread actually running the CPU. When for instance a
2238 * VMEXIT is encountered, hyper-V sends a message that the
2239 * WHvRunVirtualProcessor API retrieves (and later acknowledges) via
2240 * VidMessageSlotHandleAndGetNext. It should be noteded that
2241 * WHvDeleteVirtualProcessor doesn't do much as there seems to be no partner
2242 * function VidMessagesSlotMap that reverses what it did.
2243 *
2244 * Memory is managed thru calls to WHvMapGpaRange and WHvUnmapGpaRange (GPA does
2245 * not mean grade point average here, but rather guest physical addressspace),
2246 * which corresponds to VidCreateVaGpaRangeSpecifyUserVa and VidDestroyGpaRange
2247 * respectively. As 'UserVa' indicates, the functions works on user process
2248 * memory. The mappings are also subject to quota restrictions, so the number
2249 * of ranges are limited and probably their total size as well. Obviously
2250 * VID.SYS keeps track of the ranges, but so does WinHvPlatform, which means
2251 * there is a bit of overhead involved and quota restrctions makes sense. For
2252 * some reason though, regions are lazily mapped on VMEXIT/memory by
2253 * WHvRunVirtualProcessor.
2254 *
2255 * Running guest code is done thru the WHvRunVirtualProcessor function. It
2256 * asynchronously starts or resumes hyper-V CPU execution and then waits for an
2257 * VMEXIT message. Hyper-V / VID.SYS will return information about the message
2258 * in the message buffer mapping, and WHvRunVirtualProcessor will convert that
2259 * into it's own WHV_RUN_VP_EXIT_CONTEXT format.
2260 *
2261 * Other threads can interrupt the execution by using WHvCancelVirtualProcessor,
2262 * which which case the thread in WHvRunVirtualProcessor is woken up via a dummy
2263 * QueueUserAPC and will call VidStopVirtualProcessor to asynchronously end
2264 * execution. The stop CPU call not immediately succeed if the CPU encountered
2265 * a VMEXIT before the stop was processed, in which case the VMEXIT needs to be
2266 * processed first, and the pending stop will be processed in a subsequent call
2267 * to WHvRunVirtualProcessor.
2268 *
2269 * Registers are retrieved and set via WHvGetVirtualProcessorRegisters and
2270 * WHvSetVirtualProcessorRegisters. In addition, several VMEXITs include
2271 * essential register state in the exit context information, potentially making
2272 * it possible to emulate the instruction causing the exit without involving
2273 * WHvGetVirtualProcessorRegisters.
2274 *
2275 *
2276 * @subsection subsec_nem_win_whv_cons Issues & Feedback
2277 *
2278 * Here are some observations (mostly against build 17101):
2279 *
2280 * - The VMEXIT performance is dismal (build 17101).
2281 *
2282 * Our proof of concept implementation with a kernel runloop (i.e. not using
2283 * WHvRunVirtualProcessor and friends, but calling VID.SYS fast I/O control
2284 * entry point directly) delivers 9-10% of the port I/O performance and only
2285 * 6-7% of the MMIO performance that we have with our own hypervisor.
2286 *
2287 * When using the offical WinHvPlatform API, the numbers are %3 for port I/O
2288 * and 5% for MMIO.
2289 *
2290 *
2291 * - The WHvCancelVirtualProcessor API schedules a dummy usermode APC callback
2292 * in order to cancel any current or future alertable wait in VID.SYS during
2293 * the VidMessageSlotHandleAndGetNext call.
2294 *
2295 * IIRC this will make the kernel schedule the specified callback thru
2296 * NTDLL!KiUserApcDispatcher by modifying the thread context and quite
2297 * possibly the userland thread stack. When the APC callback returns to
2298 * KiUserApcDispatcher, it will call NtContinue to restore the old thread
2299 * context and resume execution from there. This naturally adds up to some
2300 * CPU cycles, ring transitions aren't for free, especially after Spectre &
2301 * Meltdown mitigations.
2302 *
2303 * Using NtAltertThread call could do the same without the thread context
2304 * modifications and the extra kernel call.
2305 *
2306 *
2307 * - Not sure if this is a thing, but WHvCancelVirtualProcessor seems to cause
2308 * cause a lot more spurious WHvRunVirtualProcessor returns that what we get
2309 * with the replacement code. By spurious returns we mean that the
2310 * subsequent call to WHvRunVirtualProcessor would return immediately.
2311 *
2312 *
2313 * - When WHvRunVirtualProcessor returns without a message, or on a terse
2314 * VID message like HLT, it will make a kernel call to get some registers.
2315 * This is potentially inefficient if the caller decides he needs more
2316 * register state.
2317 *
2318 * It would be better to just return what's available and let the caller fetch
2319 * what is missing from his point of view in a single kernel call.
2320 *
2321 *
2322 * - The WHvRunVirtualProcessor implementation does lazy GPA range mappings when
2323 * a unmapped GPA message is received from hyper-V.
2324 *
2325 * Since MMIO is currently realized as unmapped GPA, this will slow down all
2326 * MMIO accesses a tiny little bit as WHvRunVirtualProcessor looks up the
2327 * guest physical address to check if it is a pending lazy mapping.
2328 *
2329 * The lazy mapping feature makes no sense to us. We as API user have all the
2330 * information and can do lazy mapping ourselves if we want/have to (see next
2331 * point).
2332 *
2333 *
2334 * - There is no API for modifying protection of a page within a GPA range.
2335 *
2336 * From what we can tell, the only way to modify the protection (like readonly
2337 * -> writable, or vice versa) is to first unmap the range and then remap it
2338 * with the new protection.
2339 *
2340 * We are for instance doing this quite a bit in order to track dirty VRAM
2341 * pages. VRAM pages starts out as readonly, when the guest writes to a page
2342 * we take an exit, notes down which page it is, makes it writable and restart
2343 * the instruction. After refreshing the display, we reset all the writable
2344 * pages to readonly again, bulk fashion.
2345 *
2346 * Now to work around this issue, we do page sized GPA ranges. In addition to
2347 * add a lot of tracking overhead to WinHvPlatform and VID.SYS, this also
2348 * causes us to exceed our quota before we've even mapped a default sized
2349 * (128MB) VRAM page-by-page. So, to work around this quota issue we have to
2350 * lazily map pages and actively restrict the number of mappings.
2351 *
2352 * Our best workaround thus far is bypassing WinHvPlatform and VID entirely
2353 * when in comes to guest memory management and instead use the underlying
2354 * hypercalls (HvCallMapGpaPages, HvCallUnmapGpaPages) to do it ourselves.
2355 * (This also maps a whole lot better into our own guest page management
2356 * infrastructure.)
2357 *
2358 *
2359 * - Observed problems doing WHvUnmapGpaRange immediately followed by
2360 * WHvMapGpaRange.
2361 *
2362 * As mentioned above, we've been forced to use this sequence when modifying
2363 * page protection. However, when transitioning from readonly to writable,
2364 * we've ended up looping forever with the same write to readonly memory
2365 * VMEXIT. We're wondering if this issue might be related to the lazy mapping
2366 * logic in WinHvPlatform.
2367 *
2368 * Workaround: Insert a WHvRunVirtualProcessor call and make sure to get a GPA
2369 * unmapped exit between the two calls. Not entirely great performance wise
2370 * (or the santity of our code).
2371 *
2372 *
2373 * - Implementing A20 gate behavior is tedious, where as correctly emulating the
2374 * A20M# pin (present on 486 and later) is near impossible for SMP setups
2375 * (e.g. possiblity of two CPUs with different A20 status).
2376 *
2377 * Workaround: Only do A20 on CPU 0, restricting the emulation to HMA. We
2378 * unmap all pages related to HMA (0x100000..0x10ffff) when the A20 state
2379 * changes, lazily syncing the right pages back when accessed.
2380 *
2381 *
2382 * - WHVRunVirtualProcessor wastes time converting VID/Hyper-V messages to its
2383 * own format (WHV_RUN_VP_EXIT_CONTEXT).
2384 *
2385 * We understand this might be because Microsoft wishes to remain free to
2386 * modify the VID/Hyper-V messages, but it's still rather silly and does slow
2387 * things down a little. We'd much rather just process the messages directly.
2388 *
2389 *
2390 * - WHVRunVirtualProcessor would've benefited from using a callback interface:
2391 *
2392 * - The potential size changes of the exit context structure wouldn't be
2393 * an issue, since the function could manage that itself.
2394 *
2395 * - State handling could probably be simplified (like cancelation).
2396 *
2397 *
2398 * - WHvGetVirtualProcessorRegisters and WHvSetVirtualProcessorRegisters
2399 * internally converts register names, probably using temporary heap buffers.
2400 *
2401 * From the looks of things, they are converting from WHV_REGISTER_NAME to
2402 * HV_REGISTER_NAME from in the "Virtual Processor Register Names" section in
2403 * the "Hypervisor Top-Level Functional Specification" document. This feels
2404 * like an awful waste of time.
2405 *
2406 * We simply cannot understand why HV_REGISTER_NAME isn't used directly here,
2407 * or at least the same values, making any conversion reduntant. Restricting
2408 * access to certain registers could easily be implement by scanning the
2409 * inputs.
2410 *
2411 * To avoid the heap + conversion overhead, we're currently using the
2412 * HvCallGetVpRegisters and HvCallSetVpRegisters calls directly.
2413 *
2414 *
2415 * - The YMM and XCR0 registers are not yet named (17083). This probably
2416 * wouldn't be a problem if HV_REGISTER_NAME was used, see previous point.
2417 *
2418 *
2419 * - Why does WINHVR.SYS (or VID.SYS) only query/set 32 registers at the time
2420 * thru the HvCallGetVpRegisters and HvCallSetVpRegisters hypercalls?
2421 *
2422 * We've not trouble getting/setting all the registers defined by
2423 * WHV_REGISTER_NAME in one hypercall (around 80)...
2424 *
2425 *
2426 * - The I/O port exit context information seems to be missing the address size
2427 * information needed for correct string I/O emulation.
2428 *
2429 * VT-x provides this information in bits 7:9 in the instruction information
2430 * field on newer CPUs. AMD-V in bits 7:9 in the EXITINFO1 field in the VMCB.
2431 *
2432 * We can probably work around this by scanning the instruction bytes for
2433 * address size prefixes. Haven't investigated it any further yet.
2434 *
2435 *
2436 * - The WHvGetCapability function has a weird design:
2437 * - The CapabilityCode parameter is pointlessly duplicated in the output
2438 * structure (WHV_CAPABILITY).
2439 *
2440 * - API takes void pointer, but everyone will probably be using
2441 * WHV_CAPABILITY due to WHV_CAPABILITY::CapabilityCode making it
2442 * impractical to use anything else.
2443 *
2444 * - No output size.
2445 *
2446 * - See GetFileAttributesEx, GetFileInformationByHandleEx,
2447 * FindFirstFileEx, and others for typical pattern for generic
2448 * information getters.
2449 *
2450 *
2451 * - The WHvGetPartitionProperty function uses the same weird design as
2452 * WHvGetCapability, see above.
2453 *
2454 *
2455 * - The WHvSetPartitionProperty function has a totally weird design too:
2456 * - In contrast to its partner WHvGetPartitionProperty, the property code
2457 * is not a separate input parameter here but part of the input
2458 * structure.
2459 *
2460 * - The input structure is a void pointer rather than a pointer to
2461 * WHV_PARTITION_PROPERTY which everyone probably will be using because
2462 * of the WHV_PARTITION_PROPERTY::PropertyCode field.
2463 *
2464 * - Really, why use PVOID for the input when the function isn't accepting
2465 * minimal sizes. E.g. WHVPartitionPropertyCodeProcessorClFlushSize only
2466 * requires a 9 byte input, but the function insists on 16 bytes (17083).
2467 *
2468 * - See GetFileAttributesEx, SetFileInformationByHandle, FindFirstFileEx,
2469 * and others for typical pattern for generic information setters and
2470 * getters.
2471 *
2472 *
2473 * @section sec_nem_win_impl Our implementation.
2474 *
2475 * We set out with the goal of wanting to run as much as possible in ring-0,
2476 * reasoning that this would give use the best performance.
2477 *
2478 * This goal was approached gradually, starting out with a pure WinHvPlatform
2479 * implementation, gradually replacing parts: register access, guest memory
2480 * handling, running virtual processors. Then finally moving it all into
2481 * ring-0, while keeping most of it configurable so that we could make
2482 * comparisons (see NEMInternal.h and nemR3NativeRunGC()).
2483 *
2484 *
2485 * @subsection subsect_nem_win_impl_ioctl VID.SYS I/O control calls
2486 *
2487 * To run things in ring-0 we need to talk directly to VID.SYS thru its I/O
2488 * control interface. Looking at changes between like build 17083 and 17101 (if
2489 * memory serves) a set of the VID I/O control numbers shifted a little, which
2490 * means we need to determin them dynamically. We currently do this by hooking
2491 * the NtDeviceIoControlFile API call from VID.DLL and snooping up the
2492 * parameters when making dummy calls to relevant APIs. (We could also
2493 * disassemble the relevant APIs and try fish out the information from that, but
2494 * this is way simpler.)
2495 *
2496 * Issuing I/O control calls from ring-0 is facing a small challenge with
2497 * respect to direct buffering. When using direct buffering the device will
2498 * typically check that the buffer is actually in the user address space range
2499 * and reject kernel addresses. Fortunately, we've got the cross context VM
2500 * structure that is mapped into both kernel and user space, it's also locked
2501 * and safe to access from kernel space. So, we place the I/O control buffers
2502 * in the per-CPU part of it (NEMCPU::uIoCtlBuf) and give the driver the user
2503 * address if direct access buffering or kernel address if not.
2504 *
2505 * The I/O control calls are 'abstracted' in the support driver, see
2506 * SUPR0IoCtlSetupForHandle(), SUPR0IoctlPerform() and SUPR0IoCtlCleanup().
2507 *
2508 *
2509 * @subsection subsect_nem_win_impl_cpumctx CPUMCTX
2510 *
2511 * Since the CPU state needs to live in Hyper-V when executing, we probably
2512 * should not transfer more than necessary when handling VMEXITs. To help us
2513 * manage this CPUMCTX got a new field CPUMCTX::fExtrn that to indicate which
2514 * part of the state is currently externalized (== in Hyper-V).
2515 *
2516 *
2517 */
2518
Note: See TracBrowser for help on using the repository browser.

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