Geoff Chappell - Software Analyst
The UNSUPPORTED_PROCESSOR bug check shows that at least one processor is of a type that is not supported. Mostly, this will be the boot processor.
The summary that Microsoft offers to programmers through the !analyze debugger extension command is:
The system failed because the processor does not support all the required hardware features. This error is most likely due to lack of support for one or more of NX, PAE or SSE2.
The UNSUPPORTED_PROCESSOR bug check can occur in version 4.0 and higher.
Bug check 0x5D is omitted from the version 3.51 kernel’s message resource and from the BUGCODES.H in the Device Driver Kit (DDK) for Windows NT 3.51. Though 0x5D is evidently undefined as a bug check code for version 3.51, contemporaneous documentation in the Windows NT 3.51 Resource Kit gives the name HEAP_INITIALIZATION_FAILED. This different name is in the kernel’s message resource in versions 3.10 and 3.50. This very different original purpose for bug check 0x5D may some day be taken up separately as archaeology.
The UNSUPPORTED_PROCESSOR bug check can be more available in theory than in practice. The point to the bug check is that while the kernel initialises its use of the boot processor, it detects the absence of some feature that the kernel will depend on for all sorts of subsequent execution. Though it’s hardly ideal, it does happen that the missing feature can be relied on even for presenting a bug check. Thus can the circumstances of the UNSUPPORTED_PROCESSOR bug check result in some different bug check or even in the computer being silently rebooted.
The UNSUPPORTED_PROCESSOR bug check is documented. Even today, 7th July 2020, Microsoft’s documentation says this bug check has no parameters.
The documentation can’t have been correct about the parameters even when the documentation was written a quarter century ago. In fairness, at that time there was indeed not much to report since there was only one way that a processor could be unsupported.
In versions 4.0 and 5.0, the 80386 is the only unsupported processor and the only possible cause of this bug check. The kernel will have identified the processor in some detail, but it arguably does not matter: all 80386 processors are unsupported, and remain so for all later versions. When the unsupported processor is an 80386, the first bug-check argument says so, and the others are all zero:
|Bug Check Code:||UNSUPPORTED_PROCESSOR (0x5D)|
Though versions before 4.0 have no bug check code specifically for complaining that a processor is unsupported, they do all reject early stepppings of the 80386. Version 3.10 stops execution without a bug check. Versions 3.50 and 3.51 raise bug check 0x5C:
|Bug Check Code:||HAL_INITIALIZATION_FAILED (0x5C)|
|2nd Parameter:||processor family, i.e., 3 for 80386|
|3rd Parameter:||processor model and stepping|
The model and stepping are inferred from testing for various defects, as described in CPU Identification Before CPUID. The model must be greater than 1 to avoid the bug check. The first parameter is presumably a hexadecimal representation of the model and stepping in a standard scheme in which the model is A for 0, B for 1, etc. The tests can identify an 80386 as A0, B0, B1, which are all rejected. (Any other 80386 is identified as D1 and is acceptable. In the 3rd parameter, the stepping is the low 8 bits and the model is the next 8 bits.
Starting with version 5.1, a boot processor that is not an 80386 causes this bug check if it lacks support for particular CPU features. These features are just as much required of processors that initialise later, but the problem then is not the processor is deficient but that the multiple processors aren’t sufficiently similar. The problem is instead reported as the different bug check MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED.
|Bug Check Code:||UNSUPPORTED_PROCESSOR (0x5D)|
|1st Parameter:||family, model and stepping|
|2nd Parameter:||first four characters of CPU vendor string|
|3rd Parameter:||second four characters of CPU vendor string|
|4th Parameter:||third four characters of CPU vendor string|
The family, model and stepping are given as byte-wide bit fields in the first parameter:
|bits 0 to 7||stepping|
|bits 8 to 15||model|
|bits 16 to 23||family|
|bits 24 to 31||1 in versions 5.1 and 5.2;
3 in versions 6.0 and higher
The meaning of the highest byte is not known. The remaining bug-check parameters are the twelve characters of the CPU vendor string, presented as the three dwords that are produced in registers ebx, edx and ecx, respectively, when executing cpuid leaf 0.
Perhaps for backwards compatibility with when processor requirements were much simpler, the bug-check parameters for 32-bit Windows never were developed very much. Notably, they leave the user to wonder what it is that’s deficient about the processor:
Successive Windows versions need ever more CPU features. For the most part, each feature has a corresponding bit in feature flags that are returned in one or another register after executing one or another leaf of the cpuid instruction, and the feature is present or absent according to whether the bit is set or clear. However, 32-bit Windows dates from a time when the cpuid instruction was not nearly so well established, and the tests for some features are complicated by special cases—which are all ignored for the rest of this note!
Windows XP kicks off the escalating requirements by insisting on the cmpxchg8b instruction. At its simplest, support for the CMPXCHG8B instruction is indicated by a set CX bit (8) in the feature flags that are produced in edx from cpuid leaf 1, but there are complications for processors from several vendors since Microsoft at first ignored these vendors’ implementation of cmpxchg8b, such that the vendors then worked around being ignored and Microsoft then had to work around the work-arounds. A consequence of requiring cmpxchg8b is that all 80486 processors became unsupported in version 5.1 and higher, if Intel is to be believed that this instruction “is not supported on Intel processors earlier than the Pentium processors.” (See the Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2A: Instruction Set Reference A-M.)
Version 6.0 and higher require the rdtsc instruction and its time-stamp counter.
Version 6.1 insists on having a numerical coprocessor, such that its absence causes this bug check. Later versions do not.
Version 6.2 greatly increases the required features. Precisely how each feature is detected is beyond the present scope of this article. Some are a simple matter of testing for a corresponding bit in feature flags that are returned by the cpuid instruction. As with support for cmpxchg8b, however, there are special cases for more than a few. For now, only a summary of these newly required features can be ventured:
Note that in version 6.2 and higher, the kernel is anyway distributed only in its form that supports Physical Address Extension (PAE) and thus is already using 64-bit page table entries before it examines any processor.
The x64 builds have completely different requirements, of course. The parameters for the bug check are different, too, and have been getting more informative as 64-bit Windows matures:
|Bug Check Code:||UNSUPPORTED_PROCESSOR (0x5D)|
|1st Parameter:||feature bits edx from
cpuid leaf 1, if processor rejected because
of inadequate features;
|2nd Parameter:||extended feature bits edx from
cpuid leaf 0x80000001, if processor rejected
because of inadequate features (6.2 and higher);
|3rd Parameter:||extended feature bits ecx from
cpuid leaf 0x80000001, if processor rejected
because of inadequate features (6.3 and higher);
|4th Parameter:||non-zero count of faults from attempting to execute
prefetchw instruction (6.3 and higher);
The 2nd parameter can differ on one point from what cpuid leaf 0x80000001 returns in edx: the XD bit (20) is set if the processor has AuthenticAMD as its vendor string.
All known builds of 64-bit Windows limit their support to particular CPU vendors. These are identified from the vendor string, meaning the sequence of characters obtained by executing cpuid with 0 in eax and then copying ebx, edx and ecx to successive memory locations. Any but the following cause this bug check, with zero in all arguments:
Perhaps there’s no law that requires Microsoft to allow its software to run on every CPU that’s said by its manufacturer to be x64-compatible. Yet even if there were no other manufacturers of x64-compatible processors when Microsoft wrote this code, the effect must have been plain at the time: any manufacturers who do eventually make an x64-compatible processor to compete with AMD and Intel are denied their most substantial market until Microsoft condescends with some new release of Windows that does not dismiss competing processors as unsupported—and then, to sell their new processor, they are drafted into Microsoft’s mission of persuading computer users to obtain this latest Windows release. This is behaviour that might be expected of a cartel, with Microsoft using its operating-system monopoly to protect its friends’ interests in the competitive market of processors that share the x64 instruction set. It ought perhaps be investigated by regulatory authorities.
From the start, 64-bit Windows insists on processors that have many of the relatively modern CPU features that the contemporaneous 32-bit builds still had to be capable of doing without. Perhaps as an advantage of starting late and of having fewer vendors to accommodate, 64-bit Windows has none of the complexity that 32-bit Windows faces for testing the presence of required features. Mostly, 64-bit Windows depends just on flags returned by two executions of the cpuid instruction.
In the feature bits that are returned in edx after executing cpuid leaf 1, all the following must be set: FPU (0), DE (2), PSE (3), TSC (4), MSR (5), PAE (6), MCE (7), CX8 (8), APIC (9), MTRR (12), PGE (13), MCA (14), CMOV (15), PAT (16), CFLSH (19), MMX (23), FXSR (24), SSE (25) and SSE2 (26).
A set SYSCALL bit (11) is required in the extended feature bits that are produced in edx by executing cpuid leaf 0x80000001. In version 6.2 and higher, the XD bit (20) must be set too, except that this is taken as granted if the processor’s vendor string is AuthenticAMD.
Version 6.3 and higher also inspect what’s returned in ecx by these executions of cpuid. The CMPXCHG16B bit (13), which older Intel documentation names CX16, and the LAHF bit (0) must be set in the flags that are produced by cpuid leaves 1 and 0x80000001, respectively.
Harder to establish, but no less required, is support for the prefetchw instruction. All known builds of the 64-bit kernel are coded to use prefetchw liberally, but the instruction long predates the PREFETCHW bit (8) in ecx from cpuid leaf 0x80000001. Windows tests for support by trying to execute the instruction and seeing if the processor objects. Before version 6.3, if the processor happens not to support the instruction, then a handler for the Invalid Opcode exception keeps putting things right for continued execution, including to patch the instruction away so that the performance hit from the exception doesn’t recur. The change for version 6.3 is just to do away with the patching and instead to insist that it not be needed.
Failure on any of these counts causes the bug check. The parameters tell which features were found but leave the user to deduce which missing feature (or features) matter.
Up to and including the version 6.0 from the original Windows Vista, the 64-bit kernel checks that division of 0x004B5FA3A053724C by 0x00CB5FA3 gives 0x5EE0B7E5. The test is a 32-bit div of edx:eax by r8d. This may look plausible as defending against a defect in some processor’s arithmetic, such as Microsoft has done at various levels in 32-bit builds of earlier Windows versions (for multiplications in the 80386 and floating-point divisions in the Pentium). However, this particular test is nothing of that sort. For one thing, it is performed surprisingly late. If you genuinely doubted the accuracy of a processor’s arithmetic, you would test it before depending on the arithmetic. Yet by the time the kernel performs this test division, it has performed similar divisions already. Indeed, though the tests for vendor and features are performed almost immediately that the kernel starts executing, the test for accurate division is left until phase 1 of initialisation.
In fact, this division serves as the way to initialise Kernel Patch Protection, also called PatchGuard. This is a scheme by which the kernel records the state of various sensitive items at startup and defends against run-time changes, which are held to be malicious and are usually dealt with as bug check CRITICAL_STRUCTURE_CORRUPTION (0x0109). When the test division executes while a kernel debugger is attached, it proceeds as expected. However, in the ordinary execution, without a kernel debugger, a contrivance in the code changes the division so that the high byte of the dividend is 0x01. This makes the high 32 bits of the dividend larger than the divisor, so that the div instruction faults (for overflowing the 32 bits that the instruction allows for the quotient as output). The exception handler initialises PatchGuard. If this initialisation succeeds, the div is restarted with the 0x01 cleared from the high byte of the dividend and the expected quotient is obtained. If the initialisation fails, the div is restarted with the highest two bytes cleared from the dividend, such that the quotient is incorrect and causes the bug check UNSUPPORTED_PROCESSOR with zero for all parameters. A recommendable article on the details of PatchGuard, including its initialisation and how the initialisation is reached through this division, is Bypassing PatchGuard on Windows x64. Regular readers of this website will know that it is no small compliment for me to cite other people’s research as reliable enough to be recommended.
The version 6.0 from Windows Vista SP1 changes the mechanism by which PatchGuard gets to initialise. It still relies on contriving an exception from a (different) division but without any implications for this bug check.