By design, each driver process has read access to the configuration space of the device it's managing, via the libpci and capability module APIs.
This is enabled by the hardware-dependent module when it's demand-loaded on the first hardware access. All configuration space writes are handled by the PCI server. This design allows for optimal efficiency for reads because there's no context switch as a result of a message pass when performing read operations.
Access to device configuration space registers is facilitated through a set of read and write APIs. Most read APIs require only a BDF (Bus/Device/Function—Bus is synonymous with a PCIe link) value of type pci_bdf_t. This value can be constructed using the PCI_BDF() macro, but more usually it's obtained from a successful call to pci_device_find().
Write APIs and APIs that provide certain types of information (such as address space and interrupt information) require a handle (of type pci_devhdl_t) that you obtain from a successful call to pci_device_attach().
Read and Write APIs for common configuration space registers are provided by specifically named APIs (see the API Reference chapter for details). For example, to read and write the command register, use pci_device_read_cmd() and pci_device_write_cmd(). This provides a self-documenting interface for driver code. Access to device specific registers is provided by the pci_device_cfg_rd*() and pci_device_cfg_wr*() APIs.