初学 PCIe System (二) - 如何访问PCIe设备的配置空间

发布于:2022-12-29 ⋅ 阅读:(917) ⋅ 点赞:(0)

初学 PCIe System (一) - PCIe介绍及其配置空间有提到可以从PCIe设备的配置空间(Configuration Space)获取许多重要的资讯,那在CPU中,访问配置空间常用的两种方式如下:

Programmed Input/Output (PIO)

PIO 是由CPU主动向Device 获取资料 ,CPU 会等待Device资料传输完后才去做其他事情。使用PIO会占住CPU资源,会让系统变得低效,因此此技术已几乎被直接记忆体访问(DMA)所取代。

这边简单介绍一下PIO的作法,首先每个PCI Function的配置空间中和PCI相容的前256 Bytes会映射到一个第三个空间:配置地址空间(configuration address space)。因为PCI Function最多会有(2^8) * (2^5) * (2^3) = 65536个,每个Function都会映射256Byte的配置空间,因此配置地址空间的大小为 65536 * 256 Bytes = 16 MB。

CPU 通过主桥(Host Bridge)中的 IO 映射地址端口(Address Port )和资料端口(Data Port)进行索引来间接访问 PCI 配置空间。

地址端口(Address Port )位于 IO 地址 CF8h-CFBh,是一个大小为32 bits的暂存器,需要填入CONFIG_ADDRESS,其格式如下

Bit 31 Bits 30-24 Bits 23-16 Bits 15-11 Bits 10-8 Bits 7-10
Enable Bit Reserved Bus Number Device Number Function Number   Register Offset

Bit 31表示Enable bit,如果不设起来就不会有作用。 (1<<31) = 0x80000000,所以通常会直接设起来,直接做 | 0x80000000的动作

Bit 8-23 是上一章节提到的PCIe Device Address是由 Bus Number+Device Number+Function Number所组成的

Bit0-7这边就填想到访问的PCIe 配置空间的Offset,做多就是0x00-0xff: 256 Bytes

所以CONFIG_ADDRESS 的格式公式如下:

0x80000000 | bus << 16 | device << 11 | function <<  8 | offset

CONFIG_ADDRESS 指定完需要访问的配置地址,会对资料端口映射的位置 CFCh-CFFh的CONFIG_DATA 暂存器的访问生成配置访问,将资料传入或传出 CONFIG_DATA 暂存器。

简单来说,例如我们想要读取"bus0 dev0 fun0, offset 0x00"的位置,那他的CONFIG_ADDRESS 就是0x80000000, 我们把它写入地址端口0xCF8,那该位置的资料就可以从资料端口0xCFC读到资料

这边用Linux的程式码early.c « pci « x86 « arch - kernel/git/stable/linux.git - Linux kernel stable tree为范例,读取的话动作是将想要读取的位置填到CONFIG_ADDRESS,该位置数据就会出现在CONIG_DATA 

写入的话,动作差不多,就是将想要写入的位置填到CONFIG_ADDRESS,再把值填到CONIG_DATA就好

*另外这边可以发现,透过PIO只能读写配置空间中的前256Bytes,所以其余的部分的读取需要透过DMA。

Direct Memory Access (DMA) 

DMA 是通知Device要做什么,Device会自己去写內存。所以DMA 可以一次获取大量资料,而且不会占住CPU资源。

所以我们可以直接透过內存去访问PCI Configuration,地址公式如下:

BaseAddress + bus << 20 | device << 15 | function <<  12 | offset

这边给offset的位置大小为3 Bytes

另外BaseAddress 每个platform都不一样,需要查Spec

有了地址之后,可以直接用指标的方式取的资料,例如地址为Addr:

unsigned int data = *Addr;

本文含有隐藏内容,请 开通VIP 后查看