Skip to content

BIOS and Power-On Self Test (POST)

Technical Overview

The Basic Input/Output System (BIOS) is firmware embedded in ROM on the motherboard that provides the lowest-level interface between hardware and software. When power is applied to a system, the CPU does not begin executing an operating system — it begins executing BIOS code. BIOS performs hardware initialization, runs POST diagnostics, establishes runtime interrupt services, and ultimately transfers control to a bootloader stored on a storage device.

For over three decades (1981–2010s), BIOS was the universal firmware standard for x86 PC hardware. Understanding BIOS means understanding the constraints under which every x86 operating system has had to bootstrap itself: 16-bit real mode, 1MB addressable memory, interrupt-driven I/O services, and a rigid boot sector convention that predates modern disk capacities by twenty years.

Prerequisites

  • x86 CPU modes: real mode vs protected mode
  • Memory segmentation (segment:offset addressing)
  • Interrupt vector mechanics
  • Basic understanding of storage device geometry (CHS addressing)
  • Familiarity with hex notation and memory maps

Historical Context

The IBM Personal Computer (August 1981) shipped with a BIOS written largely by David Bradley, stored in a 64KB ROM chip. IBM published the full BIOS source listing in the Technical Reference Manual — a decision that accidentally created the clone PC market. Compaq reverse-engineered the BIOS in 1982 by writing a clean-room reimplementation, establishing legal compatibility. Phoenix Technologies and Award Software then commercialized the BIOS business, producing the firmware that shipped in the vast majority of PC clones through the 1990s and 2000s.

The BIOS remained essentially frozen at the 1981 design constraints because backward compatibility was paramount. Every architectural limitation — 16-bit real mode, the 640KB barrier, the 1MB address space — traces back to the 8088 CPU in the original IBM PC. The firmware that boots a 2008 server is functionally identical in structure to the firmware that booted the original IBM PC, just faster and with more device support.

Intel began development of the Extensible Firmware Interface (EFI) for Itanium in 1998, explicitly to escape BIOS constraints. The UEFI Forum formalized this as a cross-vendor standard in 2005. By the early 2010s, BIOS was officially deprecated on new hardware.

Power-On Self Test Sequence

When the power supply asserts PWR_GOOD, the CPU's reset vector is loaded. On x86, this is hardwired to physical address 0xFFFFFFF0 (the top 16 bytes of the 32-bit address space), which in real mode maps to 0xF000:0xFFF0. The chip select logic on the motherboard routes this address to the BIOS ROM.

POST Sequence:

  +------------------+
  | Power Applied    |
  | PSU asserts      |
  | PWR_GOOD         |
  +--------+---------+
           |
           v
  +------------------+
  | CPU Reset        |
  | CS:IP = F000:FFF0|
  | (BIOS ROM)       |
  +--------+---------+
           |
           v
  +------------------+
  | CPU Init         |
  | Test registers   |
  | Test cache       |
  | Test memory ctrl |
  +--------+---------+
           |
           v
  +------------------+
  | Memory Init      |
  | SPD read via I2C |
  | DRAM training    |
  | ECC scrub        |
  +--------+---------+
           |
           v
  +------------------+
  | Device Init      |
  | PCI enumeration  |
  | Video BIOS init  |
  | Display "BIOS    |
  | screen"          |
  +--------+---------+
           |
           v
  +------------------+
  | Beep codes if    |
  | failure (e.g.,   |
  | 1-3-3 = RAM fail)|
  +--------+---------+
           |
           v
  +------------------+
  | INT Table Setup  |
  | IVT at 0x0000    |
  | BDA at 0x0400    |
  +--------+---------+
           |
           v
  +------------------+
  | Boot Device      |
  | Selection        |
  | Check CMOS boot  |
  | order            |
  +--------+---------+
           |
           v
  +------------------+
  | Load MBR         |
  | INT 13h read     |
  | sector 0 → 0x7C00|
  | Verify 0x55AA    |
  +--------+---------+
           |
           v
  +------------------+
  | Jump to 0x7C00   |
  | Bootloader runs  |
  +------------------+

POST failures before video initialization are communicated via beep codes through the system speaker (e.g., AMI BIOS: 1 short = POST pass, 1 long + 3 short = video card failure). After video is available, POST codes are written to I/O port 0x80 and displayed on a debug LED card.

BIOS Memory Map

The x86 real mode address space is 1MB (20-bit address bus via A20 line through A19). The layout is rigidly defined:

Physical Memory Map (Real Mode, 1MB Address Space)

0x00000 +----------------------------+ 0KB
        | Interrupt Vector Table     |
        | 256 vectors × 4 bytes each |
        | (segment:offset far ptrs)  |
0x00400 +----------------------------+ 1KB
        | BIOS Data Area (BDA)       |
        | COM port addresses,        |
        | keyboard state, video mode |
        | memory size, etc.          |
0x00500 +----------------------------+ 1.25KB
        | Free Conventional Memory   |
        |                            |
        | DOS/OS loaded here         |
        | Available to programs      |
        |                            |
0x07C00 + - - - - - - - - - - - - -  + 31KB
        | MBR loaded here by BIOS    |
        | (512 bytes)                |
0x07E00 + - - - - - - - - - - - - -  +
        |                            |
        | (still free conventional)  |
        |                            |
0x9FC00 +----------------------------+ 639KB
        | Extended BIOS Data Area    |
        | (EBDA) — keyboard buffer,  |
        | hard disk params, etc.     |
0xA0000 +----------------------------+ 640KB  <-- "640KB Barrier"
        | Video RAM                  |
        | (EGA/VGA frame buffer)     |
0xB8000 + - - - - - - - - - - - - -  +
        | CGA text mode buffer       |
0xC0000 +----------------------------+ 768KB
        | Video BIOS ROM             |
        | (adapter firmware)         |
0xC8000 +----------------------------+ 800KB
        | Expansion ROM space        |
        | (NIC, SCSI ROMs)           |
0xF0000 +----------------------------+ 960KB
        | System BIOS ROM            |
        | (main BIOS code)           |
0xFFFF0 + - - - - - - - - - - - - -  |
        | Reset vector (far JMP)     |
0xFFFFF +----------------------------+ 1MB

The 640KB conventional memory limit is the single most consequential design artifact in PC history. It forced DOS into memory management contortions (EMS, XMS, UMBs, HMA) and constrained PC software architecture for fifteen years.

BIOS INT Services

BIOS provides a set of software interrupt services that abstract hardware access. These remain available after POST and are used by early bootloaders to access hardware before OS drivers load.

INT 10h — Video Services - AH=0x00: Set video mode (80×25 text, various graphics modes) - AH=0x0E: Teletype output (write character to screen) - AH=0x0F: Get current video mode - AH=0x13: Write string (BIOS 1984+)

INT 13h — Disk Services - AH=0x02: Read sectors (CHS addressing — Cylinder, Head, Sector) - AH=0x03: Write sectors - AH=0x41: Check INT 13h extensions present (LBA support) - AH=0x42: Extended read sectors (LBA — Logical Block Address) - AH=0x48: Get drive parameters (returns geometry, LBA support flags)

INT 13h extensions (introduced with Phoenix BIOS 3.0 in 1996) added LBA addressing, breaking the 8GB CHS limit. Before LBA extensions, disks were addressed as C:H:S tuples with a maximum of 1024 cylinders × 255 heads × 63 sectors = ~8GB.

INT 15h — Memory/Miscellaneous Services - AX=0xE820: Get memory map (the canonical way to discover RAM layout above 1MB) - AX=0xE801: Get extended memory size - AH=0x88: Get extended memory size (older, max 64MB)

INT 15h/E820 is critical: it returns a list of physical memory ranges tagged as available, reserved, ACPI, or non-volatile. Every OS bootloader calls this to build its physical memory map before enabling paging.

MBR Structure

The Master Boot Record occupies the first 512-byte sector of a disk. Its structure is fixed:

MBR Layout (512 bytes)

Offset  Size    Field
------  ----    -----
0x000   446B    Bootstrap code (x86 machine code, 16-bit real mode)
0x1BE    16B    Partition entry 1
0x1CE    16B    Partition entry 2
0x1DE    16B    Partition entry 3
0x1EE    16B    Partition entry 4
0x1FE     2B    Boot signature: 0x55 0xAA

Partition Entry Format (16 bytes):
  Byte 0:      Status (0x80 = bootable, 0x00 = inactive)
  Bytes 1-3:   CHS of first sector
  Byte 4:      Partition type (0x83 = Linux, 0x82 = swap, 0x07 = NTFS)
  Bytes 5-7:   CHS of last sector
  Bytes 8-11:  LBA of first sector (little-endian 32-bit)
  Bytes 12-15: Number of sectors (little-endian 32-bit)

The BIOS loads sector 0 into physical address 0x7C00 and jumps to it after verifying the 0x55AA magic. The 446-byte bootstrap code cannot contain a full bootloader — it typically contains a first-stage loader that reads additional sectors from the disk. GRUB2's boot.img fits in this 446-byte space and reads core.img from the sectors immediately following (the "MBR gap" between sector 0 and the first partition).

BIOS Limitations

1MB addressing constraint: The 20-bit A20 line caps real mode at 1MB. The "A20 gate" is a hardware kludge from IBM's AT design that must be explicitly enabled to access memory above 1MB. Every bootloader must enable A20 (via I/O port 0x92 or the keyboard controller command 0xDD) before switching to protected mode.

2TB disk limit with MBR: The 32-bit LBA field in the partition table limits addressable sectors to 2^32 × 512 bytes = 2TB. Disks larger than 2TB require GPT (GUID Partition Table), which requires UEFI.

4 primary partitions: MBR allows only 4 partition entries. Extended partitions with logical partitions inside are a workaround requiring additional complexity in bootloaders. GPT supports 128 entries by default.

No driver model: BIOS services are INT-based, not modular. Adding support for new hardware requires reflashing the BIOS.

No runtime update mechanism: BIOS updates require a DOS flash utility or vendor-specific tool, with brick risk if interrupted.

Transition to Protected Mode

The real mode to protected mode transition is a required step before an OS can access memory above 1MB and use 32-bit or 64-bit addressing. The sequence is:

Real Mode → Protected Mode Transition:

1. Disable interrupts (CLI)
   - IDT not yet valid for protected mode

2. Load GDT (Global Descriptor Table)
   - LGDT instruction: loads GDT register
   - GDT has at minimum: null descriptor, code seg, data seg

3. Enable A20 gate
   - I/O port 0x92 fast A20 enable
   - or keyboard controller method

4. Set PE bit in CR0
   - MOV EAX, CR0
   - OR  EAX, 1
   - MOV CR0, EAX

5. Far JMP to flush instruction pipeline
   - JMP CODE_SEG:protected_mode_entry
   - Forces CS reload from GDT

6. Initialize segment registers
   - MOV AX, DATA_SEG
   - MOV DS, AX  (and ES, FS, GS, SS)

7. Setup stack
   - MOV ESP, stack_top

8. Enable interrupts if needed
   - Load IDT (LIDT)
   - STI

Long mode (64-bit) requires an intermediate step through protected mode with PAE paging enabled, then setting the LME bit in EFER MSR.

Production Examples

Legacy server boot debugging: On bare-metal servers with BIOS firmware (pre-2012 servers are common in enterprise), POST failures are diagnosed via serial console. Most server BIOS implementations redirect POST output to a serial port at 115200 8N1. The POST code port 0x80 is monitored by server management controllers (BMC/iDRAC/iLO).

Virtual machines: QEMU's SeaBIOS (an open-source BIOS implementation) follows the same INT service protocol. SeaBIOS is used in millions of QEMU VMs and is instrumentable for boot timing analysis: CONFIG_DEBUG_LEVEL=4 enables verbose POST output to the QEMU debug console.

Embedded systems: Many embedded x86 boards (industrial controllers, kiosk systems) still use Coreboot with BIOS payload for compatibility with legacy bootloaders. Coreboot replaces the full BIOS initialization while maintaining the MBR boot convention.

Debugging Notes

Reading INT 13h return codes: After any INT 13h call, AH contains a status code. 0x00 = success, 0x04 = sector not found, 0x80 = timeout (drive not responding). These codes map to BIOS-era error messages ("No boot device found").

POST beep code interpretation: AMI, Award, and Phoenix BIOS use different beep code schemes. A single short beep on AMI = POST pass; 1 long + 3 short = display card failure; continuous beep = RAM not seated.

Memory test failures: If BIOS memory test fails on a specific address, it indicates failing DIMM. BIOS typically marks the bad range and reports reduced memory. memtest86 runs directly from BIOS as a bootable image and performs thorough test patterns.

A20 gate issues: Systems that fail to boot after loading the kernel from a legacy BIOS often have A20 not properly enabled. Symptom: kernel execution wraps at 1MB boundary.

Security Implications

BIOS security is essentially nonexistent by modern standards:

No signature verification: Classic BIOS loads and executes MBR code with no signature check. Any code in sector 0 runs. This enabled the entire class of MBR bootkits (Stoned, TDL, Mebroot). BIOS-era rootkits would replace the MBR, intercept INT 13h to hide their presence, and load before the OS.

Flash write protection often disabled: BIOS flash is usually writable from OS-level code. The CIH virus (1998) destroyed BIOS flash on millions of systems by writing garbage via the chipset's flash programming interface.

CMOS password bypass: The BIOS password stored in CMOS is trivially bypassed by removing the CMOS battery or shorting the CMOS clear jumper. Server BIOS often stores passwords in a separate SEEPROM, somewhat harder to clear.

Expansion ROM execution: BIOS executes option ROMs from add-in cards (video cards, NIC adapters) without verification. A malicious NIC or RAID controller could present a modified option ROM. This vector was used in the NSA ANT catalog's DEITYBOUNCE implant.

Performance Implications

POST time is a real concern in production environments:

Server cold boot latency: Full POST on enterprise servers (memory scrubbing, PCI enumeration, RAID initialization) takes 3–5 minutes for large memory configurations (1TB+ RAM). Memory scrubbing alone at ~10GB/s scrubbing rate takes ~100 seconds for 1TB.

Quick Boot / Fast Boot options: Most BIOS implementations offer abbreviated POST that skips memory test and accelerates enumeration. This reduces boot time to 15–30 seconds but masks intermittent hardware failures.

Memory training overhead: DDR memory controller training (finding optimal timing parameters) adds 30–60 seconds per cold boot on BIOS systems. Results are cached in CMOS and reused on warm boot.

Failure Modes

BIOS corruption: Power loss during BIOS flash leaves the system unbootable. Enterprise motherboards have dual-BIOS or BIOS recovery from USB. Consumer boards may offer a "BIOS Flashback" feature that can update from USB without CPU/RAM installed.

CMOS battery failure: BIOS settings (including boot order) are stored in CMOS RAM backed by a CR2032 battery. When the battery fails, settings revert to defaults. Symptom: system reports wrong time, asks for date/time on boot.

Option ROM conflicts: Multiple add-in cards with option ROMs may conflict in the 0xC0000–0xF0000 address range. BIOS must shuffle ROMs into available windows, occasionally failing with "no space for option ROM" errors.

MBR corruption: Partition table corruption in the MBR causes "Invalid partition table" or "No bootable device" errors. Recovery requires dd to write a known-good MBR or fdisk/testdisk to reconstruct the partition table.

Modern Usage

BIOS is largely obsolete on new hardware shipped after 2012, but remains relevant in:

  • Legacy enterprise infrastructure: Banks and telecoms running servers from 2008–2015 still operate BIOS-only systems
  • Industrial embedded x86: PLCs and industrial computers with 10-15 year operational lifetimes
  • Coreboot deployments: Chromebooks use Coreboot (which can boot in BIOS legacy mode for alternative OS installation)
  • QEMU/KVM default: QEMU defaults to SeaBIOS for non-OVMF VMs

Legacy boot mode (CSM — Compatibility Support Module) in UEFI firmware emulates BIOS INT services for older operating systems. CSM is being phased out: Windows 11 requires UEFI native mode, and major Linux distributions have dropped BIOS boot support from their installers.

Future Directions

BIOS as a standard is dead. UEFI with Secure Boot is the mandatory baseline for modern hardware. The remaining trajectory:

  • Coreboot expansion: Google's Coreboot project replaces BIOS initialization with open-source code on Chromebooks, some ThinkPads (via Heads project), and ODM servers
  • UEFI with measured boot: TPM integration makes firmware the root of a hardware-backed trust chain (see 08-tpm-and-measured-boot.md)
  • ARM UEFI: ARM servers use UEFI natively; the concept of BIOS never existed on ARM
  • Stateless firmware: Cloud providers are moving toward stateless UEFI implementations where firmware state is reconstructed at boot rather than stored in NVRAM

Exercises

  1. MBR Inspection: On a Linux system with a BIOS-booted disk, run dd if=/dev/sda bs=512 count=1 | xxd | head -40. Identify the bootstrap code region, partition entries, and 0x55AA signature. Decode one partition entry manually to find the LBA start and size.

  2. INT 15h E820 Map: Write a 16-bit real mode assembly program (bootable via QEMU) that calls INT 15h/E820 to enumerate the memory map and prints each range's base, length, and type. Compare output to /proc/iomem on a running Linux system.

  3. POST Code Monitor: Attach a POST code card (or use QEMU's -device isa-debug-exit,iobase=0x80 equivalent) to capture POST codes during boot. Document the POST sequence for a specific BIOS implementation.

  4. MBR Recovery Lab: In a QEMU VM, intentionally corrupt the MBR with dd if=/dev/zero of=/dev/sda bs=446 count=1. Boot from a live CD, use testdisk to recover the partition table, and restore a working MBR with grub-install or ms-sys.

  5. Real Mode Memory Map: Write a program using INT 12h and INT 15h/88h to query conventional memory size and extended memory size. Cross-reference with the E820 map from exercise 2. Explain any discrepancies.

References

  • IBM PC Technical Reference Manual (1981) — Original BIOS source listing
  • Ralf Brown's Interrupt List (1995–2000) — Comprehensive BIOS INT documentation: https://www.cs.cmu.edu/~ralf/files.html
  • OSDev Wiki, "BIOS": https://wiki.osdev.org/BIOS
  • SeaBIOS source: https://www.seabios.org/SeaBIOS
  • "The Art of Assembly Language" — Randall Hyde, Chapter on BIOS services
  • Linux kernel arch/x86/boot/ — real mode boot setup code (setup.asm, memory.c)
  • SYSLINUX Project — reference implementation of 16-bit BIOS bootloader
  • "Protected Mode Software Architecture" — Tom Shanley (MindShare Press)