Column 3: Two levels of bare-metal programming (2011-07-13)

To understand the essence of Coreboot, it is important to understand that there are two levels of bare metal programming on the PC. I will illustrate this with the RS232 serial port, under DOS known as COM1. There are generally two ways to use the serial port.

If you consider this programming at the bare metal, you ain't seen nothing yet. Some software had already configured the SuperIO chip so it would map the UART at I/O address 0x3f8 and to configure its interrupt output correctly. Further, several chips had to be configured to provide access to the SuporIO chip.

Coreboot is split into two levels:

The easy part

When you boot up the Linux kernel (or any modern OS), it disables the BIOS services (effectively by changing to protected mode and reloading the Interrupt Descriptor Table, so the BIOS interrupts cannot work the way they did in real mode). All further accesses to devices by the kernel are at the bare metal, i.e. direct access to I/O registers. But this is the easy high level of bare metal. The same Linux kernel (including a limited set of device drivers) can run on nearly every standard PC compatible machine in the world.

At the higher level of bare metal, RAM just works and all familiar I/O registers (8259 programmable interrupt controller, 8254 timer) are available at their standard I/O addresses. The PCI bus is configured, the VGA hardware is in a sane state and lots of other tiny but important things.

Originally Coreboot was called LinuxBIOS. The original intended payload was a small Linux system: a kernel plus a small root file system. As many BIOS chips have a capacity of only 512kB, they do not have sufficient capacity for a usable Linux system. Hence it never became popular. But if you do have enough capacity, you can use Linux as a boot loader. Linux can load and run a different kernel if this is desired, so the kernel in flash can obtain the kernel for the production system and run that instead of itself. The great advantage of a small Linux system over a traditional boot loader is that it has any device drivers that you may want, whether for storage media or for network interfaces and that these drivers tend to be efficient and reliable. Linux has a full-fledged USB subsystem, it has a full-fledged TCP/IP stack.

Instead of a Linux system, several other Coreboot payloads have been developed.

Several Coreboot payloads can coexist in one ROM and some payloads can provide a menu to select other payloads. SeaBIOS is one program that can do it.

Nearly all those payloads will work unmodified on almost any PC and of course they will run under a PC emulator such as QEMU.

The hard part

When you power up a PC, the chips that make up the chipset (northbridge, southbridge and SuperIO) are not yet initialized properly. Even though the BIOS ROM is as far removed from the CPU as it could be, this is accessible by the CPU, because it has to be, otherwise the CPU would have no instructions to execute. This does not mean that BIOS ROM is completely mapped, usually not. But just enough is mapped to get the boot process going. Any other devices, just forget it.

When you run Coreboot under QEMU, you can experiment with the higher layers of Coreboot and with payloads, but QEMU offers little opportunity to experiment with the low level startup code. For one thing, RAM just works right from the start.

There are four reasons why the startup code is harder than the payload code.

Coreboot does two things to make thing a bit easier for developers

Supposedly this is contrary to all known proprietary BIOSes, which run in 16-bit real mode and are almost completely written in assembly language.

Getting into 32-bit protected mode is comparatively easy. It can be done in about 16 instructions from reset. It involves loading the GDT (Global Descriptor Table) to a table stored in ROM that contains one code segment descriptor and one data segment descriptor. Then a status bit has to be set. Next a far jump (to the newly defined code segment) has to be carried out. Finally the segment registers (DS, SS and possibly ES, FS and GS) have to be reloaded.

Running C code is a bit more difficult. The problem is that the code generated by any standard C compiler for the x86 CPU will heavily depend on RAM. System RAM is not enabled yet and the code to enable it is so complex that you really want to do it in C. Two solutions have been devised and both of these are in use (one or the other depending on the hardware).

When Coreboot starts up, it runs through the following stages