Windows 3.1 Standard Mode Crash with APM

On some systems, Windows 3.1 and 3.11 in Standard mode crashes when it is configured to use APM, i.e. POWER.DRV is loaded. The crash only happens in Standard mode, not the typical default 386 Enhanced mode.

Windows 3.1 APM Crash

Windows 3.1 APM Crash

Since POWER.DRV is involved (“MS-DOS System with APM” vs. “MS-DOS System” in Windows setup), it’s obvious that power management/APM is somehow significant, but how, and why is only Standard mode affected? The explanation is a bit complicated.

Let’s start by looking at what may cause the crash. Windows 3.1 (specifically POWER.DRV) enters the idle state and calls APM service equivalent to INT 15h/5305h, CPU Idle. The call is executed using the 16-bit protected-mode interface. There are other possible failure modes which involve different APM services.

Windows 3.11 APM Crash

Windows 3.11 APM Crash

The idle-state crash is caused by the APM BIOS attempting to execute the HLT instruction with CPL (Current Privilege Level) other than zero, i.e. other than the highest privilege. The CPU state indicates that IOPL is 3 and CPL is 3 as well. The strange Intel x86 design is highlighted here: User code running with IOPL 3 can disable interrupts (CLI) and enter an endless loop, but it is not allowed to execute the HLT instruction.

Some APM implementations use port I/O to do their work; such systems have no trouble. But implementations trying to execute HLT or other instructions requiring CPL of 0 will crash Windows 3.1 in Standard mode.

But why does 386 Enhanced mode not have the same problem? The reason is very indirect and almost certainly unintentional. POWER.DRV allocates the LDT descriptor which is used for calling into APM BIOS using DPMI service 0000h (Allocate LDT Descriptors). The service returns descriptor(s) with privilege level same as the caller’s. And that’s where the difference is—in Standard mode, the descriptor is allocated in ring 3 and therefore calls into the APM BIOS with CPL of 3, but in 386 Enhanced mode, the descriptor is allocated in ring 0 and therefore the APM BIOS runs at with CPL 0 as well (and can execute HLT without trouble).

The problem is not applicable to Windows 3.11 for Workgroups because Standard mode support was removed there. It also isn’t applicable to Windows 3.0 because POWER.DRV was only introduced with Windows 3.1.

This problem is unquestionably a bug in Windows 3.1. The APM 1.0 specification clearly states that “The code segment descriptor must specify protection level 0, and the BIOS routines must be invoked with CPL = 0 so they can execute privileged instructions.”

Microsoft violated the APM requirements, but the problem is only visible on systems which provide APM (not all do), provide a 16-bit protected-mode APM interface (not all do), and execute instructions which require CPL = 0 and not just IOPL = 3 (again not all do).

The crash is reproducible on IBM ThinkPad T42 as well as in some virtualized environments. It is not clear if systems affected by this bug existed in 1992/1993 when Windows 3.1/3.11 was released. The workaround is to either use 386 Enhanced mode, disable POWER.DRV (e.g. comment it out in SYSTEM.INI) in Standard mode, or possibly prevent APM from offering the 16-bit protected-mode interface by hooking and modifying INT 15h.

This entry was posted in Bugs, Windows. Bookmark the permalink.

16 Responses to Windows 3.1 Standard Mode Crash with APM

  1. Chris M. says:

    Its a good chance this bug was never encountered back in the day. Where there any APM enabled 286s ever built? If not, just about everyone with an affected system was running Windows in Enhanaced mode by default unless one used the “/s”switch to over ride it. I don’t recall running across a single Windows program that specifically required Standard Mode at the time, so never had the need to run it that way.

    On a side note, I remember a friend of mine running Windows 3.1 with the 386SL specific system driver on an old Compaq laptop. Did SL machines offer something above and beyond the generic APM driver that required a special driver?

  2. Michal Necasek says:

    I’m almost certain there were 286s with APM. However I don’t think the HLT instruction did anything to save power on 286s. So there would be just port I/O and that worked.

    Windows 3.1 has additional support for SL processors but I never figured out exactly what it does.

  3. Yuhong Bao says:

    It would have been easy to workaround this in the BIOS by checking the RPL of the CS selector. I wonder if any BIOSes included this workaround.

  4. Yuhong Bao says:

    How does POWER.DRV allocate selectors in ring 0 though?

  5. Yuhong Bao says:

    More precisely, how does POWER.DRV execute code in ring 0 in the first place?

  6. Richard Wells says:

    Are the DPMI calls being handled in POWER.DRV or in VPOWERD.386? If the actual work is done by the VXD in enhanced mode, everything it creates should be at ring 0 just like the VXD. I think there were a few poorly documented differences between DPMI as handled in enhanced mode compared to the DPMI server used in standard mode.

    The POWER.DRV and VPOWERD.386 code was written by Intel, IIRC.

  7. Michal Necasek says:

    Well, work around as in prevent crashes but effectively disable APM functionality. Yes, that occurred to me to, but I could not find any such workaround in two or three late 1990s BIOSes I looked at. I’m definitely not saying that no BIOS out there contains any such workaround.

  8. Michal Necasek says:

    The DPMI calls are being issued by POWER.DRV but handled by DOSX of course (Standard mode). I don’t think VPOWERD.386 is involved much in the case where APM is handled by protected-mode code (i.e. APM BIOS provides protected-mode services). In the cases I looked at, HLT is definitely issued from protected mode, not real or V86 mode.

  9. Yuhong Bao says:

    The workaround would only disable the idle call (in the worst case). Most other APM calls mostly does port I/O and would be unaffected. And I am asking about how does POWER.DRV execute code in ring 0.

  10. Chris M. says:

    OK, I dragged out a circa 1994 Compaq Contura Aero 4/33c I had laying around to give this a test. It runs Windows 3.1 in Standard Mode with APM without crashing. The only added wrinkle, the machine still has the OEM load of MS-DOS 6.20/Windows 3.1 on it. Compaq clearly modified the DOS POWER.EXE TSR (copyright text shows that). Compaq might have modified the APM drivers in Windows 3.1 too. Compaq’s own enhanced power management utility won’t run in Standard Mode, but the system suspends/resumes without a problem.

    Regarding 286 machines with APM, I can’t find much evidence of them. Anything mentioning the introduction of the APM spec makes mention of the 386SL, and not much else. Desktops didn’t commonly have an APM enabled BIOS until 1994 when the Energy Star program kicked in. A notable exception was the IBM PS/1 series, which came with an ATX style soft power switch and Rapid Resume.

  11. dosfan says:

    The APM 1.0 specification was released in January 1992. At that time there were already many 486 PCs on the market so it’s unlikely that 286 PCs were still being manufactured at that point therefore there probably are no 286 PCs with APM support.

  12. Yuhong Bao says:

    To be more precise, it is unlikely that any new 286 PCs were still being designed.

  13. Richard Wells says:

    80286 machines were still being built in 1992. The Tandy 1000RLX was introduced in late 1991. IBM kept the 80286 PS/1 models in production into 1992. That is just what was available in the US. The government publication U. S. Industrial Outlook, 1994 lists 80286 systems as being half a percent of all units sold in 1993 in US, less than 1%* in Europe and 7%* in Japan. See page 26-17 and 26-18. Still that should be about 250,000 units with additional numbers from other markets.
    * These numbers are for combined 808x and 80286 systems.

    I think the PS/1 had energy saving functionality.

    Gateway 2000 tossed in a ringer with the “Handbook 286” which had instead the C&T 8680.

  14. Michal Necasek says:

    POWER.EXE definitely has an impact on the problem. But you’d have to look at exactly what the APM BIOS does… it may be doing only port I/O.

  15. Michal Necasek says:

    Interesting question. My memory is that 286s lasted longest in portables, which of course would be the systems most likely to support APM, and least well documented. Off-brand 286 portables (mostly Taiwanese) were probably sold until 1993 or 1994. Whether they had APM or not I don’t know, but I’d be very unsurprised if some did.

    The APM 1.0 specification (January 1992) does not even require a 286, it allows real-mode only implementations. The 1.2 spec (February 1996) indirectly requires a 386 because it requires 32-bit protected-mode APM interface support, and that requirement did not exist in APM 1.2 (September 1993). But at that point Windows 3.1 itself was obsolete.

  16. Yuhong Bao says:

    The point is whether they were new designs, which most of them probably were not.

Leave a Reply

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