Geoff Chappell - Software Analyst
The HV_PARTITION_PRIVILEGE_MASK (formally _HV_PARTITION_PRIVILEGE_MASK) collects the flags that a Microsoft-compatible hypervisor’s cpuid leaf 0x40000003 produces in the eax and ebx registers.
The HV_PARTITION_PRIVILEGE_MASK is defined for version 6.0 and higher.
Being only part of the output from the corresponding cpuid leaf, the HV_PARTITION_PRIVILEGE_MASK has all along been nested in a structure that has the whole output. Specifically, it is the type of the PartitionPrivileges in:
The distinctiveness of the partition privileges in eax and ebx has long been important enough to Microsoft’s programmers that they have an inline routine that retrieves just the partition privileges rather than the whole feature set that cpuid leaf 0x40000003 produces in eax, ebx, ecx and edx. Public symbol files for the kernel, even as recently as for the 2004 edition of Windows 10, confirm that this inline routine is
VOID HviGetPartitionPrivileges (HV_PARTITION_PRIVILEGE_MASK *);
The HV_PARTITION_PRIVILEGE_MASK 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, 12th November 2016, produces just five results for the name, none from Microsoft.
A C-language definition of HV_PARTITION_PRIVILEGE_MASK is presented in the text of the Hypervisor Top-Level Functional Specification (TLFS). It is adapted, not simply reproduced, from what Microsoft apparently uses in its own programming, but it is helpfully followed by a table of descriptions that are more than just an English rewording of the symbolic names.
That said, the Specification that is offered at Microsoft’s website today, 12th November 2016, as the most recent version is a PDF that is three years old (version 4.0b, dated August 2013). Flags that were added for Windows 10 were left undocumented in the Specification until version 5.0a, dated February 2017. By then, one flag that had been documented was marked instead as Reserved. 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 ahead of the documentation that Microsoft opens for general use.
The HV_PARTITION_PRIVILEGE_MASK is 8 bytes in both 32-bit and 64-bit Windows. Formally, the HV_PARTITION_PRIVILEGE_MASK is a union of an 8-byte integer and a structure of 8-byte bit fields. The integer eases the accessing of all flags together:
union { UINT64 AsUINT64; struct { /* changing bit fields, see below */ }; };
Of most interest, of course, are the bit fields. These change as Microsoft’s hypervisor becomes ever more capable. Masks and definitions are from: type information in a statically linked library named CLFSMGMT.LIB for (32-bit) Windows Vista; the published C-language definition for Windows 7; and type information in symbol files for URLMON.DLL in Windows 8 and for the kernel in Windows 8.1 and higher.
Mask | Definition | Versions |
---|---|---|
0x00000000`00000001 |
UINT64 AccessVpRunTimeMsr : 1; |
6.0 to 6.3 |
UINT64 AccessVpRunTimeReg : 1; |
10.0 and higher | |
0x00000000`00000002 |
UINT64 AccessPartitionReferenceCounter : 1; |
6.1 and higher |
0x00000000`00000002 (6.0); 0x00000000`00000004 |
UINT64 AccessSynicMsrs : 1; |
6.0 to 6.3 |
UINT64 AccessSynicRegs : 1; |
10.0 and higher | |
0x00000000`00000008 |
UINT64 AccessSyntheticTimerMsrs : 1; |
6.1 to 6.3 |
UINT64 AccessSyntheticTimerRegs : 1; |
10.0 and higher | |
0x00000000`00000010 |
UINT64 AccessApicMsrs : 1; |
6.1 to 6.3 |
UINT64 AccessIntrCtrlRegs : 1; |
10.0 and higher | |
0x00000000`00000020 |
UINT64 AccessHypercallMsrs : 1; |
6.1 and higher |
0x00000000`00000040 |
UINT64 AccessVpIndex : 1; |
6.1 and higher |
0x00000000`00000080 |
UINT64 AccessResetMsr : 1; |
6.1 to 6.3 |
UINT64 AccessResetReg : 1; |
10.0 and higher | |
0x00000000`00000100 |
UINT64 AccessStatsMsr : 1; |
6.1 to 6.3 |
UINT64 AccessStatsReg : 1; |
10.0 and higher | |
0x00000000`00000200 |
UINT64 AccessPartitionReferenceTsc : 1; |
6.1 and higher |
0x00000000`00000400 |
UINT64 AccessGuestIdleMsr : 1; |
6.1 to 6.3 |
UINT64 AccessGuestIdleReg : 1; |
10.0 and higher | |
0x00000000`00000800 |
UINT64 AccessFrequencyMsrs : 1; |
6.2 to 6.3 |
UINT64 AccessFrequencyRegs : 1; |
10.0 and higher | |
0x00000000`00001000 |
UINT64 AccessDebugMsrs : 1; |
6.2 to 6.3 |
UINT64 AccessDebugRegs : 1; |
10.0 and higher | |
0x00000000`00002000 |
UINT64 AccessReenlightenmentControls : 1; |
1607 and higher |
0x00000000`00004000 |
UINT64 AccessRootSchedulerReg : 1; |
1709 and higher |
0x00000000`00008000 |
UINT64 AccessTscInvariantControls : 1; |
2004 and higher |
UINT64 Reserved1 : 30; |
6.0 only | |
UINT64 Reserved1 : 21; |
6.1 only | |
UINT64 Reserved1 : 19; |
6.3 to 1511 | |
UINT64 Reserved1 : 18; |
1607 to 1703 | |
UINT64 Reserved1 : 17; |
1709 to 1903 | |
UINT64 Reserved1 : 16; |
2004 and higher | |
0x00000001`00000000 |
UINT64 CreatePartitions : 1; |
6.0 and higher |
0x00000002`00000000 |
UINT64 AccessPartitionId : 1; |
6.0 and higher |
0x00000004`00000000 |
UINT64 AccessMemoryPool : 1; |
6.0 and higher |
0x00000008`00000000 |
UINT64 AdjustMessageBuffers : 1; |
6.0 and higher |
0x00000010`00000000 |
UINT64 PostMessages : 1; |
6.0 and higher |
0x00000020`00000000 |
UINT64 SignalEvents : 1; |
6.0 and higher |
0x00000040`00000000 |
UINT64 CreatePort : 1; |
6.0 and higher |
0x00000080`00000000 |
UINT64 ConnectPort : 1; |
6.0 and higher |
0x00000100`00000000 (6.0) |
UINT64 IteratePhysicalHardware : 1; |
6.0 only |
0x00000200`00000000 (6.0); 0x00000100`00000000 |
UINT64 AccessStats : 1; |
6.0 and higher |
UINT64 Reserved2 : 22; |
6.0 only | |
UINT64 Reserved2 : 2; |
6.1 and higher | |
0x00000800`00000000 |
UINT64 Debugging : 1; |
6.1 and higher |
0x00001000`00000000 |
UINT64 CpuManagement : 1; |
6.1 and higher |
0x00002000`00000000 |
UINT64 ConfigureProfiler : 1; |
6.1 and higher |
0x00004000`00000000 |
UINT64 EnableExpandedStackwalking : 1; |
6.3 only |
UINT64 AccessVpExitTracing : 1; |
10.0 and higher | |
0x00008000`00000000 |
UINT64 EnableExtendedGvaRangesForFlushVirtualAddressList : 1; |
10.0 and higher |
0x00010000`00000000 |
UINT64 AccessVsm : 1; |
10.0 and higher |
0x00020000`00000000 |
UINT64 AccessVpRegisters : 1; |
10.0 and higher |
UINT64 UnusedBit : 1; |
10.0 and higher | |
0x00080000`00000000 |
UINT64 FastHypercallOutput : 1; |
10.0 and higher |
0x00100000`00000000 |
UINT64 EnableExtendedHypercalls : 1; |
10.0 and higher |
0x00200000`00000000 |
UINT64 StartVirtualProcessor : 1; |
10.0 and higher |
0x00400000`00000000 |
UINT64 Isolation : 1; |
1809 and higher |
UINT64 Reserved3 : 18; |
6.1 to 6.2 | |
UINT64 Reserved3 : 17; |
6.3 only | |
UINT64 Reserved3 : 10; |
10.0 to 1803 | |
UINT64 Reserved3 : 9; |
1809 and higher |
Comments in the C-language definition from Windows 7 make clear that there has all the while been a logic to which of the 8-byte bit fields are in which dword. The low dword, i.e., the output in eax, provides for “Access to virtual MSRs” but the output in ebx is for “Access to hypercalls”.
Some abbreviations are perhaps not well-known: Vp is Virtual processor; Synic is Synthetic Interrupt Controller.
A CLFSMGMT.LIB that is targeted for Windows 7 does not define AccessGuestIdleMsr or ConfigureProfiler. They 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).
Though ConfigureProfiler dates from Windows 7 and remains defined according to the symbol files, the TLFS version 4.0b dated August 2013 has it in the C-language definition but not among the plain-text descriptions, and all later TLFS versions have it merely as Reserved even in the C-language definition.
The original Windows 10 renamed most of the bits for eax, mostly on the pattern of changing Msr to Reg, but added none. For ebx, by contrast, one flag was repurposed and six were added. The EnableExpandedStackwalking flag had anyway been added only recently (for Windows 8.1). The TLFS version 4.0b had it too in the C-language definition but not among the plain-text descriptions. In Windows 10, it is instead AccessVpExitTracing and again all later TLFS versions denote it as Reserved in the C-language definition.
Of the additions for the original Windows 10, EnableExtendedGvaRangesForFlushVirtualAddressList and FastHypercallOutput similarly have their names replaced by Reserved in the C-language definition and AccessVpRegisters is allowed in the definiton but has no descriptive text even as late as the TLFS version 5.0c dated July 2018. That version is too early for Isolation, which isn’t in the symbol files until the second half-yearly release for 2018, but it is omitted entirely even from the TLFS version 6.0b dated February 2020 which is the latest I have found at Microsoft’s website today, 15th November 2022.
For eax, the AccessReenlightenmentControls flag is in the symbol files for the update from mid-2016 but gets no mention in the TLFS version 5.0a dated February 2017. It scrapes into the C-language definition in the TLFS version 5.0c dated July 2018. It got a plain-text description in time for the TLFS version 6.0b dated February 2020. Even this version says nothing of AccessRootSchedulerReg, which has been in the symbol files since late 2017. The AccessTscInvariantControls flag is new for the first half-yearly release of 2020: when might documentation be expected?