VirtualBox

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

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

VMM/NEM: More code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1/* $Id: NEMR3Native-win.cpp 70945 2018-02-09 23:45:56Z vboxsync $ */
2/** @file
3 * NEM - Native execution manager, native ring-3 Windows backend.
4 */
5
6/*
7 * Copyright (C) 2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_NEM
23#include <WinHvPlatform.h>
24
25#ifndef _WIN32_WINNT_WIN10
26# error "Missing _WIN32_WINNT_WIN10"
27#endif
28#ifndef _WIN32_WINNT_WIN10_RS1 /* Missing define, causing trouble for us. */
29# define _WIN32_WINNT_WIN10_RS1 (_WIN32_WINNT_WIN10 + 1)
30#endif
31#include <sysinfoapi.h>
32#include <debugapi.h>
33#include <errhandlingapi.h>
34#include <fileapi.h>
35#include <winerror.h> /* no api header for this. */
36
37#include <VBox/vmm/nem.h>
38#include "NEMInternal.h"
39#include <VBox/vmm/vm.h>
40
41#include <iprt/ldr.h>
42#include <iprt/path.h>
43#include <iprt/string.h>
44
45
46/*********************************************************************************************************************************
47* Global Variables *
48*********************************************************************************************************************************/
49/** @name APIs imported from WinHvPlatform.dll
50 * @{ */
51static decltype(WHvGetCapability) * g_pfnWHvGetCapability;
52static decltype(WHvCreatePartition) * g_pfnWHvCreatePartition;
53static decltype(WHvSetupPartition) * g_pfnWHvSetupPartition;
54static decltype(WHvDeletePartition) * g_pfnWHvDeletePartition;
55static decltype(WHvGetPartitionProperty) * g_pfnWHvGetPartitionProperty;
56static decltype(WHvSetPartitionProperty) * g_pfnWHvSetPartitionProperty;
57static decltype(WHvMapGpaRange) * g_pfnWHvMapGpaRange;
58static decltype(WHvUnmapGpaRange) * g_pfnWHvUnmapGpaRange;
59static decltype(WHvTranslateGva) * g_pfnWHvTranslateGva;
60static decltype(WHvCreateVirtualProcessor) * g_pfnWHvCreateVirtualProcessor;
61static decltype(WHvDeleteVirtualProcessor) * g_pfnWHvDeleteVirtualProcessor;
62static decltype(WHvRunVirtualProcessor) * g_pfnWHvRunVirtualProcessor;
63static decltype(WHvGetRunExitContextSize) * g_pfnWHvGetRunExitContextSize;
64static decltype(WHvCancelRunVirtualProcessor) * g_pfnWHvCancelRunVirtualProcessor;
65static decltype(WHvGetVirtualProcessorRegisters) * g_pfnWHvGetVirtualProcessorRegisters;
66static decltype(WHvSetVirtualProcessorRegisters) * g_pfnWHvSetVirtualProcessorRegisters;
67/** @} */
68
69
70/**
71 * Import instructions.
72 */
73static const struct
74{
75 uint8_t idxDll; /**< 0 for WinHvPlatform.dll, 1 for vid.dll. */
76 bool fOptional; /**< Set if import is optional. */
77 PFNRT *ppfn; /**< The function pointer variable. */
78 const char *pszName; /**< The function name. */
79} g_aImports[] =
80{
81#define NEM_WIN_IMPORT(a_idxDll, a_fOptional, a_Name) { (a_idxDll), (a_fOptional), (PFNRT *)&RT_CONCAT(g_pfn,a_Name), #a_Name }
82 NEM_WIN_IMPORT(0, false, WHvGetCapability),
83 NEM_WIN_IMPORT(0, false, WHvCreatePartition),
84 NEM_WIN_IMPORT(0, false, WHvSetupPartition),
85 NEM_WIN_IMPORT(0, false, WHvDeletePartition),
86 NEM_WIN_IMPORT(0, false, WHvGetPartitionProperty),
87 NEM_WIN_IMPORT(0, false, WHvSetPartitionProperty),
88 NEM_WIN_IMPORT(0, false, WHvMapGpaRange),
89 NEM_WIN_IMPORT(0, false, WHvUnmapGpaRange),
90 NEM_WIN_IMPORT(0, false, WHvTranslateGva),
91 NEM_WIN_IMPORT(0, false, WHvCreateVirtualProcessor),
92 NEM_WIN_IMPORT(0, false, WHvDeleteVirtualProcessor),
93 NEM_WIN_IMPORT(0, false, WHvRunVirtualProcessor),
94 NEM_WIN_IMPORT(0, false, WHvGetRunExitContextSize),
95 NEM_WIN_IMPORT(0, false, WHvCancelRunVirtualProcessor),
96 NEM_WIN_IMPORT(0, false, WHvGetVirtualProcessorRegisters),
97 NEM_WIN_IMPORT(0, false, WHvSetVirtualProcessorRegisters),
98#undef NEM_WIN_IMPORT
99};
100
101
102/*
103 * Let the preprocessor alias the APIs to import variables for better autocompletion.
104 */
105#ifndef IN_SLICKEDIT
106# define WHvGetCapability g_pfnWHvGetCapability
107# define WHvCreatePartition g_pfnWHvCreatePartition
108# define WHvSetupPartition g_pfnWHvSetupPartition
109# define WHvDeletePartition g_pfnWHvDeletePartition
110# define WHvGetPartitionProperty g_pfnWHvGetPartitionProperty
111# define WHvSetPartitionProperty g_pfnWHvSetPartitionProperty
112# define WHvMapGpaRange g_pfnWHvMapGpaRange
113# define WHvUnmapGpaRange g_pfnWHvUnmapGpaRange
114# define WHvTranslateGva g_pfnWHvTranslateGva
115# define WHvCreateVirtualProcessor g_pfnWHvCreateVirtualProcessor
116# define WHvDeleteVirtualProcessor g_pfnWHvDeleteVirtualProcessor
117# define WHvRunVirtualProcessor g_pfnWHvRunVirtualProcessor
118# define WHvGetRunExitContextSize g_pfnWHvGetRunExitContextSize
119# define WHvCancelRunVirtualProcessor g_pfnWHvCancelRunVirtualProcessor
120# define WHvGetVirtualProcessorRegisters g_pfnWHvGetVirtualProcessorRegisters
121# define WHvSetVirtualProcessorRegisters g_pfnWHvSetVirtualProcessorRegisters
122#endif
123
124
125
126/**
127 * Worker for nemR3NativeInit that gets the hypervisor capabilities.
128 *
129 * @returns VBox status code.
130 * @param pVM The cross context VM structure.
131 * @param pErrInfo Where to always return error info.
132 */
133static int nemR3WinInitCheckCapabilities(PVM pVM, PRTERRINFO pErrInfo)
134{
135#define NEM_LOG_REL_CAP_EX(a_szField, a_szFmt, a_Value) LogRel(("NEM: %-38s= " a_szFmt "\n", a_szField, a_Value))
136#define NEM_LOG_REL_CAP_SUB_EX(a_szField, a_szFmt, a_Value) LogRel(("NEM: %36s: " a_szFmt "\n", a_szField, a_Value))
137#define NEM_LOG_REL_CAP_SUB(a_szField, a_Value) NEM_LOG_REL_CAP_SUB_EX(a_szField, "%d", a_Value)
138
139 /*
140 * Is the hypervisor present with the desired capability?
141 *
142 * In build 17083 this translates into:
143 * - CPUID[0x00000001].HVP is set
144 * - CPUID[0x40000000] == "Microsoft Hv"
145 * - CPUID[0x40000001].eax == "Hv#1"
146 * - CPUID[0x40000003].ebx[12] is set.
147 * - VidGetExoPartitionProperty(INVALID_HANDLE_VALUE, 0x60000, &Ignored) returns
148 * a non-zero value.
149 */
150 /**
151 * @todo Someone at Microsoft please explain weird API design:
152 * 1. Pointless CapabilityCode duplication int the output;
153 * 2. No output size.
154 */
155 WHV_CAPABILITY Caps;
156 RT_ZERO(Caps);
157 SetLastError(0);
158 HRESULT hrc = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &Caps, sizeof(Caps));
159 DWORD rcWin = GetLastError();
160 if (FAILED(hrc))
161 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeHypervisorPresent failed: %Rhrc", hrc);
162 if (!Caps.HypervisorPresent)
163 {
164 if (!RTPathExists(RTPATH_NT_PASSTHRU_PREFIX "Device\\VidExo"))
165 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE,
166 "WHvCapabilityCodeHypervisorPresent is FALSE! Make sure you have enabled the 'Windows Hypervisor Platform' feature.");
167 return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "WHvCapabilityCodeHypervisorPresent is FALSE! (%u)", rcWin);
168 }
169 LogRel(("NEM: WHvCapabilityCodeHypervisorPresent is TRUE, so this might work...\n"));
170
171
172 /*
173 * Check what extended VM exits are supported.
174 */
175 RT_ZERO(Caps);
176 hrc = WHvGetCapability(WHvCapabilityCodeExtendedVmExits, &Caps, sizeof(Caps));
177 if (FAILED(hrc))
178 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeExtendedVmExits failed: %Rhrc", hrc);
179 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeExtendedVmExits", "%'#018RX64", Caps.ExtendedVmExits.AsUINT64);
180 pVM->nem.s.fExtendedMsrExit = RT_BOOL(Caps.ExtendedVmExits.X64MsrExit);
181 pVM->nem.s.fExtendedCpuIdExit = RT_BOOL(Caps.ExtendedVmExits.X64CpuidExit);
182 pVM->nem.s.fExtendedXcptExit = RT_BOOL(Caps.ExtendedVmExits.ExceptionExit);
183 NEM_LOG_REL_CAP_SUB("fExtendedMsrExit", pVM->nem.s.fExtendedMsrExit);
184 NEM_LOG_REL_CAP_SUB("fExtendedCpuIdExit", pVM->nem.s.fExtendedCpuIdExit);
185 NEM_LOG_REL_CAP_SUB("fExtendedXcptExit", pVM->nem.s.fExtendedXcptExit);
186 if (Caps.ExtendedVmExits.AsUINT64 & ~(uint64_t)7)
187 LogRel(("NEM: Warning! Unknown VM exit definitions: %#RX64\n", Caps.ExtendedVmExits.AsUINT64));
188 /** @todo RECHECK: WHV_EXTENDED_VM_EXITS typedef. */
189
190 /*
191 * Check features in case they end up defining any.
192 */
193 RT_ZERO(Caps);
194 hrc = WHvGetCapability(WHvCapabilityCodeFeatures, &Caps, sizeof(Caps));
195 if (FAILED(hrc))
196 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeFeatures failed: %Rhrc", hrc);
197 if (Caps.Features.AsUINT64 & ~(uint64_t)0)
198 LogRel(("NEM: Warning! Unknown feature definitions: %#RX64\n", Caps.Features.AsUINT64));
199 /** @todo RECHECK: WHV_CAPABILITY_FEATURES typedef. */
200
201 /*
202 * Check that the CPU vendor is supported.
203 */
204 RT_ZERO(Caps);
205 hrc = WHvGetCapability(WHvCapabilityCodeProcessorVendor, &Caps, sizeof(Caps));
206 if (FAILED(hrc))
207 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeProcessorVendor failed: %Rhrc", hrc);
208 switch (Caps.ProcessorVendor)
209 {
210 /** @todo RECHECK: WHV_PROCESSOR_VENDOR typedef. */
211 case WHvProcessorVendorIntel:
212 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d - Intel", Caps.ProcessorVendor);
213 pVM->nem.s.enmCpuVendor = CPUMCPUVENDOR_INTEL;
214 break;
215 case WHvProcessorVendorAmd:
216 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d - AMD", Caps.ProcessorVendor);
217 pVM->nem.s.enmCpuVendor = CPUMCPUVENDOR_AMD;
218 break;
219 default:
220 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d", Caps.ProcessorVendor);
221 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Unknown processor vendor: %d", Caps.ProcessorVendor);
222 }
223
224 /*
225 * CPU features, guessing these are virtual CPU features?
226 */
227 RT_ZERO(Caps);
228 hrc = WHvGetCapability(WHvCapabilityCodeProcessorFeatures, &Caps, sizeof(Caps));
229 if (FAILED(hrc))
230 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeProcessorFeatures failed: %Rhrc", hrc);
231 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorFeatures", "%'#018RX64", Caps.ProcessorFeatures.AsUINT64);
232#define NEM_LOG_REL_CPU_FEATURE(a_Field) NEM_LOG_REL_CAP_SUB(#a_Field, Caps.ProcessorFeatures.a_Field)
233 NEM_LOG_REL_CPU_FEATURE(Sse3Support);
234 NEM_LOG_REL_CPU_FEATURE(LahfSahfSupport);
235 NEM_LOG_REL_CPU_FEATURE(Ssse3Support);
236 NEM_LOG_REL_CPU_FEATURE(Sse4_1Support);
237 NEM_LOG_REL_CPU_FEATURE(Sse4_2Support);
238 NEM_LOG_REL_CPU_FEATURE(Sse4aSupport);
239 NEM_LOG_REL_CPU_FEATURE(XopSupport);
240 NEM_LOG_REL_CPU_FEATURE(PopCntSupport);
241 NEM_LOG_REL_CPU_FEATURE(Cmpxchg16bSupport);
242 NEM_LOG_REL_CPU_FEATURE(Altmovcr8Support);
243 NEM_LOG_REL_CPU_FEATURE(LzcntSupport);
244 NEM_LOG_REL_CPU_FEATURE(MisAlignSseSupport);
245 NEM_LOG_REL_CPU_FEATURE(MmxExtSupport);
246 NEM_LOG_REL_CPU_FEATURE(Amd3DNowSupport);
247 NEM_LOG_REL_CPU_FEATURE(ExtendedAmd3DNowSupport);
248 NEM_LOG_REL_CPU_FEATURE(Page1GbSupport);
249 NEM_LOG_REL_CPU_FEATURE(AesSupport);
250 NEM_LOG_REL_CPU_FEATURE(PclmulqdqSupport);
251 NEM_LOG_REL_CPU_FEATURE(PcidSupport);
252 NEM_LOG_REL_CPU_FEATURE(Fma4Support);
253 NEM_LOG_REL_CPU_FEATURE(F16CSupport);
254 NEM_LOG_REL_CPU_FEATURE(RdRandSupport);
255 NEM_LOG_REL_CPU_FEATURE(RdWrFsGsSupport);
256 NEM_LOG_REL_CPU_FEATURE(SmepSupport);
257 NEM_LOG_REL_CPU_FEATURE(EnhancedFastStringSupport);
258 NEM_LOG_REL_CPU_FEATURE(Bmi1Support);
259 NEM_LOG_REL_CPU_FEATURE(Bmi2Support);
260 /* two reserved bits here, see below */
261 NEM_LOG_REL_CPU_FEATURE(MovbeSupport);
262 NEM_LOG_REL_CPU_FEATURE(Npiep1Support);
263 NEM_LOG_REL_CPU_FEATURE(DepX87FPUSaveSupport);
264 NEM_LOG_REL_CPU_FEATURE(RdSeedSupport);
265 NEM_LOG_REL_CPU_FEATURE(AdxSupport);
266 NEM_LOG_REL_CPU_FEATURE(IntelPrefetchSupport);
267 NEM_LOG_REL_CPU_FEATURE(SmapSupport);
268 NEM_LOG_REL_CPU_FEATURE(HleSupport);
269 NEM_LOG_REL_CPU_FEATURE(RtmSupport);
270 NEM_LOG_REL_CPU_FEATURE(RdtscpSupport);
271 NEM_LOG_REL_CPU_FEATURE(ClflushoptSupport);
272 NEM_LOG_REL_CPU_FEATURE(ClwbSupport);
273 NEM_LOG_REL_CPU_FEATURE(ShaSupport);
274 NEM_LOG_REL_CPU_FEATURE(X87PointersSavedSupport);
275#undef NEM_LOG_REL_CPU_FEATURE
276 if (Caps.ProcessorFeatures.AsUINT64 & (~(RT_BIT_64(43) - 1) | RT_BIT_64(27) | RT_BIT_64(28)))
277 LogRel(("NEM: Warning! Unknown CPU features: %#RX64\n", Caps.ProcessorFeatures.AsUINT64));
278 pVM->nem.s.uCpuFeatures.u64 = Caps.ProcessorFeatures.AsUINT64;
279 /** @todo RECHECK: WHV_PROCESSOR_FEATURES typedef. */
280
281 /*
282 * The cache line flush size.
283 */
284 RT_ZERO(Caps);
285 hrc = WHvGetCapability(WHvCapabilityCodeProcessorClFlushSize, &Caps, sizeof(Caps));
286 if (FAILED(hrc))
287 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeProcessorClFlushSize failed: %Rhrc", hrc);
288 NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorClFlushSize", "2^%u", Caps.ProcessorClFlushSize);
289 if (Caps.ProcessorClFlushSize < 8 && Caps.ProcessorClFlushSize > 9)
290 return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Unsupported cache line flush size: %u", Caps.ProcessorClFlushSize);
291 pVM->nem.s.cCacheLineFlushShift = Caps.ProcessorClFlushSize;
292
293 /*
294 * See if they've added more properties that we're not aware of.
295 */
296 /** @todo RECHECK: WHV_CAPABILITY_CODE typedef. */
297 if (!IsDebuggerPresent()) /* Too noisy when in debugger, so skip. */
298 {
299 static const struct
300 {
301 uint32_t iMin, iMax; } s_aUnknowns[] =
302 {
303 { 0x0003, 0x000f },
304 { 0x1003, 0x100f },
305 { 0x2000, 0x200f },
306 { 0x3000, 0x300f },
307 { 0x4000, 0x400f },
308 };
309 for (uint32_t j = 0; j < RT_ELEMENTS(s_aUnknowns); j++)
310 for (uint32_t i = s_aUnknowns[j].iMin; i <= s_aUnknowns[j].iMax; i++)
311 {
312 RT_ZERO(Caps);
313 hrc = WHvGetCapability((WHV_CAPABILITY_CODE)i, &Caps, sizeof(Caps));
314 if (SUCCEEDED(hrc))
315 LogRel(("NEM: Warning! Unknown capability %#x returning: %.*Rhxs\n", i, sizeof(Caps), &Caps));
316 }
317 }
318
319#undef NEM_LOG_REL_CAP_EX
320#undef NEM_LOG_REL_CAP_SUB_EX
321#undef NEM_LOG_REL_CAP_SUB
322 return VINF_SUCCESS;
323}
324
325
326/**
327 * Creates and sets up a Hyper-V (exo) partition.
328 *
329 * @returns VBox status code.
330 * @param pVM The cross context VM structure.
331 * @param pErrInfo Where to always return error info.
332 */
333static int nemR3WinInitCreatePartition(PVM pVM, PRTERRINFO pErrInfo)
334{
335 AssertReturn(!pVM->nem.s.hPartition, RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER, "Wrong initalization order"));
336 AssertReturn(!pVM->nem.s.hPartitionDevice, RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER, "Wrong initalization order"));
337
338 /*
339 * Create the partition.
340 */
341 WHV_PARTITION_HANDLE hPartition;
342 HRESULT hrc = WHvCreatePartition(&hPartition);
343 if (FAILED(hrc))
344 return RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "WHvCreatePartition failed with %Rhrc", hrc);
345
346 int rc;
347
348 /*
349 * Set partition properties, most importantly the CPU count.
350 */
351 /**
352 * @todo Someone at microsoft please explain another weird API:
353 * - Why this API doesn't take the WHV_PARTITION_PROPERTY_CODE value as an
354 * argument rather than as part of the struct. That is so weird if you've
355 * used any other NT or windows API, including WHvGetCapability().
356 * - Why use PVOID when WHV_PARTITION_PROPERTY is what's expected. We
357 * technically only need 9 bytes for setting/getting
358 * WHVPartitionPropertyCodeProcessorClFlushSize, but the API insists on 16. */
359 WHV_PARTITION_PROPERTY Property;
360 RT_ZERO(Property);
361 Property.PropertyCode = WHvPartitionPropertyCodeProcessorCount;
362 Property.ProcessorCount = pVM->cCpus;
363 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
364 if (SUCCEEDED(hrc))
365 {
366 RT_ZERO(Property);
367 Property.PropertyCode = WHvPartitionPropertyCodeExtendedVmExits;
368 Property.ExtendedVmExits.X64CpuidExit = pVM->nem.s.fExtendedCpuIdExit;
369 Property.ExtendedVmExits.X64MsrExit = pVM->nem.s.fExtendedMsrExit;
370 Property.ExtendedVmExits.ExceptionExit = pVM->nem.s.fExtendedXcptExit;
371 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
372 if (SUCCEEDED(hrc))
373 {
374 /*
375 * We'll continue setup in nemR3NativeInitAfterCPUM.
376 */
377 pVM->nem.s.fCreatedEmts = false;
378 pVM->nem.s.hPartition = hPartition;
379 LogRel(("NEM: Created partition %p.\n", hPartition));
380 return VINF_SUCCESS;
381 }
382
383 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED,
384 "Failed setting WHvPartitionPropertyCodeExtendedVmExits to %'#RX64: %Rhrc",
385 Property.ExtendedVmExits.AsUINT64, hrc);
386 }
387 else
388 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED,
389 "Failed setting WHvPartitionPropertyCodeProcessorCount to %u: %Rhrc", pVM->cCpus, hrc);
390 WHvDeletePartition(hPartition);
391
392 Assert(!pVM->nem.s.hPartitionDevice);
393 Assert(!pVM->nem.s.hPartition);
394 return rc;
395}
396
397
398/**
399 * Try initialize the native API.
400 *
401 * This may only do part of the job, more can be done in
402 * nemR3NativeInitAfterCPUM() and nemR3NativeInitCompleted().
403 *
404 * @returns VBox status code.
405 * @param pVM The cross context VM structure.
406 * @param fFallback Whether we're in fallback mode or use-NEM mode. In
407 * the latter we'll fail if we cannot initialize.
408 * @param fForced Whether the HMForced flag is set and we should
409 * fail if we cannot initialize.
410 */
411int nemR3NativeInit(PVM pVM, bool fFallback, bool fForced)
412{
413 /*
414 * Error state.
415 *
416 * The error message will be non-empty on failure, 'rc' may or may not
417 * be set. Early API detection failures will not set 'rc', so we'll sort
418 * that out at the other end of the function.
419 */
420 RTERRINFOSTATIC ErrInfo;
421 int rc = VINF_SUCCESS;
422 PRTERRINFO pErrInfo = RTErrInfoInitStatic(&ErrInfo);
423
424 /*
425 * Check that the DLL files we need are present, but without loading them.
426 * We'd like to avoid loading them unnecessarily.
427 */
428 WCHAR wszPath[MAX_PATH + 64];
429 UINT cwcPath = GetSystemDirectoryW(wszPath, MAX_PATH);
430 if (cwcPath >= MAX_PATH || cwcPath < 2)
431 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "GetSystemDirectoryW failed (%#x / %u)", cwcPath, GetLastError());
432 else
433 {
434 if (wszPath[cwcPath - 1] != '\\' || wszPath[cwcPath - 1] != '/')
435 wszPath[cwcPath++] = '\\';
436 RTUtf16CopyAscii(&wszPath[cwcPath], RT_ELEMENTS(wszPath) - cwcPath, "WinHvPlatform.dll");
437 if (GetFileAttributesW(wszPath) == INVALID_FILE_ATTRIBUTES)
438 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "The native API dll was not found (%ls)", wszPath);
439 else
440 {
441 /*
442 * Check that we're in a VM and that the hypervisor identifies itself as Hyper-V.
443 */
444 if (!ASMHasCpuId())
445 rc = RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "No CPUID support");
446 else if (!ASMIsValidStdRange(ASMCpuId_EAX(0)))
447 rc = RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "No CPUID leaf #1");
448 else if (!(ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_HVP))
449 rc = RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Not in a hypervisor partition (HVP=0)");
450 else
451 {
452 uint32_t cMaxHyperLeaf = 0;
453 uint32_t uEbx = 0;
454 uint32_t uEcx = 0;
455 uint32_t uEdx = 0;
456 ASMCpuIdExSlow(0x40000000, 0, 0, 0, &cMaxHyperLeaf, &uEbx, &uEcx, &uEdx);
457 if (!ASMIsValidHypervisorRange(cMaxHyperLeaf))
458 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Invalid hypervisor CPUID range (%#x %#x %#x %#x)",
459 cMaxHyperLeaf, uEbx, uEcx, uEdx);
460 else if ( uEbx != UINT32_C(0x7263694d) /* Micr */
461 || uEcx != UINT32_C(0x666f736f) /* osof */
462 || uEdx != UINT32_C(0x76482074) /* t Hv */)
463 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE,
464 "Not Hyper-V CPUID signature: %#x %#x %#x (expected %#x %#x %#x)",
465 uEbx, uEcx, uEdx, UINT32_C(0x7263694d), UINT32_C(0x666f736f), UINT32_C(0x76482074));
466 else if (cMaxHyperLeaf < UINT32_C(0x40000005))
467 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Too narrow hypervisor CPUID range (%#x)", cMaxHyperLeaf);
468 else
469 {
470 /** @todo would be great if we could recognize a root partition from the
471 * CPUID info, but I currently don't dare do that. */
472
473 /*
474 * Now try load the DLLs and resolve the APIs.
475 */
476 static const char * const s_pszDllPrefixes[] = { "WinHvPlatform.dll!", "vid.dll!" };
477 RTLDRMOD ahMods[2] = { NIL_RTLDRMOD, NIL_RTLDRMOD };
478 rc = RTLdrLoadSystem("vid.dll", true /*fNoUnload*/, &ahMods[1]);
479 if (RT_SUCCESS(rc))
480 {
481 rc = RTLdrLoadSystem("WinHvPlatform.dll", true /*fNoUnload*/, &ahMods[0]);
482 if (RT_SUCCESS(rc))
483 {
484 for (unsigned i = 0; i < RT_ELEMENTS(g_aImports); i++)
485 {
486 int rc2 = RTLdrGetSymbol(ahMods[g_aImports[i].idxDll], g_aImports[i].pszName,
487 (void **)g_aImports[i].ppfn);
488 if (RT_FAILURE(rc2))
489 {
490 *g_aImports[i].ppfn = NULL;
491
492 LogRel(("NEM: %s: Failed to import %s%s: %Rrc",
493 g_aImports[i].fOptional ? "info" : fForced ? "fatal" : "error",
494 s_pszDllPrefixes[g_aImports[i].idxDll], g_aImports[i].pszName, rc2));
495 if (!g_aImports[i].fOptional)
496 {
497 if (RTErrInfoIsSet(pErrInfo))
498 RTErrInfoAddF(pErrInfo, rc2, ", %s%s",
499 s_pszDllPrefixes[g_aImports[i].idxDll], g_aImports[i].pszName);
500 else
501 rc = RTErrInfoSetF(pErrInfo, rc2, "Failed to import: %s%s",
502 s_pszDllPrefixes[g_aImports[i].idxDll], g_aImports[i].pszName);
503 Assert(RT_FAILURE(rc));
504 }
505 }
506 }
507 if (RT_SUCCESS(rc))
508 {
509 Assert(!RTErrInfoIsSet(pErrInfo));
510
511 /*
512 * Check the capabilties of the hypervisor, starting with whether it's present.
513 */
514 rc = nemR3WinInitCheckCapabilities(pVM, pErrInfo);
515 if (RT_SUCCESS(rc))
516 {
517 /*
518 * Create and initialize a partition.
519 */
520 rc = nemR3WinInitCreatePartition(pVM, pErrInfo);
521 if (RT_SUCCESS(rc))
522 {
523 pVM->fNEMActive = true;
524 Log(("NEM: Marked active!\n"));
525 }
526 }
527 }
528 RTLdrClose(ahMods[0]);
529 }
530 else
531 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
532 "Failed to load API DLL 'WinHvPlatform.dll': %Rrc", rc);
533 RTLdrClose(ahMods[1]);
534 }
535 else
536 rc = RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Failed to load API DLL 'vid.dll': %Rrc", rc);
537 }
538 }
539 }
540 }
541
542 /*
543 * We only fail if in forced mode, otherwise just log the complaint and return.
544 */
545 Assert(pVM->fNEMActive || RTErrInfoIsSet(pErrInfo));
546 if ( (fForced || !fFallback)
547 && !pVM->fNEMActive)
548 return VMSetError(pVM, RT_SUCCESS_NP(rc) ? VERR_NEM_NOT_AVAILABLE : rc, RT_SRC_POS, "%s", pErrInfo->pszMsg);
549
550 if (RTErrInfoIsSet(pErrInfo))
551 LogRel(("NEM: Not available: %s\n", pErrInfo->pszMsg));
552 return VINF_SUCCESS;
553}
554
555
556/**
557 * This is called after CPUMR3Init is done.
558 *
559 * @returns VBox status code.
560 * @param pVM The VM handle..
561 */
562int nemR3NativeInitAfterCPUM(PVM pVM)
563{
564 /*
565 * Validate sanity.
566 */
567 WHV_PARTITION_HANDLE hPartition = pVM->nem.s.hPartition;
568 AssertReturn(hPartition != NULL, VERR_WRONG_ORDER);
569 AssertReturn(!pVM->nem.s.hPartitionDevice, VERR_WRONG_ORDER);
570 AssertReturn(!pVM->nem.s.fCreatedEmts, VERR_WRONG_ORDER);
571 AssertReturn(!pVM->fNEMActive, VERR_WRONG_ORDER);
572
573 /*
574 * Continue setting up the partition now that we've got most of the CPUID feature stuff.
575 */
576
577 /* Not sure if we really need to set the vendor. */
578 WHV_PARTITION_PROPERTY Property;
579 RT_ZERO(Property);
580 Property.PropertyCode = WHvPartitionPropertyCodeProcessorVendor;
581 Property.ProcessorVendor = pVM->nem.s.enmCpuVendor == CPUMCPUVENDOR_AMD ? WHvProcessorVendorAmd
582 : WHvProcessorVendorIntel;
583 HRESULT hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
584 if (FAILED(hrc))
585 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
586 "Failed to set WHvPartitionPropertyCodeProcessorVendor to %u: %Rhrc", Property.ProcessorVendor, hrc);
587
588 /* Not sure if we really need to set the cache line flush size. */
589 RT_ZERO(Property);
590 Property.PropertyCode = WHvPartitionPropertyCodeProcessorClFlushSize;
591 Property.ProcessorClFlushSize = pVM->nem.s.cCacheLineFlushShift;
592 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
593 if (FAILED(hrc))
594 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
595 "Failed to set WHvPartitionPropertyCodeProcessorClFlushSize to %u: %Rhrc",
596 pVM->nem.s.cCacheLineFlushShift, hrc);
597
598 /*
599 * Sync CPU features with CPUM.
600 */
601 /** @todo sync CPU features with CPUM. */
602
603 /* Set the partition property. */
604 RT_ZERO(Property);
605 Property.PropertyCode = WHvPartitionPropertyCodeProcessorFeatures;
606 Property.ProcessorFeatures.AsUINT64 = pVM->nem.s.uCpuFeatures.u64;
607 hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property));
608 if (FAILED(hrc))
609 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
610 "Failed to set WHvPartitionPropertyCodeProcessorFeatures to %'#RX64: %Rhrc",
611 pVM->nem.s.uCpuFeatures.u64, hrc);
612
613 /*
614 * Set up the partition and create EMTs.
615 *
616 * Seems like this is where the partition is actually instantiated and we get
617 * a handle to it.
618 */
619 hrc = WHvSetupPartition(hPartition);
620 if (FAILED(hrc))
621 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Call to WHvSetupPartition failed: %Rhrc", hrc);
622
623 /* Get the handle. */
624 HANDLE hPartitionDevice;
625 __try
626 {
627 hPartitionDevice = ((HANDLE *)hPartition)[1];
628 }
629 __except(EXCEPTION_EXECUTE_HANDLER)
630 {
631 hrc = GetExceptionCode();
632 hPartitionDevice = NULL;
633 }
634 if ( hPartitionDevice == NULL
635 || hPartitionDevice == (HANDLE)(intptr_t)-1)
636 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
637 "Failed to get device handle for partition %p: %Rhrc", hPartition, hrc);
638 /** @todo Do a Vid query that uses the handle to check that we've got a
639 * working value. */
640 pVM->nem.s.hPartitionDevice = hPartitionDevice;
641
642 /*
643 * Create EMTs.
644 */
645 VMCPUID iCpu;
646 for (iCpu = 0; iCpu < pVM->cCpus; iCpu++)
647 {
648 hrc = WHvCreateVirtualProcessor(hPartition, iCpu, 0 /*fFlags*/);
649 if (FAILED(hrc))
650 {
651 while (iCpu-- > 0)
652 {
653 HRESULT hrc2 = WHvDeleteVirtualProcessor(hPartition, iCpu);
654 AssertLogRelMsg(SUCCEEDED(hrc2), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc\n", hPartition, iCpu, hrc2));
655 }
656 return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Call to WHvSetupPartition failed: %Rhrc", hrc);
657 }
658 }
659 pVM->nem.s.fCreatedEmts = true;
660
661 LogRel(("NEM: Successfully set up partition (device handle %p)\n", hPartitionDevice));
662 return VINF_SUCCESS;
663}
664
665
666int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat)
667{
668 NOREF(pVM); NOREF(enmWhat);
669 return VINF_SUCCESS;
670}
671
672
673int nemR3NativeTerm(PVM pVM)
674{
675 /*
676 * Delete the partition.
677 */
678 WHV_PARTITION_HANDLE hPartition = pVM->nem.s.hPartition;
679 pVM->nem.s.hPartition = NULL;
680 pVM->nem.s.hPartitionDevice = NULL;
681 if (hPartition != NULL)
682 {
683 VMCPUID iCpu = pVM->nem.s.fCreatedEmts ? pVM->cCpus : 0;
684 LogRel(("NEM: Destroying partition %p with its %u VCpus...\n", hPartition, iCpu));
685 while (iCpu-- > 0)
686 {
687 HRESULT hrc = WHvDeleteVirtualProcessor(hPartition, iCpu);
688 AssertLogRelMsg(SUCCEEDED(hrc), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc\n", hPartition, iCpu, hrc));
689 }
690 WHvDeletePartition(hPartition);
691 }
692 pVM->nem.s.fCreatedEmts = false;
693 return VINF_SUCCESS;
694}
695
696
697void nemR3NativeReset(PVM pVM)
698{
699 NOREF(pVM);
700}
701
702
703void nemR3NativeResetCpu(PVMCPU pVCpu)
704{
705 NOREF(pVCpu);
706}
707
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