本文参考PCIe协议 5.0:https://download.csdn.net/download/zz2633105/89204842
配置空间结构
PCI协议定义了256 byte配置空间供PCI设备使用,随着科技发展,PCI设备种类增多,各厂商自定义的cap已经不满足于256 byte空间,此外,并行总线的PCI设备传输速率已经达到了瓶颈,因此推出了PCIe设备以替代传统PCI设备。
PCIe设备将配置空间扩展到了4096 byte,且前256 byte兼容PCI配置空间,因此,PCIe设备可以很好的替代PCI设备,在主机主板支持的情况下,它们能在同一系统下工作。如上图所示,传统PCI设备和PCIe设备都拥有标准配置空间头(0-0x3f),但传统PCI设备的其他cap只能放置于 0x3f~0xff 之间的配置空间中,而PCIe设备的其他cap,则可以放置于 0x3f~0xfff 之间的配置空间中,这意味着PCIe设备能提供更强劲的功能。
访问方式
PCI Express配置模型支持两种配置空间访问机制:
- PCI-compatible Configuration Access Mechanism(CAM,PCI兼容配置访问机制,参见协议第7.2.1节)
- PCI Express Enhanced Configuration Access Mechanism(ECAM,PCI Express增强型配置访问机制,参见协议第7.2.2节)
CAM模式是PCI协议定义的访问方式,而ECAM则是PCIe协议定义的访问方式。为了兼容传统PCI设备,PCIe协议要求ECMA机制与CAM机制访问配置空间前256 byte等效。
CAM
以x86为例,X86处理器定义了两个I/O端口寄存器,分别为CONFIG_ADDRESS何CONFIG_DATA寄存器,器地址为0xCF8和0xCFC,CAM访问机制就是通过这两个寄存器实现的。简单来说就是把需要访问的PCI设备信息按特定格式写入CONFIG_ADDRESS(0xCF8)寄存器,然后读写CONFIG_DATA(0xCFC)寄存器即可。
CONFIG_ADDRESS寄存器信息格式如下。
- Enable位,第31位。该位为1时,对CONFIG_DATA寄存器进行读写时将引发PCI总线的配置周期。
- Bus Number字段,第23~16位,访问的PCI设备总线号,每个pci桥扩展出来的信号总线编号,最多256个总线号,host主桥出来的是0号。
- Device Number字段,第15~11位,访问的PCI设备设备号,每个总线编号下的设备编号,最多32个设备号,从0开始。
- Function Number字段,第10~8位,访问的PCI设备功能号,设备内部实现的功能编号,最多8个,从0开始。
- Register Number字段,第7~2位,访问的的配置空间寄存器号,由于寄存器是DW访问了,所以不需要第1~0位。
将PCI设备的总线号、设备号、功能号以及寄存器号填写到CONFIG_ADDRESS寄存器并使能第31位,然后读写CONFIG_DATA寄存器即可读写指定PCI设备的指定寄存器了。可以注意到,Register Number字段只有8bit(DW访问),所以只能访问到配置空间的前256byte。
ECAM
ECAM(Enhanced Configuration Access Mechanism)利用一个扁平的存储器映射地址空间来访问PCIe设备的配置寄存器。根据特定的格式组装访问存储器空间地址,就可以确定访问PCIe设备的配置寄存器,并且将数据更新(对于写操作)或返回(对于读操作)被寻址寄存器的内容。从存储器地址空间到PCI Express配置空间地址的映射格式参见下图定义。
相比CAM机制,其增加了11~8 bit,用于扩展访问256byte~4k之间的配置空间寄存器。Bus Number正常应该为20~27bit,正好对应256个bus号。更高的31~28 bit则用来作为ECAM的base地址,比如:
X86架构
[root@localhost ~]# cat /proc/iomem | grep MMCONFIG
c0000000-cfffffff : PCI MMCONFIG 0000 [bus 00-ff]
[root@localhost ~]# cat /proc/iomem | grep MMCONFIG
f8000000 - fbffffff : PCI MMCONFIG 0000 [bus 00-3f]
可以看到base地址既有0xf8000000,也有0xc0000000
ARM架构
[root@localhost ~]# cat /proc/iomem | grep ECAM
40000000-4fffffff : PCI ECAM
可以看到base地址为0x40000000
那linux系统怎么知道ECAM机制的base地址呢?对于有BIOS的系统,则由BIOS告诉linux内核;对于ARM(不带BIOS)的系统,则由设备树中填写。
ECAM机制是如何实现的呢?很简单,就是CPU发出地址访问请求,由地址转换部件判断地址是否为预留的ECAM地址区间,再将请求路由给RC或host主桥,当RC或host主桥接收到这段地址的访问,按上述格式解析填写CFG TLP报文下发即可。注意:地址转换部件不是一个真正存在的实体,更多是指功能概念。
一般来说,系统地址空间会预留256MB(256bus * 32dev * 8func * 4k)空间给ECAM机制使用,当然如果系统比较差,不支持那么多bus号,也可以预留小一点。
用户态访问配置空间工具
lspci
通过lcpsi可以查看系统下有哪些PCIe设备。
[root@localhost ~]# lspci
00:00.0 Host bridge: Intel Corporation Device 4648 (rev 02)
00:02.0 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:0a.0 Signal processing controller: Intel Corporation Device 467d (rev 01)
00:0e.0 RAID bus controller: Intel Corporation Volume Management Device NVMe RAID Controller
00:14.0 USB controller: Intel Corporation Device 7ae0 (rev 11)
00:14.2 RAM memory: Intel Corporation Device 7aa7 (rev 11)
00:15.0 Serial bus controller [0c80]: Intel Corporation Device 7acc (rev 11)
00:15.1 Serial bus controller [0c80]: Intel Corporation Device 7acd (rev 11)
00:15.2 Serial bus controller [0c80]: Intel Corporation Device 7ace (rev 11)
00:16.0 Communication controller: Intel Corporation Device 7ae8 (rev 11)
00:17.0 SATA controller: Intel Corporation Device 7ae2 (rev 11)
00:1a.0 PCI bridge: Intel Corporation Device 7ac8 (rev 11)
00:1b.0 PCI bridge: Intel Corporation Device 7ac0 (rev 11)
00:1b.4 PCI bridge: Intel Corporation Device 7ac4 (rev 11)
00:1c.0 PCI bridge: Intel Corporation Device 7ab8 (rev 11)
00:1c.2 PCI bridge: Intel Corporation Device 7aba (rev 11)
00:1d.0 PCI bridge: Intel Corporation Device 7ab0 (rev 11)
00:1f.0 ISA bridge: Intel Corporation Device 7a84 (rev 11)
00:1f.3 Audio device: Intel Corporation Device 7ad0 (rev 11)
00:1f.4 SMBus: Intel Corporation Device 7aa3 (rev 11)
00:1f.5 Serial bus controller [0c80]: Intel Corporation Device 7aa4 (rev 11)
03:00.0 Serial controller: Xilinx Corporation Device 9028
05:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controller (rev 05)
通过 lspci -vvv -s xxx 查看具体设备的配置空间
[root@localhost ~]# lspci -vvv -s 05:00.0
05:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controller (rev 05)
Subsystem: ASUSTeK Computer Inc. Device 87d7
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 18
Region 0: I/O ports at 4000 [size=256]
Region 2: Memory at 85e00000 (64-bit, non-prefetchable) [size=64K]
Region 4: Memory at 85e10000 (64-bit, non-prefetchable) [size=16K]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=375mA PME(D0+,D1+,D2+,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
Address: 0000000000000000 Data: 0000
Masking: 00000000 Pending: 00000000
Capabilities: [70] Express (v2) Endpoint, MSI 01
DevCap: MaxPayload 256 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset- SlotPowerLimit 10.000W
DevCtl: CorrErr- NonFatalErr- FatalErr- UnsupReq-
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 256 bytes, MaxReadReq 4096 bytes
DevSta: CorrErr- NonFatalErr- FatalErr- UnsupReq- AuxPwr+ TransPend-
LnkCap: Port #0, Speed 5GT/s, Width x1, ASPM L0s L1, Exit Latency L0s unlimited, L1 <64us
ClockPM+ Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes, Disabled- CommClk+
ExtSynch- ClockPM+ AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 5GT/s (ok), Width x1 (ok)
TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range ABCD, TimeoutDis+ NROPrPrP- LTR+
10BitTagComp- 10BitTagReq- OBFF Via message/WAKE#, ExtFmt- EETLPPrefix-
EmergencyPowerReduction Not Supported, EmergencyPowerReductionInit-
FRS- TPHComp+ ExtTPHComp-
AtomicOpsCap: 32bit- 64bit- 128bitCAS-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis- LTR+ OBFF Disabled,
AtomicOpsCtl: ReqEn-
LnkCap2: Supported Link Speeds: 2.5-5GT/s, Crosslink- Retimer- 2Retimers- DRS-
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete- EqualizationPhase1-
EqualizationPhase2- EqualizationPhase3- LinkEqualizationRequest-
Retimer- 2Retimers- CrosslinkRes: unsupported
Capabilities: [b0] MSI-X: Enable+ Count=32 Masked-
Vector table: BAR=4 offset=00000000
PBA: BAR=4 offset=00000800
Capabilities: [d0] Vital Product Data
pcilib: sysfs_read_vpd: read failed: Input/output error
Not readable
Capabilities: [100 v2] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- AdvNonFatalErr-
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- AdvNonFatalErr+
AERCap: First Error Pointer: 00, ECRCGenCap+ ECRCGenEn- ECRCChkCap+ ECRCChkEn-
MultHdrRecCap- MultHdrRecEn- TLPPfxPres- HdrLogCap-
HeaderLog: 00000000 00000000 00000000 00000000
Capabilities: [148 v1] Virtual Channel
Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
Arb: Fixed- WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
Status: NegoPending- InProgress-
Capabilities: [168 v1] Device Serial Number 01-00-00-00-68-4c-e0-00
Capabilities: [178 v1] Transaction Processing Hints
No steering table available
Capabilities: [204 v1] Latency Tolerance Reporting
Max snoop latency: 3145728ns
Max no snoop latency: 3145728ns
Capabilities: [20c v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
PortCommonModeRestoreTime=150us PortTPowerOnTime=150us
L1SubCtl1: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+
T_CommonMode=0us LTR1.2_Threshold=317440ns
L1SubCtl2: T_PwrOn=150us
Capabilities: [21c v1] Vendor Specific Information: ID=0002 Rev=4 Len=100 <?>
Kernel driver in use: r8169
Kernel modules: r8169
更多使用方法查看 lspci -h。
setpci
如果想要读某个设备的某个配置空间寄存器
[root@localhost ~]# setpci -s 05:00.0 0x70.b
10
[root@localhost ~]# setpci -s 05:00.0 0x70.w
b010
[root@localhost ~]# setpci -s 05:00.0 0x70.l
0202b010
如果想要修改某个配置空间寄存器
[root@localhost ~]# setpci -s 05:00.0 0x70.l=0x0202b010 #注意,确保寄存器可修改
devmem2
devme2工具读数据
devmem2 address [ type ]
参数说明:
address : 物理地址
type :要访问的数据类型 : [b]yte, [h]alfword, [w]ord
注意:ubuntu自带的devmem2工具有点问题,读w实际上是读两个dw,容易将设备读挂死
假设ECAM地址区间base为0xc0000000,想要读PCIe设备(BDF=1:00.0)的0x10寄存器地址,则根据ECAM要求组装地址为0xc0100010,执行 devmem 0xc0100010 w
即可。
devme2工具写数据
devmem2 address type data
参数说明:
address : 物理地址
type :要访问的数据类型 : [b]yte, [h]alfword, [w]ord
data :想要写入的数据
假设ECAM地址区间base为0xc0000000,想要写PCIe设备(BDF=1:00.0)的0x10寄存器地址,则根据ECAM要求组装地址为0xc0100010,执行 devmem 0xc0100010 w 0xffff0000
即可。