Software Implementation
Configuration reads and writes can be initiated from the CPU in two ways: one legacy method via I/O addresses 0xCF8 and 0xCFC, and another called memory-mapped configuration.
The legacy method was present in the original PCI, and it is called Configuration Access Mechanism (CAM). It allows for 256 bytes of a device's address space to be reached indirectly via two registers called PCI CONFIG_ADDRESS and PCI CONFIG_DATA. These registers live at addresses 0xCF8 and 0xCFC in the x86 I/O address space. For example, a software driver (firmware, OS kernel or kernel driver) can use these registers to configure a PCI device by putting the data that is supposed to be written to the device into CONFIG_DATA, and by writing the address of the device's register into CONFIG_ADDRESS. Since this process requires a write to a register in order to write the device’s register, it is referred to as "indirection."
The format of CONFIG_ADDRESS is the following:
bus << 16 | device << 11 | function << 8 | offsetAs explained previously, addressing a device via Bus, Device, and Function (BDF) is also referred to as "addressing a device geographically." See arch/i386/pci/early.c in the Linux kernel code for an example of code that uses geographical addressing.
The second method was created for PCI Express. It is called Enhanced Configuration Access Mechanism (ECAM). It extends device's configuration space to 4k, with the bottom 256 bytes overlapping the original (legacy) configuration space in PCI. The section of the addressable space is "stolen" so that the accesses from the CPU don't go to memory but rather reach a given device in the PCI Express fabric. During system initialization, firmware determines the base address for this “stolen” address region and communicates it to the root complex and to the operating system. This communication method is implementation-specific, and not defined in the PCI express specification.
Read more about this topic: PCI Configuration Space