PCIE背景知识学习(8)

枚举——搜索发现拓扑(Enumeration-Discovering the Topology)

在完成了系统上电或是复位之后,配置软件需要扫描PCIe网络结构,来搜索发现整个机器的拓扑,并学习这个网络结构是如何被填充的(例如里面都有多少总线、多少设备以及它们的编号等等)。在这进行之前,如图 3‑10所示,软件唯一知道的就是拓扑中有一个Host/PCI Bridge以及这个Bridge的次级总线Bus 0。需要注意,一个Bridge自身上方相连的总线称为主总线(Primary Bus),而这个Bridge自身下方相连的总线称为次级总线(Secondary Bus)。扫描PCIe结构来发现整体拓扑结构的过程被称为枚举过程。

 

pci bios枚举 pcie枚举过程_地址空间

 

 基地址寄存器BARs(Base Address Registers)

IO设备的内部寄存器/存储被映射到了memory地址空间(Memory Address Space,内存地址空间),一般被称作内存映射IO或者简称MMIO(Memory-Mapped IO)

基于PCI的设备不允许自己来决定哪些地址可以用来访问它们内部的位置,做这些决定是系统软件负责的工作(例如BIOS和操作系统内核)。因此设备必须为系统软件提供一个途径用来确定设备对地址空间的需求。一旦软件知道了设备对地址空间的需求是什么样的,并假设这个需求是可以被满足的,软件就会给对应的设备分配一段可用的地址范围和相应的地址空间类型(IO、NP-MMIO、P-MMIO)。

这些都是通过配置空间Header中的基地址寄存器BARs(Base Address Registers)来完成的。如图 4‑2所示,一个Type 0 Header拥有6个可用的BAR(每个大小为32bit),而一个Type 1 Header只拥有2个BAR。Type 1 Header是存在于所有Bridge设备中的,这意味着每个Switch端口和RC端口都会拥有1个Type 1 Header。Type 0 Header只存在于非Bridge设备中,例如EP。

 

pci bios枚举 pcie枚举过程_pci bios枚举_02

 

BARs的高位bit是软件可进行写入的。一旦系统软件通过检查BARs的低位bit确定了设备所请求的地址空间的大小和类型,系统软件就会将分配给这个设备的地址范围的基地址写入BAR中。由于一个EP(使用Type 0 Header)拥有6个BARs,它最多可以请求6个不同的地址空间。然而,一般实际中请求6个不同的地址空间并不常见。绝大多数设备会请求1到3个不同的地址范围。

 

pci bios枚举 pcie枚举过程_内存地址_03

 

 

pci bios枚举 pcie枚举过程_内存地址_04

 

 我们可以看到BAR处于未初始化的状态。设备的设计者已经将低位bit固定为一个数值,来指示需要的memory的大小和类型,但是高位bit(可写可读的)则仍然是用X来表示,这代表它们的值还未知。

 系统软件将会首先把每个BAR都通过配置写操作来将可写入的bit写为全1(当然,被固定的低位bit不会受到配置写操作的影响)。在图 4‑4的(2)中展示的BAR就是处于第二阶段的样子,除了被固定的低位bit以外,所有的bit都被写为1。

 写为全1这个操作是为了确定最低位的可写入的bit(least-significant writable bit)是哪一位,这个bit的位置指示了需要被请求的地址空间的大小。在本例中,最低位的可写入的bit为bit 12,因此这个BAR需要请求2的12次方(或者说是4KB)的地址空间。如果最低位的可写入的bit为bit 20,那么这个BAR就要请求2的20次方(1MB)的地址空间。

在软件将BARs中所有可写bit都写为1后,软件将从BAR0开始,依次读取每个BAR的数值,以此来确定各个BAR要请求的地址空间的大小和类型。

这个过程中的最后一步就是系统软件为BAR 0分配一个地址范围,因为对于软件来说现在已经知道了BAR 0请求的地址空间的大小和类型。图 4‑4的(3)中展示了BAR处于第三阶段的样子,此时系统软件已经将一块地址区域的起始地址写入了BAR 0中。在本例中,这个起始地址为F900_0000h。

到这里为止,对BAR 0的配置就完成了。一旦软件启用了命令寄存器(Command register,偏移地址04h)中的内存地址译码(memory address decoding),那么这个设备就会接受所有地址在F900_0000h-F900_0FFFh(4KB大小)范围内的memory请求。

BAR 1和BAR 2被用来请求一块64MB的可预取内存地址空间。这里使用了两个连续相连的BAR是因为这个设备支持这个内存地址空间的请求使用64bit地址,这意味着如果需要的话,软件给它分配的地址空间可以超过4GB地址边界(并非必要)。由于地址可以是64bit位宽,因此必须将两个连续相连的BAR一起使用。

 

pci bios枚举 pcie枚举过程_内存地址_05

 

BAR示例2——64bit内存地址空间请求

在上一个例子中,我们看到了通过BAR 0来请求不可预取内存地址空间(NP-MMIO)。在当前的例子中,如图 4‑5所示,BAR 1和BAR 2被用来请求一块64MB的可预取内存地址空间。这里使用了两个连续相连的BAR是因为这个设备支持这个内存地址空间的请求使用64bit地址,这意味着如果需要的话,软件给它分配的地址空间可以超过4GB地址边界(并非必要)。由于地址可以是64bit位宽,因此必须将两个连续相连的BAR一起使用。

跟上面一样,将BAR在配置过程中分为3个节点:

在图 4‑5中的(1),我们可以看到一对BAR都处于未初始化的状态。设备的设计者已经将低位BAR(在本例中为BAR 1)中的低位bit固定为一个数值,来指示需要的memory的大小和类型,但是高位BAR(BAR 2)中的bit则都是可读可写的,没有被固定。系统软件的第一步就是把每个BAR都通过配置写操作来将可写入的bit写为全1。在图 4‑5的(2)中展示的BAR就是处于第二阶段的样子,除了BAR 1中被固定的低位bit以外,所有的bit都被写为1。

如上一个例子所述,系统软件已经评估过BAR 0。因此软件的下一步就是读取下一个BAR(BAR 1),并对其进行评估,以此来确定设备是否正在请求更多的地址空间。一旦BAR 1被读取,软件就会发现设备正在请求更多的地址空间,并且所请求的地址空间类型为可预取内存地址空间(P-MMIO),这个地址空间可以被分配为64bit地址范围的任何一个位置。由于这个地址空间请求支持64bit地址,那么下一个连续相连的BAR(本例中为BAR 2)就会被当做是BAR 1的高32bit。因此软件也会读取BAR 2的内容。然而,软件并不会对BAR 2进行像对BAR 1一样的低位bit的评估,因为软件仅仅是将BAR 2简单的当做BAR 1发起的64bit地址请求的高32bit。表 4‑2中总结了本例中对两个BAR都写入全1后再进行配置读的读取结果。

这个过程中的最后一步就是系统软件为这一对BAR分配一个地址范围,因为对于软件来说现在已经知道了这一对BAR请求的地址空间的大小和类型。图 4‑5的(3)中展示了这两个BAR处于第三阶段的样子,软件已经通过两次配置写操作将分配的地址区域的64bit起始地址写入BAR 1与BAR 2的组合体中。在本例中,高位BAR的bit 1(BAR pair的bit 33)被置为1,低位BAR的bit 30(也是BAR pair的bit 30)也被置为1,这表示其实地址为2_4000_0000h。两个BAR中所有其他的可写bit都已被清零。

到这里为止,BAR Pair(BAR 1和BAR 2)的配置就已经完成了。一旦软件启用了命令寄存器(Command register,偏移地址04h)中的内存地址译码,那么这个设备就会接受所有地址在2_4000_0000h-2_4300_0000h(64MB大小)这个范围内的memory请求。

 

pci bios枚举 pcie枚举过程_pci bios枚举_06

 

 BAR示例3——IO地址空间请求

让我们在前面两个例子的基础上继续,我们假设当前的Function还需要请求IO空间,如图 4‑6所示。在图中,进行请求的BAR(本例中为BAR 3)在配置过程中分为3个节点:

在图 4‑6中的(1),我们可以看到BAR 3处于未初始化的状态。系统软件此前已经将每一个BAR都写入了全1(写入BAR中可写的bit,不可写的不受影响),并且已经对BAR 0、BAR 1 BAR 2组成的BAR Pair进行了评估。现在软件要继续看看设备是否还需要通过BAR 3来请求更多的地址空间。图 4‑6中的状态(2)展示了BAR 3被写入全1后的样子。

软件现在将会读取BAR 3的内容,以此来评估请求地址空间的大小和类型。表 4‑3中总结了本次配置读的读取结果。

读取BAR 3的内容之后,软件就知道了这个地址空间请求是要请求256byte大小的IO地址空间,那么软件的最后一步操作就是要为设备分配一个IO地址空间范围,并将这个地址范围的起始地址写入BAR 3。图 4‑6中的状态(3)所展示的BAR 3就是完成了写入起始地址的样子。在我们的例子中,这个设备的起始地址为16KB,所以bit 14是被置为1的,也就是说基地址为4000h,所有的高位bit都是清0的。

到目前为止,对BAR 3的配置已经完成了。一旦软件启用了命令寄存器(Command register,偏移地址04h)中的IO地址译码,那么这个设备就会接受并响应所有地址在4000h-40FFh(256Byte大小)这个范围内的IO事务。