Computing History · Lessons from Failures

The IBM PC BIOS Standard and the Cost of Freezing a Firmware Interface

What a ROM-resident interrupt interface guaranteed to software, how its fixed data structures blocked hardware progress, and why the replacement took the form of an emulated decades-old machine rather than a new specification.

A ROM that presents a numbered interrupt service to software is a specification in the most literal sense: the caller sets registers to particular values, transfers control, and reads results from registers and memory at fixed addresses. The IBM PC’s system BIOS did this for disk access, video output, keyboard input, serial ports, the real-time clock and a handful of memory-map queries. Every entry point was published in an appendix and every data structure was described byte by byte. Software written against that appendix continued to run on machines from many manufacturers for four decades, and the mechanism that produced that longevity is the same one that prevented the interface from being replaced.

Registers as an ABI

The BIOS interface is defined entirely in terms of machine state. To write a character to the console in teletype mode, software loads the character code into one 8-bit register, the display page into another, and issues a software interrupt with a function number in yet another register. To read a sector, software loads the drive number, cylinder, head and sector into four registers, sets the destination segment and offset in another pair, and issues a different function number. Results, including a status byte, come back in registers, and a carry flag indicates failure.

This is a calling convention with no type information, no version negotiation and no way to extend an argument list. It is also an extremely cheap one: the entry point is a two-byte instruction (int 0x13, int 0x10, int 0x14 and their siblings), it needs no library, no relocation and no dynamic linking, and it works from a boot sector with 512 bytes of code. A boot loader written in assembly can call it directly. That property is why the interface was in every machine’s boot path: nothing else could be reached so early.

The published data structures were the second half of the contract. The memory map inquiry returns a list of ranges terminated by a sentinel entry, formatted in a 20-byte record with a base address, a length and a type field. The disk parameter table is a 16-byte structure with the step rate, head settle time, motor spin-up time, sector size code, sectors per track and a handful of gap lengths. The equipment word at a fixed segment and offset describes which display and storage devices are attached, bit by bit.

Each of these structures encodes an assumption about the hardware that was true in 1981 and false later. The disk parameter table’s sector size field is 2 bits wide, enough for four powers of 512 starting at 128. The equipment word’s display field has two bits, sufficient for four display classes. The memory map’s 20-byte record uses a 64-bit address but a 32-bit length in the extended version, so a single contiguous range cannot exceed 4 GB, which is a limit that real firmware still reproduces.

Where the extension mechanism ran out

The BIOS did get extensions. The disk services gained a function that takes a 64-bit block address in a parameter block, and the memory map gained a record format with 64-bit lengths. The pattern is consistent: extend by adding a function number and a structure, and keep the old function behaving as it did, because software exists that calls it.

That pattern has a hard limit, and the limit is the boot path. The first instruction executed after the firmware hands control is in a 512-byte sector, and it is written by an operating system vendor who must run on machines of unknown vintage. It cannot assume the extension exists until it has checked a flag, and checking requires the extension to be present to read the flag in a defined way. The convention adopted was to probe: set a register to a known pattern and see whether the call preserves it. Probing is exactly what happens when a specification cannot state its own version.

The boot specification published in the middle of that decade, by Compaq, Phoenix and Intel, formalized the handoff: the firmware enumerates boot devices through a table, each device provides an entry point that is called iteratively, and the program receives a drive number and a set of memory for boot parameters. This is a genuine specification of the boot process, and it still had to describe devices as BIOS disks with BIOS geometries, because the software being booted was written for those.

The compatibility tax, in concrete form

The interface’s guarantee is a guarantee about a virtual x86 machine with a particular peripheral set, and the cost of sustaining that guarantee appears in three places.

Firmware as an architecture

The first is that the interface’s existence forces every later chipset to be described in terms of 1981 devices. An ATA or SATA controller must present a register-compatible interface at the legacy I/O ports, because the BIOS’s disk functions are specified against those ports. A USB keyboard must appear as a PS/2 keyboard to the input functions, which is why firmware contains a USB stack whose only consumer is a legacy interface. A display adapter must support the text mode framebuffer at its legacy address, because the console output function writes characters there and the video function configures modes by their old numbering. Each of these is documented in the chipset and firmware specifications and each exists to serve software that has not changed.

The second is that the firmware has no way to be honest about failure. A disk function returns a status code; the code set has no entry for “the data could not be corrected but a remapped copy was returned”, because that concept dates from after the interface. The caller’s options are to retry or to give up, and the retry has the side effect of re-executing whatever the device does internally.

The third, and the one with the clearest record, is the interaction with memory. The interface is specified in real mode, with a 1 MB address space and segment registers, and the memory above 1 MB is reached through an address line toggle that the firmware must manage. Software that wants to load an operating system above 1 MB has to negotiate with firmware that may be switching that line, and the documented convention is to ask the firmware to disable its own memory management before taking over. A machine that has more than 4 GB of memory reports it in a structure whose 32-bit length fields cannot describe the region in one entry, so the map must be enumerated carefully and the extended record format used where available.

Why the replacement was a virtual machine

The successful successor, the Unified Extensible Firmware Interface, replaced the register-and-interrupt model with a table of function pointers in a defined calling convention. A caller looks up a service by a GUID, receives a pointer to a protocol interface, and calls through it with a defined C ABI. Versioning is explicit because the protocol structure has a revision field and can be extended by appending members. The boot path is a set of applications that the firmware loads and calls, and the disk and console services are protocols rather than interrupt numbers.

The interesting part is what UEFI had to keep. Firmware that implements UEFI on a PC also implements a compatibility support module that re-creates the real-mode interrupt services for operating systems that cannot boot natively. The module runs the legacy option ROMs, provides the legacy interrupt vectors, and hands control to a boot sector. The compatibility module is a deliberate emulation of the machine the old interface described, and its existence is the clearest evidence of the cost being measured: the industry found it cheaper to ship a second firmware inside the first than to require existing boot code to change.

The same obligation appears one layer down, where The Floppy Sector Interface as a Lossy Abstraction traces how a controller designed for one removable medium became the description every later storage device had to satisfy.

The ACPI specification’s role is a partial escape from the same trap. Firmware describes the hardware to the operating system through tables in memory, and the operating system’s drivers interpret the tables. This replaced the older situation where the only description of the hardware was the firmware’s own code, which meant any operating system change required a firmware change. That the description moved from code to data is the substantive advance of the era, and it came after the intervening decades of the interrupt interface had established that a description embedded in executable firmware cannot be revised independently.

The transferable property

A published interface that is guaranteed to keep working creates a permanent obligation to synthesize the world it describes. The obligation is not in the document; it is in the fact that software written against the document cannot be changed. That is why replacement interfaces on this path were delivered as emulation layers inside the new implementation, and why each one had to be written by the vendor of the machine rather than agreed among vendors of the software.

The general shape appears wherever a contract is fixed before the hardware is understood. Header Files as an Interface Contract and the ODR Landmine describes the same mechanism at compile time, where a declaration frozen into a header outlives the implementation behind it and the check that would catch the mismatch is assigned to a stage that no longer has the information. In both cases the failure is not in the interface’s design. It is in the assumption that the interface’s description and the world it describes will keep matching without anyone maintaining the correspondence.

The legacy interrupt set is still present in the address space of every PC-compatible machine. Its continued presence is not inertia in the pejorative sense; it is the cost of the guarantee, paid at boot time on every machine, and it will be paid until the last piece of software that calls it is gone.

References

  1. IBM PC Technical Reference Manual — IBM, 1981
  2. BIOS Boot Specification 1.01 — Compaq, Phoenix Technologies, Intel, 1996
  3. Unified Extensible Firmware Interface Specification — UEFI Forum