Read and Safely Test PCI Configuration with setpci

setpci pokes PCI configuration registers directly, and a careless write here can quietly disable a network card. You will finish with a repeatable way to identify a device, read its registers, preview a write without applying it, and restore a value if you deliberately change one. The examples use setpci 3.10.0 from pciutils 3.10.0-2build1, as installed on this machine.

Allow about fifteen minutes for read-only inspection. A real register write needs a device datasheet, a maintenance window and a tested recovery plan. Root privileges are required for almost all operations; reading the standard configuration header may work without them, depending on the operating system and access method.

1. Confirm the installed command

Check the binary and version before relying on a script or copied example. These commands only read local metadata:

$ command -v setpci
/usr/bin/setpci
$ setpci --version
setpci version 3.10.0
$ dpkg-query -W -f='${Package} ${Version}\n' pciutils
pciutils 1:3.10.0-2build1

The manual page is dated 1 May 2023 and documents the 3.10.0 interface. Keep the version in your notes when troubleshooting another host: register names and access behaviour can depend on the installed pciutils release, the kernel and the PCI device itself.

2. Choose one exact device

Use lspci -D to get a domain-qualified address, normally written as domain:bus:slot.function with every component in hexadecimal. On this host, the first entry is a host bridge at 0000:00:00.0:

$ lspci -Dnn | sed -n '1,4p'
0000:00:00.0 Host bridge [0600]: Intel Corporation ... [8086:591f] (rev 05)
0000:00:02.0 VGA compatible controller [0300]: Intel Corporation ... [8086:5912] (rev 04)
0000:00:14.0 USB controller [0c03]: Intel Corporation ... [8086:a12f] (rev 31)
0000:00:14.2 Signal processing controller [1180]: Intel Corporation ... [8086:a131] (rev 31)

Use the exact address you found, not the address from this output, because PCI layouts differ between machines. Set a shell variable to make later commands easier to review:

$ BDF='0000:00:00.0'
$ printf '%s\n' "$BDF"
0000:00:00.0

Checkpoint: confirm that $BDF names the device you intend to inspect. A selector such as -s 00:00.0 can match more broadly than a full domain-qualified selector on a multi-domain host.

3. Read named registers

Pass -s followed by the device address, then one or more operations. Register names are case-insensitive, and values are hexadecimal. This reads the vendor ID, device ID and command register without changing them:

$ setpci -s "$BDF" VENDOR_ID DEVICE_ID COMMAND
8086
591f
0146

The first two results are 16-bit identifiers. COMMAND is also a word-sized register, so 0146 is the value read from this particular device at this moment; your output will differ. A successful read is evidence the selector and access method worked, not evidence a different machine has the same hardware.

You can use the numeric address and an explicit width instead of a named register. The standard command register starts at offset 04:

$ setpci -s "$BDF" 04.W
0146

.B, .W and .L request one, two or four bytes. A named register has a known width where setpci knows it; for less familiar registers, use the explicit width so the read is obvious in a review.

4. Inspect names and capabilities

Ask the installed program for the register names it knows. This is read-only and does not scan or modify hardware:

$ setpci --dumpregs | sed -n '1,18p'
cap pos w name
     00 W VENDOR_ID
     02 W DEVICE_ID
     04 W COMMAND
     06 W STATUS
     08 B REVISION
     09 B CLASS_PROG
     0a W CLASS_DEVICE
     0c B CACHE_LINE_SIZE
     0d B LATENCY_TIMER
     0e B HEADER_TYPE
     0f B BIST
     10 L BASE_ADDRESS_0
     14 L BASE_ADDRESS_1
     18 L BASE_ADDRESS_2
     1c L BASE_ADDRESS_3
     20 L BASE_ADDRESS_4
     24 L BASE_ADDRESS_5

Capabilities use names such as CAP_PM or CAP_EXP. Add an offset when you need a register within a capability, for example CAP_PM+2.W. Extended capabilities can be addressed by a name such as ECAP_AER or by a numeric form such as ECAP108.L. Do not assume a capability exists just because its name is accepted: the device still has to advertise it, and the relevant PCI specification defines what the field actually means.

5. Preview a write with demo mode

Writes use register=value. A value can also be data:mask, which performs a read-modify-write and changes only bits present in the mask. Before doing either, add -D: demo mode lists the change but does not commit it:

$ sudo setpci -vD -s "$BDF" COMMAND=0000:0004
0000:00:00.0:04.W 0146 -> 0142

The exact verbose wording can vary, but the old and proposed values should be visible. Here the mask 0004 selects bit 2 and the data clears it. Because -D is present, the command register remains unchanged. Verify that by reading it again:

$ setpci -s "$BDF" COMMAND
0146

Safety boundary: do not remove -D merely to see whether a device accepts a write. A PCI configuration write can disable bus mastering, change address decoding, alter interrupt behaviour or disrupt a live driver. Use the device documentation and an explicit change record first.

6. If you must write, save and restore the value

The following is a workflow template, not a command to run against an arbitrary device. It writes the command register and therefore needs elevated privileges and a maintenance window. Replace the placeholder address after checking it twice:

$ BDF='0000:00:00.0'
$ OLD_COMMAND=$(setpci -s "$BDF" COMMAND)
$ printf 'saved COMMAND=%s\n' "$OLD_COMMAND"
saved COMMAND=0146
# Review the datasheet, the target bit and the demo-mode result before this line.
$ sudo setpci -s "$BDF" COMMAND=0000:0004
$ setpci -s "$BDF" COMMAND
0142

Restore the exact saved word when the experiment is complete, or immediately if the device or driver behaves unexpectedly:

$ sudo setpci -s "$BDF" COMMAND="$OLD_COMMAND"
$ setpci -s "$BDF" COMMAND
0146

That restoration is not a universal undo. A device may have side effects, a driver may reprogram the register, and a reset or service restart may be safer than another write. If the host becomes unstable, stop issuing PCI writes and use the platform's documented recovery procedure.

7. Diagnose selection and access failures

If setpci says no devices were selected, first rerun lspci -D and compare the exact domain, bus, slot and function. The -d selector can match by vendor, device, class and programming-interface IDs, but broad selectors are a poor choice for a write. When every operation already names a specific device, -r can avoid a bus scan and fail directly if that device is absent.

If a read is denied, use the privilege required by the host, normally sudo, and check the command version and access method. The -A and -O options change how the PCI library accesses hardware; do not add them from a random example. setpci -A help and setpci -O help list the options supported by this installation.

When a script may run on machines where the device is optional, -f suppresses the complaint about no selected device. That suits a deliberately tolerant read-only inventory script, but it can also hide a missing target. Do not combine it with a configuration change unless silently skipping that change is explicitly safe.

Done means