setpci 是 pciutils 软件包的一部分,它允许用户空间程序直接访问和修改PCI设备的配置空间。PCI配置空间是一个256字节的区域,包含设备的识别信息、配置和控制寄存器。
setpci 通常包含在 pciutils 包中:
sudo apt update
sudo apt install pciutils
sudo yum install pciutils
which setpci 或 setpci --version 2>/dev/null || echo "未安装"
setpci [选项] 设备 [操作...]
选项:控制命令行为的各种参数设备:PCI设备标识符(格式:域:总线:设备.功能)操作:对寄存器执行的操作(读取或写入)寄存器[.范围]=值:写入寄存器寄存器[.范围]:读取寄存器PCI设备标识符的完整格式:
域:总线:设备.功能
| 部分 | 示例 | 说明 |
|---|---|---|
| 域 (Domain) | 0000 |
PCI域编号,通常为0000 |
| 总线 (Bus) | 01 |
总线编号,十六进制 |
| 设备 (Device) | 00 |
设备编号,十六进制 |
| 功能 (Function) | 0 |
功能编号,0-7,通常为0 |
常见简写形式:
01:00.0:省略域(默认为0000)01:00:省略功能和域(功能默认为0)-s 01:00.0:使用-s选项指定设备| 参数 | 说明 |
|---|---|
-v 或 --verbose |
详细输出模式 |
-f 或 --fill |
静默模式,不显示错误 |
-D 或 --dry-run |
模拟运行,不实际修改寄存器 |
-s [[域]:]总线:设备.功能 |
指定要操作的PCI设备 |
-d [厂商]:[设备] |
按厂商和设备ID选择设备 |
-G |
启用对配置空间的完全访问 |
-H 1 |
通过Type 1访问方式访问PCI桥 |
-H 2 |
通过Type 2访问方式访问PCI桥 |
-F 文件 |
从文件读取寄存器操作 |
-i 索引文件 |
使用指定的索引文件 |
-x |
以十六进制显示所有字节 |
-A <方法> |
指定访问方法(sysfs, proc, auto) |
-O <参数> |
指定访问参数 |
--version |
显示版本信息 |
--help |
显示帮助信息 |
首先使用lspci找到设备ID:
lspci | grep -i nvidia
输出示例:
01:00.0 VGA compatible controller: NVIDIA Corporation GA102 [GeForce RTX 3090] (rev a1)
查看设备ID(厂商ID和设备ID在偏移0x00处):
sudo setpci -s 01:00.0 0x00.L
输出示例:10de 2204,其中10de是NVIDIA的厂商ID,2204是设备ID。
读取命令寄存器(偏移0x04):
sudo setpci -s 01:00.0 04.w
读取状态寄存器(偏移0x06):
sudo setpci -s 01:00.0 06.w
读取类代码寄存器(偏移0x08,3字节):
sudo setpci -s 01:00.0 08.L
以十六进制显示完整配置空间(前64字节):
sudo setpci -s 01:00.0 00.L 04.L 08.L 0c.L 10.L 14.L 18.L 1c.L 20.L 24.L 28.L 2c.L 30.L 34.L 38.L 3c.L
启用总线主控(设置命令寄存器的位2):
# 首先读取当前值
sudo setpci -s 01:00.0 04.w
# 假设输出为 0000,则设置位2
sudo setpci -s 01:00.0 04.w=0004
禁用设备(设置命令寄存器的位0):
# 禁用设备(通常用于调试)
sudo setpci -s 01:00.0 04.w=0001
重新启用设备:
sudo setpci -s 01:00.0 04.w=0000
查看BAR0寄存器(基地址寄存器0,偏移0x10):
sudo setpci -s 01:00.0 10.L
查看所有BAR寄存器:
for i in 0 1 2 3 4 5; do
offset=$((0x10 + i*4))
printf "BAR%d (0x%02x): " $i $offset
sudo setpci -s 01:00.0 $(printf "%02x.L" $offset)
done
临时修改BAR值(仅用于调试):
# 注意:这可能导致系统崩溃
sudo setpci -s 01:00.0 10.L=12345678
查看中断线寄存器(偏移0x3C):
sudo setpci -s 01:00.0 3c.b
查看中断引脚寄存器(偏移0x3D):
sudo setpci -s 01:00.0 3d.b
修改中断线:
# 将中断线设置为10(谨慎操作)
sudo setpci -s 01:00.0 3c.b=0a
比较两个设备的配置空间:
# 创建两个设备的配置空间转储
sudo setpci -s 01:00.0 0.L-3f.L > device1.conf
sudo setpci -s 02:00.0 0.L-3f.L > device2.conf
diff device1.conf device2.conf
监控寄存器变化:
#!/bin/bash
DEVICE="01:00.0"
REGISTER="04.w"
echo "监控设备 $DEVICE 的寄存器 $REGISTER 变化..."
OLD_VALUE=$(sudo setpci -s $DEVICE $REGISTER)
while true; do
NEW_VALUE=$(sudo setpci -s $DEVICE $REGISTER)
if [ "$OLD_VALUE" != "$NEW_VALUE" ]; then
echo "$(date): $REGISTER 从 $OLD_VALUE 变为 $NEW_VALUE"
OLD_VALUE=$NEW_VALUE
fi
sleep 0.1
done
批量修改多个设备:
# 为所有NVIDIA设备启用总线主控
for dev in $(lspci -d 10de: -n | awk '{print $1}'); do
echo "处理设备 $dev"
sudo setpci -s $dev 04.w=0004
done
| 偏移 | 寄存器名 | 大小 | 说明 |
|---|---|---|---|
0x00 |
设备ID / 厂商ID | 32位 | 设备ID(高16位)+ 厂商ID(低16位) |
0x04 |
命令寄存器 | 16位 | 控制设备的基本功能 |
0x06 |
状态寄存器 | 16位 | 设备状态信息 |
0x08 |
修订ID / 类代码 | 32位 | 类代码(高24位)+ 修订ID(低8位) |
0x0C |
BIST / 头类型 / 延迟计时器 / Cache大小 | 32位 | 多个字段组合 |
0x10-0x24 |
BAR0-BAR5 | 各32位 | 基地址寄存器,定义设备的内存或I/O空间 |
0x28 |
Cardbus CIS指针 | 32位 | Cardbus卡信息结构指针 |
0x2C |
子系统ID / 子系统厂商ID | 32位 | 子系统ID(高16位)+ 子系统厂商ID(低16位) |
0x30 |
扩展ROM基地址 | 32位 | 扩展ROM的基地址 |
0x34 |
保留 / 能力指针 | 32位 | 能力列表指针(低8位有效) |
0x38 |
保留 | 32位 | 保留 |
0x3C |
中断线 | 8位 | 设备使用的中断线 |
0x3D |
中断引脚 | 8位 | 设备使用的中断引脚 |
0x3E |
Min_Gnt / Max_Lat | 16位 | 最小授权时间 / 最大延迟时间 |
| 位 | 名称 | 说明 |
|---|---|---|
| 0 | I/O Space Enable | 启用I/O空间访问 |
| 1 | Memory Space Enable | 启用内存空间访问 |
| 2 | Bus Master Enable | 启用总线主控 |
| 3 | Special Cycle Monitor | 监控特殊周期 |
| 4 | Memory Write and Invalidate | 内存写无效使能 |
| 5 | VGA Palette Snoop | VGA调色板侦听 |
| 6 | Parity Error Response | 奇偶错误响应 |
| 7 | Wait Cycle Control | 等待周期控制 |
| 8 | System Error Enable | 系统错误使能 |
| 9 | Fast Back-to-Back Enable | 快速背对背传输使能 |
.B:字节(8位)访问.W:字(16位)访问.L:双字(32位)访问.BAR0等:访问指定BAR寄存器0-3F:访问偏移0到3F的范围lspci -vvv查看设备信息-D参数进行模拟运行-v参数获取详细输出dmesg查看内核消息watch命令监控寄存器变化#!/bin/bash
# 备份PCI设备配置空间
DEVICE="01:00.0"
BACKUP_FILE="/tmp/pci_backup_${DEVICE//:/_}.bin"
echo "备份设备 $DEVICE 的配置空间到 $BACKUP_FILE"
# 备份前64字节(标准配置头)
for offset in $(seq 0 60 4); do
hex_offset=$(printf "%02x" $offset)
value=$(sudo setpci -s $DEVICE ${hex_offset}.L)
echo "${hex_offset}: $value" >> "$BACKUP_FILE"
done
echo "备份完成"
# 恢复示例(谨慎使用):
# while read line; do
# offset=$(echo $line | cut -d: -f1)
# value=$(echo $line | cut -d' ' -f2)
# sudo setpci -s $DEVICE ${offset}.L=${value}
# done < "$BACKUP_FILE"
设备无响应:
dmesg中的错误信息性能问题:
lspci - 列出PCI设备信息update-pciids - 更新PCI ID数据库pciutils - PCI工具包(包含setpci和lspci)pcimem - 直接访问PCI内存空间pci-edid - 获取PCI设备的EDID信息dmidecode - 显示DMI(SMBIOS)信息hdparm - 硬盘参数和性能测试ethtool - 以太网设备配置smartctl - SMART磁盘监控i2c-tools - I2C总线工具setpci直接操作硬件寄存器,错误的修改可能导致系统崩溃、硬件损坏或数据丢失