PCI(4)
π PCI(4) β pci: generic PCI/PCIe bus driver π
okay bestie, welcome to PCI(4) from the Kernel Interfaces Manual β yes, this is the pci driver page, and we are doing the whole thing, no skipping, no βtl;drβ erasure. Group-chat recap: itβs the generic PCI/PCIe bus driver, and itβs lowkey a big deal. π
πΈ NAME πΈ
pci β generic PCI/PCIe bus driver
β¨ SYNOPSIS β¨
To compile the PCI bus driver into the kernel, place this line in your kernel configuration file:
device pci
To compile in support for Single Root I/O Virtualization (SR-IOV):
options PCI_IOV
To compile in support for native PCI-express HotPlug:
options PCI_HP
π¦ DESCRIPTION π¦
The pci driver gives the kernel support for PCI and PCIe devices, plus limited access to PCI devices for userland. Itβs giving βkernel bus driver with a little userland side door,β and honestly? iconic. β¨
The pci driver provides a /dev/pci character device that userland programs can use to read and write PCI configuration registers. Programs can also use this device to get a list of all PCI devices, or all PCI devices that match various patterns.
Real talk, bestie: since thepcidriver provides a write interface for PCI configuration registers, system administrators should exercise caution when granting access to thepcidevice. If used improperly, this driver can allow userland applications to crash a machine or cause data loss. In particular, the driver only allows operations on the opened/dev/pcito modify system state if the file descriptor was opened for writing. For instance, thePCIOCREADandPCIOCBARMMAPoperations require a writeable descriptor, because reading a config register or a BAR read access could have function-specific side-effects. No cap, that is a real warning β not a vibe check. π₯
The pci driver implements the PCI bus in the kernel. It enumerates any devices on the PCI bus and gives PCI client drivers the chance to attach to them. It assigns resources to children, when the BIOS does not. It takes care of routing interrupts when necessary. It reprobes the unattached PCI children when PCI client drivers are dynamically loaded at runtime. The pci driver also includes support for PCI-PCI bridges, various platform-specific Host-PCI bridges, and basic support for PCI VGA adapters. Like, itβs doing a lot. π«Ά
π₯ IOCTLS π₯
The following ioctl(2) calls are supported by the pci driver. They are defined in the header file <sys/pciio.h>.
π PCIOCGETCONF π
This ioctl(2) takes a pci_conf_io structure. It lets the user retrieve information on all PCI devices in the system, or on PCI devices matching patterns supplied by the user. The call may set errno to any value specified in either copyin(9) or copyout(9). The pci_conf_io structure consists of a number of fields:
pat_buf_lenβ the length, in bytes, of the buffer filled with user-supplied patterns.num_patternsβ the number of user-supplied patterns.patternsβ pointer to a buffer filled with user-supplied patterns.patternsis a pointer tonum_patternspci_match_confstructures. Thepci_match_confstructure consists of the following elements:pc_selβ PCI domain, bus, slot and function.pd_nameβ PCI device driver name.pd_unitβ PCI device driver unit number.pc_vendorβ PCI vendor ID.pc_deviceβ PCI device ID.pc_classβ PCI device class.flagsβ the flags describe which of the fields the kernel should match against. A device must match all specified fields in order to be returned. The match flags are enumerated in thepci_getconf_flagsstructure. Hopefully the flag values are obvious enough that they do not need to be described in detail.
match_buf_lenβ length of the matches buffer allocated by the user to hold the results of thePCIOCGETCONFquery.num_matchesβ number of matches returned by the kernel.matchesβ buffer containing matching devices returned by the kernel. The items in this buffer are of typepci_conf, which consists of the following items:pc_selβ PCI domain, bus, slot and function.pc_hdrβ PCI header type.pc_subvendorβ PCI subvendor ID.pc_subdeviceβ PCI subdevice ID.pc_vendorβ PCI vendor ID.pc_deviceβ PCI device ID.pc_classβ PCI device class.pc_subclassβ PCI device subclass.pc_progifβ PCI device programming interface.pc_revidβ PCI revision ID.pd_nameβ driver name.pd_unitβ driver unit number.pd_numa_domainβ device NUMA domain.pc_reported_lenβ length of the valid portion of the encompassingpci_confstructure. This should always be equivalent to the offset of thepc_sparemember.pc_secbusβ secondary PCI bus number. (Only valid for bridge devices)pc_subbusβ subordinate PCI bus number. (Only valid for bridge devices)pc_spareβ reserved for future use.
offsetβ the offset is passed in by the user to tell the kernel where it should start traversing the device list. The value passed out by the kernel points to the record immediately after the last one returned. The user may pass the value returned by the kernel in subsequent calls to thePCIOCGETCONFioctl. If the user does not intend to use the offset, it must be set to zero.generationβ PCI configuration generation. This value only needs to be set if the offset is set. The kernel will compare the current generation number of its internal device list to the generation passed in by the user to determine whether its device list has changed since the user last called thePCIOCGETCONFioctl. If the device list has changed, a status ofPCI_GETCONF_LIST_CHANGEDwill be passed back.statusβ the status tells the user the disposition of their request for a device list. The possible status values are:PCI_GETCONF_LAST_DEVICEβ this means that there are no more devices in the PCI device list matching the specified criteria after the ones returned in thematchesbuffer.PCI_GETCONF_LIST_CHANGEDβ this status tells the user that the PCI device list has changed since their last call to thePCIOCGETCONFioctl and they must reset the offset and generation to zero to start over at the beginning of the list.PCI_GETCONF_MORE_DEVSβ this tells the user that their buffer was not large enough to hold all of the remaining devices in the device list that match their criteria.PCI_GETCONF_ERRORβ this indicates a general error while servicing the userβs request. If thepat_buf_lenis not equal tonum_patternstimessizeof(struct pci_match_conf),errnowill be set toEINVAL.
π PCIOCREAD π
This ioctl(2) reads the PCI configuration registers specified by the passed-in pci_io structure. The pci_io structure consists of the following fields:
pi_selβ apciselstructure which specifies the domain, bus, slot and function the user would like to query. If the specific bus is not found,errnowill be set toENODEVand-1returned from the ioctl.pi_regβ the PCI configuration registers the user would like to access.pi_widthβ the width, in bytes, of the data you wanna read. This value may be either1,2, or4. 3-byte reads and reads larger than 4 bytes are not supported, bestie. If an invalid width is passed,errnowill be set toEINVAL.pi_dataβ the data returned by the kernel. β¨
PCIOCWRITE β this ioctl(2) lets users write to the PCI configuration registers specified in the passed-in pci_io structure. The pci_io structure is described above. The limitations on data width described for reading registers, above, also apply to writing PCI configuration registers. Same energy, same rules π
PCIOCATTACHED β this ioctl(2) lets users query if a driver is attached to the PCI device specified in the passed-in pci_io structure. The pci_io structure is described above, however, the pi_reg and pi_width fields are not used. The status of the device is stored in the pi_data field. A value of 0 indicates no driver is attached, while a value larger than 0 indicates that a driver is attached. iykyk π«Ά
PCIOCBARMMAP β this ioctl(2) command lets userspace processes mmap(2) the memory-mapped PCI BAR into its address space. The input parameters and results are passed in the pci_bar_mmap structure, which has the following fields:
void *pbm_map_baseβ reports the established mapping base to the caller. IfPCIIO_BAR_MMAP_FIXEDflag was specified, then this field must be filled before the call with the desired address for the mapping.size_t pbm_map_lengthβ reports the mapped length of the BAR, in bytes. Itssize_tvalue is always multiple of machine pages.uint64_t pbm_bar_lengthβ reports length of the bar as exposed by the device.int pbm_bar_offβ reports offset from the mapped base to the start of the first register in the bar.struct pcisel pbm_selβ should be filled before the call. Describes the device to operate on.int pbm_regβ the BAR index to mmap.int pbm_flagsβ flags which augments the operation. See below.int pbm_memattrβ the caching attribute for the mapping. Typical values areVM_MEMATTR_UNCACHEABLEfor control registers BARs, andVM_MEMATTR_WRITE_COMBININGfor frame buffers. Regular memory-like BAR should be mapped withVM_MEMATTR_DEFAULTattribute.
Currently defined flags are:
PCIIO_BAR_MMAP_FIXEDβ the resulted mappings should be established at the address specified by thepbm_map_basemember, otherwise fail.PCIIO_BAR_MMAP_EXCLβ must be used together withPCIIO_BAR_MMAP_FIXED. If the specified base contains already established mappings, the operation fails instead of implicitly unmapping them.PCIIO_BAR_MMAP_RWβ the requested mapping allows both reading and writing. Without the flag, read-only mapping is established. Note that it is common for the device registers to have side-effects even on reads. πPCIIO_BAR_MMAP_ACTIVATEβ (Unimplemented) If the BAR is not activated, activate it in the course of mapping. Currently attempt to mmap an inactive BAR results in error.
PCIOCBARIO β this ioctl(2) command lets users read from and write to BARs. The I/O request parameters are passed in a struct pci_bar_ioreq structure, which has the following fields:
struct pcisel pbi_selβ describes the device to operate on.int pbi_opβ the operation to perform. Currently supported values arePCIBARIO_READandPCIBARIO_WRITE.uint32_t pbi_barβ the index of the BAR on which to operate.uint32_t pbi_offsetβ the offset into the BAR at which to operate.uint32_t pbi_widthβ the size, in bytes, of the I/O operation. 1-byte, 2-byte, 4-byte and 8-byte operations are supported.uint32_t pbi_valueβ for reads, the value is returned in this field. For writes, the caller specifies the value to be written in this field.
Note that this operation maps and unmaps the corresponding resource and so is relatively expensive for memory BARs. ThePCIOCBARMMAPioctl(2)can be used to create a persistent userspace mapping for such BARs instead. Lowkey important π«‘
β¨ LOADER TUNABLES β¨
Okay so these are all the little knobs you can twist π
You can set them at the loader(8) prompt before the kernel boots, or stash them in loader.conf(5). Later, once you're actually running, you can peek at the current value through sysctl(8) nodes that have the same name β cute, right? And unless it says otherwise, each one is a boolean, so setting it to a non-zero value = turned on. No cap.
hw.pci.clear_bars(Defaults to 0) β Straight-up ignores any memory and I/O port resources the firmware already assigned. It forces the PCI bus driver to allocate resource ranges for memory and I/O port stuff from scratch. Fresh start energy β¨hw.pci.clear_buses(Defaults to 0) β Ignores any bus number registers the firmware assigned in PCI-PCI bridges. This makes the PCI bus driver and the PCI-PCI bridge driver go allocate bus numbers themselves for the secondary buses sitting behind PCI-PCI bridges. π¦hw.pci.clear_pcib(Defaults to 0) β Ignores any firmware-assigned memory and I/O port resource windows in PCI-PCI bridges, forcing the PCI-PCI bridge driver to allocate memory and I/O port resources for those resource windows from scratch.
Okay so lowkey important context: by default the PCI-PCI bridge driver allocates windows that contain the firmware-assigned resources of the devices behind the bridge. On top of that, it'll suballocate from existing window regions when it can, to satisfy a resource request. Which means β and this is the key bit β you need both hw.pci.clear_bars and hw.pci.clear_pcib enabled if you genuinely want to fully ignore firmware-supplied resource assignments. Both. Not one. Both. π
hw.pci.default_vgapci_unit(Defaults to -1) β By default, the very first PCI VGA adapter the system runs into is assumed to be the boot display device. Set this tunable to pick a specific VGA adapter by giving it the unit number of the associatedvgapciXdevice. Main character selection fr πΈhw.pci.do_power_nodriver(Defaults to 0) β Puts devices into a low power state (D3) when no suitable device driver is found for them. It takes one of these values:3β Powers down all PCI devices that don't have a device driver.2β Powers down most devices without a device driver. PCI devices with the display, memory, and base peripheral device classes are not powered down.1β Similar to a setting of2, except that storage controllers are also not powered down.0β All devices are left fully powered.
Heads up bestie: a PCI device has to support power management to be powered down at all. And placing a device into a low power state may not actually reduce power consumption β no promises π
hw.pci.do_power_resume(Defaults to 1) β Puts PCI devices into the fully powered state when resuming either the whole system or an individual device. Setting this to zero is discouraged, because then the system won't try to power up non-powered PCI devices after a suspend. Like... don't πhw.pci.do_power_suspend(Defaults to 1) β Puts PCI devices into a low power state when suspending either the system or individual devices. Normally the D3 state is the low power state used, but firmware may override the desired power state during a system suspend. πhw.pci.enable_ari(Defaults to 1) β Enables support for PCI-express Alternative RID Interpretation. This is often used together with SR-IOV. Slay πhw.pci.enable_io_modes(Defaults to 1) β Enables memory or I/O port decoding in a PCI device's command register if it has firmware-assigned memory or I/O port resources. Here's the tea: the firmware (BIOS) on some systems doesn't enable memory or I/O port decoding for some devices even when it has assigned resources to the device. This turns decoding on for those resources during bus probe. π«Άhw.pci.enable_msi(Defaults to 1) β Enables support for Message Signalled Interrupts (MSI). You can disable MSI interrupts by setting this tunable to0.hw.pci.enable_msix(Defaults to 1) β Enables support for extended Message Signalled Interrupts (MSI-X). You can disable MSI-X interrupts by setting this tunable to0.hw.pci.enable_pcie_ei(Defaults to 0) β Enables support for PCI-express Electromechanical Interlock. βhw.pci.enable_pcie_hp(Defaults to 1) β Enables support for native PCI-express HotPlug.hw.pci.honor_msi_blacklist(Defaults to 1) β When this tunable is set, MSI and MSI-X interrupts get disabled for certain chipsets that are known to have broken MSI and MSI-X implementations. You can set it to zero to allow MSI and MSI-X interrupts to be used if the chipset match turns out to be a false positive. πhw.pci.iov_max_config(Defaults to 1MB) β The maximum amount of memory allowed for the configuration parameters used when creating Virtual Functions via SR-IOV. You can also change this tunable at runtime throughsysctl(8). π₯hw.pci.realloc_bars(Defaults to 0) β Tries to allocate a new resource range during the initial device scan for any memory or I/O port resources whose firmware-assigned ranges conflict with another active resource. π«hw.pci.usb_early_takeover(Defaults to 1 on amd64 and i386) β Disables legacy device emulation of USB devices during the initial device scan. Set this tunable to zero if you want to use USB devices via legacy emulation when you're running a custom kernel without USB controller drivers. π§hw.pci<D>.<B>.<S>.INT<P>.irqβ These tunables let you override the interrupt routing for legacy PCI INTx interrupts. And yes, this one's different from everything else in this list: these do not have correspondingsysctlnodes. The tunable name packs in the address of the PCI device plus the pin of the INTx IRQ you want to override:<D>β The domain (or segment) of the PCI device, in decimal.<B>β The bus address of the PCI device, in decimal.<S>β The slot of the PCI device, in decimal.<P>β The interrupt pin of the PCI slot to override. One ofβAβ,βBβ,βCβ, orβDβ.
The value of the tunable is the raw IRQ value to use for the INTx interrupt pin that the tunable name identifies. And mapping IRQ values to platform interrupt sources is machine dependent, so iykyk π
πΈ DEVICE WIRING πΈ
You can wire the device unit at a given location using device.hints(5). Two flavours, coming right up π
BSF Based Wiring
Devices can be wired to a Bus / Slot / Function (BSF) address β that's the form pciconf(8) reports. Entries that look like hints.<name>.<unit>.at="pci<B>:<S>:<F>" or hints.<name>.<unit>.at="pci<D>:<B>:<S>:<F>" will force the driver named name to probe and attach at unit unit for any PCI device that matches the spec. Where:
<D>β The domain (or segment) of the PCI device, in decimal. Defaults to0if you don't specify it.<B>β The bus address of the PCI device, in decimal.<S>β The slot of the PCI device, in decimal.<F>β The function of the PCI device, in decimal.
β οΈ The matching code needs an exact string match. Do not put the angle brackets (< >) in the hints file. And wiring multiple devices to the same name and unit produces undefined results β like, don't do that, bestie π
Examples
So say you've got these lines in /boot/device.hints:
hint.nvme.3.at="pci6:0:0"
hint.igb.8.at="pci14:0:0"
If there's a device that supports igb(4) at PCI bus 14 slot 0 function 0, it gets assigned igb8 for probe and attach. Same deal the other way: if there's an nvme(4) device at PCI bus 6 slot 0 function 0, it gets assigned nvme3 for probe and attach. β¨
Now β if a different type of card is sitting in either of those locations, that card keeps the default names and these hints don't touch it. And if other igb or nvme cards are somewhere else entirely, they get their unit numbers assigned sequentially, skipping the unit numbers that have 'at' hints. Iconic, honestly π
Location Based Wiring
So BSF wiring is easy to figure out physically β you know where to put the device. But the bus number? Not invariant, bestie. Literally any number of changes to the devices in the system can make that value vary from boot to boot π
The UEFI Standard defines a device path based only on the invariant parts of the address: the root complex (domain), the slot number and the function. These paths are hard to construct by hand, so please go look at the devctl(8) βgetpathβ command with a βUEFIβ locator. The example above could also be written as:
hint.nvme.3.at="PciRoot(0x2)/Pci(0x1,0x3)/Pci(0x0,0x0)/Pci(0x0,0x0)/Pci(0x0,0x0)"
hint.nvme.8.at="PciRoot(0x1)/Pci(0x2,0x2)/Pci(0x0,0x0)/Pci(0x0,0x0)"
The whole point of this notation is that you can specify the exact location a device will be at. For deployments of multiple systems with the same configuration, that's genuinely helpful for managing the devices π«Ά
But β and this is a big but β even slight variation in motherboards can cause the path to change substantially. Also it's less natural to think in UEFI Device Paths, since pretty much nothing else reports them π
π FILES π
/dev/pciβ Character device for thepcidriver. That's the one π
π¦ SEE ALSO π¦
Go read them, bestie, they're right there β¨
β HISTORY β
The pci driver (not the kernel's PCI support code, okay, keep up π
) first appeared in FreeBSD 2.2, and it was written by Stefan Esser and Garrett Wollman. Then support for device listing and matching got re-implemented by Kenneth Merry, and that first appeared in FreeBSD 3.0. Glow-up era π
π AUTHORS π
Kenneth Merry <ken@FreeBSD.org>
π BUGS π
Okay so here's the catch: users can't specify an accurate offset into the device list without calling the PCIOCGETCONF at least once, because otherwise they have literally no way of knowing the current generation number. Rude, honestly π
That said, it's probably not a serious problem, since users can easily narrow their search by specifying a pattern or patterns for the kernel to match against. So like... it's giving minor inconvenience, not catastrophe π
and that's the whole page, bestie!! π This one's the Debian build, dated March 10, 2026 β PCI(4), and yes she ate. Go forth and wire your devices responsibly π«Άβ¨
π¦ where this page comes from
- freebsd 15.1: base 15.1
- Copyright (c) 1999 Kenneth D. Merry
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms