Geoff Chappell - Software Analyst
The HV_CPUID_RESULT union (formally _HV_CPUID_RESULT) collects the various sorts of flags and other data that a Microsoft-compatible hypervisor’s implementation of the cpuid instruction produces in the eax, ebx, ecx and edx registers.
The HV_CPUID_RESULT is documented in the Windows Driver Kit (WDK) for Windows 7, which was the last to have documentation included. This kit also provides a C-language definition in the HVGDK.H header file. Except that documentation continued to a separately downloadable package for merging into Visual Studio 2012, later kits have neither the documentation nor the header. A search through Google today, 13th November 2016, produces just five results for the name, none from Microsoft.
If the HV_CPUID_RESULT has become undocumented, it may be that Microsoft regards the structure as no more than a convenience for Microsoft’s own programming in the loader and kernel, if not in the hypervisor itself. The HV_CPUID_RESULT just repackages material that Microsoft document elsewhere.
The obvious, if not the only, candidate for elsewhere is the Hypervisor Top-Level Functional Specification (TLFS) which today, 12th November 2016, offers as its most recent version a PDF that is three years old. The reality behind the superficially fine words of the Microsoft Open Specification Promise is that the Hyper-V implementation, and presumably Microsoft’s use of it to Microsoft’s advantage for Microsoft’s purposes, runs years ahead of the documentation that Microsoft opens for general use.
As for the HV_CPUID_RESULT as Microsoft’s particular packaging of its hypervisor’s output from cpuid leaves, disclosure since the WDK for Windows 7 is conspicuously thin. The practical equivalent of a C-language definition is published as type information in symbol files, but in the downloadable packages of public symbols for 32-bit and 64-bit Windows 10, type information for HV_CPUID_RESULT appears only in the symbol files for URLMON.DLL. This is a user-mode DLL that originated as a component of Internet Explorer (and still follows Internet Explorer’s version numbering). Well might you wonder what this DLL has to do with the hypervisor such that its symbol files have type information for this structure but the kernel’s don’t! Symbol files for URLMON.DLL starting with Windows 8, both in packages of Windows symbols and when downloaded from Microsoft’s public symbol server, happen to be private symbols. This surely was an oversight, but however this disclosure came about, Microsoft stopped it for Version 1803. That URLMON.DLL knows of the HV_CPUID_RESULT is because some URLMON.DLL source file has a #include for a header named hvgdk_mini.h. Microsoft is known to use his header for its own programming of the Windows kernel but is not known to have ever made it available for use by programmers in general (let alone by the programmers of competing web browsers).
Type information for the HV_CPUID_RESULT also turns out to have been hiding in the relatively plain sight of two statically linked libraries. One, named CLFSMGMT.LIB, is among the many that are supplied with the Software Development Kit (SDK) for user-mode programming. Type information in this library has the special value of confirming that the HV_CPUID_RESULT was defined as long ago as Windows Vista, i.e., before Microsoft published HVGDK.H for Windows 7. Against this is that type information for HV_CPUID_RESULT is not in all versions of the library. It doesn’t start in the 64-bit library until WIndows 8 and it drops out for both 32-bit and 64-bit in Version 2004. Beware anyway that type information in statically linked libraries has less forensic value (than in the operating system’s symbol files) because it does not always match the operating system that the library is supposedly intended for.
The other static library that has type information for the HV_CPUID_RESULT is more obviously related to hypervisors. It is named KDHV.LIB. Microsoft distributes it among the programming samples in the Debugging Tools for Windows. It has type information for the HV_CPUID_RESULT for all versions of Windows 10 at least to the 2004 release.
The HV_CPUID_RESULT is 0x10 bytes in both 32-bit and 64-bit Windows. Offsets, names and types in the tabulation below are from (in roughly decreasing order of forensic quality): the C-language definition in the WDK for Windows 7; type information in symbol files for the kernel in Windows 8.1 (only) and for URLMON.DLL in Windows 8 through to the 1709 release of Windows 10; and type information in most versions of CLFSMGMT.LIB from Windows Vista through to the 1903 release of Windows 10 and in KDHV.LIB from the original Windows 10 and higher.
The HV_CPUID_RESULT is a union whose many branches allow for different interpretations of the cpuid output, both in general and for specific cpuid leaves:
The first member of the union, originally, but swapped with the second in Version 1703, is an unnamed structure for access to each dword that cpuid produces in its four output registers:
Offset | Definition |
---|---|
0x00 |
UINT32 Eax; |
0x04 |
UINT32 Ebx; |
0x08 |
UINT32 Ecx; |
0x0C |
UINT32 Edx; |
A second member, originally, but then the first after the rearrangement for Version 1703, provides for similar access but as an array:
UINT32 AsUINT32 [4];
A third member, named VersionAndFeatures, is defined inline as an unnamed structure. It picks out two items that are produced by the Intel-defined cpuid leaf 1 but which have particular interest to a hypervisor.
Offset | Definition | Versions |
---|---|---|
0x00 |
UINT32 ReservedEax; |
6.0 and higher |
0x04 |
UINT32 ReservedEbx; |
6.0 only |
UINT32 ReservedEbx : 24; UINT32 InitialApicId : 8; // 0xFF000000 |
6.1 and higher | |
0x08 |
UINT32 ReservedEcx : 31; UINT32 HypervisorPresent : 1; // 0x80000000 |
6.0 and higher |
0x0C |
UINT32 ReservedEdx; |
6.0 and higher |
Type information in CLFSMGMT.LIB has it that InitialApicId is not defined before Windows 8, no matter that the published HVGDK.H shows it for Windows 7. It may have been defined only shortly before the final build for Windows 7: the library in the Windows 7 SDK was built a month before the Windows 7 kernel (11th June 2009 versus 14th July).
The HypervisorPresent flag is described in Intel’s cpuid documentation as “Not Used” and “Always returns 0”. If this bit is set, the kernel infers that some sort of hypervisor is present such that cpuid supports the vendor-neutral leaves 0x40000000 and 0x40000001. The first tells how far this sequence of hypervisor leaves continues, the second whether the higher-numbered leaves conform to Microsoft’s interface.
The remaining members are specialised for input values of eax beginning at 0x40000000 as expected by a Microsoft-compatible hypervisor. They were at first arranged in increasing order of this input, but then Version 1703 moved two to the front. Another irregularity that is yet to be explained is that although Windows 10 inserts MsHvPlatformCapabilities among these members that plainly correspond to CPU leaves, the kernel is not known to query any cpuid leaf in the 0x40000000 series for anything like the flags that are defined for that member. (Present thinking is that MsHvPlatformCapabilities is instead for cpuid leaf 0x40000082 in an undocumented second set of hypervisor leaves at 0x40000080.)
Before version 6.3, each member other than MsHvVersion is defined inline as an unnamed structure. These inline definitions are not reproduced below. To find what these early versions define for a leaf, refer to the separate notes on the structure that corresponds to the leaf in later versions.
Leaf | Definition | Versions |
---|---|---|
HV_X64_PLATFORM_CAPABILITIES MsHvPlatformCapabilities; |
1703 and higher | |
0x4000000A |
HV_HYPERVISOR_NESTED_VIRT_FEATURES MsHvNestedVirtFeatures; |
1703 and higher |
0x40000000 |
struct { /* two members, follow link */ } HvVendorAndMaxFunction; |
6.0 to 6.2 |
HV_VENDOR_AND_MAX_FUNCTION HvVendorAndMaxFunction; |
6.3 and higher | |
0x40000001 |
struct { /* four members, follow link */ } HvInterface; |
6.0 to 6.2 |
HV_HYPERVISOR_INTERFACE_INFO HvInterface; |
6.3 and higher | |
0x40000002 |
HV_HYPERVISOR_VERSION_INFO MsHvVersion; |
6.0 and higher |
0x40000003 |
struct { /* changing members, follow link */ } MsHvFeatures; |
6.0 to 6.2 |
HV_HYPERVISOR_FEATURES MsHvFeatures; |
6.3 only | |
HV_X64_HYPERVISOR_FEATURES MsHvFeatures; |
10.0 and higher | |
0x40000004 |
struct { /* changing members, follow link */ } MsHvEnlightenmentInformation; |
6.0 to 6.2 |
HV_ENLIGHTENMENT_INFORMATION MsHvEnlightenmentInformation; |
6.3 only | |
HV_X64_ENLIGHTENMENT_INFORMATION MsHvEnlightenmentInformation; |
10.0 and higher | |
0x40000005 |
struct { /* changing members, follow link */ } MsHvImplementationLimits; |
6.0 to 6.2 |
HV_IMPLEMENTATION_LIMITS MsHvImplementationLimits; |
6.3 and higher | |
0x40000006 |
struct { /* changing members, follow link */ } MsHvHardwareFeatures; |
6.2 only |
HV_HYPERVISOR_HARDWARE_FEATURES MsHvHardwareFeatures; |
6.3 only | |
HV_X64_HYPERVISOR_HARDWARE_FEATURES MsHvHardwareFeatures; |
10.0 and higher | |
HV_X64_PLATFORM_CAPABILITIES MsHvPlatformCapabilities; |
10.0 to 1607 | |
0x40000007 |
HV_X64_HYPERVISOR_CPU_MANAGEMENT_FEATURES MsHvCpuManagementFeatures; |
10.0 and higher |
0x40000008 |
HV_HYPERVISOR_SVM_FEATURES MsHvSvmFeatures; |
10.0 and higher |
0x4000000A |
HV_HYPERVISOR_NESTED_VIRT_FEATURES MsHvNestedVirtFeatures; |
1511 to 1607 |
0x4000000B |
HV_HYPERVISOR_IPT_FEATURES MsHvIptFeatures; |
1903 and higher |
Note that the members’ names have prefixes that differentiate cpuid leaves 0x40000000 and 0x40000001 as generic and subsequent leaves as particular to Microsoft hypervisors.
Incidentally, symbol files for the Windows 8.1 kernel reveal that the HV_CPUID_RESULT is used for two internal routines, apparently only ever inlined (at least in some instances into other routines that are defined in headers):
VOID HvipClearCpuIdResult (HV_CPUID_RESULT *); VOID HvipCpuId (ULONG, HV_CPUID_RESULT *);