The PCI standard has become the de-facto standard for system buses. Linux provide extensive support for PCI, and contains numerous drivers for network, storage and 3rd party adapters. This article will discuss how the Linux kernel represents PCI devices, and will show how to decode devices given a PCI identifier.
Making sense of PCI sysfs entries
The Linux kernel represents PCI devices as pseudo-devices in the sysfs file system:
$ ls -la /sys/bus/pci/devices
total 0
drwxr-xr-x 2 root root 0 2009-08-03 10:38 .
drwxr-xr-x 5 root root 0 2009-08-03 10:38 ..
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:01.0 -> ../../../devices/pci0000:00/0000:00:01.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:01.1 -> ../../../devices/pci0000:00/0000:00:01.1
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:02.0 -> ../../../devices/pci0000:00/0000:00:02.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:02.1 -> ../../../devices/pci0000:00/0000:00:02.1
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:04.0 -> ../../../devices/pci0000:00/0000:00:04.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:05.0 -> ../../../devices/pci0000:00/0000:00:05.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:05.1 -> ../../../devices/pci0000:00/0000:00:05.1
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:05.2 -> ../../../devices/pci0000:00/0000:00:05.2
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:06.0 -> ../../../devices/pci0000:00/0000:00:06.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:0a.0 -> ../../../devices/pci0000:00/0000:00:0a.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:0b.0 -> ../../../devices/pci0000:00/0000:00:0b.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:0c.0 -> ../../../devices/pci0000:00/0000:00:0c.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:0d.0 -> ../../../devices/pci0000:00/0000:00:0d.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:0e.0 -> ../../../devices/pci0000:00/0000:00:0e.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:0f.0 -> ../../../devices/pci0000:00/0000:00:0f.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:18.0 -> ../../../devices/pci0000:00/0000:00:18.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:18.1 -> ../../../devices/pci0000:00/0000:00:18.1
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:18.2 -> ../../../devices/pci0000:00/0000:00:18.2
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:00:18.3 -> ../../../devices/pci0000:00/0000:00:18.3
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:01:05.0 -> ../../../devices/pci0000:00/0000:00:06.0/0000:01:05.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:01:06.0 -> ../../../devices/pci0000:00/0000:00:06.0/0000:01:06.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:02:00.0 -> ../../../devices/pci0000:00/0000:00:0a.0/0000:02:00.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:03:00.0 -> ../../../devices/pci0000:00/0000:00:0a.0/0000:02:00.0/0000:03:00.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:03:00.1 -> ../../../devices/pci0000:00/0000:00:0a.0/0000:02:00.0/0000:03:00.1
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:04:00.0 -> ../../../devices/pci0000:00/0000:00:0b.0/0000:04:00.0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:05:00.0 -> ../../../devices/pci0000:00/0000:00:0c.0/0000:05:00.0
Given an entry such as:
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 0000:04:00.0 -> ../../../devices/pci0000:00/0000:00:0b.0/0000:04:00.0
We can break the device string “0000:04:00.0” down as follows:
0000 : PCI domain (each domain can contain up to 256 PCI buses)
04 : the bus number the device is attached to
00 : the device number
.0 : PCI device function
To get additional information about the device, we can change into the 0000:04:00.0 directory and execute our favorite pager to display one or more pseudo-device entries:
$ cd 0000:04:00.0
$ ls -la
total 0
drwxr-xr-x 4 root root 0 2009-08-03 10:38 .
drwxr-xr-x 7 root root 0 2009-08-03 10:38 ..
-rw-r--r-- 1 root root 4096 2009-08-03 12:23 broken_parity_status
-r--r--r-- 1 root root 4096 2009-08-03 10:38 class
-rw-r--r-- 1 root root 4096 2009-08-03 11:34 config
-r--r--r-- 1 root root 4096 2009-08-03 10:38 device
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 driver -> ../../../../bus/pci/drivers/tg3
-rw------- 1 root root 4096 2009-08-03 12:23 enable
lrwxrwxrwx 1 root root 0 2009-08-03 12:12 firmware_node -> ../../../LNXSYSTM:00/device:00/PNP0A03:00/device:1a/device:1b
-r--r--r-- 1 root root 4096 2009-08-03 10:38 irq
-r--r--r-- 1 root root 4096 2009-08-03 12:23 local_cpulist
-r--r--r-- 1 root root 4096 2009-08-03 10:38 local_cpus
-r--r--r-- 1 root root 4096 2009-08-03 12:23 modalias
-rw-r--r-- 1 root root 4096 2009-08-03 12:23 msi_bus
drwxr-xr-x 3 root root 0 2009-08-03 10:38 net
-r--r--r-- 1 root root 4096 2009-08-03 12:23 numa_node
drwxr-xr-x 2 root root 0 2009-08-03 12:12 power
-r--r--r-- 1 root root 4096 2009-08-03 11:34 resource
-rw------- 1 root root 65536 2009-08-03 12:23 resource0
lrwxrwxrwx 1 root root 0 2009-08-03 10:38 subsystem -> ../../../../bus/pci
-r--r--r-- 1 root root 4096 2009-08-03 10:38 subsystem_device
-r--r--r-- 1 root root 4096 2009-08-03 10:38 subsystem_vendor
-rw-r--r-- 1 root root 4096 2009-08-03 10:38 uevent
-r--r--r-- 1 root root 4096 2009-08-03 10:38 vendor
-rw------- 1 root root 32768 2009-08-03 12:23 vpd
$ cat vendor
0x14e4
$ cat device
0x1659
$ cat class
0x020000
Each sysfs entry contains a unique piece of data, such as the PCI vendor id (vendor) the device class (class), the device identifier (device), and information on irq and resource assignments. In the next section, we will see how to decode this data.
Most Linux distributions ship with the pciutils package, which provides tools to query, set and update PCI device information. To install the package on a CentOS or Fedora host, you can run yum with the install option:
$ yum install pciutils
Once installed, you can run lspci to view the devices connected to your system:
$ lspci | tail -5
02:00.0 PCI bridge: Intel Corporation 6702PXH PCI Express-to-PCI Bridge A (rev 09)
03:00.0 Fibre Channel: QLogic Corp. ISP2312-based 2Gb Fibre Channel to PCI-X HBA (rev 02)
03:00.1 Fibre Channel: QLogic Corp. ISP2312-based 2Gb Fibre Channel to PCI-X HBA (rev 02)
04:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5721 Gigabit Ethernet PCI Express (rev 21)
05:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5721 Gigabit Ethernet PCI Express (rev 21)
$ lspci -n | tail -5
02:00.0 0604: 8086:032c (rev 09)
03:00.0 0c04: 1077:2312 (rev 02)
03:00.1 0c04: 1077:2312 (rev 02)
04:00.0 0200: 14e4:1659 (rev 21)
05:00.0 0200: 14e4:1659 (rev 21)
In the first set of output, lspci read through the sysfs entries and decoded the vendor and device numbers using the vendor and device information in /usr/share/hwdata/pci.ids (to be 100% accurate, lspci uses libpci, which returns the data using the PCI identification data in /usr/share/hwdata/pci.ids). In the second set of output, lspci displayed the raw PCI identification data.
Now you may be asking yourself, what happens if you receive a brand new device and lspci isn’t able to display any details about it? Well, luckily for us the vendor and device information is stored in a centralized PCI ID repository, so we can figure out what a given device is by decoding the PCI data manually. Given the following device from the lspci -n output above:
05:00.0 0200: 14e4:1659 (rev 21)
We can break it down like this:
Field 1 : 05:00.0 : bus number (05), device number (00) and function (0)
Field 2 : 0200 : device class
Field 3 : 14e4 : vendor ID
Field 4 : 1659 : device ID
To convert the identifiers to human-readable strings, we can look up the identifiers in the PCI ID repository:
Field 2 : 0200 : class 0200 is listed as a "Network controller"
Field 3 : 14e4 : vendor ID 14e4 is listed as the "Broadcom Corporation"
Field 4 : 1659 : device ID 1659 is listed as a "NetXtreme BCM5721 Gigabit Ethernet PCI Express"
This matches up almost identically to the default lspci output:
05:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5721 Gigabit Ethernet PCI Express (rev 21)
As mentioned previously, the lspci utility uses the pci.ids file to determine the vendor and device type. This file will grow as new vendors and devices are added, and can be updated automatically by running the update-pciids utility:
$ /sbin/update-pciids
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 149k 100 149k 0 0 375k 0 --:--:-- --:--:-- --:--:-- 459k
Done.
If the human-readable form a device doesn’t show up in the lspci output, you may want to update to the latest version of the pci ids file to see if your device has been included.
This article provided a brief overview of the Linux PCI device structure, and showed how to decode sysfs and lspci data. For additional information on the PCI standard, you can check out the PCI sig website. To learn more about how the Linux kernel deals with PCI devices, check out the book Linux Device Drivers.