【PCI】PCIe配置空间访问(九)

本文参考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即可。

  • 20
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的示例代码,用于在 Linux 系统中通过访问 PCIe 配置空间来读取和写入寄存器值: ``` #include <linux/pci.h> /* 定义 PCI 设备 ID */ #define MY_VENDOR_ID 0x1234 #define MY_DEVICE_ID 0x5678 /* 定义需要读写的寄存器地址 */ #define MY_CONFIG_REG_OFFSET 0x10 /* 访问 PCIe 配置空间 */ void access_pcie_config_space(struct pci_dev *dev) { u32 value; /* 检查设备是否匹配 */ if (dev->vendor != MY_VENDOR_ID || dev->device != MY_DEVICE_ID) return; /* 读取寄存器值 */ pci_read_config_dword(dev, MY_CONFIG_REG_OFFSET, &value); printk(KERN_INFO "Read value from config space: 0x%x\n", value); /* 写入寄存器值 */ value = 0x12345678; pci_write_config_dword(dev, MY_CONFIG_REG_OFFSET, value); printk(KERN_INFO "Write value to config space: 0x%x\n", value); } /* 遍历 PCI 总线上的所有设备 */ static int pcie_driver_probe(struct pci_dev *dev, const struct pci_device_id *id) { /* 注册驱动回调函数 */ pci_read_config_dword(dev, PCI_VENDOR_ID, &vendor); pci_read_config_dword(dev, PCI_DEVICE_ID, &device); access_pcie_config_space(dev); return 0; } /* 定义 PCI 设备 ID 列表 */ static const struct pci_device_id pcie_driver_id_table[] = { { PCI_DEVICE(MY_VENDOR_ID, MY_DEVICE_ID), }, { 0, } }; /* 定义 Linux 驱动结构体 */ static struct pci_driver pcie_driver = { .name = "pcie_driver", .id_table = pcie_driver_id_table, .probe = pcie_driver_probe, }; /* 注册 Linux 驱动 */ module_pci_driver(pcie_driver); ``` 在上面的代码中,`access_pcie_config_space` 函数用于读取和写入 PCIe 配置空间中的寄存器值。它首先检查设备的厂商 ID 和设备 ID 是否匹配,然后使用 `pci_read_config_dword` 和 `pci_write_config_dword` 函数读取和写入寄存器值。 在 `pcie_driver_probe` 函数中,我们遍历 PCI 总线上的所有设备,并在匹配的设备上调用 `access_pcie_config_space` 函数。 最后,我们定义了一个 PCI 设备 ID 列表,并使用 `module_pci_driver` 宏来注册 Linux 驱动。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值