Archives
- October 2025
- September 2025
- August 2025
- July 2025
- June 2025
- May 2025
- April 2025
- March 2025
- February 2025
- January 2025
- December 2024
- November 2024
- October 2024
- September 2024
- August 2024
- July 2024
- June 2024
- May 2024
- April 2024
- March 2024
- February 2024
- January 2024
- October 2023
- September 2023
- August 2023
- July 2023
- June 2023
- May 2023
- April 2023
- March 2023
- January 2023
- December 2022
- November 2022
- October 2022
- September 2022
- July 2022
- June 2022
- May 2022
- April 2022
- March 2022
- February 2022
- January 2022
- December 2021
- November 2021
- October 2021
- September 2021
- August 2021
- July 2021
- June 2021
- May 2021
- April 2021
- March 2021
- February 2021
- January 2021
- December 2020
- November 2020
- October 2020
- September 2020
- August 2020
- July 2020
- June 2020
- May 2020
- April 2020
- March 2020
- February 2020
- January 2020
- December 2019
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- February 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- July 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- April 2013
- March 2013
- February 2013
- January 2013
- December 2012
- November 2012
- October 2012
- September 2012
- August 2012
- July 2012
- June 2012
- May 2012
- April 2012
- March 2012
- February 2012
- January 2012
- December 2011
- November 2011
- October 2011
- September 2011
- August 2011
- July 2011
- June 2011
- May 2011
- April 2011
- March 2011
- January 2011
- November 2010
- October 2010
- August 2010
- July 2010
LOADALL Strikes Again
A minor mystery recently popped up while running IBM’s OS/2 1.1 (1988), the first OS/2 version with the Presentation Manager GUI. While Microsoft’s and IBM’s releases of OS/2 were fully compatible from application perspective, there were differences in the drivers supplied and the kernel itself was slightly different.
One of the differences was that while all Microsoft’s OS/2 kernels (starting with the May 1987 beta) detected a 386 CPU and used 386-specific instructions to switch from protected back to real mode, IBM’s OS/2 1.0 and 1.1 kernels did not. IBM only used the 286-specific method (which MS’s OS/2 kernels also used, but only on 286s) of mode switching: triple fault the CPU, trigger a shutdown cycle, let the motherboard reset the CPU, and ask the BIOS to skip the POST code and jump to a specified address soon after the CPU restarts execution.
This method is not supported by most virtualization products (Virtual PC, VMware) and may not work with some recent BIOSes. But that’s not the interesting part. The really interesting part is that when running a DOS box and hence executing in real mode, IBM OS/2 1.0 and 1.1 in some cases executes the infamous 286 LOADALL instruction. In OS/2 1.0 this appears to be fairly rare, in version 1.1 LOADALL is typically executed within seconds of switching to the DOS box.
Why would OS/2 do this? It is clearly a performance optimization. OS/2 is executing in real mode but needs to access some memory beyond 1MB. The LOADALL instruction is used to load the in-CPU descriptor cache that will allow a segment register (usually ES) address up to 64K of memory anywhere in the 16MB 286 address space.
Memory past the 1MB boundary is of course normally accessed in protected mode, but that has numerous disadvantages. The machine state must be completely changed, real-mode code can (typically) no longer be executed, interrupts must be disabled for relatively long periods of time. Worst of all, returning back to real mode takes quite a while; resetting the CPU in order to switch from protected to real mode works, but it is not fast.
Using LOADALL neatly sidesteps most of these issues. The CPU never leaves real mode and although interrupts are still dangerous (reloading a segment register will overwrite its “magic” contents set up by LOADALL), the entire operation takes far less time and it is even possible to execute memory copies “speculatively” with interrupts enabled.
It is well known that Microsoft’s HIMEM.SYS uses LOADALL on 286 systems for exactly these reasons. It is also well known that although 386s and perhaps some later CPUs implemented a LOADALL instruction, it used a different opcode and was not compatible with the 286 variant. For completeness it should be added that the opcode used by 286 LOADALL (0Fh 05h) was later reused for the SYSCALL instruction.
With these facts in mind, one might wonder how IBM’s OS/2 1.0/1.1 ever worked on 386 and later CPUs, or if it ever did? Work it certainly did, but how then?
IBM sold 386 systems at the time, initially the PS/2 Model 80, eventually joined by Model 70 and others. OS/2 1.0/1.1 “naturally” includes specific support for these systems and does not execute LOADALL. However, generic 386 AT-compatibles can also run OS/2 1.0/1.1. On those systems, IBM’s OS/2 will triple fault the CPU to get back to real mode and it will execute LOADALL. That should not work, but it does. Why?
The answer lies in a typical 386+ BIOS. The BIOS installs an invalid opcode handler, detects attempts to execute a 286 LOADALL instruction, and emulates it well enough to satisfy questionably written software such as IBM OS/2 1.0/1.1.
The BIOS cannot fully emulate all aspects of LOADALL, but can reliably emulate the typical use case of LOADALL: The caller is executing in real mode, wishes to remain in real mode, and wants to modify the descriptor cache of one or more segments such that the base is above 1MB. It is assumed that CS and SS remain normal real-mode segments. The GDT/LDT/IDT/TSS may be modified.
In theory, software could use LOADALL in order to switch from real to protected mode, but that is somewhat pointless because such a switch can be achieved without the use of undocumented instructions. Using LOADALL to switch from protected to real mode is not possible at all, and utilizing LOADALL in protected mode is probably more work than achieving the same effect through documented means. These limitations allow a BIOS to emulate the common LOADALL uses.
It was previously mentioned that the 286 LOADALL opcode was later recycled for use by the SYSCALL instruction. Does that mean systems with newer CPUs cannot utilize the BIOS LOADALL emulation? On Intel processors, SYSCALL must be explicitly enabled and even then is only recognized in 64-bit mode, so it cannot possibly conflict with software written for the 286. On AMD processors, SYSCALL is not limited to 64-bit mode but must still be enabled via EFER.SCE, which again won’t be the case with software designed to run on 286s. Therefore SYSCALL does not conflict with 286 LOADALL emulation.
It would be interesting to know what Intel thought about Microsoft’s use of LOADALL. It’s fairly certain that Intel supplied Microsoft with non-public LOADALL documentation, but it’s unknown whether Intel sanctioned LOADALL use in retail software. Especially since Microsoft only started using the 286 LOADALL instruction after the 386 was already on the market and did not support it.
13 Responses to LOADALL Strikes Again
Did IBM really infer the CPU used based on the model/submodel instead of detecting the 286/386 CPUs properly?
Yes and no. There is 386 CPU detection, but that appears to mostly control FPU management. The use of LOADALL depends on whether the system has ABIOS (Microchannel). The assumption seems to be that if the system doesn’t have MCA, it must be a 286 and LOADALL is safe to use. That was certainly true for IBM machines at the time.
The detection couldn’t have only been for MCA since the PS/2 models 50 and 60 were 286-based and had MCA. I don’t remember if those models also had ABIOS or not.
Note pre-XMS versions of RAMDRIVE.SYS and SMARTDRV.SYS also used the 286 LOADALL. Additionally DOS 3.3 and 4.0 reserved a 102-buffer at 800h (70h:100h) for use by these programs so that they didn’t have to save and restore the contents of this area. The LOADALL buffer was removed in DOS 5 to save memory since 386 and 486 PCs were common at that point (1991) and programs used XMS to access extended memory leaving HIMEM.SYS to deal with LOADALL on 286 PCs.
The OS/2 kernel has 386 detection but most of the decisions depend on whether the machine is a PS/2 (MCA, ABIOS) or not. Model 50/60 definitely should have ABIOS. The result of INT 15h/04h determines whether LOADALL will be executed or not. The alternate code path seems to switch to protected mode and back, but it is a lot more complex.
I see references to LOADALL in SMARTDRV sources from ’89 (part of Windows 2.11 BAK) dating back to 1986. There’s also a mention of “INTEL special documentation of LOADALL instruction”. It’s pretty clear that Microsoft had the info from Intel and that they had it in 1986 or sooner.
It would be interesting to know whether Compaq’s early 386s already had LOADALL emulation built into the BIOS, the way it was done in the 1990s as a matter of course.
Are you aware of IBM ever using LOADALL in IBM-written software? I assume the OS/2 LOADALL code had been written by Microsoft π
To my knowledge IBM never used LOADALL in any of their software. For example IBM VDISK.SYS never used it (it always used INT 15h function 87h) while pre-XMS versions of MS RAMDRIVE.SYS did. All IBM ever did in regard to LOADALL was reserve the 102-byte buffer at 800h in DOS 3.3 and 4.0 so that part of the IBMBIO.COM/IO.SYS data wasn’t there and didn’t have to be saved and restored.
As for early Compaq 386s emulating LOADALL; if you have access to one of these systems then boot DOS and disassemble the INT 06h handler and it should be readily apparent if it does or does not. I would suggest using the enhanced DEBUG on my PC DOS Retro site as it recognizes 386+ opcodes unlike the standard DOS DEBUG which only recognizes 8086/8087 opcodes.
No, I don’t have an old 386 Compaq. If I did I wouldn’t ask π Actually a BIOS ROM image would be enough.
I have a disassembly of the Phoenix BIOS from a Gateway 2000 486/33 system I had back in 1991. It has LOADALL emulation. This BIOS is dated 05/25/91 but it has an earlier date of 01/15/88 at F000h:FFF5h which is likely the date of the core BIOS codebase so it’s likely that 386+ BIOSes emulated LOADALL at least as far back as 1988.
Interestingly, IBM’s OS/2 Technical Reference actually infers to this behavior:
http://bitsavers.informatik.uni-stuttgart.de/pdf/ibm/pc/os2/84X1434_OS2_Technical_Reference_Volume_1_Sep87.pdf
“On AMD processors, SYSCALL is not limited to 64-bit mode but must still be enabled via EFER.SCE”
I wonder, on a such CPU if SYSCALL is not enabled, and the LOADALL 286 instruction is encountered, what exception would be generated? If it’s just “invalid opcode” then fine, but if it’s something like “SYSCALL disabled”, then perhaps it would be trouble for an eventual BIOS emulation of the LOADALL 286 instruction. If such a thing even exists on new systems, of course.
I’m pretty sure it’s #UD (invalid opcode). That generally happens for all instructions that are disabled or not available in whatever mode the CPU is in (not counting privileged instructions, those produce #GP or general protection fault).
The bitsavers link has changed to http://bitsavers.informatik.uni-stuttgart.de/pdf/ibm/pc/os2/OS2_1.x/84X1434_OS2_Technical_Reference_Volume_1_Sep87.pdf .
Ancient thread by now, but:
Where there ever any device drivers, TSRs and whatnot that were available in a version that uses the 286 LOADALL trick to have a large part of their code above the 1M limit?
In particular network stacks and such could probably benefit from doing this, in order to gain as much low memory free as possible.
This site uses Akismet to reduce spam. Learn how your comment data is processed.