/* $Id: NEMR3Native-win.cpp 70945 2018-02-09 23:45:56Z vboxsync $ */ /** @file * NEM - Native execution manager, native ring-3 Windows backend. */ /* * Copyright (C) 2018 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_NEM #include #ifndef _WIN32_WINNT_WIN10 # error "Missing _WIN32_WINNT_WIN10" #endif #ifndef _WIN32_WINNT_WIN10_RS1 /* Missing define, causing trouble for us. */ # define _WIN32_WINNT_WIN10_RS1 (_WIN32_WINNT_WIN10 + 1) #endif #include #include #include #include #include /* no api header for this. */ #include #include "NEMInternal.h" #include #include #include #include /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** @name APIs imported from WinHvPlatform.dll * @{ */ static decltype(WHvGetCapability) * g_pfnWHvGetCapability; static decltype(WHvCreatePartition) * g_pfnWHvCreatePartition; static decltype(WHvSetupPartition) * g_pfnWHvSetupPartition; static decltype(WHvDeletePartition) * g_pfnWHvDeletePartition; static decltype(WHvGetPartitionProperty) * g_pfnWHvGetPartitionProperty; static decltype(WHvSetPartitionProperty) * g_pfnWHvSetPartitionProperty; static decltype(WHvMapGpaRange) * g_pfnWHvMapGpaRange; static decltype(WHvUnmapGpaRange) * g_pfnWHvUnmapGpaRange; static decltype(WHvTranslateGva) * g_pfnWHvTranslateGva; static decltype(WHvCreateVirtualProcessor) * g_pfnWHvCreateVirtualProcessor; static decltype(WHvDeleteVirtualProcessor) * g_pfnWHvDeleteVirtualProcessor; static decltype(WHvRunVirtualProcessor) * g_pfnWHvRunVirtualProcessor; static decltype(WHvGetRunExitContextSize) * g_pfnWHvGetRunExitContextSize; static decltype(WHvCancelRunVirtualProcessor) * g_pfnWHvCancelRunVirtualProcessor; static decltype(WHvGetVirtualProcessorRegisters) * g_pfnWHvGetVirtualProcessorRegisters; static decltype(WHvSetVirtualProcessorRegisters) * g_pfnWHvSetVirtualProcessorRegisters; /** @} */ /** * Import instructions. */ static const struct { uint8_t idxDll; /**< 0 for WinHvPlatform.dll, 1 for vid.dll. */ bool fOptional; /**< Set if import is optional. */ PFNRT *ppfn; /**< The function pointer variable. */ const char *pszName; /**< The function name. */ } g_aImports[] = { #define NEM_WIN_IMPORT(a_idxDll, a_fOptional, a_Name) { (a_idxDll), (a_fOptional), (PFNRT *)&RT_CONCAT(g_pfn,a_Name), #a_Name } NEM_WIN_IMPORT(0, false, WHvGetCapability), NEM_WIN_IMPORT(0, false, WHvCreatePartition), NEM_WIN_IMPORT(0, false, WHvSetupPartition), NEM_WIN_IMPORT(0, false, WHvDeletePartition), NEM_WIN_IMPORT(0, false, WHvGetPartitionProperty), NEM_WIN_IMPORT(0, false, WHvSetPartitionProperty), NEM_WIN_IMPORT(0, false, WHvMapGpaRange), NEM_WIN_IMPORT(0, false, WHvUnmapGpaRange), NEM_WIN_IMPORT(0, false, WHvTranslateGva), NEM_WIN_IMPORT(0, false, WHvCreateVirtualProcessor), NEM_WIN_IMPORT(0, false, WHvDeleteVirtualProcessor), NEM_WIN_IMPORT(0, false, WHvRunVirtualProcessor), NEM_WIN_IMPORT(0, false, WHvGetRunExitContextSize), NEM_WIN_IMPORT(0, false, WHvCancelRunVirtualProcessor), NEM_WIN_IMPORT(0, false, WHvGetVirtualProcessorRegisters), NEM_WIN_IMPORT(0, false, WHvSetVirtualProcessorRegisters), #undef NEM_WIN_IMPORT }; /* * Let the preprocessor alias the APIs to import variables for better autocompletion. */ #ifndef IN_SLICKEDIT # define WHvGetCapability g_pfnWHvGetCapability # define WHvCreatePartition g_pfnWHvCreatePartition # define WHvSetupPartition g_pfnWHvSetupPartition # define WHvDeletePartition g_pfnWHvDeletePartition # define WHvGetPartitionProperty g_pfnWHvGetPartitionProperty # define WHvSetPartitionProperty g_pfnWHvSetPartitionProperty # define WHvMapGpaRange g_pfnWHvMapGpaRange # define WHvUnmapGpaRange g_pfnWHvUnmapGpaRange # define WHvTranslateGva g_pfnWHvTranslateGva # define WHvCreateVirtualProcessor g_pfnWHvCreateVirtualProcessor # define WHvDeleteVirtualProcessor g_pfnWHvDeleteVirtualProcessor # define WHvRunVirtualProcessor g_pfnWHvRunVirtualProcessor # define WHvGetRunExitContextSize g_pfnWHvGetRunExitContextSize # define WHvCancelRunVirtualProcessor g_pfnWHvCancelRunVirtualProcessor # define WHvGetVirtualProcessorRegisters g_pfnWHvGetVirtualProcessorRegisters # define WHvSetVirtualProcessorRegisters g_pfnWHvSetVirtualProcessorRegisters #endif /** * Worker for nemR3NativeInit that gets the hypervisor capabilities. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pErrInfo Where to always return error info. */ static int nemR3WinInitCheckCapabilities(PVM pVM, PRTERRINFO pErrInfo) { #define NEM_LOG_REL_CAP_EX(a_szField, a_szFmt, a_Value) LogRel(("NEM: %-38s= " a_szFmt "\n", a_szField, a_Value)) #define NEM_LOG_REL_CAP_SUB_EX(a_szField, a_szFmt, a_Value) LogRel(("NEM: %36s: " a_szFmt "\n", a_szField, a_Value)) #define NEM_LOG_REL_CAP_SUB(a_szField, a_Value) NEM_LOG_REL_CAP_SUB_EX(a_szField, "%d", a_Value) /* * Is the hypervisor present with the desired capability? * * In build 17083 this translates into: * - CPUID[0x00000001].HVP is set * - CPUID[0x40000000] == "Microsoft Hv" * - CPUID[0x40000001].eax == "Hv#1" * - CPUID[0x40000003].ebx[12] is set. * - VidGetExoPartitionProperty(INVALID_HANDLE_VALUE, 0x60000, &Ignored) returns * a non-zero value. */ /** * @todo Someone at Microsoft please explain weird API design: * 1. Pointless CapabilityCode duplication int the output; * 2. No output size. */ WHV_CAPABILITY Caps; RT_ZERO(Caps); SetLastError(0); HRESULT hrc = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &Caps, sizeof(Caps)); DWORD rcWin = GetLastError(); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeHypervisorPresent failed: %Rhrc", hrc); if (!Caps.HypervisorPresent) { if (!RTPathExists(RTPATH_NT_PASSTHRU_PREFIX "Device\\VidExo")) return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "WHvCapabilityCodeHypervisorPresent is FALSE! Make sure you have enabled the 'Windows Hypervisor Platform' feature."); return RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "WHvCapabilityCodeHypervisorPresent is FALSE! (%u)", rcWin); } LogRel(("NEM: WHvCapabilityCodeHypervisorPresent is TRUE, so this might work...\n")); /* * Check what extended VM exits are supported. */ RT_ZERO(Caps); hrc = WHvGetCapability(WHvCapabilityCodeExtendedVmExits, &Caps, sizeof(Caps)); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeExtendedVmExits failed: %Rhrc", hrc); NEM_LOG_REL_CAP_EX("WHvCapabilityCodeExtendedVmExits", "%'#018RX64", Caps.ExtendedVmExits.AsUINT64); pVM->nem.s.fExtendedMsrExit = RT_BOOL(Caps.ExtendedVmExits.X64MsrExit); pVM->nem.s.fExtendedCpuIdExit = RT_BOOL(Caps.ExtendedVmExits.X64CpuidExit); pVM->nem.s.fExtendedXcptExit = RT_BOOL(Caps.ExtendedVmExits.ExceptionExit); NEM_LOG_REL_CAP_SUB("fExtendedMsrExit", pVM->nem.s.fExtendedMsrExit); NEM_LOG_REL_CAP_SUB("fExtendedCpuIdExit", pVM->nem.s.fExtendedCpuIdExit); NEM_LOG_REL_CAP_SUB("fExtendedXcptExit", pVM->nem.s.fExtendedXcptExit); if (Caps.ExtendedVmExits.AsUINT64 & ~(uint64_t)7) LogRel(("NEM: Warning! Unknown VM exit definitions: %#RX64\n", Caps.ExtendedVmExits.AsUINT64)); /** @todo RECHECK: WHV_EXTENDED_VM_EXITS typedef. */ /* * Check features in case they end up defining any. */ RT_ZERO(Caps); hrc = WHvGetCapability(WHvCapabilityCodeFeatures, &Caps, sizeof(Caps)); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeFeatures failed: %Rhrc", hrc); if (Caps.Features.AsUINT64 & ~(uint64_t)0) LogRel(("NEM: Warning! Unknown feature definitions: %#RX64\n", Caps.Features.AsUINT64)); /** @todo RECHECK: WHV_CAPABILITY_FEATURES typedef. */ /* * Check that the CPU vendor is supported. */ RT_ZERO(Caps); hrc = WHvGetCapability(WHvCapabilityCodeProcessorVendor, &Caps, sizeof(Caps)); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeProcessorVendor failed: %Rhrc", hrc); switch (Caps.ProcessorVendor) { /** @todo RECHECK: WHV_PROCESSOR_VENDOR typedef. */ case WHvProcessorVendorIntel: NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d - Intel", Caps.ProcessorVendor); pVM->nem.s.enmCpuVendor = CPUMCPUVENDOR_INTEL; break; case WHvProcessorVendorAmd: NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d - AMD", Caps.ProcessorVendor); pVM->nem.s.enmCpuVendor = CPUMCPUVENDOR_AMD; break; default: NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorVendor", "%d", Caps.ProcessorVendor); return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Unknown processor vendor: %d", Caps.ProcessorVendor); } /* * CPU features, guessing these are virtual CPU features? */ RT_ZERO(Caps); hrc = WHvGetCapability(WHvCapabilityCodeProcessorFeatures, &Caps, sizeof(Caps)); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeProcessorFeatures failed: %Rhrc", hrc); NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorFeatures", "%'#018RX64", Caps.ProcessorFeatures.AsUINT64); #define NEM_LOG_REL_CPU_FEATURE(a_Field) NEM_LOG_REL_CAP_SUB(#a_Field, Caps.ProcessorFeatures.a_Field) NEM_LOG_REL_CPU_FEATURE(Sse3Support); NEM_LOG_REL_CPU_FEATURE(LahfSahfSupport); NEM_LOG_REL_CPU_FEATURE(Ssse3Support); NEM_LOG_REL_CPU_FEATURE(Sse4_1Support); NEM_LOG_REL_CPU_FEATURE(Sse4_2Support); NEM_LOG_REL_CPU_FEATURE(Sse4aSupport); NEM_LOG_REL_CPU_FEATURE(XopSupport); NEM_LOG_REL_CPU_FEATURE(PopCntSupport); NEM_LOG_REL_CPU_FEATURE(Cmpxchg16bSupport); NEM_LOG_REL_CPU_FEATURE(Altmovcr8Support); NEM_LOG_REL_CPU_FEATURE(LzcntSupport); NEM_LOG_REL_CPU_FEATURE(MisAlignSseSupport); NEM_LOG_REL_CPU_FEATURE(MmxExtSupport); NEM_LOG_REL_CPU_FEATURE(Amd3DNowSupport); NEM_LOG_REL_CPU_FEATURE(ExtendedAmd3DNowSupport); NEM_LOG_REL_CPU_FEATURE(Page1GbSupport); NEM_LOG_REL_CPU_FEATURE(AesSupport); NEM_LOG_REL_CPU_FEATURE(PclmulqdqSupport); NEM_LOG_REL_CPU_FEATURE(PcidSupport); NEM_LOG_REL_CPU_FEATURE(Fma4Support); NEM_LOG_REL_CPU_FEATURE(F16CSupport); NEM_LOG_REL_CPU_FEATURE(RdRandSupport); NEM_LOG_REL_CPU_FEATURE(RdWrFsGsSupport); NEM_LOG_REL_CPU_FEATURE(SmepSupport); NEM_LOG_REL_CPU_FEATURE(EnhancedFastStringSupport); NEM_LOG_REL_CPU_FEATURE(Bmi1Support); NEM_LOG_REL_CPU_FEATURE(Bmi2Support); /* two reserved bits here, see below */ NEM_LOG_REL_CPU_FEATURE(MovbeSupport); NEM_LOG_REL_CPU_FEATURE(Npiep1Support); NEM_LOG_REL_CPU_FEATURE(DepX87FPUSaveSupport); NEM_LOG_REL_CPU_FEATURE(RdSeedSupport); NEM_LOG_REL_CPU_FEATURE(AdxSupport); NEM_LOG_REL_CPU_FEATURE(IntelPrefetchSupport); NEM_LOG_REL_CPU_FEATURE(SmapSupport); NEM_LOG_REL_CPU_FEATURE(HleSupport); NEM_LOG_REL_CPU_FEATURE(RtmSupport); NEM_LOG_REL_CPU_FEATURE(RdtscpSupport); NEM_LOG_REL_CPU_FEATURE(ClflushoptSupport); NEM_LOG_REL_CPU_FEATURE(ClwbSupport); NEM_LOG_REL_CPU_FEATURE(ShaSupport); NEM_LOG_REL_CPU_FEATURE(X87PointersSavedSupport); #undef NEM_LOG_REL_CPU_FEATURE if (Caps.ProcessorFeatures.AsUINT64 & (~(RT_BIT_64(43) - 1) | RT_BIT_64(27) | RT_BIT_64(28))) LogRel(("NEM: Warning! Unknown CPU features: %#RX64\n", Caps.ProcessorFeatures.AsUINT64)); pVM->nem.s.uCpuFeatures.u64 = Caps.ProcessorFeatures.AsUINT64; /** @todo RECHECK: WHV_PROCESSOR_FEATURES typedef. */ /* * The cache line flush size. */ RT_ZERO(Caps); hrc = WHvGetCapability(WHvCapabilityCodeProcessorClFlushSize, &Caps, sizeof(Caps)); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "WHvGetCapability/WHvCapabilityCodeProcessorClFlushSize failed: %Rhrc", hrc); NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorClFlushSize", "2^%u", Caps.ProcessorClFlushSize); if (Caps.ProcessorClFlushSize < 8 && Caps.ProcessorClFlushSize > 9) return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Unsupported cache line flush size: %u", Caps.ProcessorClFlushSize); pVM->nem.s.cCacheLineFlushShift = Caps.ProcessorClFlushSize; /* * See if they've added more properties that we're not aware of. */ /** @todo RECHECK: WHV_CAPABILITY_CODE typedef. */ if (!IsDebuggerPresent()) /* Too noisy when in debugger, so skip. */ { static const struct { uint32_t iMin, iMax; } s_aUnknowns[] = { { 0x0003, 0x000f }, { 0x1003, 0x100f }, { 0x2000, 0x200f }, { 0x3000, 0x300f }, { 0x4000, 0x400f }, }; for (uint32_t j = 0; j < RT_ELEMENTS(s_aUnknowns); j++) for (uint32_t i = s_aUnknowns[j].iMin; i <= s_aUnknowns[j].iMax; i++) { RT_ZERO(Caps); hrc = WHvGetCapability((WHV_CAPABILITY_CODE)i, &Caps, sizeof(Caps)); if (SUCCEEDED(hrc)) LogRel(("NEM: Warning! Unknown capability %#x returning: %.*Rhxs\n", i, sizeof(Caps), &Caps)); } } #undef NEM_LOG_REL_CAP_EX #undef NEM_LOG_REL_CAP_SUB_EX #undef NEM_LOG_REL_CAP_SUB return VINF_SUCCESS; } /** * Creates and sets up a Hyper-V (exo) partition. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pErrInfo Where to always return error info. */ static int nemR3WinInitCreatePartition(PVM pVM, PRTERRINFO pErrInfo) { AssertReturn(!pVM->nem.s.hPartition, RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER, "Wrong initalization order")); AssertReturn(!pVM->nem.s.hPartitionDevice, RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER, "Wrong initalization order")); /* * Create the partition. */ WHV_PARTITION_HANDLE hPartition; HRESULT hrc = WHvCreatePartition(&hPartition); if (FAILED(hrc)) return RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "WHvCreatePartition failed with %Rhrc", hrc); int rc; /* * Set partition properties, most importantly the CPU count. */ /** * @todo Someone at microsoft please explain another weird API: * - Why this API doesn't take the WHV_PARTITION_PROPERTY_CODE value as an * argument rather than as part of the struct. That is so weird if you've * used any other NT or windows API, including WHvGetCapability(). * - Why use PVOID when WHV_PARTITION_PROPERTY is what's expected. We * technically only need 9 bytes for setting/getting * WHVPartitionPropertyCodeProcessorClFlushSize, but the API insists on 16. */ WHV_PARTITION_PROPERTY Property; RT_ZERO(Property); Property.PropertyCode = WHvPartitionPropertyCodeProcessorCount; Property.ProcessorCount = pVM->cCpus; hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property)); if (SUCCEEDED(hrc)) { RT_ZERO(Property); Property.PropertyCode = WHvPartitionPropertyCodeExtendedVmExits; Property.ExtendedVmExits.X64CpuidExit = pVM->nem.s.fExtendedCpuIdExit; Property.ExtendedVmExits.X64MsrExit = pVM->nem.s.fExtendedMsrExit; Property.ExtendedVmExits.ExceptionExit = pVM->nem.s.fExtendedXcptExit; hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property)); if (SUCCEEDED(hrc)) { /* * We'll continue setup in nemR3NativeInitAfterCPUM. */ pVM->nem.s.fCreatedEmts = false; pVM->nem.s.hPartition = hPartition; LogRel(("NEM: Created partition %p.\n", hPartition)); return VINF_SUCCESS; } rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "Failed setting WHvPartitionPropertyCodeExtendedVmExits to %'#RX64: %Rhrc", Property.ExtendedVmExits.AsUINT64, hrc); } else rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "Failed setting WHvPartitionPropertyCodeProcessorCount to %u: %Rhrc", pVM->cCpus, hrc); WHvDeletePartition(hPartition); Assert(!pVM->nem.s.hPartitionDevice); Assert(!pVM->nem.s.hPartition); return rc; } /** * Try initialize the native API. * * This may only do part of the job, more can be done in * nemR3NativeInitAfterCPUM() and nemR3NativeInitCompleted(). * * @returns VBox status code. * @param pVM The cross context VM structure. * @param fFallback Whether we're in fallback mode or use-NEM mode. In * the latter we'll fail if we cannot initialize. * @param fForced Whether the HMForced flag is set and we should * fail if we cannot initialize. */ int nemR3NativeInit(PVM pVM, bool fFallback, bool fForced) { /* * Error state. * * The error message will be non-empty on failure, 'rc' may or may not * be set. Early API detection failures will not set 'rc', so we'll sort * that out at the other end of the function. */ RTERRINFOSTATIC ErrInfo; int rc = VINF_SUCCESS; PRTERRINFO pErrInfo = RTErrInfoInitStatic(&ErrInfo); /* * Check that the DLL files we need are present, but without loading them. * We'd like to avoid loading them unnecessarily. */ WCHAR wszPath[MAX_PATH + 64]; UINT cwcPath = GetSystemDirectoryW(wszPath, MAX_PATH); if (cwcPath >= MAX_PATH || cwcPath < 2) rc = RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "GetSystemDirectoryW failed (%#x / %u)", cwcPath, GetLastError()); else { if (wszPath[cwcPath - 1] != '\\' || wszPath[cwcPath - 1] != '/') wszPath[cwcPath++] = '\\'; RTUtf16CopyAscii(&wszPath[cwcPath], RT_ELEMENTS(wszPath) - cwcPath, "WinHvPlatform.dll"); if (GetFileAttributesW(wszPath) == INVALID_FILE_ATTRIBUTES) rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "The native API dll was not found (%ls)", wszPath); else { /* * Check that we're in a VM and that the hypervisor identifies itself as Hyper-V. */ if (!ASMHasCpuId()) rc = RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "No CPUID support"); else if (!ASMIsValidStdRange(ASMCpuId_EAX(0))) rc = RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "No CPUID leaf #1"); else if (!(ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_HVP)) rc = RTErrInfoSet(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Not in a hypervisor partition (HVP=0)"); else { uint32_t cMaxHyperLeaf = 0; uint32_t uEbx = 0; uint32_t uEcx = 0; uint32_t uEdx = 0; ASMCpuIdExSlow(0x40000000, 0, 0, 0, &cMaxHyperLeaf, &uEbx, &uEcx, &uEdx); if (!ASMIsValidHypervisorRange(cMaxHyperLeaf)) rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Invalid hypervisor CPUID range (%#x %#x %#x %#x)", cMaxHyperLeaf, uEbx, uEcx, uEdx); else if ( uEbx != UINT32_C(0x7263694d) /* Micr */ || uEcx != UINT32_C(0x666f736f) /* osof */ || uEdx != UINT32_C(0x76482074) /* t Hv */) rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Not Hyper-V CPUID signature: %#x %#x %#x (expected %#x %#x %#x)", uEbx, uEcx, uEdx, UINT32_C(0x7263694d), UINT32_C(0x666f736f), UINT32_C(0x76482074)); else if (cMaxHyperLeaf < UINT32_C(0x40000005)) rc = RTErrInfoSetF(pErrInfo, VERR_NEM_NOT_AVAILABLE, "Too narrow hypervisor CPUID range (%#x)", cMaxHyperLeaf); else { /** @todo would be great if we could recognize a root partition from the * CPUID info, but I currently don't dare do that. */ /* * Now try load the DLLs and resolve the APIs. */ static const char * const s_pszDllPrefixes[] = { "WinHvPlatform.dll!", "vid.dll!" }; RTLDRMOD ahMods[2] = { NIL_RTLDRMOD, NIL_RTLDRMOD }; rc = RTLdrLoadSystem("vid.dll", true /*fNoUnload*/, &ahMods[1]); if (RT_SUCCESS(rc)) { rc = RTLdrLoadSystem("WinHvPlatform.dll", true /*fNoUnload*/, &ahMods[0]); if (RT_SUCCESS(rc)) { for (unsigned i = 0; i < RT_ELEMENTS(g_aImports); i++) { int rc2 = RTLdrGetSymbol(ahMods[g_aImports[i].idxDll], g_aImports[i].pszName, (void **)g_aImports[i].ppfn); if (RT_FAILURE(rc2)) { *g_aImports[i].ppfn = NULL; LogRel(("NEM: %s: Failed to import %s%s: %Rrc", g_aImports[i].fOptional ? "info" : fForced ? "fatal" : "error", s_pszDllPrefixes[g_aImports[i].idxDll], g_aImports[i].pszName, rc2)); if (!g_aImports[i].fOptional) { if (RTErrInfoIsSet(pErrInfo)) RTErrInfoAddF(pErrInfo, rc2, ", %s%s", s_pszDllPrefixes[g_aImports[i].idxDll], g_aImports[i].pszName); else rc = RTErrInfoSetF(pErrInfo, rc2, "Failed to import: %s%s", s_pszDllPrefixes[g_aImports[i].idxDll], g_aImports[i].pszName); Assert(RT_FAILURE(rc)); } } } if (RT_SUCCESS(rc)) { Assert(!RTErrInfoIsSet(pErrInfo)); /* * Check the capabilties of the hypervisor, starting with whether it's present. */ rc = nemR3WinInitCheckCapabilities(pVM, pErrInfo); if (RT_SUCCESS(rc)) { /* * Create and initialize a partition. */ rc = nemR3WinInitCreatePartition(pVM, pErrInfo); if (RT_SUCCESS(rc)) { pVM->fNEMActive = true; Log(("NEM: Marked active!\n")); } } } RTLdrClose(ahMods[0]); } else rc = RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Failed to load API DLL 'WinHvPlatform.dll': %Rrc", rc); RTLdrClose(ahMods[1]); } else rc = RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "Failed to load API DLL 'vid.dll': %Rrc", rc); } } } } /* * We only fail if in forced mode, otherwise just log the complaint and return. */ Assert(pVM->fNEMActive || RTErrInfoIsSet(pErrInfo)); if ( (fForced || !fFallback) && !pVM->fNEMActive) return VMSetError(pVM, RT_SUCCESS_NP(rc) ? VERR_NEM_NOT_AVAILABLE : rc, RT_SRC_POS, "%s", pErrInfo->pszMsg); if (RTErrInfoIsSet(pErrInfo)) LogRel(("NEM: Not available: %s\n", pErrInfo->pszMsg)); return VINF_SUCCESS; } /** * This is called after CPUMR3Init is done. * * @returns VBox status code. * @param pVM The VM handle.. */ int nemR3NativeInitAfterCPUM(PVM pVM) { /* * Validate sanity. */ WHV_PARTITION_HANDLE hPartition = pVM->nem.s.hPartition; AssertReturn(hPartition != NULL, VERR_WRONG_ORDER); AssertReturn(!pVM->nem.s.hPartitionDevice, VERR_WRONG_ORDER); AssertReturn(!pVM->nem.s.fCreatedEmts, VERR_WRONG_ORDER); AssertReturn(!pVM->fNEMActive, VERR_WRONG_ORDER); /* * Continue setting up the partition now that we've got most of the CPUID feature stuff. */ /* Not sure if we really need to set the vendor. */ WHV_PARTITION_PROPERTY Property; RT_ZERO(Property); Property.PropertyCode = WHvPartitionPropertyCodeProcessorVendor; Property.ProcessorVendor = pVM->nem.s.enmCpuVendor == CPUMCPUVENDOR_AMD ? WHvProcessorVendorAmd : WHvProcessorVendorIntel; HRESULT hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property)); if (FAILED(hrc)) return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Failed to set WHvPartitionPropertyCodeProcessorVendor to %u: %Rhrc", Property.ProcessorVendor, hrc); /* Not sure if we really need to set the cache line flush size. */ RT_ZERO(Property); Property.PropertyCode = WHvPartitionPropertyCodeProcessorClFlushSize; Property.ProcessorClFlushSize = pVM->nem.s.cCacheLineFlushShift; hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property)); if (FAILED(hrc)) return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Failed to set WHvPartitionPropertyCodeProcessorClFlushSize to %u: %Rhrc", pVM->nem.s.cCacheLineFlushShift, hrc); /* * Sync CPU features with CPUM. */ /** @todo sync CPU features with CPUM. */ /* Set the partition property. */ RT_ZERO(Property); Property.PropertyCode = WHvPartitionPropertyCodeProcessorFeatures; Property.ProcessorFeatures.AsUINT64 = pVM->nem.s.uCpuFeatures.u64; hrc = WHvSetPartitionProperty(hPartition, &Property, sizeof(Property)); if (FAILED(hrc)) return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Failed to set WHvPartitionPropertyCodeProcessorFeatures to %'#RX64: %Rhrc", pVM->nem.s.uCpuFeatures.u64, hrc); /* * Set up the partition and create EMTs. * * Seems like this is where the partition is actually instantiated and we get * a handle to it. */ hrc = WHvSetupPartition(hPartition); if (FAILED(hrc)) return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Call to WHvSetupPartition failed: %Rhrc", hrc); /* Get the handle. */ HANDLE hPartitionDevice; __try { hPartitionDevice = ((HANDLE *)hPartition)[1]; } __except(EXCEPTION_EXECUTE_HANDLER) { hrc = GetExceptionCode(); hPartitionDevice = NULL; } if ( hPartitionDevice == NULL || hPartitionDevice == (HANDLE)(intptr_t)-1) return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Failed to get device handle for partition %p: %Rhrc", hPartition, hrc); /** @todo Do a Vid query that uses the handle to check that we've got a * working value. */ pVM->nem.s.hPartitionDevice = hPartitionDevice; /* * Create EMTs. */ VMCPUID iCpu; for (iCpu = 0; iCpu < pVM->cCpus; iCpu++) { hrc = WHvCreateVirtualProcessor(hPartition, iCpu, 0 /*fFlags*/); if (FAILED(hrc)) { while (iCpu-- > 0) { HRESULT hrc2 = WHvDeleteVirtualProcessor(hPartition, iCpu); AssertLogRelMsg(SUCCEEDED(hrc2), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc\n", hPartition, iCpu, hrc2)); } return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS, "Call to WHvSetupPartition failed: %Rhrc", hrc); } } pVM->nem.s.fCreatedEmts = true; LogRel(("NEM: Successfully set up partition (device handle %p)\n", hPartitionDevice)); return VINF_SUCCESS; } int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) { NOREF(pVM); NOREF(enmWhat); return VINF_SUCCESS; } int nemR3NativeTerm(PVM pVM) { /* * Delete the partition. */ WHV_PARTITION_HANDLE hPartition = pVM->nem.s.hPartition; pVM->nem.s.hPartition = NULL; pVM->nem.s.hPartitionDevice = NULL; if (hPartition != NULL) { VMCPUID iCpu = pVM->nem.s.fCreatedEmts ? pVM->cCpus : 0; LogRel(("NEM: Destroying partition %p with its %u VCpus...\n", hPartition, iCpu)); while (iCpu-- > 0) { HRESULT hrc = WHvDeleteVirtualProcessor(hPartition, iCpu); AssertLogRelMsg(SUCCEEDED(hrc), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc\n", hPartition, iCpu, hrc)); } WHvDeletePartition(hPartition); } pVM->nem.s.fCreatedEmts = false; return VINF_SUCCESS; } void nemR3NativeReset(PVM pVM) { NOREF(pVM); } void nemR3NativeResetCpu(PVMCPU pVCpu) { NOREF(pVCpu); }