Linux kernel point of view
A good way to learn something is to interact with it, so let's use the Linux kernel for that.
Here is a minimal PCI example on a QEMU emulated device: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/366b1c1af269f56d6a7e6464f2862ba2bc368062/kernel_module/pci.c
The first 64 bytes of the PCI configuration are standardized as:
Image from LDD3.
So we can see that there are 6 BARs. The wiki page then shows the contents of each BAR:
The region width requires a magic write however: How is a PCI / PCIe BAR size determined?
This memory is setup by the PCI device, and gives information to the kernel.
Each BAR corresponds to an address range that serves as a separate communication channel to the PCI device.
The length of each region is defined by the hardware, and communicated to software via the configuration registers.
Each region also has further hardware defined properties besides length, notably the memory type:
IORESOURCE_IO
: must be accessed with inX
and outX
IORESOURCE_MEM
: must be accessed with ioreadX
and iowriteX
Several Linux kernel PCI functions take the BAR
as a parameter to identify which communication channel is to be used, e.g.:
mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR));
pci_resource_flags(dev, BAR);
pci_resource_start(pdev, BAR);
pci_resource_end(pdev, BAR);
By looking into the QEMU device source code, we see that QEMU devices register those regions with:
memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
"edu-mmio", 1 << 20);
pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);
and it is clear that properties of the BAR are hardware defined, e.g. the BAR number 0
, has type memory PCI_BASE_ADDRESS_SPACE_MEMORY
, and the memory region is 1MiB long 1 << 20
.
See also: http://wiki.osdev.org/PCI#Base_Address_Registers of course.