跳转到主要内容

开发者分享 | 使用 lspci 和 setpci 调试 PCIe 问题

本文转载自:<span id="profileBt"><a href="https://mp.weixin.qq.com/s?__biz=Mzg3NDAxNzU1MA==&mid=2247487286&idx=1&…技术社区微信公众号</a></span>

lspci 命令和 setpci 命令均为 Linux 发行版中原生可用的命令。

这 2 条命令均可提供多级输出,适合在不同时间点用于查看 PCI 总线上训练的不同组件的功能和状态。其中大部分功能均可反映《PCI Express 基本规范》中所需的配置空间寄存器。与大部分命令一样,在 Linux 中可通过运行“lspci --help”或“man lspci”来获取实用的指示信息。

<strong>// lspci</strong>

默认情况下 lspci 命令可显示所有器件信息,如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114009-1.p…; alt=""></center>

<strong>// lspci-tv</strong>

此命令可按树状格式显示 PCI 器件,并提供根端口总线、器件和功能 (BDF) 编号。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114010-2.p…; alt=""></center>

在以上日志中,赛灵思器件连接到总线编号 (Bus Number)“00”、器件编号 (Device Number )“01”和功能编号 (Function Number)“1”。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114011-3.p…; alt=""></center>

<strong>// lspci-vvv</strong>

此命令可用于显示最详细的信息。需要 root 用户权限才能运行此命令。

以下日志仅显示与赛灵思 PCIe 器件相关的部分。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114012-4.p…; alt=""></center>

要点:

•“链路功能寄存器 (Link Capability Register)”和“链路状态寄存器 (Link Status Register)”均显示 Gen3x8。有时由于链路问题,可能发生链路向下训练。向下训练的链路状态会反映在链路状态寄存器中:
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114013-5.p…; alt=""></center>

• “可纠正错误状态寄存器 (Correctable Error Status register)”显示“非致命错误 (Non-Fatal Error)”。启动期间,主机还会探测未配置的功能。由于设计是针对单一功能配置的,因此探测其它功能的操作将报告为请求不受支持。此不受支持的请求将报告为“非致命错误:建议 (Advisory Non-Fatal error)”。如果在启动期间出现“非致命错误 (Non-Fatal Error)”、“请求不受支持 (Unsupported Request)”和“可纠正错误 (Correctable Error)”,此错误可忽略。通过在对应寄存器中对相应的位执行配置写入即可清除此类错误。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114014-6.p…; alt=""></center>

•用户必须监控不可纠正错误状态寄存器中是否存在错误。如果在此寄存器中报告了错误,则必须对其进行调查并解决。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114015-7.p…; alt=""></center>

• PCI Express 功能起始位置为“80”。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114016-8.p…; alt=""></center>

<strong>// lspci-vs <BDF></strong>

此命令可提供有关选定器件的详细输出,如下所示:
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114017-9.p…; alt=""></center>

<strong>// lspci-vvvs <BDF></strong>

此命令作用相同,但可提供更详细的输出,如下所示:
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114018-10…; alt=""></center>

<strong>// lspci-nvmms <BDF></strong>

此命令可用于以数字形式显示 PCI 器件供应商 ID 和器件 ID。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114019-11…; alt=""></center>

<strong>// lspci-xxx</strong>

此命令可用于提供整个 PCI 配置空间的十六进制转储。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114020-12…; alt=""></center>

0x00 的前 2 个字节实际为 0x10EE。在 PCIe 规范中,所有数据均按偏移来定义,例如,包含 0x80 的黄色框位于偏移 0x34 处 - 表示该指针指向扩展功能寄存器的第一个地址。

PCI-ID:在位于 0x00 的红色框内显示的是供应商 ID (0x10EE),后接蓝色框内显示的器件 ID (0x7038)。其下方位于偏移 0x2C 和 0x2E 的红色框和蓝色框内分别是子供应商 ID (0x10EE) 和子器件 ID (0x0700)。

总线主控制器启用:回到上方位于 0x04 处的黄色框内,其中显示的是 16 位字“0000 0000 0000 0111”。它表示命令寄存器。位 2 是“总线主控制器启用”位。

位于 0x06 处的绿色框内显示的是 16 位字“0000 0000 0000 0001”。它表示状态寄存器,可能随时间而改变,它可用于向根联合体 (Root complex) 发送信号,以表示发生了某些状况。

BAR 和存储器:位于 0x10 处,其中显示的是 32 位字“0000 0000 0000 0000 1111 01111010 0000”

<li>位 0 = 0 - 针对存储器空间的请求</li>

<li>位 2:1 =“00”- 基址,位宽为 32 位</li>

<li>位 3 =“0”- 不可预取</li>

<li>位 31:4 = 0xF7C0(最低的 4 个位假定为 0,因为必须在字节和 Dword 边界上赋值)</li>
注:如果该请求位宽为 64 位,那么下一个 D-Word 将包含上位内存地址,下一个 BAR 将为 BAR2。

<li>0x14 - 此处是 BAR1 地址,但由于全部为 0,因此该器件仅含 1 个 BAR 选项</li>

<strong>检查 PCIe 链路宽度</strong>

PCIe 宽度用于判定 PCIe 通道数。以下命令可便于查找“链路功能寄存器”中的“PCIe 链路宽度”信息,以及“链路状态寄存器”中协商的链路宽度。

<strong>检查 PCIe 速度</strong>

与用于检查 PCIe 链路宽度信息的命令类似,以下命令可提供有关 PCIe 速度的信息。

<strong>检查 PCIe 最大有效载荷大小 (MPS)</strong>

以下命令可提供“器件控制寄存器 (Device Control Register)”下的“最大有效载荷大小 (Max Payload Size)”值。

<strong>检查 PCIe 最大读取请求大小</strong>

列出所有 PCIe 器件
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114021-13…; alt=""></center>

<strong>// setpci</strong>

setpci 命令可用于读取和写入配置寄存器。请参阅“setpci –help”以获取有关 setpci 功能的详细信息。

setpci 包含标准配置报头中的所有寄存器的名称。“setpci –dumpregs”命令可显示包含所有 PCI 寄存器和功能的列表,如下所示:
<pre>[root@localhost xilinx]# setpci --dumpregs
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
28 L CARDBUS_CIS
2c L SUBSYSTEM_VENDOR_ID
2e W SUBSYSTEM_ID
30 L ROM_ADDRESS
3c B INTERRUPT_LINE
3d B INTERRUPT_PIN
3e B MIN_GNT
3f B MAX_LAT
18 B PRIMARY_BUS
19 B SECONDARY_BUS
1a B SUBORDINATE_BUS
1b B SEC_LATENCY_TIMER
1c B IO_BASE
1d B IO_LIMIT
1e W SEC_STATUS
20 W MEMORY_BASE
22 W MEMORY_LIMIT
24 W PREF_MEMORY_BASE
26 W PREF_MEMORY_LIMIT
28 L PREF_BASE_UPPER32
2c L PREF_LIMIT_UPPER32
30 W IO_BASE_UPPER16
32 W IO_LIMIT_UPPER16
38 L BRIDGE_ROM_ADDRESS
3e W BRIDGE_CONTROL
10 L CB_CARDBUS_BASE
14 W CB_CAPABILITIES
16 W CB_SEC_STATUS
18 B CB_BUS_NUMBER
19 B CB_CARDBUS_NUMBER
1a B CB_SUBORDINATE_BUS
1b B CB_CARDBUS_LATENCY
1c L CB_MEMORY_BASE_0
20 L CB_MEMORY_LIMIT_0
24 L CB_MEMORY_BASE_1
28 L CB_MEMORY_LIMIT_1
2c W CB_IO_BASE_0
2e W CB_IO_BASE_0_HI
30 W CB_IO_LIMIT_0
32 W CB_IO_LIMIT_0_HI
34 W CB_IO_BASE_1
36 W CB_IO_BASE_1_HI
38 W CB_IO_LIMIT_1
3a W CB_IO_LIMIT_1_HI
40 W CB_SUBSYSTEM_VENDOR_ID
42 W CB_SUBSYSTEM_ID
44 L CB_LEGACY_MODE_BASE
01 00 - CAP_PM
02 00 - CAP_AGP
03 00 - CAP_VPD
04 00 - CAP_SLOTID
05 00 - CAP_MSI
06 00 - CAP_CHSWP
07 00 - CAP_PCIX
08 00 - CAP_HT
09 00 - CAP_VNDR
0a 00 - CAP_DBG
0b 00 - CAP_CCRC
0c 00 - CAP_HOTPLUG
0d 00 - CAP_SSVID
0e 00 - CAP_AGP3
0f 00 - CAP_SECURE
10 00 - CAP_EXP
11 00 - CAP_MSIX
12 00 - CAP_SATA
13 00 - CAP_AF
0001 00 - ECAP_AER
0002 00 - ECAP_VC
0003 00 - ECAP_DSN
0004 00 - ECAP_PB
0005 00 - ECAP_RCLINK
0006 00 - ECAP_RCILINK
0007 00 - ECAP_RCECOLL
0008 00 - ECAP_MFVC
000a 00 - ECAP_RBCB
000b 00 - ECAP_VNDR
000d 00 - ECAP_ACS
000e 00 - ECAP_ARI
000f 00 - ECAP_ATS
0010 00 - ECAP_SRIOV</pre>

<strong>// 识别 setpci 中的寄存器</strong>

以下是识别 setpci 命令中所使用的寄存器的各种方法。

<li>使用十六进制地址</li>

<li>提供寄存器名称</li>

<li>对于属于 PCI 功能的一部分的寄存器,可通过功能名称来找到第一个寄存器。在 --dumpregs 输出中。查找以 `CAP_' 或 `ECAP_' 开头的名称。在此名称后可接 +offset 以向该地址添加偏移(十六进制值)。这样即可便于找到包含在已设置的相应功能寄存器内的寄存器。</li>

<li>宽度说明符(b、.w 或 .l)用于选择要读取或写入的字节数(1、2 或 4)。如果按名称来引用寄存器且该寄存器宽度已知,则可删除该说明符。</li>

<li>寄存器的所有名称和宽度说明符都区分大小写。</li>

示例:

COMMAND

<li>指向命令寄存器中的值。如果将其替换为 4.w,则将指向相同位置。</li>

COMMAND.l

<li>指向命令寄存器和状态寄存器的值。</li>

VENDOR_ID+1.b

<li>指向供应商 ID 寄存器的上位字节。</li>

CAP_PM+2.w

<li>对应于功耗管理功能的第二个字。</li>

ECAP108.l

<li>指向 ID 为 0x108 的扩展功能的第一个 32 位字。</li>

setpci –s 24:00.0 04.w=6

<li>要使 MSI 中断生效,必须在“PCIe 配置”中设置“总线主控制器启用 (Bus Master Enable)”位。以上命令可用于在命令寄存器中设置“总线主控制器启用”位。此示例中的“24:00.0”表示 BDF 编号。不同器件采用不同编号,且因系统而异。要找到正确的器件 BDF,请参阅对应 lspci 日志。</li>

<li>值为 6 表示当前设置的是“存储器启用 (Memory Enable)”位和“总线主控制器启用”位。</li>
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114022-14…; alt=""></center>

<strong>// setpci –s 24:00.0 4a.w=1</strong>

在 PCIe 配置空间内还必须启用 MSI 寄存器才能使 MSI 中断正常工作。在 UltraScale+ 器件中,它位于偏移 0x48 处(在 lspci 日志中也显示为 Capabilities: [48])。为此,请发出“PCIe 配置写入 (PCIe Configuration Write)”以将位 16(MSI 控制寄存器位 0)设为 1;以上命令可用于执行此操作。
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114023-15…; alt=""></center>

<li>执行以上命令后,可运行 lspci 命令。这样应可改为显示“MSI: Enable +”。</li>

<strong>// setpci -s 01:00.0 82.b</strong>

<li>以上命令用于从 UltraScale+ PCIe 端点器件的“链路状态寄存器”中执行读取</li>

<li>地址“82”表示 UltraScale+ 器件。请参阅 (PG213) 以查看详细表格。</li>
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114024-16…; alt=""></center>

<strong>// setpci -s 00:01.0 d0.b</strong>

以上命令用于从根端口 (Root Port) 的链路控制 2 寄存器中执行读取

在对应 lspci 日志中,链路功能基址为 a0,如下所示:
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114025-17…; alt=""></center>

<li>链路控制 2 寄存器偏移为“30”。将“a0”添加至“30”即可生成“d0”。“a0”地址表示根端口器件,如以上截屏中所示。此地址因使用的器件而异。对于使用 UltraScale+ 器件的根端口,功能地址起始位置为“70”,如下所示,而链路控制 2 寄存器偏移仍为“30”,即表示十进制值 48。</li>
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114026-18…; alt=""></center>

从 lspci 日志中还可读取基址值“70”,如下所示:
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114027-19…; alt=""></center>

<strong>// setpci -s 00:01.0 d0.b=42</strong>

<li>以上命令用于写入链路控制 2 寄存器,以将速度设置为 Gen2。</li>

<li>此处值“2”表示 Gen2,另一个位为“插槽时钟”,该位已启用,因此不发生更改。发出以上命令后,如果运行 lspci,那么寄存器中显示的速度值将更改为 Gen2。但这只是它再次执行链路训练时的训练目标速度。</li>

<li>要将链路速度更改为 Gen2,必须对链路进行重新训练。可通过执行以下所示命令来进行重新训练:</li>

setpci -s 00:01.0 b0.b=62
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114028-20…; alt=""></center>

更改 PCIe 最大读取请求大小

<li>查询寄存器以避免覆盖其它属性。</li>

setpci -s 04:00.0 78.w

<li>将期望值写入寄存器。</li>

setpci -s 04:00.0 78.w=2936

<li>“最大读取请求”所在的“器件控制寄存器”的字节偏移针对 UltraScale+ 器件为 78h。该值对于其它器件可能发生改变。用户应查询相应的产品指南。下表来自 (PG213)。</li>
<center><img src="http://xilinx.eetrend.com/files/2020-12/wen_zhang_/100059215-114029-21…; alt=""></center>