Geoff Chappell - Software Analyst
Processors supported by the x86 builds of the Windows kernel are identified by a combination of family, model and stepping numbers (in order of decreasing significance). The Windows kernel executes an identification routine for each processor.
The primary means of identifying a processor is of course the CPUID instruction, which was introduced with some models of the 80486 processor. The kernel regards the CPUID instruction as unimplemented by the processor if either:
The CPUID instruction takes a function number in the EAX register. Execution with zero in EAX produces in eax the maximum supported function number. (This article makes a convention of using upper and lower case to indicate whether a register is used for input or output, respectively.) The family, model and stepping are expected as results of executing function 1. If the execution of function 0 did not return at least 1 in eax, then although the CPUID instruction is implemented, it is not the slightest bit useful to the kernel and is dismissed as unsupported, such that the processor must be an 80386 or 80486.
Having confirmed that the CPUID instruction is usable for obtaining a family, model and stepping, the kernel re-executes functions 0 and 1.
Execution with 0 in EAX returns something in eax, as noted above, but it also returns a vendor identifier in other registers. Specifically, if the values returned in ebx, edx and ecx are stored at successive memory locations, they read as a string of single-byte characters. The Windows Vista kernel knows the following vendor strings, at various times, for various purposes:
but for the basic matter of discovering the family, model and stepping, only “GenuineIntel” is given any significance.
When CPUID is executed with 1 in EAX, it returns version information (also called a processor signature) in eax, in which the family, model and stepping are bit fields:
| bits 0 to 3 | stepping |
| bits 4 to 7 | model |
| bits 8 to 11 | family |
| bits 16 to 19 | extended model |
| bits 20 to 27 | extended family |
The family and model were originally 4-bit numbers, represented directly by corresponding 4-bit fields. Recent processors provide for 8-bit family and model numbers, composed from two fields. The 8-bit family and model are indicated when the 4-bit family field is full, i.e., contains 15. If the vendor is GenuineIntel, then the 8-bit model is also indicated when the 4-bit family field contains 6.
The 8-bit family is formed by adding the 4-bit family field to the 8-bit extended family field. The 8-bit model is formed by taking the 4-bit model field for the low bits and the 4-bit extended model field for the high bits.
Before the version 4.0 from Windows NT 4.0 SP6, the family field is taken as 3 bits, not 4, and the two extended fields are ignored.
In one notable case for versions 3.51 and 4.0, again not including the version 4.0 from Windows NT 4.0 SP6, the kernel does not ask for the processor signature from CPUID but assigns the processor to family 5, model 0 and stepping 0, and mostly continues as if CPUID is unavailable. This case applies if executing CPUID with zero in EAX returns with eax greater than 3. Whether this was intended as a defence against implausibility when CPUID was a relatively new and perhaps unstable facility, it at least risked a future compatibility problem, if not for Microsoft then certainly for others. Later processors from Intel can be configured to limit the returned maximum to 3 by setting bit 22 in the machine-specific register IA32_MISC_ENABLE (0x01A0). Intel’s literature suggests that the “BIOS should contain a setup question that allows users to specify when the installed OS does not support CPUID functions greater than 3.” It is not known whether these early versions of Windows NT were the one and only cause, but if you wanted to install any of them on any computer manufactured in at least the last 10 years, you might do well to look for this BIOS option.
Version 5.1 recognises the two extended fields when the 4-bit family field holds 15. Coding for Intel’s use of the extended model when the 4-bit family is 6 begins with the version 5.1 from Windows XP SP2 and the version 5.2 from Windows Server 2003 SP1.
The processor signature predates the CPUID instruction, but only as the initial value of the EDX register immediately after the processor is reset. Many, if not all, machines with these processors have BIOS support through which this value can be retrieved on a running machine. This magic involves resetting the processor without losing memory, having configured the BIOS not to reinitialise as if from a reboot but to resume execution at a given address, such that the EDX register is preseved from the reset. This is perhaps a bit much for the kernel, if not for anyone. When faced with a processor that does not have a CPUID instruction to report the processor signature, the kernel invents family, model and stepping numbers from the results of various tests. How closely these correspond with the actual processor signature (or with information from Intel) is not known.
Of course, with the Pentium being a minimal requirement since Windows XP (because of CMPXCHG8B support), the tests that the Windows kernel uses for identifying processors that do not support CPUID are surely of historical interest only. The relevant code is unchanged, byte for byte, since at least version 3.51—even as late as Windows 7.
If the AC bit (0x00040000) in the EFLAGS register can be changed, then the processor is deemed to be an 80486 (family 4). Otherwise, it is an 80386 (family 3).
Some, perhaps even many, 80486 processors do implement the CPUID instruction. For those that do not, the kernel tests successively for what seem mostly to be defects. Any 80486 that does not have the CPUID instruction but passes all the tests is said to be model 3.
| Family | Model | Stepping | Test |
|---|---|---|---|
| 4 | 0 | 0 | ET bit (0x10) of CR0 register can be cleared |
| 4 | 1 | 0 | reading DR4 register causes Invalid Opcode exception |
| 4 | 2 | 0 | numeric coprocessor not present; or pseudo-denormal not normalised for fractional FSCALE |
| 4 | 3 | 0 |
According to Intel (see, for instance, section 17.17.1 of the Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1), the ET bit of CR0 is hard-wired to 1 for Intel486 processors. Presumably then, it can be changed on some early 80486 processors only as a defect, which distinguishes what Microsoft regards as model 0.
The DR4 register has long been documented as reserved, but Intel notes (see sections 17.22.3 and 18.2.2) that it has long been aliased to DR6. No indication is given of when this started, but Microsoft seems to think it begins after what Microsoft calls model 1.
Detection of a numeric coprocessor is a standard test. The kernel clears the MP, EM, TS and ET bits of the CR0 register, initialises the floating-point unit (FPU) and reads the floating-point status word. An FPU is present if all flags in the low byte are clear. With the test done, the kernel sets the EM, TS and NE bits of the CR0 register, and also the ET bit if the coprocessor was detected.
Model 2 is identified by a defect in the FSCALE instruction’s handling of pseudo-denormals. These are 80-bit floating-point numbers that have zero as the biased exponent and 1 as the integer part. They ought never be given as operands, but are tolerated for compatibility. They supposedly cannot be generated as the result of a floating-point operation. They, along with actual denormals, are supposed to be normalised automatically if the Denormal Operand exception is masked. Scaling by a fraction leaves a normalised operand unchanged. Model 2 is apparently defective in that fractional scaling leaves a pseudo-denormal operand un-normalised. For testing the FSCALE instruction, the kernel clears the MP, EM, TS and ET bits of the CR0 register and masks all floating-point exceptions (by setting the low 8 bits of the floating-point control word). The 80-bit pseudo-denormal used for the test is zero except for having 1 as its integer part. If scaling this pseudo-denormal by 0.5 leaves the exponent as zero, then the processor is model 2.
Finer identification of 80386 processors is largely academic. Whatever the model or stepping, the 80386 processor is unsupported since version 4.0, and soon causes the bug check UNSUPPORTED_PROCESSOR (0x5D), though not without the kernel having worked its way through more tests for defects to identify models and steppings. For any 80386 processor that passes all tests, the model and stepping leap ahead to 3 and 1. Version 3.51, which was the last to support the 80386 (and only then in a single-processor configuration), rejects any 80386 that does not pass all these tests.
| Family | Model | Stepping | Test |
|---|---|---|---|
| 3 | 0 | 0 | 32-bit MUL not reliably correct |
| 3 | 1 | 0 | supports XBTS instruction |
| 3 | 1 | 1 | set TF bit (0x0100) in EFLAGS causes Debug exception (interrupt 0x01) only at completion of REP MOVSB |
| 3 | 3 | 1 |
The particular multiplication that distinguishes model 0 is of 0x81 by 0x0417A000. This same test was used by Microsoft at least as far back as Windows 3.10 Enhanced Mode, to advise
The Intel 80386 processor in this computer does not reliably execute 32-bit
multiply operations. Windows usually works correctly on computers with this
problem but may occasionally fail. You may want to replace your 80386 processor.
Press any key to continue...
The instruction whose support is tested for model 1 stepping 0 has opcode bytes 0x0F 0xA6 followed by a Mod R/M byte and by whatever more this byte indicates is needed for the operand. This opcode is disassembled as XBTS by Microsoft’s DUMPBIN utility from Visual C++, and has been since at least the mid-90s. However, the same opcode was apparently reused for the CMPXCHG instruction on some 80486 processors. The confusion seems to have left a lasting mark: Intel’s opcode charts leave 0x0F 0xA6 unassigned even now. The specific test performed by the Windows kernel is to load EAX and EDX with zero and ECX with 0xFF00. If executing XBTS ECX,EDX does not cause an Invalid Opcode exception and clears ecx to zero (which CMPXCHG ECX,EDX would not), then XBTS is deemed to be supported and the processor is model 1 stepping 0. This case of 80386 processor also was known to Windows 3.10 Enhanced Mode, and was rejected as fatal:
Windows may not run correctly with the 80386 processor in this computer.
Upgrade your 80386 processor or start Windows in standard mode by typing
WIN /s at the MS-DOS prompt.
When string instructions such as MOVSB are repeated because of a REP prefix, each operation is ordinarily interruptible. As Intel says (for REP in the Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference N-Z), this “allows long string operations to proceed without affecting the interrupt response time of the system.” It ordinarily applies also to the Debug exception, such as raised by the processor at the end of executing an instruction for which the TF bit is set in the EFLAGS when the instruction started. Programmers may have noticed this in the real world of assembly-language debugging. If the debugger actually does implement its trace command as a trace, as opposed to setting an INT 3 breakpoint where the instruction is calculated to end, then a two-byte REP MOVSB may take many keystrokes to trace through! That model 1 stepping 1 traces through a REP MOVSB without interruption may be helpful when debugging, but it is surely a defect.
The primary place at which the kernel stores the results of this identification is in the formally opaque KPRCB structure, reachable from (and embedded in) the partially opaque KPCR structure. Each processor has its own KPCR structure. The kernel defines a segment for each KPCR and ordinarily keeps the corresponding selector in the processor’s FS register. The members of both structures are named by Microsoft in symbol files. The main ones that are relevant to CPU identification are:
| Offset | Name | Size | Description |
|---|---|---|---|
| 0x18 (most versions); 0x14 (version 6.1) |
CpuType | byte | family |
| 0x19 (most versions); 0x15 (version 6.1) |
CpuID | byte | 1, if CPUID instruction is supported;
0, otherwise |
| 0x1A (most versions); 0x16 (version 6.1) |
CpuStepping | byte | stepping |
| 0x1B (most versions); 0x17 (version 6.1) |
CpuModel | byte | model |
| 0x1BA8 (see notes below) | CpuVendor | byte | numeric code for identified vendor |
| 0x1BAC (see notes below) | VendorString | 13 bytes | vendor string, null-terminated |
The CpuType, CpuID, CpuStepping and CpuModel kept these same offsets all the way back from version 3.51 until a change for Windows 7. The CpuStepping and CpuModel members are also accessed as a union, named CpuStep, again from as far back as version 3.51. The offsets for CpuVendor and VendorString vary even from one build to another. The offsets given above are for the version 6.0 from the original Windows Vista.
Note that the CpuType member corresponds most closely with what the Intel literature describes as the family, not the type.
The CpuVendor member is new for version 6.0. Its value is 0 if no vendor string is known. The identified vendors run from 1 to 6 (in the same order as given near the beginning of this article). The value 7 indicates that a vendor string is known but is not identified.
No interface is known for retrieving each processor’s identification from its KPRCB. However, all known versions of the kernel write the details to the registry.
| Key: | HKEY_LOCAL_MACHINE\Hardware\Description\System\CentralProcessor\index |
| Value: | Identifier |
| Type: | REG_SZ |
Data for the Identifier value is typically x86 Family family Model model Stepping stepping, in which the placeholders are resolved as decimal numbers from CpuType, CpuModel and CpuStepping respectively. For processors on which a usable CPUID instruction is unavailable, the data for the Identifier value takes the form 80x86-yz , where x, y and z represent the family, model and stepping as found by testing for defects, but with y as a letter (A for 0, B for 1, etc).
| Key: | HKEY_LOCAL_MACHINE\Hardware\Description\System\CentralProcessor\index |
| Value: | VendorIdentifier |
| Type: | REG_SZ |
The data for the VendorIdentifier value is the vendor string. Except for the curiosity noted in the next paragraph, this value is not set unless a vendor string is known from the CPUID instruction. In versions 4.0 and higher, the data is set from the VendorString member of the KPRCB. Though version 3.51 also retains a VendorString, it gets the vendor string afresh when writing to the registry.
Some Cyrix processors that do not support CPUID can be recognised by the presence of configuration registers that are accessible through I/O ports 0x22 and 0x23. For these, the kernel sets the VendorIdentifier value to CyrixInstead without having recorded a VendorString in the KPRCB. This is coded in all known versions.