Column 9: Configuring PCI. (2011-08-24)

The main task of Coreboot is initializing the hardware, including the peripheral devices. Coreboot has to provide a device tree to the payload, so it knows which devices are present and where they can be found. Even payloads that scan the PCI bus themselves (and do not make use of the device tree), such as the Linux kernel expect the PCI devices to be configured in a certain way. In particular all memory-mapped registers must be mapped before the Linux kernel starts running.

On modern x86 PCs. device access is centered around the PCI bus, especially from a software point of view. PCI software protocols are used for much more than just the physical PCI bus. AGP, PCIe, and internal chips on the motherboard (both northbridge and southbridge) are all programmed in a way very similar to real PCI devices. For instance the USB host controllers and SATA controller inside the southbridge chip each have their own PCI ID.

The PCI Bus

The basic PCI-bus runs at 33MHz and has 32 data lines. The same 32-bit bus is used for both data and addresses. When a transfer is started, the first bus cycle carries the address and subsequent bus cycles carry data. When data is transferred in large blocks, almost all bus cycles are data transfers and theoretical speed of 133Mbytes/s can be achieved. There exist 64-bit and 66MHz versions of the PCI bus, which support higher transfer rates, but these are rare in normal PCs. Modern PCs use PCI Express instead, which offers 250MByte/s for the simplest variant (1 bit lane), but considerably more for x8 and x16 slots (8 or 16 bit lanes).

Device registers are mainly memory-mapped, though the PCI standard also supports I/O-mapped devices. Some devices (such as video cards contain RAM, which is of course also memory-mapped. Memory mapping is not static (as it was on the ISA bus), but it can be configured through configuration registers. Allocating the proper address ranges to each of the PCI devices is one of the tasks performed by Coreboot.

Configuration registers also provide a unique ID (16 bit vendor ID and 16 bit device ID) for each type of device. Each vendor (such as Intel or Realtek) has a 16-bit vendor ID and each vendor is responsible for assigning unique device IDs to each of its products. Of course the same mechanism is also used for internal chips on the motherboard and for AGP or PCIe devices, but it is even more widely used. The vendor ID/device ID mechanism is also used for the USB bus and for ISA PnP. The physical ISA bus is long dead, but some SuperIO chips use the same mechanism to identify themselves or their subdevices.

PCI devices can support bus mastering. As opposed to the ISA bus, the PCI bus does not have a centralized DMA controller. Instead, each PCI device is itself responsible for generating the required bus cycles for accessing main memory. Of course each device has to request the bus master access and has to wait until it is granted. So the PCI bus has to contain a bus arbiter.

Traditional PCI devices support up to four interrupts, but most use only one interrupt. Coreboot also has to find out which interrupt lines are connected to which devices.

For more information on PCI see the Wikipedia article about PCI.

Configuration space

Each PCI device has a configuration space of 256 bytes (which could be extended to 4kB). The first few registers in this configuration space have standardized meanings. The first four bytes are read-only and specify the device and vendor ID. A few 32-bit registers starting at address 0x10 specify the base address registers (BARs). A PCI-device can have several addressable resources (register set, memory space), each of which can be mapped to a different address. Practically speaking, all addresses accessible from the PCI bus are 32-bit, so BAR registers on most devices are 32-bit as well.

While the main registers of each device are memory-mapped once they are configured, the configuration registers are only accessible indirectly. Essentially all PCI chipsets on x86 systems support the following mechanism using just two I/O registers:

There are different mechanisms, which do not require two I/O instructions to access each configuration register.

Bus 0 represents the devices internal to the chipset. Each physical bus (AGP, PCI, PCIe) has its own distinct bus number. Each bus can have up to 32 different device numbers (one per slot) and each device can have up to 8 different functions, each of which has its own configuration space with its own device ID. Some devices are bridges, which provide access to another bus. In a normal PC. all bridges are included in the chipset (northbridge, southbridge).

Bus enumeration

The ROM stage of Coreboot has already performed some accesses to configuration registers, just to get access to the SMBus and the northbridge, so RAM access can be set up. Device configuration starts in earnest in the RAM stage.

Coreboot starts out by scanning bus 0. It tries to read the device and vendor ID for every device on that bus. If it reads 0xffffffff, it knows that no device exists at that position. Scanning a bus to find all possible devices on it, is called enumeration. If it finds a device that it knows, it can further initialize it. There might even be an option ROM for that device, which Coreboot can run. But even if Coreboot does not know a specific device, it can still perform some configuration. Procedures to configure base address registers are standardized. Each device will be added to the device tree in memory. If the device represents a bridge (PCI or otherwise), Coreboot configures it first, next it enumerates the bus that belongs to it. Each bridge adds another bus to the system.

First Coreboot enumerates all devices on all buses and it records the sizes of each of the memory mapped resources. Then it allocates addresses for these resources, starting at the largest address range, moving down to smaller ranges. When a device resource has an address, Coreboot programs the base address registers to actually map this address.