一、配置机制
我们之前提到过,配置空间存在于PCIe设备上,而处理器通常无法直接执行配置读写请求,因为它只能生成内存和I/O请求。这意味着RC(Root Complex)需要将某些访问请求转换为配置请求,以支持配置空间的访问。配置空间的访问可以通过以下两种机制进行:
传统的PCI配置机制(Legacy PCI Configuration Mechanism):使用I/O间接访问方式。在这种方式下,处理器通过I/O端口访问配置空间的地址和数据。
增强的配置机制(Enhanced Configuration Mechanism):使用内存映射访问方式。在这种方式下,配置空间被映射到内存地址范围,处理器可以像访问内存一样访问配置空间。
通过这两种机制,系统能够根据具体需求对PCIe设备进行灵活的配置,满足兼容性和性能的需求。
二、传统的PCI配置机制(Legacy PCI Configuration Mechanism)
PCI规范定义了一种间接IO的方法,用于指示系统(通常是RC或其等效体)执行PCI配置访问。由于主流的x86处理器只能访问64KB的IO地址空间,到PCI定义时,这些有限的IO空间已经被大量占用,仅剩下少数地址范围可用:0800h - 08FFh 和 0C00h - 0CFFh。因此,将所有可能的PCI设备功能直接映射到IO空间是不可行的。同时,内存地址空间也很有限,将所有配置空间映射到内存中也被认为不是一个好的解决方案。
为了解决这个问题,规范编写者选择了一种常用的解决方案——间接地址映射。这个方法通过两个寄存器来操作:一个寄存器保存目标地址,另一个寄存器保存发送到目标或从目标读取的数据。向地址寄存器写入目标地址,接着进行数据寄存器的读或写操作,就可以对目标功能的内部地址进行读或写操作。虽然这种方法解决了地址空间不足的问题,但它也意味着每次配置访问需要进行两次IO操作。
PCI兼容机制使用RC主桥中的两个32位IO端口:配置地址端口(IO地址为0CF8h - 0CFBh)和配置数据端口(IO地址为0CFCh - 0CFFh)。要访问PCI兼容的配置寄存器,首先需要将目标总线号、设备号、功能号以及dword(双字)号写入配置地址端口,并同时设置其启用位。接着,向配置数据端口发送一次一字节、两字节或四字节的IO读或写操作。RC中的主桥将目标总线与其下游存在的总线范围进行比较。如果目标总线在该范围内,主桥将发起配置读或写请求(取决于对配置数据端口的IO访问是读操作还是写操作)。
关于这部分的详细内容,可以阅读:
从零开始讲PCIe(4)——PCI总线的地址空间分配https://blog.csdn.net/apple_53311083/article/details/142697565?spm=1001.2014.3001.5501
之前我们讨论的内容中,都是围绕CPU的操作方式进行的,在CPU完成访问请求后,RC(Root Complex)负责发起配置请求。
当处理器发出针对配置空间的内存或IO访问指令时,RC会识别这些指令,并将它们翻译为PCIe配置请求。如果是通过传统的PCI配置机制(IO间接访问),处理器会先写入目标总线号、设备号、功能号和地址信息到RC中的“配置地址端口”,然后对“配置数据端口”进行读写操作。RC接收这些信息后,生成对应的PCIe配置请求。
RC在生成配置请求后,负责将其发送到PCIe拓扑结构中的目标设备。这个配置请求通过PCIe链路传输,使用目标设备的总线号(Bus)、设备号(Device)和功能号(Function)来标识目标。PCIe设备接收到该请求后,读取或写入其对应的配置空间(如配置头部、扩展配置空间等)。
这其中还涉及一个PCIe配置请求通过PCIe链路传输的过程。
2.1 总线比较和数据端口的使用(Bus Compare and Data Port Usage)
在PCIe系统中,RC 中的主桥(Host Bridge)通过配置不同的寄存器来管理总线的编号,确保请求可以被正确路由到下游设备。具体来说,主桥实现了两个关键寄存器:次级总线号寄存器(Secondary Bus Number Register)和从属总线号寄存器(Subordinate Bus Number Register)。
- 次级总线号(Secondary Bus Number):这是当前Bridge下方直接连接的总线的编号。(对于RC来说,就是图 3-5 中 RC 的 Sec=0 中的 Sec)
- 从属总线号(Subordinate Bus Number):表示当前Bridge下方的目标总线编号的范围。(对于RC来说,就是图 3-5 中 RC 的 Sub=9 中的 Sub)
简单来说就是 Sec 和 Sub 共同指定出了这个设备下可访问总线的范围(对于Bus0 Device 1 来说就是 5-9)。
在一个单 RC 的系统中,Host-Bridge 的次级总线号应该被固定为 0,也就是它的可读可写的次级总线号寄存器从一复位就被强制置为0,或者说,Host-Bridge 知道它访问到的第一个总线一定是 Bus 0。
当配置请求发出时,主桥会根据目标总线号进行判断。如果配置地址端口的第31位(bit 31,配置使能位)设置为1,主桥将目标总线号与次级总线号和下属总线号之间的范围进行比较:
Type 0 配置请求:如果目标总线号与次级总线号匹配,则请求会被作为Type 0配置请求处理,表示目标设备位于次级总线上(即与该总线直接相连的设备)。
Type 1 配置请求:如果目标总线号大于次级总线号但小于或等于从属总线号,主桥会将该请求作为Type 1配置请求转发到次级总线。这意味着该请求并不直接针对次级总线上的设备,而是会被该总线上的某个 Bridge 继续转发到下游,直到找到目标设备。
Type 0 配置请求用于访问本地总线上的设备,而 Type 1 配置请求则用于跨越多个总线的层次结构,将请求递送到远程的设备。只有 Bridge 设备会对Type 1请求作出响应。
2.2 单主机系统
在单主机系统中,处理器通过对配置地址端口的写操作发起配置请求,这些信息会被根复合体(RC)内的Host/PCI桥锁存。当配置地址端口的第31位为1,且目标总线在桥的下游总线号范围内时,Host/PCI桥会将处理器对配置数据端口的访问转换为配置请求。处理器随后对地址 0CFCh 的配置数据端口进行IO读或写操作,Host/PCI桥会根据这些操作生成相应的配置请求。如果目标总线为总线0,则生成的是Type 0配置事务;如果目标总线处于其他范围内,则生成Type 1配置事务,而如果目标总线超出范围,请求将不会被转发。
2.3 多主机系统
在多主机系统中,多个根复合体可能存在,每个Host/PCI桥都有各自的配置地址和数据端口,并且这些端口的IO地址是相同的。为避免资源冲突,只有一个Host/PCI桥会响应处理器对配置端口的访问:
- 当处理器对配置地址端口进行IO写操作时,只有一个Host/PCI桥会参与这次事务,其他桥保持不响应状态。
- 在总线枚举过程中,软件会发现并为活动的桥下的总线编号。当该桥的枚举完成后,软件会激活之前处于非活动状态的Host/PCI桥,并为其分配一个不与已分配总线号重叠的总线号,继续枚举过程。
- 枚举完成后,处理器对配置地址端口的访问会发送到所有的Host/PCI桥,但对配置数据端口的读或写操作只会被负责目标总线的Host/PCI桥接受。该桥负责响应处理器的事务,而其他桥将忽略该请求。
- 如果目标总线是次级总线,桥将访问转换为Type 0配置访问。
- 如果目标总线不是次级总线,桥会将其转换为Type 1配置访问。
这种机制确保了在多主机系统中,通过合理的桥接配置和总线编号,可以避免配置请求的冲突,同时保证系统资源的正确访问。
三、增强型配置访问机制(Enhanced Configuration Access Mechanism)
当规范制定者在设计PCI-X和后来的PCIe如何访问配置空间时,他们考虑了两个主要问题。首先,每个功能仅有256字节的配置空间,这对于那些希望在配置空间中放入专有信息的厂商以及未来规范编写者来说,空间明显不够,无法容纳更多标准化的能力结构。为了解决这个问题,配置空间从每个功能256字节扩展到4KB。
其次,当PCI最初开发时,系统中使用多处理器的情况很少见。当只有一个CPU,并且只运行一个线程时,老的两步访问模型(通过写入配置地址端口,然后访问配置数据端口)不会造成问题。但在现代多核、多线程CPU的系统中,这种IO间接访问模型会出现问题。没有任何机制可以阻止多个线程同时尝试访问配置空间。在这种情况下,如果线程A写入了配置地址端口的值(0xCF8),而在其进行配置数据端口(0xCFC)访问之前,线程B覆盖了配置地址端口的值,就会导致冲突。为了应对这种多线程的访问问题,规范编写者选择了一种不同的方式。
相较于节省地址空间,新的设计直接将所有配置空间映射到内存地址,这样每个访问配置空间的请求可以通过一个内存访问完成。内存访问被映射到配置请求的地址范围内,生成一个配置请求,不再需要两步操作。这种设计的代价是需要分配大量的地址空间,因为每个功能的4KB配置空间映射到256MB的内存地址空间。但对于现代系统来说,256MB的地址空间占用微不足道。
为了实现这种映射,每个功能的4KB配置空间会从256MB地址空间中的某个4KB对齐地址开始,并且地址中的某些位将用于识别目标功能(参考表3-1)。
A[63:28]:表示256MB内存映射地址范围的上部分,用于增强配置机制的基地址分配。这部分是与具体实现有关的,通常由系统固件(如通过ACPI表)提供给操作系统。
A[27:20]:表示目标总线号(Target Bus Number),范围为0到255。
A[19:15]:表示目标设备号(Target Device Number),范围为0到31。
A[14:12]:表示目标功能号(Target Function Number),范围为0到7。
A[11:2]:这部分可以访问1024个dword(双字),与传统的访问方法只能访问64个dword不同,提供了更大的访问范围。
A[1:0]:定义了访问的大小和字节启用设置。
在PCIe的增强型配置访问机制(Enhanced Configuration Access Mechanism, ECAM)中,不再使用传统的IO间接访问方式,而是通过内存映射访问方式来进行配置空间的读写操作。因此,这种机制与PCI的传统配置访问模式相比,解决了多处理器、多线程系统中可能出现的冲突问题,简化了访问的过程,不再需要通过两个步骤(先写地址再读写数据)进行配置操作。
增强型配置访问机制中,配置空间被直接映射到系统的内存地址空间,通过标准的内存读写操作即可完成对配置空间的访问。这意味着,当CPU发起对PCIe配置空间的访问时,它只需执行一个内存读或写操作,而不需要先通过配置地址端口(如传统PCI机制中的0xCF8端口)指定目标地址,然后通过配置数据端口(如0xCFC端口)进行访问。
因为配置空间已经被映射到内存地址空间,RC可以根据内存地址来确定目标设备、总线和功能号,这些信息通过内存地址的特定位来编码。这一机制保证了访问的原子性和一致性,尤其是在多核处理器或多线程环境下,避免了多个线程同时尝试访问配置空间时可能出现的数据竞争问题。
因此,对于增强型配置访问机制来说,不需要像传统PCI那样考虑复杂的配置信息传输问题。每次访问都是通过单个内存读写操作完成的,并且由于采用了内存映射的方式,PCIe系统中的不同RC或桥接器都能根据内存地址空间的划分来处理各自下游设备的配置请求。这种机制不仅提升了性能,也增加了可扩展性,特别是在支持多总线、多设备的大型系统中。
四、小结
至此,我们了解了PCIe支持的两种配置机制,分别是(1)传统的PCI配置机制(Legacy PCI Configuration Mechanism):使用I/O间接访问方式。在这种方式下,处理器通过I/O端口访问配置空间的地址和数据。(2)增强的配置机制(Enhanced Configuration Mechanism):使用内存映射访问方式。在这种方式下,配置空间被映射到内存地址范围,处理器可以像访问内存一样访问配置空间。如今的PCIe设备,我们可以直接采用增强的配置机制(Enhanced Configuration Mechanism)。