Decoding PCI data and lspci output on Linux hosts


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.

Viewing PCI data with lspci<

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)

Updating the PCI identification list

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.

Conclusion

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.