Why Windows NT from October 1992 refuses to install on modern CPUs

Attempting to install the October 1992 pre-release on any CPU less than about 20 years old is likely to result in the following error message:

This is similar to the behavior of the official Windows NT 3.1 and 3.5 releases, but harder to work around, and likely to happen even on Pentiums and some 486s. The reason for that is that Microsoft knew too much, but not enough.

In the October 1992 beta version of Windows NT, the installer recognized 386, 486, and 586 CPUs. This was a change from the July 1992 pre-release, which only knew about 386s and 486s. The Pentium processor was in fact only released in March 1993, after unexpected delays.

Microsoft had clearly inside information from Intel and the October 1992 Windows NT beta uses the CPUID instruction to identify the processor. This may well be the first more or less public use of the CPUID instruction, about six months before the official release of the first processors with CPUID support.

Unfortunately Microsoft’s usage completely contradicts the published Pentium documentation. It seems safe to assume that Intel gave Microsoft early specifications which then changed before the chip was released. Why Microsoft applied this information before it could be tested on any released hardware is a different question.

What is certain is that the NT kernel (typically NTOSKRNL.EXE) detects the CPU type during startup. This is done in a function called KiSetProcessorType.

KiSetProcessorType uses the standard Intel CPU identification algorithm, with the assumption that the CPU has to be at least a 386, as it is already executing 32-bit code. First the code attempts to modify the AC (Alignment Check) bit in EFLAGS. If that succeeds, the CPU is assumed to be at least a 486. Otherwise KiSetProcessorType goes off to determine the 386 stepping by checking for specific implementation quirks.

If the CPU is determined to be at least a 486, KiSetProcessorType attempts to modify the ID bit in EFLAGS to verify CPUID support. If the bit can’t be modified, the function again goes off to determine the 486 stepping. If CPUID support is found, KiSetProcessorType executes CPUID and uses the standard technique to extract the processor family and stepping.

Except Microsoft’s code doesn’t work. It appears that some early CPUID specification presumed that only a single set of information would be returned by CPUID in EAX/EBX/ECX/EDX. Later someone realized that it would be much better to make the mechanism extensible, and the contents of EAX register determine what information CPUID returns. For the original Pentium, only EAX values of 0 and 1 were supported, and all other values returned zeros in EAX/EBX/ECX/EDX.

The writers of Windows NT were clearly unaware of this and did not set EAX to any specific value prior to executing CPUID. KiSetProcessorType copies CR0 contents into EAX on function entry, so the typical EAX value when executing CPUID might be something like 8000003Fh.

The effect this has on CPUID is processor specific. For the Pentium, the documented response is to return zeros. For the Pentium Pro and for 486s with CPUID support, the response is undocumented. For current Intel processors, the documented response is to return the data for the highest supported standard CPUID leaf. The latter behavior was verified with Core 2 and Core i7 CPUs, as well as with an old Pentium II system.

The upshot is that the October 1992 beta of Windows NT probably won’t get the expected response on any processor with CPUID support, not even on a Pentium or a 486. The detected family might end up as 0 (on a Pentium), as 1 (on at least some Pentium II systems), or some other more or less random value which the installer certainly will not like.

What can be done about this? Either run this beta version of Windows NT on a CPU old enough that it doesn’t support the CPUID instruction (a 386 or an old 486), or patch the NT kernel. If the latter strategy is chosen, the following instruction sequence

or ebx,0x00200000
push ebx
popfd
cpuid

needs to be replaced with something along the lines of

xor eax,eax
inc eax
nop ...
cpuid

The effect of this change is that the CPUID bit in EFLAGS is not restored (which isn’t necessary) and EAX is forced to 1, which will cause CPUID to deliver the data the rest of the routine expects. Naturally this will also require the actual CPUID to be faked, except when running on a 486 or a Pentium. But that’s a lot easier.

Alternatively the NT kernel could be patched to force a specific processor family (not using CPUID at all), though this was not explored. Note that this build of Windows NT has no checksums or other mechanisms that would make patching troublesome; simply modifying the code in the executable image is enough.

With the patched kernel in place, the Windows NT setup finally relents and does not prevent installation anymore.

This entry was posted in Intel, NT. Bookmark the permalink.

7 Responses to Why Windows NT from October 1992 refuses to install on modern CPUs

  1. Yuhong Bao says:

    From http://www.sandpile.org/x86/cpuid.htm :
    “#1 According to [1] and [2] the pre-B0 step Intel P5 processors return EAX=0000_05xxh.
    #2 According to [1] and [2] the pre-B0 step Intel P5 processors don’t return a vendor ID string.”
    Also see http://fornax.elf.stuba.sk/SUPERMAN/SYSTEMS/DOS/ASM/p5asm.mac

  2. michaln says:

    Thanks for confirming what I already deduced…

  3. Rauli says:

    Wasn’t there a Cyrix CPU (P5 or P6 compatible) in which CPUID instruction could be disabled? In fact I remember it had to be enabled, because it was initially disabled. If it is correct, NT could run on that Cyrix CPU, but not on a regular Pentium… The idea of a “Designed for Cyrix” sticker on the NT box makes me laugh :)

  4. michaln says:

    Assuming that NT would otherwise run on such a CPU (presumably it would), then yes, disabling the CPUID capability would actually help with this NT build. The mind boggles…

    Intel was lucky that by the time they actually shipped the Pentium, there was a newer NT build without this problem. Otherwise they might have implemented some CPUID disabling hack for NT, similar to what they did a few times later.

  5. Yuhong Bao says:

    Rauli: AFAIK the Cyrix 6×86 disabled it by default. Intel tried to hide many of the new features of the Pentium from competitors by requiring an NDA to be signed. AMD reverse-engineered the new features and did the K5 with them, Cyrix only implemented the 486 features and even disabled CPUID by default.

  6. Figures as much… Just as there is some other issue installing NT 4.0 on P4′s or higher … Although I’ve had fine luck xcopy’ing a previously installed NT 4.0 with SP6a installed onto a 2Gig fat partition, and converting away to NTFS without issue…

  7. michaln says:

    The funny part of the NT 3.x installation issues is that it’s only the installer/setup that’s upset by the unknown CPU. The OS itself works just fine… And it’s not too hard to fix up the .INF file throwing the error, either.

    With NT 4 it’s worse, as the system BSODs when it finds a CPU it doesn’t like.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>