Geoff Chappell, Software Analyst
The primary means of identifying a modern x86 or x64 processor is the cpuid instruction. This was developed in the early 1990s for what was then Intel’s new Pentium processor but it also exists in some models of Intel’s 80486 processor and of 80486 look-alikes from other manufacturers.
The 64-bit kernel, knowing that it executes only on the relatively modern processors that have the 64-bit instruction set, has the luxury of taking the cpuid instruction’s existence as granted. The 32-bit kernel, in contrast, was developed before the Pentium’s release and had to run on older processors—including, for the first few years, on the 80386. Even though the 80486 has been unable to run new Windows versions—not formally but for all practical effect—since Windows XP made the cmpxchg8b instruction essential, the 32-bit kernel only recently stopped defending against being run on a processor that does not have cpuid.
Up to and including version 6.2, the 32-bit kernel regards the cpuid instruction as unimplemented if either:
A set ID bit in the eflags might ideally guarantee that the processor offers the cpuid instruction for execution, but this ideal is frustrated because although earlier processors have the bit as reserved, they cannot all be relied on to have implemented reserved as clear. What apparently is reliable is that if the bit is stuck on either 0 or 1, then it was not intended as the ID bit, and so the processor certainly has no cpuid instruction and is presumably an 80486 or even an 80386 (to be sorted out by methods of CPU Identification Before CPUID).
The converse, however, is not true—or was not thought so by Microsoft’s programmers when revising Windows NT 3.1 for release in 1993. Intel does seem to have started with the intention that it should be true. In what looks to be the first Pentium Processor User’s Manual, Volume 3: Architecture and Programming Manual from 1993 (order number 241430-001), Intel repeats several times with slight variations that “The ability to set and clear this bit indicates whether the processor supports the CPUID instruction” and even spells out that it’s meant as an implication: “If software is able to change the value of the ID bit, then the processor supports the CPUID instruction.” Yet although the earliest Intel Processor Identification With the CPUID Instruction that I have yet found (order number 241618-003, dated October 1994) continues the seeming implication that “If software can change the value of this flag, the CPUID instruction is available”, a change of title to Intel Processor Identification and the CPUID Instruction (order number 241618-005, dated December 1996) throws in a little mystery with a footnote which might easily be read not as clarifying when to expect that the flag can be changed but as warning that the implication itself holds “Only in some Intel486 and succeeding processors.” If you suspect that the ID bit can be changed yet cpuid be not certainly supported, then there is nothing to do except to try executing it having arranged that you can recover if the processor objects.
It may never be known whether Microsoft’s programmers were being overly cautious or actually were caught by a processor (not necessarily made by Intel) that has a changeable ID bit but no cpuid. What can be known is that their defence against this possibility was among the last additions to their cpuid detection code before release. The Internet is dark and full of terrors, but in its shadows are junkyards of things whose public disclosure would once have been unlawful (and may still be) but whose survival is necessary if our technology’s early history is to be accurately preserved. From pre-release builds of Windows NT 3.1 that are easily found in an afternoon’s search for relics, it’s evident that the defence against cpuid triggering an Invalid Opcode exception (despite having established that the ID bit is changeable) was added between 22nd April 1993 for build 3.10.428.1 and 24th July 1993 for the publicly released build 3.10.5098.1.
Even earlier pre-release builds of version 3.10 suggest very strongly that the cpuid instruction’s early design, if not also its implementation, was very different from what everyone has coded for since 1993. It didn’t have the eax register as an implied operand and it plausibly had no other reason for existence than to load a processor identification signature—the CPU ID, if you like—into eax. But the several stories in this history I leave to the separate pages on eax from cpuid leaf 0 and eax from cpuid leaf 1. Here, I proceed only with introducing the instruction as tested for and used by the released versions of Windows.
The point to trying the instruction with eax set to zero is that the instruction is designed for extensible functionality. Input in eax selects what Intel variously terms a function or a leaf or even a leaf function. Some leaves take additional input in ecx. All may use any or all of eax, ebx, ecx and edx for output. The leaves naturally start at zero. The extensibility is that leaf 0 tells which other leaves are supported. Though the instruction will execute with an unsupported eax as input, the programmer arguably does better to treat such execution as undefined. The way to learn what input is defined is to execute cpuid leaf 0: what it produces in eax is the maximum valid leaf for input.
Or so was the intention, at least as far as Windows knew as long ago as 1993. Over time, the possible input for eax has been divided into ranges. This started not with Intel but with AMD. Perhaps so that AMD could describe features of its own independently of Intel’s (future) descriptions, AMD had its cpuid distinguish standard leaves from a separate set of extended leaves that start at 0x80000000. That “the functions are divided into two types” is asserted at least as far back as AMD Processor Recognition (publication 20734 revision D, dated January 1997). Perhaps inevitably, AMD presents the low-numbered leaves for “software to access information common to all x86 processors.” Also inevitably, Intel wouldn’t have cared to play along with this suggestion that the x86 instruction was any sort of standard for general use or imitation, and so when its own processors implemented the extended functions it talked of the low-numbered leaves not as standard but as basic.
Other imitators of Intel’s x86 instruction set have since defined their own ranges and hypervisors have got into the game too. Windows knows of only three ranges, starting at zero, 0x40000000 and 0x80000000. What’s common to each is that executing a range’s first leaf produces the range’s maximum leaf number in eax.
Not until version 5.0 does the Windows kernel use any cpuid leaf other than 0 and 1. The kernel never uses any basic leaf other than 0 and 1 without checking that it is within the range reported by leaf 0. Versions 3.50 to 6.2 of the 32-bit kernel even check this for leaf 1 (with one exception, only in versions 4.0 and 5.0, and it is unusually noteworthy because it is much of the reason that most editions of Windows NT 4.0 crash when installed on modern processors).
|CPUID Leaf||Kernel Versions|
|0x00000002||5.0 and higher (x86 only)|
|0x00000004||6.0 and higher (x86);
|0x00000006||6.1 and higher|
|0x00000007||6.2 and higher|
|0x0000000B||10.0 and higher (x86);
6.1 and higher (x64)
|0x0000000D||6.1 and higher|
|0x00000010||1803 and higher (x64 only)|
|0x00000012||1511 and higher|
|0x00000018||1803 and higher|
Remember that “all” begins with version 3.10 for 32-bit Windows but with the version 5.2 from Windows Server 2003 SP1 for 64-bit Windows. See that two of the basic leaves are put to use for 64-bit Windows earlier than for 32-bit Windows, one much earlier.
Be aware also that these lists are only of use by the kernel. Other use of cpuid, including by the HAL, is outside this note’s present scope.
From the Revision History in Intel® Processor Identification and the CPUID Instruction (Application Note 485, apparently no longer available online from Intel in any revision), extended leaves were first documented for Intel’s processors in June 2001. This is not certainly when they were first implemented by Intel and it’s certainly not when they were first implemented by AMD. The Windows 2000 kernel tries leaf 0x80000000 no matter what the vendor except for AMD processors before family 5.
In these early days, of course, support for the extended leaves could not be assumed. Executing cpuid with 0x80000000 in eax might load just about anything into eax, most likely whatever happens to be the usual output for the processor’s highest basic leaf. That eax would come back with the maximum leaf number for the extended leaves was at best uncertain. To this day, the 32-bit kernel imposes a sanity check. The maximum for the extended leaves is disbelieved if it’s not between 0x80000000 and 0x800000FF inclusive.
|CPUID Leaf||Kernel Versions|
|0x80000000||5.0 and higher|
|0x80000001||5.0 and higher|
|5.0 and higher|
|0x80000005||5.1 and higher|
|0x80000006||5.1 and higher|
|0x80000008||6.0 and higher (x86);
|0x8000000A||6.2 and higher|
|0x8000001D||6.2 and higher|
|0x8000001E||6.2 and higher|
See again that 64-bit Windows runs ahead of 32-bit Windows, though in this case just by one version for one leaf.
Starting with Windows Vista, both the 32-bit and 64-bit kernels recognise a third range of cpuid leaves starting at 0x40000000. If only to begin with—it’s not yet verified to be still true—the kernel does not try even the first of these leaves unless bit 31 is set in ecx from cpuid leaf 1. Both Intel and AMD long documented this feature flag as reserved. Intel still does. It should ordinarily be clear. Microsoft has its hypervisor re-implement cpuid so that this feature flag is set. Other hypervisors do too (and perhaps did first, but this is presently not within this note’s scope). If the kernel finds this feature flag is set, it executes leaf 0x40000000 to determine the maximum leaf for this range and to identify the hypervisor.
|CPUID Leaf||Kernel Versions|
|0x40000000||6.0 and higher|
|0x40000001||6.0 and higher|
|0x40000002||6.2 and higher|
|0x40000003||6.1 and higher|
|0x40000004||6.0 and higher|
|0x40000005||6.2 and higher|
|0x40000006||10.0 and higher|
|0x40000007||10.0 and higher|
|0x40000008||10.0 and higher (x64 only)|
|0x40000082||6.1 to 6.3|
The versions shown above are for the kernel’s known use of each leaf. Other pages at this website, notably for the HV_CPUID_RESULT structure and the HV_CPUID_FUNCTION enumeration show the versions for which Microsoft defines these leaves for programmers. Such definition has mostly been just for Microsoft’s own programmers. Whatever one makes of the Microsoft Open Specification Promise, the practical reality is that Microsoft’s Hypervisor Top-Level Functional Specification and other documentation of its hypervisor’s cpuid interface, if only as published online, has often been years out of date and leaves no small amount to further study.
Leaf 0x40000082 is, of course, outside the contiguous range of Microsoft’s known definitions of cpuid leaves for its hypervisor. Yet it is Microsoft’s. Both the 32-bit and 64-bit kernels execute it only if both: bit 31 is set in ecx from leaf 1; and leaf 0x40000000 reports that Microsoft is the hypervisor vendor. What leaf 0x40000000 reports as the maximum leaf in the hypervisor range is immaterial for the kernel’s execution of leaf 0x40000082.