您的位置首页  散文随感

一篇读懂ffff(ffffh的真值)

系统地址映射之 PCIe 篇

一篇读懂ffff(ffffh的真值)

 

0. 目录1. 前言2. 术语约定3. 在现代 x64 硬件中保持固件代码的兼容性4. 深入 PCI-to-PCI 桥5. PCIe 设备类型6. PCIe packets 与设备分层7. PCIe 地址空间

8. PCIe 配置机制9. PCIe capabilities 寄存器组10. PCIe BAR 初始化10.1 PCIe BAR 格式10.2 PCIe BAR 大小决议11. 剖析基于 PCIe 的系统地址映射

12. Haswell CPU 与 Intel 8-series 芯片组平台13. Haswell 内存 transaction 路由14. Haswell 系统地址映射15. Haswell 平台的 PCIe 增强配置空间

16. Haswell 平台的系统管理模式(SMM) 内存17. Haswell 平台的图形地址重映射/重定位表(GART)18. Haswell 平台系统地址映射初始化19. 深入分析 UEFI GetMemoryMap() 接口

20. 结语1. 前言本文是澄清 PCI 扩展 ROM 地址映射到系统地址映射系列文章的第二篇第一篇文章聚焦基于 PCI 系统的 x86/x64 系统,本文聚焦更现代的系统:基于 PCIe x86/x64 系统。

我们将展开讨论基于 PCIe 的 x86/x64 的系统地址映射初始化类似第一篇,我们主要聚焦理解 PCIe 总线协议的地址映射机制对地址映射的知识,有助于我们理解基于 PCIe 的系统中对 PCI 扩展 ROM 的访问。

物理层面上 PCIe 与 PCI 差异很大但逻辑层面上,PCIe 是 PCI 的一个扩展事实上,你完全可以在 PCIe 系统上启动一个只支持 PCI 总线的 OS,这完全没有问题,只要 OS 支持 PCI 总线协议规范即可。

正因为 PCIe 是 PCI 的一个扩展,所以在理解 PCIe 之前读者最好熟悉 PCI 总线协议所以强烈推荐先读第一篇文章2. 术语约定本文做如下术语约定:"main memory(主内存)" 指代插在主板上的 RAM 模块。

"memory controller(内存控制器)" 指代控制及访问 RAM 模块的组件,该组件可能是芯片组的一部分,也可能是 CPU 的一部分"flash memory(闪存)" 指代主板上存储 BIOS/UEFI 或 PCI 扩展 ROM 的芯片。

"memory range(内存区域)" 或 "memory address range(内存地址区域)"  表达的是区域,也即一个设备所占用的基地址/起始地址 到结束地址(基地址 + 内存大小)区域内的 CPU 内存空间。

"memory space(内存空间)" 表达的是 CPU 所能访问到的一组内存地址,也即 CPU 能寻址到的内存本语境下的 memory,可以是 RAM、ROM 或者其他可以被 CPU 寻址到的 memory 形式。

"PCI expansion ROM(PCI 扩展 ROM)" 大多数情况下指代 PCI 设备上的 ROM 芯片,除非语境下对此有其他特殊解释"hostbridge(主桥)" 和 "northbrige(北桥)" 术语在本文中指代相同的逻辑组件。

二者皆指代将 CPU core 和系统其余部分连接在一起的数字逻辑组件,也即将 CPU core 连接到 RAM 模块、PCIe 显卡、南桥芯片组,等等Intel 4 代 Generation Core Architecture CPUs 被称为 "Haswell CPU" 或在本文中简称 "Haswell"。

Intel 使用 "Haswell" 作为这一代 CPU 的代号16 进制数以 "h" 作为结尾,如 0B0Ah;或以 "0x" 作为开头,如 0xB0A二进制数以 "b" 作为结尾,如 1010b除非另有说明,"memory transaction routing(内存事务路由)"是指基于 transaction 目标地址的内存 transaction 路由。

本文另一个反复出现的术语是 "platform firmware(平台固件)"平台固件指的是在系统复位时对平台做初始化的代码,也即位于主板 flash ROM 芯片中的 BIOS 或 UEFI 代码3. 在现代 x64 硬件中保持固件代码的兼容性

x64 架构是 x86 架构的一个扩展因此 x64 继承了 x86 架构的大多数特性,包括在 boot 阶段的特性以及大多数系统地址映射相关的特性在固件代码执行方面,x64 保留了 x86 的两个重要方面:。

CPU 复位向量的地址虽然 x64 架构是一个 64 位 CPU 架构,但复位向量与 x86(32 位)架构下相同,即位于地址 4GB - 16byte(FFFF_FFF0h)如此设计是为了兼容迁移到 x64 平台的旧硬件,以及兼容大量依赖复位向量的底层代码。

系统地址映射中的 "compatibility/legacy" 内存区域"compatibility" 内存区域为 legacy 设备所使用举例来说,一些最低 1MB 内存区域中的区域,映射到 legacy 硬件或者是映射到对它们进行模拟的等价物。

更重要的是,部分内存区域映射到 BIOS/UEFI flash ROM 芯片的引导块(bootblock)部分BIOS/UEFI flash ROM 的内存区域没有改变,因为 CPU 复位向量并没有改变。

此外,很多固件代码也依赖该地址映射如果打破 x86 依赖的兼容性,则在对 x64 64 位架构的迁移上会引入很多问题,更遑论商业上的影响这就是在两个架构之间保持兼容性(一直到固件和芯片级别)为何如此重要。

在了解了为啥要提供兼容性之后,我们看一下芯片级别需要做哪些才能提供 x86 架构的向后兼容性图 1 显示了 Haswell 平台上与 UEFI/BIOS 代码取指/读有关的逻辑组件如你所见,提供向后兼容性的两个逻辑组件,其中一个位于 CPU 中,另一个位于 Platform Controller Hub(PCH) 中。

它们分别是 CPU 中的 compatibility memory range logic,以及 PCH 中的 internal memory target decoder logic至于 Direct Media Interface(DMI) 2.0 控制器逻辑,它在软件方面是透明的,包括固件代码 —— 它只是作为一个非常快速的“直通”设备;它不会改变通过它的,由固件代码所启动的任何 transaction。

图 1:现代平台中的 BIOS/UEFI 代码读 transaction图 1 展示了 CPU core 通过 serial peripheral interface(SPI),从连接到 PCH(南桥)上的 BIOS/UEFI flash ROM 中取指令的过程 —— 图 1 中的红色虚线。

这发生在很早期的 boot 阶段,在 CPU 完成其自身初始化并开始从复位向量处取指时图 1 中,平台里“兼容性”逻辑的存在,让 DOS 或 32 位 OS 在其上运行毫无压力图 1 显示了 CPU 中有 4 个 core。

然而,它们的地位并非都相等;其中一个被称为 boost strap processor(BSP),其他三个被称为 application processor(AP)当系统最初 boot 或硬件复位时,只有一个 core 是活动的,也就是 BSP。

APs 此时尚未处于活动状态初始化并激活 APs 的任务,由在 BSP 中所运行的固件(BIOS/UEFI)代码在系统初始化阶段完成注意,图 1 并没有展示 CPU 及 PCH 上的所有连接以及硬件逻辑,只展示了与 BIOS/UEFI 代码执行相关的。

这是为了突出系统复位后,BIOS/UEFI 代码执行中所涉及的组件如 图 1 所示,到达 BIOS/UEFI flash ROM 芯片的 transaction 并不涉及任何 PCIe 逻辑或结构;即使主桥中包含了 PCIe root complex,transaction 也不会通过它。

尽管如此,您仍然需要了解 PCIe 总线协议,因为 PCIe 扩展卡中的 PCI 扩展 ROM 将会使用到 PCIe 结构和逻辑这也是本文后续会展开 PCIe 相关介绍的原因4. 深入 PCI-to-PCI 桥

PCIe 硬件在逻辑上表现为一个 PCI 设备或一组 PCI 设备有些包含逻辑 PCI-to-PCI 桥本系列的第一部分并没有深入探讨 PCI-to-PCI 桥因此,我们将在本文对其展开讨论,因为它被大量用作逻辑 PCIe 设备的拓扑构建(building block)。

举例来说,root port(从 root complex 出来的 "outgoing" port)逻辑上是一个 PCI-to-PCI 桥,PCIe switch 从逻辑上看来就像几个互相连接的 PCI-to-PCI 桥。

我们从 PCI 配置寄存器首部来着手对 PCI-to-PCI 桥的解构PCI-to-PCI 桥必须在其 PCI 配置空间寄存器中实现 type 1 的 PCI 配置寄存器首部,这不同于非 PCI-to-PCI 桥设备 —— type 0 的 PCI 配置寄存器首部(参考上一篇文章)。

图 2 展示了 PCI-to-PCI 桥配置空间首部的格式,也即 type 1 PCI 配置寄存器首部该格式由 PCISIG 发布的 PCI-to-PCI Bridge Architecture Specification v1.1 描述。

图 2:type 1 PCI 配置寄存器首部(PCI-to-PCI 桥)图 2 顶部的数字标记了 PCI 配置空间首部中寄存器的 bit 位置图 2 右侧的数字标记了寄存器在 PCI 配置空间首部中的偏移。

图 2 中黄色标记的寄存器,决定了 PCI-to-PCI 桥从其 primary interface(离 CPU 较近的接口)转发到其 secondary interface(离 CPU 较远的接口)的内存和 I/O 范围。

图 2 中绿色标记的寄存器,决定了 PCI-to-PCI 桥 primary interface 中总线的 PCI 总线号(Primary Bus Number),secondary interface 中总线的 PCI 总线号(Secondary Bus Number)和 PCI-to-PCI 桥下游的最高 PCI 总线编号(Subordinate Bus Number)。

注意:每个 PCIe 设备必须将 Status 寄存器中的 Capabilities List bit 设置为 1,且每个 PCIe 设备必须实现 Capabilities Pointer 寄存器(图 2 中紫色标记)。

之所以如此要求,是因为 PCIe 是 PCI 协议的一种扩展实现,而对 PCIe 设备配置空间进行扩展(相较于“传统”PCI 设备而言)的方式就是基于 Capabilities Pointer 寄存器图 3 展示了一个假想的平台(该假想平台内部组件的工作方式与真实世界完全一致,为了易于理解做了简化)下 PCI-to-PCI 桥 primary 和 secondary interface。

如 图 3,PCI bus 1 连接到 PCI-to-PCI 桥的 primary interface,PCI bus 2 连接到 PCI-to-PCI 桥的 secondary interface

图 3:PCI-to-PCI 桥 interfaces如果 IO limit 寄存器的值大于 IO base 寄存器的值,且 transaction 的地址落在这两个寄存器覆盖的范围内,则 PCI-to-PCI 桥将会对下行(downstream)的 IO transaction 进行转发(从 primary interface 到 secondary interface)。

类似的,如果 memory limit 寄存器的值大于 memory base 寄存器的值,且 transaction 的地址落在这两个寄存器覆盖的范围内,则 PCI-to-PCI 桥将会对下行(downstream)的 IO transaction 进行转发。

memory base/limit 寄存器与 prefetchable memory base/limit 寄存器有着本质的不同memory base/limit 寄存器用于设备所占用的内存区域,这些设备对读 transaction 有副作用。

prefetchable memory base/limit 寄存器用于对读 transaction 没有副作用的设备,该场景下,PCI-to-PCI 桥可以在读 transaction 时 prefetch 数据而不会产生任何问题。

之所以可以 prefetch,就是因为读 transaction 没有副作用另一个不同之处在于,prefetchable memory base/limit 寄存器可以处理位于 4GB 之上的设备,因为它们可以处理 64 位地址空间。

映射到 4GB 以上的设备是没有 memory base/limit 寄存器的,因为 PCI 规范假定所有需要大地址区域的设备其表现行为应该类似内存,也即它们的“内存”内容是 prefetchable 的,并且对读操作没有副作用。

因此,PCI 规范规定需要消耗大地址区域的设备,它们应该实现 prefetchable memory base/limit 寄存器,而不是 memory base/limit 寄存器,并且所有具有对读操作有副作用的内存的设备,都应该被平台固件映射至低于 4GB 以下的地址范围。

关于 PCI-to-PCI 桥的一个容易被忽视的事实是,桥会对上行(upstream)的内存 transaction 进行转发(从 secondary interface 到 primary interface),也即从 PCI 设备到 CPU 的方向 —— 如果 transaction 的地址范围不在 memory base/limit 或 prefetchable memory base/limit 寄存器所覆盖范围内的话。

你可能会问,为啥需要这种行为?答案是,我们需要支持连接在 PCI-to-PCI 桥 secondary interface 上的设备的 DMA(direct memory access)在 DMA 中,PCI-to-PCI 桥的“下游”设备初始化 transaction(读取或写入 RAM),PCI-to-PCI 桥必须确保 transaction 从设备转发到 RAM 所在的“上游”方向。

设备的 DMA(PCI 设备)需要向系统内存中写入数据 —— 也就是 DMA 写 transaction如 图 3,连在 PCI-to-PCI 桥 secondary interface 上的设备发起的 DMA transaction,必须通过 PCI-to-PCI 桥才能到达系统内存;如果 PCI-to-PCI 桥不对写 transaction 做上行转发,DMA 就无法工作,因为来自设备的数据无法写入系统内存。

现在结合 图 3,我们看一个 PCI-to-PCI 桥下行转发内存 transaction 的例子在具体分析之前,我们有如下假设:图 3 系统有 8GB 内存前 3GB 的系统内存映射到 CPU 内存地址空间的低 3GB;其余部分映射到 CPU 内存地址空间的 4GB - 9GB 地址区域 —— 也就是高于 4GB。

平台固件已完成系统中 PCI 设备的基地址寄存器(BARs)的初始化;包括 PCI-to-PCI 桥的 BARs平台固件将 3GB - 4GB 的 CPU 内存区域初始化为被 PCI 设备所使用;当然,要排除掉硬编码被其他所使用的范围:高级可编程控制器(APIC)、平台固件 flash ROM 芯片,以及靠近 4GB 上限的其他 legacy 系统功能。

已初始化的 PCI 设备 BARS 的内容以及相关寄存器如下:PCI 设备 1,只有一个占用 16 MB(非 prefetchable)内存空间的 BAR,起始地址 E000_0000h(3.5GB)此 BAR 声明(claim)了从 E000_0000h - E0FF_FFFFh 的非 prefetchable 内存区域的 transaction。

PCI 设备 2,只有一个占用 16 MB(非 prefetchable)内存空间的 BAR,起始地址 E100_0000h(3.5GB + 16MB)此 BAR 声明(claim)了从 E100_0000h - E1FF_FFFFh 的非 prefetchable 内存区域的 transaction。

PCI 设备 3,只有一个占用 32 MB(prefetchable)内存空间的 BAR,起始地址 E200_0000h(3.5GB + 32MB)此 BAR 声明(claim)了从 E200_0000h - E3FF_FFFFh 的 prefetchable 内存区域的 transaction。

PCI 设备 4,只有一个占用 128 MB(prefetchable)内存空间的 BAR,起始地址 C000_0000h(3GB)此 BAR 声明(claim)了从 C000_0000h - C7FF_FFFFh 的 prefetchable 内存区域的 transaction。

PCI 设备 5,只有一个占用 128 MB(prefetchable)内存空间的 BAR,起始地址 C800_0000h(3GB + 128MB)此 BAR 声明(claim)了从 C800_0000h - CFFF_FFFFh 的 prefetchable 内存区域的 transaction。

PCI 设备 5,只有一个占用 256 MB(prefetchable)内存空间的 BAR,起始地址 D000_0000h(3GB + 256MB)此 BAR 声明(claim)了从 D000_0000h - DFFF_FFFFh 的 prefetchable 内存区域的 transaction。

PCI-to-PCI 桥地址以及路由相关的配置寄存器内容:— Primary Bus Number 寄存器:1— Secondary Bus Number 寄存器:2— Subordinate Bus Number 寄存器:2(注意:其与 Secondary Bus Number 相同,因为 PCI-to-PCI 桥的下游没有更大的总线编号)。

— Memory Base:未使能— Memory Limit:未使能— Prefetchable Memory Base:C000_0000h(3GB)— Prefetchable Memory Limit:DFFF_FFFFh(3.5GB - 1)。

现在我们基于上面的地址编排假设,来看一个具体的读 transaction假设 CPU 想从 PCI 设备地址 D100_0000h(3GB + 16MB)处读取内容到 RAM 中下面是具体的过程:CPU core 发起一个读 transaction。

该读 transaction 到达集成在 CPU 中的主桥/北桥北桥将读 transaction 转发至南桥,因为北桥知道所请求的地址在南桥上南桥将读 transaction 转发给 PCI bus 1,bus 1 直连到南桥。

PCI-to-PCI 桥确认并处理(claim)读 transaction,因为 transaction 的地址在桥的范围内PCI-to-PCI 桥确认并处理读 transaction,并对其进行回复,因为所请求的地址在桥的 prefetchable 内存区域内(位于 prefetchable memory base 和 prefetchable memory limit 之间)。

PCI-to-PCI 桥将读 transaction 转发给其 secondary bus,也就是 PCI bus 2.PCI 设备 6 确认并处理读 transaction,因为其地址位于该设备的 BAR 范围内。

PCI 设备 6 通过 PCI bus 2 返回目标地址(D100_0000h)处的数据PCI-to-PCI 桥通过 PCI bus 1 将数据转发给南桥CPU 中的北桥将数据写入 RAM,并结束此读 transaction。

从上面的流程可以看出,如果请求地址位于 PCI-to-PCI 桥的区域内,则桥会将读/写 transaction 从其 primary interface 转发至其 secondary interface;反之,如果请求地址不位于 PCI-to-PCI 桥的区域内,则不进行转发。

关于 PCI-to-PCI 桥的另一个鲜为人知的事实是 subtractive decode PCI-to-PCI 桥上述流程中所展现的 "decoding" 方法(也就是对读 transaction 的确认并处理),被称为 positive decode,也即如果其位于设备所占据的区域内(其中一个 BAR),则设备确认并处理此 transaction。

与 positive decode 相反的即为 subtractive decode在 subtractive decode 设备(支持 subtractive decode)中,如果总线上没有其他设备确认并处理 transaction 时,无论 transaction 是否在该设备的地址区域内,其将对此 transaction 进行确认并处理(译者注:相当于默认的路由端口)。

一棵 PCI 总线数上只能有一个 subtractive 设备支持 subtractive decode 的是一种特殊类型的 PCI-to-PCI 桥其用于对较老芯片组中的 legacy 设备地址做 decoding,比如 BIOS 芯片。

然而,该技术在现代芯片组中基本上被淘汰了,因为芯片组和 CPU 中已经提供了 legacy 支持的逻辑5. PCIe 设备类型通过前面章节你已经掌握了理解 PCIe 协议的前置知识现在我们根据在 PCIe 设备树拓扑中的作用,展开对 PCIe 设备类型的分析。

如果你要理解 PCIe 设备的初始化,就必须得先了解 PCIe 设备类型PCIe 设备分类如下:PCIe root complex:root complex(译者注:下文皆简称 RC)类似基于 PCI 系统中的北桥。

其作用是将 PCIe 设备树与主内存(RAM)以及 CPU 连接起来大多数情况下,RC 还提供了到 GPU 的高速 PCIe 连接在使用两个物理芯片作为芯片组逻辑的系统中,RC 可以被实现为北桥的一部分。

然而,如 图 1 所示,现代设计中 RC 总是集成在 CPU 芯片中图 1 显示了 PCIe RC 作为集成在 CPU 中的主桥的一部分RC 通过一个逻辑端口也就是 root port 来连接 PCIe 设备树。

之所以称其为“逻辑”端口,是因为 root port 可以实现为一个独立于 RC 芯片之外的物理芯片举例来说,RC 可以位于 CPU 之内,但 root port 位于芯片组上Haswell CPU 及 Intel 8-series PCH 就采用了该 root port 实现。

注意,RC 可以有多于一个 root portPCIe switch:PCIe switch 是连接两个或多个 PCIe 连接(link)的设备一个 switch 内部包含多个互连的虚拟 PCI-to-PCI 桥。

这也是为啥你需要先深入理解 PCI-to-PCI 桥,方能理解 PCIe 设备树拓扑总之,RC 可以包含一个 switch,此情况下 RC 有多个 root portPCIe endpoint device

:这就是大多数人所知道的 PCIe 设备的 PCIe 设备类型PCIe endpoint 设备是终结 PCIe 连接(link)的 PCIe 设备;其只有一个到 PCIe 树拓扑的连接 —— 尽管它可以连接到另一种总线。

例如,PCIe 网卡在大多数场景下是一个 endpoint 设备,就像是 PCIe 存储控制器,等PCIe endpoint 设备也可以充当 legacy/compatibility 总线的桥,例如 PCIe-to-PCI 桥,或连接到 low pin count(LPC) 总线上的桥。

对 PCIe switch 与 endpoint 设备的解释可能仍然不太清晰图 4 展示了一个 PCIe 设备树拓扑中的 PCIe switch 与 endpoint 设备图 4 显示,PCIe switch 由三个互连的虚拟(逻辑的)PCI-to-PCI 桥组成。

该 switch 有一个入端口(inbound port,在 PCIe 下称为 ingress port),以及两个出端口(outbound port,在 PCIe 下称为 egress port)有两个连接到 switch 上的 endpoint 设备,一个 add-in network 及一个 add-in SCSI controller。

每个 endpoint 设备通过 switch 的虚拟 PCI-to-PCI 桥连接到 switch 上图 4 还显示了 PCIe RC 的 root port 的物理位置其中一个直连到 PCIe RC 上,另一个并非直连 PCIe RC —— 即通过芯片组 interconnect 连接。

从后者场景可以看出,对于 PCIe 设备树拓扑来说,芯片组 interconnect 是透明的图 4 显示了,external PCIe graphics(译者注:这里没有进行翻译,是因为方便对比原图,同下文中翻译的“外部 PCIe 显卡”。

独立显卡?)连接到位于 PCIe RC 中的 root port 上,而 PCIe switch 则通过芯片组 interconnect 连接到 root port 上从 PCIe 逻辑的角度来看,这两者之间并无区别(译者注:意思是通过直连 root port,或是通过芯片组 interconnect 连接到 root port 上,这二者没有区别)。

图 4:PCIe switch 与 endpoint 设备图 4 展现了 PCIe 设备之间的 interconnection该 interconnection 在 PCIe 总线协议中被称为一个连接(。

link)link 是一个逻辑上的 interconnection,其将两个不同 PCIe 设备上的 PCIe 端口连接起来每个 link 由一或多个路(lanes)组成每个 lane 由一对物理 interconnect 组成,其中一个是 PCIe 设备的出方向(outgoing),另一个是 PCIe 设备的入方向(incoming)。

物理 interconnect 在不同的方向上使用不同的信号实现 PCIe packets 的传输6. PCIe packets 与设备分层PCIe 与 PCI 总线协议的另一个重要不同点,在于 PCIe 中实现了更高层次的抽象。

PCIe 中的每个 transaction 在被发往另外一个 PCIe 设备之前,都被封装为一个 PCIe packet因此,PCIe 是一个基于 packet 的 chip-to-chip 通信协议这使得 PCIe 可以通过 packet 优先次序(prioritization)实现服务质量(quality of service,QoS)的能力。

然而我不打算展开解释 PCIe 中的 QoS,你只需要知道 PCIe 中有 QoS 即可现在我们看一下 packet 的细节PCIe 协议采用了与 TCP/IP 相同的通信分层的设计哲学,每一层为 packet 内容加上头部("header"),如此实现路由、纠错及保序。

图 5 显示了 PCIe 是如何实践该哲学的

图 5:PCIe 基于 packet 的通信PCIe 协议中共有三种类型的 packet(可以从最高层抽象,到 PCIe link 中最底层 packet 的发送看出):Transaction Layer Packet(TLP):如 图 5 所示,该 packet 由 PCIe 设备中的 transaction 层生成。

TLP 由 TLP 头部以及要发送的数据内容构成数据内容的源头是 PCIe 设备核心以及设备中的 PCIe 核心逻辑接口(core logic interface)TLP 头部包含 CRC 等其他数据TLP 可以走遍 PCIe 设备树,在源设备和目的设备之间经过一个以上的 PCIe 设备。

这看起来像是穿过源端和目的端之间的 PCIe 设备的 packet "隧道(tunnel)"但实际情况是 packet 在 PCIe 设备树中是被路由的在源端和目的端之间的 PCIe 设备必须是一个 PCIe switch,因为只有 switch 可以从其 ingress 端口向 egress port 做 packet 转发/路由。

Data Link Layer Packet(DLLP):如 图 5 所示,该 packet 由 PCIe 设备中的 data link 层生成DLLP 对 TLP 封装了另一个头部DLLP 在 DLLP 头部中为 packet 提供了另一个 CRC。

DLLP 只能在通过 PCIe link 直连的 PCIe 设备之间传输因此,DLLP 的 CRC 与 TLP 中的 CRC 作用并不相同,DLLP CRC 用来在相邻 PCIe 设备之间确保收到的 packet 是正确的。

有一些特殊的 DLLP 其内部并不包含 TLP packet,比如做功耗管理、流控制的 DLLP 等Physical Layer Packet(PLP):如 图 5 所示,该 packet 由 PCIe 设备中的 physical 层生成。

取决于 DLLP 的大小,PLP 会将 DLLP 封装为一个或多个 PLP;如果 DLLP 的大小无法封装进一个 PLP 之内,PLP 逻辑会将 DLLP 切分为多个 PLP 帧("frame")PLP 在两个互连的 PCIe 设备的 link 之间传输。

也有一些特殊的 PLP 并不包含任何 DLLP packet,比如用于 link training、clock tolerance compensation 等的 PLP上述对 PCIe packet 类型的解释似乎暗示着,一个 PCIe 设备必须实现三个设备层,每一层对应一种类型的 packet。

然而工程实践中其并非总是这样只要 PCIe 设备可以生成符合规范标准的 PCIe packet 即可7. PCIe 地址空间从前文你已知晓 PCIe 是一个基于 packet 的 chip-to-chip 通信协议。

这意味着此协议需要实现在芯片之间做 DLLP 或 TLP 路由的方法DLLP 只能到达直连的 PCIe 芯片,因此,我们更关注 TLP 路由,因为很多情况下,读写 transaction 的目标芯片与读写 transaction 源头之间隔着多个芯片。

TLP 路由有好几种机制这里我们只深入其一,也就是基于地址的 TLP 路由,又称为地址路由(address routing)PCIe 中有四种地址空间而 PCI 中只有三种PCIe 的地址空间如下:PCI 配置空间:该地址空间用来访问 PCIe 设备中兼容 PCI 的配置寄存器,以及 PCIe 的增强配置寄存器。

向后兼容(也即与 PCI 总线协议兼容)的缘故,该地址空间的一部分位于 CPU IO 空间中PCIe 配置空间的其余部分位于 CPU 内存空间对前 256 个寄存器访问的机制与 x64 体系结构下的 PCI 类似,也即 0xCF8 - 0xCFB IO 端口作为地址端口、0xCFC - 0xCFF IO 端口作为数据端口。

如同 PCI 设备,PCIe 中有 256 个 8 bit 配置空间寄存器被映射到该 IO 地址空间上前 256 byte 的配置寄存器在很早期的 boot 阶段即可通过 CPU IO 空间访问(因为该映射无需固件做初始化),剩余部分在平台固件完成 PCIe 配置空间的 CPU 内存空间初始化之后才可用。

相较于 PCI,PCIe 支持更大数量的配置空间寄存器每个 PCIe 设备有 4KB 配置空间寄存器最前端的 256 byte 的寄存器,既被映射到 legacy PCI 配置空间,也被映射至 CPU 内存空间中的 PCIe 配置空间。

完整的 4KB PCIe 配置空间寄存器可以通过 PCIe 增强配置机制访问PCIe 增强配置机制使用 CPU 内存空间而不是 CPU IO 空间(x86/x64 架构下的 PCI 配置机制使用 CPU IO 空间)。

PCIe 内存空间:与 PCI 一样,该地址空间位于 CPU 内存地址空间中然而,默认情况下 PCIe 支持 64 bit 寻址PCIe 配置寄存器的一部分位于 PCIe 内存空间中然而这里所说的“PCIe 内存空间”,指的是 PCIe 设备为非配置目的所占用的 CPU 内存空间。

具体来说,此 CPU 内存空间存储的是 PCIe 设备数据,比如 PCIe 网卡控制器中的本地 RAM,或者用作图形 buffer 的 PCIe 显卡的本地 RAMPCIe IO 空间:与 PCI 总线协议中的 IO 空间相同。

其存在的意义只是为了向后兼容  PCIPCIe 消息空间(message space):该地址空间在 PCI 中并未实现该地址空间是为了消除对物理 sideband 信号的依赖(译者注:比如 MSI,无需再借助专门的线来通过物理电平触发中断,可以直接以消息的形式触发中断)。

因此,在之前总线协议中必须以物理方式实现的东东,比如中断 sideband 信号,现在都可以作为 PCIe 设备树中的消息来实现我们不会深入这个地址空间,只需要知道它的用处即可本文只讨论上述四种 PCIe 地址空间中的其二,PCIe 配置空间与 PCIe 内存空间。

在后续的“PCIe 配置机制”章节中,我们会展开对 PCIe 配置空间的分析本节中我们深入 PCIe 内存空间的细节具体来说,我们会深入分析一个走遍 PCIe 体系(fabric,device tree)的 PCIe 内存读 transaction,该读 transaction 通过地址路由(address-routing)机制路由。

我们会分析包含一个 PCIe switch 的超级复杂的 PCIe 平台该配置通常不会出现在桌面类型 PCIe 平台上,只会存在于服务器类型 PCIe 平台该案例的复杂性,有助于读者搞清楚现实中桌面类型的硬件,因为相较于服务器类型平台,桌面类型简单多了。

图 6 展示了一个内存读 transaction 示例,目标地址是 C000_0000h(3GB)内存读 transaction 由 CPU core 1 发起,目标是 PCIe infiniband "network" controller 的本地内存,因为该目标地址映射的就是该设备的内存。

该 transaction 通过 PCIe 体系被路由图 6 中,读 transaction 路径由紫色虚线双箭头标记,从中可以看出,访问 PCIe 设备内存内容的路径,与将请求数据返回给到 CPU core 1 的路径相同。

只有当所有 PCIe 设备中所有地址相关的寄存器皆完成初始化之后,PCIe 体系中的地址路由才可以工作我们假设平台固件按如下方式对 图 6 的平台进行初始化:系统有 8GB RAM;其中 3GB 被映射到 0 - 3GB 的内存范围,其余部分被映射至 4GB - 8GB 内存区域。

该映射关系可以通过主桥中相关的映射寄存器来控制PCIe infiniband network controller 有 32MB 本地内存,映射到 C000_0000h 至 C1FF_FFFFh(3GB 至 3GB + 32MB - 1)。

PCIe SCSI controller card 有 32MB 内地内存,映射到 C200_0000h 至 C3FF_FFFFh(3GB + 32MB 至 3GB + 64MB - 1)虚拟 PCI-to-PCI 桥 1,2 和 3 其本身并不占用内存或 IO 区域,也即这些设备的 BAR 0 和 BAR 1 被初始化为并不占用任何内存区域的值。

虚拟 PCI-to-PCI 桥 1 将 C000_0000h 至 C3FF_FFFFh 区域(3GB 至 3GB + 64MB - 1)的内存声明(claim)为 prefetchable memory。

虚拟 PCI-to-PCI 桥 2 将 C000_0000h 至 C1FF_FFFFh 区域(3GB 至 3GB + 32MB - 1)的内存声明为 prefetchable memory虚拟 PCI-to-PCI 桥 3 将 C200_0000h 至 C3FF_FFFFh 区域(3GB + 32MB 至 3GB + 64MB - 1)的内存声明为 prefetchable memory。

在内存相关的东东完成初始化后,我们继续看读 transaction 在 PCIe 体系中是如何流动的。

图 6:通过地址路由遍历 PCIe 体系的 PCIe 内存读 transaction 示例现在我们看一下 图 6 中读 transaction 的流程:CPU core 1 发起目标地址 C000_0000h 的内存读 transaction。

内存读 transaction 到达主桥主桥映射寄存器指示将 transaction 转发给 PCIe RC,因为对该内存区域的地址映射属于 PCIe主桥中的 PCIe RC 根据主桥映射寄存器的设置,获知读 transaction 的目标是 PCIe 体系,于是将内存读 transaction 转换成一个 PCIe 读 TLP。

TLP 被放到“逻辑”PCI bus 0 上PCI bus 0 由 PCIe RC 推出,并终止于芯片组 PCIe switch 中的虚拟 PCI-to-PCI 桥 1注意,芯片组 interconnect 对于 PCI 及 PCIe 协议来说是透明的。

虚拟 PCI-to-PCI 桥 1 检查 TLP 中的目标地址首先,虚拟 PCI-to-PCI 桥 1 通过将目标地址与其自身 BAR 0 和 BAR 1 寄存器做比对,来确认 TLP 目标地址是否在虚拟 PCI-to-PCI 桥 1 其本身的范围内。

然而,虚拟 PCI-to-PCI 桥 1 BAR 0 和 BAR 1 按照平台固件初始化的值,其并不声明(claim)任何内存读/写 transaction于是,其继续检查 TLP 的目标地址是否位于其自身的 memory base/limit 或 prefetchable memory base/limit 寄存器的范围内。

图 6 中的 point(a)和 point(b)显示的就是这几个步骤virtual PCI-to-PCI 桥 1 发现 TLP 的目标地址位于其 prefetchable memory 范围内因此,虚拟 PCI-to-PCI 桥 1 接收 PCI bus 0 中的 TLP 并将其路由给 PCI bus 1。

虚拟 PCI-to-PCI 桥 2 及虚拟 PCI-to-PCI 桥 3 对 PCI bus 1 中 TLP 的处理,与虚拟 PCI-to-PCI 桥 1 对 PCI bus 0 中处理相同,也即检查它们的 BAR 及 memory base/limit 与 prefetchable memory base/limit。

虚拟 PCI-to-PCI 桥 2 发现 TLP 目标地址位于其 secondary interface因此,虚拟 PCI-to-PCI 桥 2 接收 PCI bus 1 中的 TLP,并将其路由给 PCI bus 2。

PCI bus 2 中的 PCIe infiniband network controller 检查由虚拟 PCI-to-PCI 桥 2 路由过来的 TLP 的目标地址,因为目标地址在其某个 BAR 范围内,故而接收该 TLP。

PCIe infiniband network controller 通过 PCIe 体系将目标地址的内容返回给 CPU注意:我们不会对该过程的细节再进行展开,因为我们已经学习过从 CPU 到 PCIe infiniband network controller 的 TLP 地址路由的细节。

至此,你应该搞清楚了 PCIe 地址空间以及 PCIe 地址路由下一节聚焦 PCIe 配置空间,以及将 PCIe 配置 transaction 路由至目标的机制8. PCIe 配置机制你必须要掌握 PCIe 配置机制,因为它们是对平台中 PCIe 设备进行初始化的基本方法。

PCIe 中有两种配置机制:兼容 PCI 的配置机制:该配置机制与 PCI 配置机制相同在一个 x86/x64 平台上,该配置机制通过使用 CF8h - CFBh IO 端口作为地址端口,使用 CFCh - CFFh IO 端口作为数据端口,读取/写入 PCIe 设备的配置寄存器。

该配置机制可以访问每个设备的 256 byte 配置寄存器,关于 PCI 配置机制的细节参考第一篇文章,“5.2 PCI 配置寄存器”相较于 PCI 只支持每个设备 256 byte 的配置寄存器,PCIe 支持 4KB 的配置寄存器。

其余的配置寄存器,可以通过第 2 种 PCIe 配置机制访问,也就是 PCIe 增强配置机制PCIe 增强(enhanced)配置机制:在该配置机制中,PCIe 设备的所有 PCIe 配置寄存器皆映射至 CPU 内存空间,包括前面的 256 byte 兼容 PCI 的配置寄存器,其既被映射至 CPU IO 空间,也被映射至 CPU 内存空间。

PCIe 配置寄存器所占用的 CPU 内存区域必须 256MB 对齐内存区域的大小为 256MB该内存区域的大小计算相当简单:每个 PCIe 设备有 4KB 配置寄存器,PCIe 支持与 PCI 相同的 bus 数量,也即 256 条 bus,每条 bus 上 32 个设备,每个设备 8 个 function。

因此,所需内存区域的总大小为:256 x 32 x 8 x 4KB,也就是 256 MB需要注意的是,每个 PCIe 设备配置寄存器的前 256 byte 被映射到两个不同的空间:通过兼容 PCI 的配置机制映射至 CPU IO 空间,通过 PCIe 增强配置机制映射至 CPU 内存空间。

如果你仍然为此感到困惑,看一眼 图 7图 7 展示了将 PCIe 设备配置空间寄存器同时映射进 CPU IO 空间和 CPU 内存空间

图 7:从 CPU 视角来看 PCIe 设备配置空间寄存器的映射你可能会问为啥 PCIe 系统还要实现 PCI 配置机制原因之一,是给 PCIe 诞生之前的 OS 提供向后兼容性,原因之二,是给 PCIe 增强配置机制提供初始化的方法。

在 x64 平台上,由 PCIe 增强配置机制所占用的 CPU 内存区域并非是硬编码的,而是在 64 bit CPU 内存空间中是可重定位的平台固件必须先通过 PCIe RC 中的特定寄存器,来将 PCIe 设备的配置寄存器映射到 64 bit CPU 内存空间中的特定地址。

映射 PCIe 配置寄存器的起始地址必须 256MB 对齐另一方面,x86 与 x64 中,PCI 配置寄存器在 CPU IO 空间中的位置是固定的(硬编码);这提供了一种方法,来配置“控制所有 PCIe 配置寄存器映射“的寄存器(寄存器位于 PCIe RC 中):通过兼容 PCI 的配置机制,因为兼容 PCI 的配置机制总是可用的,包括系统 boot 的很早期阶段。

PCIe 增强配置机制的隐含语义是,对 PCIe 设备的 PCIe 配置寄存器的读或写,需要通过对内存的读或写这与 PCI 配置机制不同,PCI 配置机制下需要 IO 读或 IO 写这在 90 年代成为一种趋势,将硬件相关的寄存器挪到 CPU 内存空间中,以简化硬件和系统软件的设计。

它不仅被 PCIe 总线协议所采用,还被 x64 以外 CPU 架构中的其他总线协议所采用

图 8:PCIe 增强配置机制中控制 CPU 内存空间映射的 bits图 8 显示了将 PCIe 增强配置空间映射到 64 bit CPU 内存空间下面是对 64 bit PCIe enhanced configuration space register address 的拆解:。

address bits 28 - 63,是为增强配置机制分配的 256MB MMIO(memory-mapped IO)地址区域的、256MB 对齐的基地址的高位基地址的分配方式是 implementation-specific 的。

大多数情况下,基地址由芯片组中或集成在 CPU 中的可编程寄存器控制address bits 20 - 27,选定目标 bus(256 中的其一)address bits 15 - 19,选定目标设备(32 中的其一)。

address bits 12 - 14,选定设备中的目标 function(8 中的其一)address bits 2 - 11,选定目标双字(double-word,又称 dword);所选定 function 配置空间的 1024 中的其一(译者注:4KB 配置空间中,共有 1024 个双字,一个双字是 32 字节)。

address bits 0 - 1,定义所选双字的起始位置译者注:这里本质上就是,如果你要对某个function 的某个配置寄存器做读写,你应该怎么指定其地址如同对 PCI 配置寄存器地址的访问,对 PCIe 增强配置寄存器的读或写需要对齐到双字(32 bit)。

因为在通往 PCIe 增强配置寄存器路径上的 CPU 和芯片组,只保证对 32 bit 对齐的配置 transaction 的转发在 x64 体系结构下,通过 CPU 中的一个特殊寄存器(PCIe RC 的一部分)来控制 PCIe 配置空间的基地址(36 bit)。

该基地址寄存器必须由平台固件在 boot 时进行初始化寄存器初始化是通过兼容 PCI 的配置机制完成的,因为在 boot 的很早期,寄存器包含一个默认值,该值不能用于寻址 PCIe 增强配置空间中的寄存器(译者注:意思是此时 MMIO 还工作不了,只能通过 IO 端口的方式)。

后面我们解构基于 PCIe 的系统地址映射时,会更深入地了解这个基地址的实现现在,我们看一个 PCIe 增强配置寄存器映射到 CPU 地址空间的简单例子有如下假设:PCIe 增强配置地址空间的基地址,在 PCIe RC 寄存器中设置为 C400_0000h(3GB + 64MB)。

目标 PCIe 设备位于 bus 1 中目标 PCIe 设备的设备号在对应 bus 中为 0目标 PCIe function 的 function 号为 0目标寄存器位于 PCIe 设备配置空间偏移 256(100h)处。

目标寄存器的大小为 32 bit(1 个 dword)基于以上假设,目标 PCIe 增强配置寄存器位于地址 C410_0100h 处PCIe 增强配置寄存器地址的更高 32 bit 的值一般为 0;目标地址只用到 CPU 内存地址空间的低 32 bit。

如果对 PCIe 配置寄存器所对应目标地址的计算还感到迷惑的话,可以对照 图 8 进行逐一分解9. PCIe capabilities 寄存器组在 PCIe 和 legacy PCI 设备之间有几个根本性的不同。

在进一步探讨 PCIe BAR 初始化之前,我们先看看这些不同点,也就是 PCIe capabilities 寄存器组(register set),因为它们会影响 PCIe BAR 的实现所有 PCIe 设备必须在其配置空间寄存器的前 256 byte 中实现 PCIe capabilities 寄存器组。

相反的,一个 legacy 的 PCI 设备无需实现任何 capabilities 寄存器组在 legacy PCI 设备中,对 capabilities pointer 的实现是可选的,而不是强制性的。

图 9 显示了 PCIe 设备配置空间寄存器中的 PCIe capabilities 寄存器组实现

图 9:PCIe 设备 capabilities 寄存器组图 9 紫色高亮部分即是 capabilities pointer 寄存器,位于 PCIe 设备配置空间中,指向 PCIe capabilities 寄存器组。

具体工程实践中,capabilities pointer 寄存器通过 PCIe capabilities 寄存器组起始地址的 8 bit 偏移(单位字节),来指向 PCIe capabilities 寄存器组的起始地址,该偏移量从 PCIe 设备配置空间的起始地址计算,该 8 bit 偏移量存在 capabilities pointer 寄存器中(译者注:原文这里写的比较拧巴,意思应该是 capabilities pointer 寄存器里存放的是 PCIe capabilities 寄存器组的偏移,该偏移是相对 PCIe 配置空间起始地址的偏移)。

PCIe capabilities 寄存器组的具体位置是设备 specific 的然而, PCIe capabilities 寄存器组保证位于 PCIe 设备配置空间的前 256 byte 中,并位于 PCI 首部之后。

type 0 和 type 1 的首部皆必须在 PCIe 设备配置空间中实现 PCIe capabilities 寄存器组现在我们深入拆解一下 PCIe capabilities 寄存器组图 9 显示了 capabilities 寄存器组中的另一个寄存器 PCIe capabilities 寄存器(译者注:天蓝色标记)。

图 10 展示了此寄存器内容的具体格式

图 10:PCIe capabilities 寄存器格式PCIe capabilities 寄存器中的 device/port type bits(bits 4 - 7)影响 PCIe 设备到系统地址的映射。

device/port type bits 决定了 PCIe 设备是一个 native PCIe endpoint function 还是一个 legacy 的 PCIe endpoint function。

这两种类型 PCIe 设备的区别是:native PCIe endpoint function 的 device/port type bits 值为 0000bnative PCIe endpoint function 设备必须在运行时(一个运行的 OS 内部)将设备的所有东东都映射至 CPU 内存空间,比如它的寄存器和本地内存。

只有在 boot 阶段的早期、平台固件尚未完成系统初始化之前,设备才允许使用 CPU IO 空间legacy  PCIe endpoint function 的 device/port type bits 值为 0001b。

legacy PCIe endpoint function 设备甚至在运行时亦被允许使用 CPU IO 空间PCIe 标准假设 legacy PCIe endpoint function 设备扮演 legacy 总线的前端,比如 PCI 或 PCI-X。

至此,你应该搞清楚了 PCIe capabilities 寄存器的内容,其决定了 PCIe 设备在运行时是将其 BARs 映射到 CPU 内存空间还是 CPU IO 空间但也有特殊情况,尤其是处理 legacy IO 设备时。

具体来说,诸如 VGA 或 IDE 控制器这类 legacy PCI 兼容设备,通常希望位于固定的 legacy IO 区域内这类 function 并不实现基地址寄存器(BAR),取而代之的是,配置软件通过它们的 class codes(PCIe 配置空间偏移 09h)来将其识别为 legacy function,并通过将 command 寄存器中的 IO space bit 设置为 1,来使能其 IO decoder(译者注:意思就是 legacy 的 IO 设备,不实现 BAR,因为其内存区域不是可重定位的,它们只想位于 legacy IO 区域,这类设备利用 command 寄存器控制其通过 IO 的方式来访问)。

10. PCIe BAR 初始化PCIe 设备与 PCI 设备使用 BAR 的方法相同因此,一个 PCIe 设备的 BAR 必须在该设备可用之前被初始化PCI BAR 的初始化是平台固件的活PCI 标准为 PCI BAR 初始化提供了实现参考(implementation note)。

PCIe 支持相同的 BAR 初始化方法这里不再重复 PCI BAR 的初始化;本节中我只会指出 PCIe BAR 初始化与 PCI BAR 初始化的不同之处对于 PCI BAR 的格式以及 PCI BAR 初始化,参考第一篇文章。

10.1 PCIe BAR 格式BAR 有两种类型:一种是 IO BAR,其映射到 CPU IO 空间,另一种是 memory BAR,其映射到 CPU 内存空间PCIe 的 IO BAR 与 PCI IO BAR 完全一样。

但是 PCIe 标准推荐新的 PCIe 设备废弃对 IO BAR 的使用这些新的设备应该使用 memory BAR

图 11:PCI/PCIe memory BAR 格式图 11 显示了 memory BAR 的格式图 11 显示了映射到 CPU 内存空间的 BAR,其最低位硬编码为 0另外可以看出,bit 1 和 bit 2 决定了一个 BAR 是 32 bit BAR 还是 64 bit BAR。

图 11 显示 bit 3 控制映射至 CPU 内存空间的 BAR 的 prefetching该语境下的 prefetching 的概念是,CPU 可以在向由 BAR 指定的特定内存地址发起请求之前,获取该地址所对应的内容,也即提前的 fetching,也就是预取。

该特性用来提升 PCI/PCIe 设备内存读速度PCI 与 PCIe memory BAR 之间的一个重要区别是,PCIe endpoint function 中的所有 prefetchable bit 为 1 的 memory BAR 寄存器,必须被实现为 64 bit memory BAR。

prefetchable bit 不为 1 的 memory BAR ,可以实现为 32 bit BAR一个 memory BAR 所指定的最小内存区域大小为 128 bytePCIe 和 PCI 之间的另一个区别是双地址周期(dual address cycle,DAC)。

PCIe 是一种串行总线协议,并不实现 DACPCIe 在设计时考虑了本地 64 bit 寻址因此 PCIe 原生支持目标为 64 bit 地址的内存 transaction执行对 64 bit 地址的内存 transaction 不会带来性能损失。

10.2 PCIe BAR 大小决议PCIe BAR 大小决议的算法与第一篇文章中的 PCI 设备 BAR 大小决议完全一样只在 prefetchable memory BAR 上有所不同,因为 PCIe 中 prefetchable memory BAR 必须是 64 bit 宽度,BAR 大小决议算法使用相邻的两个 32 bit BAR,而不是一个 32 bit BAR。

11. 剖析基于 PCIe 的系统地址映射在对系统地址映射初始化做进一步了解之前,本节先拿一个具体的 x86/x64 系统地址映射的实现作为示例该实现的示例基于 Haswell,带有集成的北桥/主桥,以及 Intel 8-series PCH 平台。

该平台实现了 PCIe 总线,并且是一个很新的平台因此,这是学习现实世界中 PCIe 实现的完美案例Intel 8-series PCH 可以视为经典系统布局中的南桥;但二者并不是相同的逻辑,因为有些 PCH 的功能并没有出现在“经典”南桥中。

CPU 的 datasheet 见 http://www.intel.com/content/www/us/en/processors/core/CoreTechnicalResources.html原文 PCH 的 datasheet 链接已失效。

PCIe 与 PCI 不同之处在于 PCIe 的任何东东都在 CPU 内存空间中,包括其配置空间,正如“8. PCI 配置机制”一节所述在 CPU IO 空间中的 PCIe 配置寄存器部分,仅作向后兼容之用。

这也意味着相较基于 PCI 的系统,基于 PCIe 的系统的 CPU 内存空间会更加分散然而,这也降低了 CPU 设计的复杂度,并提供对映射至 CPU 内存空间的所有内存区域的快速访问,包括 PCIe 配置寄存器,因为默认情况下对 CPU 内存空间的访问要比对 IO 空间的访问更快。

12. Haswell CPU 与 Intel 8-series 芯片组平台图 12 显示了 Haswell CPU 及 8-series 芯片组的组合系统的构成图 12 显示了从芯片组到系统中其他组件的所有连接,包括那些可能并不存在于所有芯片组 stock keeping units(SKUs) 中的组件。

图 12:带 8-series 芯片组的 Intel Haswell CPU 构成图并非 图 12 中的所有系统 interconnect 都对系统地址映射产生影响本文中我们聚焦于会影响系统地址映射的 interconnect 以及控制寄存器。

图 12 中我们关心的 interconnect 有 DMI 2.0 interconnect,从 CPU 到 PCIe graphics 的 interconnect,从 Intel H87 芯片组到系统固件的 SPI interconnect,从 Intel H87 芯片组到 PCIe 设备的 interconnect,以及从 CPU 到 DDR3 DRAM module 的 interconnect。

在下一节“Haswell 内存 transaction 路由”中我们探究对这些 interconnect 内存 transaction 做路由的细节13. Haswell 内存 transaction 路由

Haswell CPU 中基于地址(address-based)的内存 transaction 路由决定了系统内存映射作为 CPU 一部分的主桥,其有若干个控制寄存器,控制该平台中内存 transaction 的路由。

在深入寄存器的细节之前,我们先构建一下北桥中内存 transaction 路由的高维视图图 13 显示了北桥中负责内存 transaction 的逻辑组件在任何 Intel 的公开 datasheet 中你都找不到此图中的逻辑组件。

我根据 datasheet 中的细节绘就了 图 13这其中的逻辑组件是为了辅助理解内存 transaction 路由的形象化组件

图 13:Haswell 北桥/主桥中的内存 transaction 路由图 13 中的内存 transaction 由 CPU 发起,目标是 DRAM、DMI 或 external PCIe graphics。

本文不打算深入 DMA 的细节,因为 DMA 可由 PCIe graphics 或 DMI 发起这意味着 DMA 会给我们理解主桥中的内存 transaction 路由带来不必要的麻烦图 13 显示了直连到 Haswell CPU cores 上的五个不同的内存 transaction 路由逻辑组件。

内存 transaction 路由逻辑组件有:"Normal" DRAM range logic:此逻辑组件对目标地址在 DRAM 区域内的内存 transaction(读/写)做路由,此类 transaction 无需重映射,也即 transaction 的目标地址在送入内存/DRAM 控制器之前无需做任何翻译。

控制此内存区域的控制寄存器是 top of low usage DRAM(TOLUD)寄存器以及 remap base 寄存器此二者寄存器皆位于主桥中TOLUD 控制 DRAM 在 4GB 以下所占用的 CPU 内存区域。

remap base 只在系统 DRAM 大小超过 4GB 时才使用;该场景下,remap base 标记了 4GB 以上“普通”CPU DRAM 区域的结束地址Remapped DRAM range logic:此逻辑组件对目标地址在 DRAM 区域内且需要重映射(remapping)的内存 transaction(读/写)做路由,也即 transaction 的目标地址在送入内存/DRAM 控制器之前需要先做翻译。

控制重映射内存区域的控制寄存器有两个:remap base 寄存器及 remap limit 寄存器这些寄存器位于主桥中Compatibility memory range logic:此逻辑组件对目标地址在 compatibility 内存区域的内存 transaction(读/写)做路由。

该内存区域包括从 A_0000h 到 F_FFFFh 的区域,以及从 F0_0000h 到 FF_FFFFh(15MB 至 16MB)区域的 ISA 洞该内存区域又进一步被划分为三个子区域:在 A_0000h 到 B_FFFFh 之间的 legacy VGA 内存区域:

VGA memory map mode 控制寄存器控制了从 A_0000h 到 B_FFFFh 范围内的 compatibility 内存区域取决于 VGA memory map mode 控制寄存器的值,该区域可以映射给 PCIe、DMI 或 Internal Graphics Device(IGD)。

因此,目标地址在 A_0000h 到 B_FFFFh 内存区域内的内存 transaction,可能会被路由到 PCIe 或 DMI 或 IGD非 VGA compatibility 与非 "ISA Hole" 的内存区域,此区域由从 C_0000h 到 F_FFFFh 的内存区域组成:所有目标地址在此 compatibility 内存区域内的内存 transaction,都会通过 DMI 接口被路由给内存/DRAM 控制器或 8-series PCH 芯片组(图 12 中的 Intel H87 芯片组),具体取决于相应内存区域逻辑的控制寄存器的值。

从 C_0000h 至 F_FFFFh 的 compatibility 内存区域的控制寄存器称为 programmable attribute map(PAM) 寄存器从 PAM0 到 PAM6,一共有 7 个 PAM 寄存器;它们皆位于 CPU 内的主桥中。

从 F0_0000h 到 FF_FFFFh(15MB 至 16MB)的 "ISA Hole" 内存区域:主桥中的 legacy access control(LAC) 寄存器控制了目标地址位于 F0_0000h 至 FF_FFFFh(15MB 至 16MB)的 ISA 洞内存区域的内存 transaction 的路由。

所有目标地址在此 compatibility 内存区域的内存 transaction,皆通过 DMI 接口被路由至内存/DRAM 控制器或者 8-series PCH 芯片组(图 12 中的 Intel H87 芯片组),具体取决于 LAC 控制寄存器的值。

此 ISA 洞是一个可选的区域;默认情况下由主桥禁能平台固件 flash,message-signal interrupt(MSI)以及高级可编程中断控制器(APIC)内存区域:此区域位于 4GB - 20MB 至 4GB(FEC0_0000h - FFFF_FFFFh)。

所有目标地址在此 compatibility 内存区域的内存 transaction,除去目标是 MSI 地址区域以及 CPU cores 中 LAPIC 的 APIC 内存区域,总是被路由给 DMI 接口。

一旦 transaction 通过 DMI 到达南桥,目标地址是被平台固件 flash 所占用内存区域的内存 transaction,将由南桥转发至平台固件 flash 芯片此内存区域是硬编码的;对此内存区域的内存 transaction 路由无需主桥中的控制寄存器介入。

PCIe 3.0 graphics memory range logic:此逻辑组件对目标地址在被外部  PCIe 显卡 BARs 所覆盖内存区域的内存 transaction(读/写)做路由如果此区域低于 4GB,则主桥中没有特定的控制寄存器会更改对此内存区域的访问,决定路由的只有外部 PCIe 显卡 BARs。

如果外部 PCIe 显卡使用的内存区域高于 4GB,则由 PMBASEU 及 PMLIMITU 寄存器来控制对 PCIe 显卡内存区域的访问此二者寄存器皆是集成在 Haswell CPU 中的 PCIe 控制器的一部分。

PCIe 2.0/PCI memory range logic:此逻辑组件对目标地址在“从 TOLUD 寄存器值到 4GB 的区域”的内存 transaction(读/写),通过 DMI 接口路由至 8-series PCH 芯片组。

此逻辑组件也对目标地址区域位于 PMBASEU 至 PMLIMITU 寄存器之间,但并未落入 PCIe 3.0 graphics 内存区域范围(如果系统有 4GB 或更多 RAM 的话)的内存 transaction(读/写)做路由。

“从 TOLUD 值到 4GB 的区域”是为 PCI/PCIe 内存预留的并未被 PCIe 3.0 graphics 所声明(claim)的 PCI/PCIe 内存区域位于 8-series PCH 芯片组中。

该区域的控制寄存器是主桥中的 TOLUD、PMBASEU 以及 PMLIMITU 寄存器这 5 个内存 transaction 路由逻辑组件是互斥的,也即每个内存 transaction 只能由其中一个逻辑组件所声明(claim)。

每个内存 transaction 应该只被一个内存 transaction 路由逻辑组件声明不过,内存 transaction 路由也会出现“混乱”的状态该情况下,会有不止一个逻辑组件对同一个内存 transaction 进行声明(claim)。

该混乱情况的发生,是因为平台固件错误地设置了这些逻辑块的一个或多个控制寄存器14. Haswell 系统地址映射前面的章节中,你已经搞清楚了 Haswell 中北桥是如何基于 transaction 的目标地址来对内存 transaction 做路由的。

本节深入路由的结果,也就是系统地址映射北桥中的地址重映射,让整个系统地址映射变的相当复杂,具体的地址映射取决于所站的视角,也即是否是从 CPU core(s) 的视角来看地址映射图 14 显示了一个有 4GB 或更多 RAM 的 Haswell 系统的地址映射。

我没有选择讨论少于 4GB RAM 的 Haswell 系统,因为此(低于 4GB RAM 的)配置下不会使用地址重映射

图 14:Haswell 系统地址映射(系统内存 >= 4GB)图 14 展示了从 CPU core 视角以及 DRAM 控制器视角的 Haswell 系统地址映射这二者视角下的系统地址映射是不同的,因为 DRAM 控制器是看不到被 PCI/PCIe 设备所占用的内存区域的,并且其也无需看到。

从 CPU 看来,TOLUD 至 4GB 的内存区域是分配给 PCI/PCIe 设备的,而相同的内存区域在 DRAM 控制器看来是被分配给 DRAM 的这种不同视图是可能的,因为北桥会将 DRAM 中 TOLUD 至 4GB(从 DRAM 控制器视角来看的)的内存区域,重新映射到 CPU 内存空间中高于 4GB 的、被称为 "reclaim" 内存区域的新内存区域上。

"reclaim" 内存区域由两个寄存器决定:北桥中的 REMAP BASE 以及 REMAP LIMIT 寄存器如 图 13 所示,北桥中的内存重映射逻辑负责重映射任务图 14 中浅蓝色的框代表被 RAM 占用的内存区域。

这意味着,从 DRAM 控制器看来,可用的 RAM 是连续的内存区域,而从 CPU core 看来却不是从 CPU core 看来,4GB 以下的内存区域有不属于 RAM 的“洞” —— “洞”在 图 14 中由非浅蓝色的框表示。

图 14 展示的内存区域的细节有:legacy address range(从 CPU core 的视角来看):该区域是 DOS compatibility 区域,位于 0 至 1MB 之间目标地址是 0 - 640KB 的内存 transaction 总是被路由给 DRAM,而对目标地址位于 640KB - 1MB 区域的内存 transaction 的路由,则取决于具体的控制视图区域的 PAM 寄存器的值。

回想一下,控制 650KB - 1MB 内存区域的共有 7 个 PAM 寄存器main memory ranges:这些区域由 DRAM 占据,且并不需要地址重映射OS 可以看到这些区域图 13 中的 "normal" memory range logic 负责对这些内存区域的处理。

这些内存区域在 CPU 视角和 DRAM 控制器视角下,有着相同的映射TSEG 区域:此内存区域无论从 CPU 视角还是 DRAM 控制器视角来看都是一样的TESG 是 "top of main memory segment" 的缩写。

然而,今天的语境下,其表示位于主内存区域顶部且低于 4GB 的段这段内存区域的起始地址由主桥中的 TSEG memory base(TSEGMB) 寄存器决定只有当 CPU 运行在系统管理模式(SMM)下才可以看到该寄存器的内容。

因此,SMM 模式之外的代码无法在 RAM 中看到此区域,即使是 OS 代码也不行此段(segment)存储了平台固件的运行时数据和代码"GFX GTT Stolen" 内存区域:从 CPU 视角来看,这段内存区域是 PCI/PCIe 内存区域的一部分,而从 DRAM 控制器的视角来看,它又是 DRAM 的一部分。

只有主桥中的 GMCH graphics control(GGC) 寄存器(通过其 VAMEN bit,平台固件必须基于固件的配置来初始化该 bit)中启用了 CPU 中的集成图形设备(integrated graphics device,IGD)(译者注:集成显卡?),该内存区域才会存在。

此内存区域存储了 graphics translation table(GTT) entries —— GTT entries 类似 CPU 中的页表 entries(PTEs),但 GTT entries 是用于显存的。

从 CPU 的视角来看,该内存区域占据了 PCI/PCIe 内存区域,尽管它物理上位于系统内存(DRAM)中,而不是 PCIe 显卡的本地 RAM 中主桥中 GGC 寄存器中的 bits 8-9(GGMS bits)决定了该内存区域的大小,而主桥中 GTT stolen memory(。

BGSM) 寄存器决定了该内存区域的起始/基地址"GFF stolen" 内存区域:从 CPU 视角来看,这段内存区域是 PCI/PCIe 内存区域的一部分,而从 DRAM 控制器的视角来看,它又是 DRAM 的一部分。

只有 CPU 中的 IGD 被使能的情况下,该内存区域才会存在 —— 使能 IGD 的细节参考上面“GFX GTT Stolen”该内存区域存储了图形数据,也即其充当了 IGD 的显存从 CPU 的视角来看,该内存区域占据了 PCI/PCIe 内存区域,尽管它物理上位于系统内存(DRAM)中,而不是 PCIe 显卡的本地 RAM 中。

主桥中 GGC 寄存器中的 bits 3-7(GMS bits)决定了该内存区域的大小,而主桥中 base data of stolen memory(BDSM) 寄存器决定了该内存区域的起始/基地址低于 4GB 的 PCI/PCIe 内存区域:此内存区域只有 CPU 视角能看得到。

此区域取决于 IGD 的使能与否如果 IGD 使能,则此内存区域的起始地址为 BGSM 寄存器的值;否则此内存区域的起始地址为 TOLUD 寄存器的值该内存区域的上限为 4GB - 20MB(FEC0_0000h)。

对此区域访问的转发,要么由 external PCIe graphics 通过 PCIe 3.0 connection 进行,要么由南桥通过 DMI 进行高于 4GB 的 PCI/PCIe 内存区域:此内存区域只有 CPU 视角能看得到。

此内存区域的起始地址由主桥中的 top of upper usable DRAM(TOUUD) 寄存器的值决定TOUUD 的值等于 REMAPLIMIT 寄存器的值加 1 对此区域访问的转发,要么由 external PCIe graphics 通过 PCIe 3.0 connection 进行,要么由南桥通过 DMI 进行。

graphics aperture 区域:如 图 14,此内存区域为低于 4GB 的 PCI/PCIe 内存区域的一部分然而工程实践中,取决于平台固件以及系统配置,此内存区域也可能在高于 4GB 的 PCI/PCIe 内存区域中。

此内存区域总是映射给 PCI/PCIe 内存区域其用作连续的地址空间,或者当 IGD 或外部 PCIe 显卡的显存用完时,充当额外的显存空间必须在平台固件配置中使能 graphics aperture range,此区域才会存在,否则该区域将不存在。

通过 graphics translation table 来对,分配作为 graphics aperture 的系统内存进行内存管理,就像 legacy AGP aperture 一样不同之处在于 aperture 可能位于 4GB 之上,及对 graphics aperture 内存管理的处理。

OS(graphics 设备驱动)负责对 graphics aperture 进行内存管理有关 graphics aperture 的基础知识,参阅本系列第一篇文章当 IGD 作为 Haswell 平台的图形芯片时,IGD 中的 graphics memory aperture base address register(。

GMADR) 决定了 graphics aperture 的起始地址Flash BIOS,APIC,MSI 中断内存区域(FEC0_0000h - FFFF_FFFFh):如前面章节所解释的,所有目标地址在此 compatibility 内存区域的内存 transaction,都将被路由给 DMI 接口,除去那些目标地址为,相应 CPU cores 中 local APIC 的 MSI 地址区域以及 APIC 内存区域。

对后两者区域的内存 transaction 总是被直接发往 CPU主内存 reclaim 地址区域:在 CPU 视角及 DRAM 控制器视角下,此内存区域占据着不同的地址主桥中的 REMAPBASE 及

REMAPLIMIT 寄存器决定了从 CPU 视角看来,这段内存区域的起始与结束地址TOLUD 寄存器及 4GB 上限,分别是 DRAM 控制器视角下此内存区域的起始与结束地址(译者注:此内存区域在 CPU 视角和 DRAM 视角下同时存在)。

图 13 中主桥中的 "remapped memory range logic",负责对从 CPU 发出的,目标为此内存区域的内存 transaction,在到达 DRAM 控制器之前进行重映射manageability engine UMA 内存区域:此区域不被 CPU 所控制。

manageability engine(ME) 集成在 8-series PCH(南桥)中平台固件通过读取 8-series PCH 中的 Intel management engine UMA register

来获取此区域的大小平台固件必须从 top of memory(TOM) 开始,申请 UMA 寄存器所指定大小的区域平台固件初始化主桥中的 MESEG_BASE 及 MESEG_MASK 寄存器,以从 DRAM 中申请所需的区域。

ME 基本上是一个运行于其自身执行环境中的一个微控制器,不受 CPU 的控制当系统未处于低功耗模式时,ME 使用 RAM 中的 manageability engine UMA 内存区域来 cache 其固件。

如果系统处于低功耗模式,则ME 使用其自身(集成的)静态 RAM当系统掉电时,ME 不会运行至此,Haswell 系统的内存映射应该比较清楚了尽管如此,为了提升对 Haswell 系统内存映射的理解,我们将研究一个假想的内存读 transaction。

系统配置假设如下:物理内存(DRAM)大小:6GB分配给 memory mapped IO(包括 Flash,APIC,MSI 以及 Intel TXT)的内存空间:1GBremapped 物理内存的大小:1GB。

top of memory(TOM):1_8000_0000h(6GB)ME UMA 大小:0 —— ME 未使能TOUUD:1_C000_0000h(7GB) —— 该地址 1MB 对齐TOLUD:C000_0000h(3GB)。

REMAPBASE:1_8000_0000h(6GB)REMAPLIMIT:1_BFF0_0000h(7GB - 1)注意:remap 区域包括基地址及上限地址(base and limit addresses)。

在地址解码器中,remap 基地址的 bit 0-19 假定为 0h类似的,remap 上限的 bits 0-19 假定为 0Fh该配置确保了 remap 区域在 1MB 边界内现在我们来追踪该系统配置下,一个目标地址为 1_8000_0000h(6GB) 物理地址的内存读 transaction。

图 15 显示了此内存读 transaction 在系统中的流程

图 15:Haswell 内存读 transaction 示例图 15 中的红线标记了此内存读 transaction图 15 中有意未显示与内存读 transaction 不相关的逻辑组件,以便更易于理解此 transaction 流。

图 15 显示 CPU core 1 发起此内存读 transaction随后,当此内存读 transaction 进入主桥后,因为其位于 REMAPBASE 及 REMAPLIMIT 寄存器所覆盖的区域内,故而 "remapped memory range logic" 接收并处理(claim)此 transaction。

"remapped memory range logic" 随后将 transaction 目标地址重映射为一个 DRAM 控制器视角的地址,并随后将此 transaction 转发给 DRAM 控制器。

于是 DRAM 控制器处理该内存读 transaction,即从 DRAM 模块中获取正确的内容该示例内存读 transaction 演示了主桥中的逻辑组件是如何获取并处理(claim)一个内存读 transaction,并对其进行相应处理的。

如果你完全搞懂了此内存读 transaction 示例,你也就搞清楚了 Haswell 系统的地址映射你可能会问 PCIe 扩展 ROM 在 Haswell 中是如何寻址的它与基于 PCI 的系统非常相似。

必须对特定 PCIe 扩展卡中的 XROMBAR 寄存器进行使能并编程,以使用 PCI/PCIe 内存区域中的内存区域其余部分与基于 PCI 的系统相同PCIe 总线协议在这方面没有进行特别的增强15. Haswell 平台的 PCIe 增强配置空间

本节我们研究一下 Haswell 系统地址映射中的 PCIe 增强(enhanced)配置空间PCIe 配置空间寄存器的前 256 byte 被映射至 CPU IO 空间的端口 CF8h - CFFh(与 legacy PCI 总线相同),另外,这些寄存器还被映射到 PCIe 增强配置空间。

与 legacy PCI 配置空间相反,整个 PCIe 配置空间(每个设备 4KB)皆位于 CPU 内存空间中在 x86/x64 平台上,PCIe 配置空间所占用的内存区域在 CPU 内存空间中是可重定位的。

平台固件必须初始化此配置空间在 CPU 内存空间中的位置本节中我们重点看 Haswell 相关的实现现在,我们计算一下 PCIe 配置空间寄存器的内存空间需求:系统中 PCIe bus 的最大数量为 256。

每条 bus 上 PCIe 设备的最大数量为 32每个设备上 function 的最大数量为 8每个 function 最多可以实现 4KB 配置寄存器基于以上统计,整个 PCIe 配置空间寄存器需要的内存空间为:256 x 32 x 8 x 4KB,一共是 256MB 内存空间。

因此,平台固件必须对系统地址映射进行相应初始化以满足此 PCIe 配置空间的要求然而,工程实践中,特定系统中 PCIe 增强配置空间对内存空间的需求可能小于 256MB,因为系统无法在物理上支持那么多 PCIe 设备。

大多数情况下,PCIe 增强配置空间是从 PCI/PCIe 内存区域中划分出来的如 图 16 所示,Haswell 内存映射中,PCIe 配置空间可以映射到 4GB 以下的 PCI/PCIe 内存区域(从 TOLUD 到 4GB),或者映射到 4GB 以上的 PCI/PCIe 内存。

在 Haswell 平台上,位于主桥中的 PCI express register range base address(PCIEXBAR) 决定了 PCIe 增强配置空间的位置PCIEXBAR 的值决定了 PCIe 增强配置空间的起始地址和大小。

图 16 显示了映射 PCIe 增强配置空间的两种可能的方案它们分别被标记为 "Mapping Alternative 1"(位于低于 4GB 的 PCI/PCIe 内存区域)以及 "Mapping Alternative 2"(位于高于 4GB  的内存区域。

译者注:within the PCI/PCIe memory range above TOUUD,这里原文应该写错了,“高于 TOUUD”与“低于 4GB”某种意义上是一回事,作者这里想表达的应该是,Alternative 2 方案下,被映射到“高于 4GB”的 PCI/PCIe 区域,从图中亦可看出)。

PCIEXBAR 可以将 PCIe 增强配置空间的大小设置为 64MB、128MB 或 256MB平台固件应当在 boot 时初始化 PCIEXBAR 中控制 PCIe 增强配置空间大小的 bits图 16:Haswell 平台上 PCIe 增强配置空间寄存器映射

至此你应该清楚了将 PCIe 增强配置空间映射到 Haswell 系统地址现在,我们看看如何访问 PCIe 增强配置空间寄存器Haswell 平台上,访问一个特定设备 function PCIe 配置空间的内存地址,按如下计算:。

PCIe_reg_addr_in_CPU_memory_space = PCIEXBAR + Bus_Number * 1MB +Device_Number * 32KB + Function_Number * 4KB +

Register_Offset你可能会问为什么要乘上 1MB、32KB 以及 4KB其实很简单:对于每条 bus,我们需要 32(设备) * 8(function) * 4KB 的内存空间,也就是 1MB;对于每个设备,我们需要 8(function) * 4KB 的内存空间,也就是 32KB。

现在来看一个简单的示例假设 PCIEXBAR 初始化为 C000_0000h(3GB),并且我们想访问 Bus 0,device 2,function 1,偏移 40h 的 PCIe 配置寄存器最终寄存器所对应的地址是:。

Register_address_in_memory = C000_0000h + 0 * 1MB + 2 * 32KB + 1 * 4KB + 40hRegister_address_in_memory

= C000_0000h + 0 + 1_0000h + 1000h + 40hRegister_address_in_memory = C001_1040h可以看到,最终的目标 PCIe 配置寄存器位于 CPU 内存空间中 C001_1040h 处。

通过此示例,对 PCIe 增强配置空间寄存器的处理应该不再有任何问题16. Haswell 平台的系统管理模式(SMM) 内存本系列第一篇文章中提到,存储 SMM 代码及数据的有两个内存区域:high segment(HSEG) 以及 TSEG。

然而,在 Haswell 平台上,HSEG 已被弃用且不再支持因此 Haswell 上存储 SMM 代码及数据的只有一个内存区域,也就是 TSEG 内存区域图 14 中显示了系统地址映射中 SMM 内存的位置。

主桥中的 TSEGMB 寄存器控制 TSEG 的起始地址TSEG 内存区域总是以 BGSM 寄存器的值结束(译者注:意思就是 BGSM 寄存器的值是 TSEG 内存区域的上限)TSEG 内存区域的内容只在两种场景下可访问。

第一种是系统刚刚启动且平台固件尚未对 TSG 配置进行初始化第二种是 CPU 运行在系统管理模式下主桥中 system management RAM control(SMRAMC) 控制对 TSEG 的访问。

Haswell 主桥会阻止非 CPU core 对 TSEG 的访问这可以防止“流氓”硬件或是在附加设备上运行的固件代码搞乱 TSEG 的内容这么做的主要原因是,如果 CPU 以外的设备可以访问TSEG,那么系统的安全性就会受到损害。

至此,典型的基于 Haswell 的系统中有关 SMM 内存的一切应该都清楚了17. Haswell 平台的图形地址重映射/重定位表(GART)本节我们讨论 GART第一篇文章中讨论了 legacy 系统中的 GART,也即 AGP GART。

本节讨论现代 GART,也即基于 PCIe 系统中的 GART微软概述了在基于 PCIe 系统中实现 GART(简称 PCIe GART)的要求具体参考:https://learn.microsoft.com/en-us/windows-hardware/drivers/pci/pci-express-faq-for-graphics。

这是相关节选:根据定义,AGP 需要一个带有 graphics address relocation table(GART) 的芯片组,其可以给显卡设备在非线性系统内存上呈现一个线性的视图然而,PCIe 要求内存线性化硬件实现在图形设备中,而非在芯片组上。

因此,支持 PCIe 中内存线性化的驱动必须存在于视频驱动中,而不是作为一个 AGP-style 的独立的 GART miniport 驱动如果想在 Windows XP 驱动模型(XPDM)驱动中使用非本地显存,图形硬件设备 vendor 必须同时实现内存线性化硬件及相应软件。

所有与 WDDM 兼容的 PCIe 显卡,都必须在它们的硬件和软件中支持内存线性化由以上节选可以看出,GART 逻辑必须实现在 PCIe 图形芯片中,而不是在芯片组逻辑中然而就 Haswell 而言,有一个集成的 PCIe 图形芯片(IGD),它是北桥的一部分。

不过这并不是一个问题,只要集成 PCIe 显卡实现其 GART 逻辑,也即 GART 逻辑是 IGD 的一部分,而不是北桥中其他逻辑的一部分(译者注:意思就是虽然 IGD 其本身实现在北桥中,但只要在 IGD 中实现 GART,就不违背 PCIe 标准中,GART 必须实现在显卡而非芯片组中的要求)。

如此这样的系统就符合上述微软的要求事实上,Haswell 将 GART 逻辑作为 IGD 的一部分来实现我们将在本节中更深入地研究它

图 17:Haswell GART 实现图 17 显示了 IGD 中 GART 逻辑的内部工作,IGD 位于北桥/主桥内图 17 显示 GART 逻辑将 graphics aperture 中的三个内存块(位于 PCI/PCIe 内存区域),映射到主内存(系统 DRAM)中的三个不同内存块。

在详细介绍 GART 逻辑之前,我想指出与 图 17 中所示 GART 相关的缩写以及组件命名的含义这些细节有:IGD(internal graphics device) 是集成在 Haswell 平台上的图形芯片。

该芯片集成在 CPU 硅片中(silicon die)该芯片包含 GART 逻辑GTTADR 是 graphics translation table base register该寄存器包含了 graphics translation table 的基地址,也即 CPU 内存空间中 GART entries 的起始地址。

图 17 显示了 GTTADR 区域(浅绿色标记)中的 GART entriesGTADR 区域是一个 memory-mapped IO 区域,因为此区域的内存是在一个 PCIe 设备(IGD 设备)上的。

该内存区域位于 IGD 中而不是系统内存中你可以将其视为一片位于 IGD 中的、包含了 GART entries 的 buffer(内存)这与 legacy AGP 系统中的 GART entries 不同,legacy AGP 中的 GART entries 位于系统内存中。

GMADR 是 graphics memory aperture base register该寄存器是 GART 逻辑的一部分该寄存器的值包含了 graphics aperture 在 CPU 内存空间中的起始地址。

图 17 显示 GMADR 指向了 Block #1 的起始地址,其乃 graphics aperture 区域的第一个块TLBs 是 translation look-aside buffers,用来处理图形内存 transaction。

它们是 IGD 中 GART 逻辑的一部分这些 TLBs 类似于 CPU 内存管理单元(MMU)中的 TLBsPTEs 是 page table entries我采用此术语,是因为想强调 GART entries 类似 CPU 中的 PTEs,只是它们是专用于图形的。

DSM 是 graphics data stolen memory此乃被 IGD 当做图形内存来使用的内存区域如果看一下 图 14,该区域是 PCI/PCIe 内存区域的一部分,尽管其位于系统 DRAM 中。

其位于系统 DRAM 中,是因为它们低于 TOLUD该内存区域只能被运行时的 IGD 访问因此,其表现的就像是外部 PCIe 显卡的本地内存GSM 是 graphics translation table(GTT) stolen memory。

该内存区域完全不同于 GTTADR 区域该内存区域包含只被 IGD 内部使用的 GTT entries该内存区域中的 GTT entries 与 GART 的 GTT entries 不同这一点需要注意。

TOLUD 是 top of  low usable DRAM register此寄存器包含了系统 DRAM 在 4GB 以下可用的最高地址TSEGMB 是 TSEG memory base register。

该寄存器包含了 TSEG 的起始地址graphics aperture 是 CPU 内存地址空间中的一个内存区域,其是 PCI/PCIe 内存空间的一部分,为额外的图形内存提供线性地址空间这很类似于第一篇文章中所述的,基于 AGP 的 legacy 系统中的 AGP aperture。

唯一的不同点在于 graphics aperture 可能存在的位置在基于 AGP 的 legacy 系统中,graphics aperture 只可能位于低 4GB 区域,而基于 PCIe 的系统中,graphics aperture 既可以位于低 4GB 区域也可以位于高 4GB 区域,只要它们在 PCI/PCIe 内存区域中即可。

Block #1,Block #2,Block #3这些块显示了从 graphics aperture 区域映射到系统 DRAM 内存区域的相邻内存块的映射(译者注:These blocks illustrate mapping of adjacent memory blocks from the graphics aperture range to the system DRAM memory range. 有点拧巴了,不重要,看图就行了)。

它们展示了 graphics aperture 区域中是如何呈现线性内存空间的,该内存空间由 GART 逻辑翻译成 OS 在 DRAM 中分配的内存块如果你对此映射是如何计算出来的仍然感到困惑,请阅读第一篇文章中的 GART 部分。

图 17  对一些东东进行了简化;被简化的就有 graphics aperture 内存区域的位置GMADR 所指向的 graphics aperture 可以从 CPU 内存空间的任何位置开始,可以在 4GB 以下,可以在 4GB 以上。

然而,图 17 显示的是位于低 4GB 范围的 graphics aperture 内存区域你需要意识到这一点我们来总结一下 legacy AGP GART 与现代 PCIe GART 之间的区别其一,AGP GART 逻辑是主桥实现的一部分,而现代 GART 逻辑是 PCIe 图形芯片实现的一部分。

当 PCIe 图形芯片位于主桥中时(恰如 Haswell 的情况),GART 逻辑将成为主桥的一部分OS 以不同的方式对待 AGP GART 以及 PCIe GARTAGP GART 有其自己的 miniport 驱动,而 PCIe GART 驱动是 PCIe 图形设备驱动的一部分。

其二,主要的区别在于 graphics aperture 所在的位置:legacy AGP 系统中,graphics aperture 总是位于 4GB 以下,而现代 PCIe graphics aperture 可以位于 4GB 以下或 4GB 以上。

至此你应该搞清楚了 Haswell 平台上的 GART虽然本节讨论的是 IGD PCIe 图形芯片中的 GART,但对于实现在外部 PCIe 显卡中的 GART,亦可轻松理解,因为它们的底层原理是相同的。

唯一的不同之处就是图形内存/buffer 的位置,从系统地址映射的角度来看,这基本上类似18. Haswell 平台系统地址映射初始化本节我们看一下 Haswell 系统地址映射的初始化我们不会对初始化的细节展开深入分析,只需对整个过程建立足够深的理解即可。

在 Haswell boot 过程中,有几个步骤是做系统地址映射初始化的,它们是:manageability engine(ME) 初始化:ME 的初始化在平台固件执行之前ME 初始化 8-series PCH 中的 。

Intel management engine UMA register,以让平台固件知晓其在系统 DRAM 中需要多大的空间来作为 ME UMA 内存区域芯片组初始化:该步中,完成芯片组寄存器的初始化,包括芯片组 base address registers(BARs)。

我们特别关注芯片组 BAR 的初始化,是因为其初始化影响到系统地址映射Haswell 平台中有两个芯片组:北桥和南桥北桥是 CPU 的一部分(有时候被称为 uncore 部分),南桥就是 8-series PCH。

如你所知,芯片组中有大量与系统地址映射有关的寄存器TOLUD、TSEGMB 以及 TOUUD 是它们中的一部分然而,在获知系统 DRAM 的大小之前,这些寄存器的大部分并不会进行初始化因此,它们中的大部分是作为主内存初始化的一部分(或后续初始化)被初始化的。

主内存初始化(RAM):该步中,进行内存控制器的初始化内存控制器的初始化与 RAM 的初始化作为互补的代码是同时被执行的,因为平台固件代码需要搞清楚内存控制器以及系统中所安装 RAM 模块支持的正确参数,以将此二者组件初始到一个“正确”的配置。

内存大小的计算过程也发生在这一步内存大小的计算决定了系统 DRAM 的大小这主要通过读取 DRAM 模块中 serial presence detect(SPD) 芯片的内容完成然而,大多数平台固件还会执行 DRAM 的随机读写操作,以确认 SPD 所声称的大小是否真的可用。

至于随机读写的算法,取决于具体的平台固件在知道系统 DRAM 的实际大小之后,芯片组中的 BAR,如 MESEG_BASE、TOLUD、TOUUD、TSEGMB 等的初始化也在这一步进行PCI/PCIe 设备发现及初始化:该步中,检测并初始化 PCI 设备(扩展为 PCIe 设备,以及连接到 PCI 兼容总线的其他设备)。

该步中检测的设备,可以是芯片组的一部分,也可以是系统中的其他 PCI 设备,焊接在主板上或插在 PCI/PCIe 扩展槽上该步中会为设备进行多项资源的分配:IO 空间分配,memory-mapped IO(MMIO) 空间分配,IRQ 分配(对于需要 IRQ 的设备)以及扩展 ROM 的检测及执行。

对内存及 IO 地址空间的分配通过使用 PCI/PCIe 设备的 BAR该步中还会进行 USB 设备的初始化,因为 USB 是一个 PCI 总线兼容的协议该步中还会做其他非 legacy 设备的初始化,比如 SATA、SPI,等。

PCIe GART 逻辑的寄存器初始化也是在这一步完成,因为它们所指向的内存区域都在 PCI/PCIe 内存区域中这一步实际上包含两个子步骤:通过使用 CPU IO 端口的 legacy PCI 配置机制,初始化 PCI 及 PCIe 设备中的关键 PCI 配置空间寄存器。

这一步是必须的,因为只有用于初始化的 CPU IO 端口是硬编码的(译者注:初始化所需的寄存器必须是已知、固定、硬编码的,方能完成初始化设置,所以需要通过 legacy PCI 配置机制老外的从句太多,翻起来比较拧巴,这里点一下,其实上文也提到了);故而其可以被立即使用。

此步还包括初始化主桥中的 PCIEXBAR在通过 legacy PCI 配置机制初始化此寄存器之前,无法访问 PCIe 增强配置空间寄存器初始化 PCIe 增强配置空间寄存器:一旦完成 PCIEXBAR 的初始化,并且 PCIe 设备被识别,平台固件就可以初始化所有 PCIe 配置寄存器,包括 PCIe 增强配置空间寄存器。

一旦主桥中的所有寄存器、8-series PCH 以及所有 PCI/PCIe 设备完成初始化,系统地址映射也就形成了执行此初始化过程的 Haswell 平台固件代码一定很复杂,因为正如你在“Haswell 系统地址映射”一节中所看到的的,系统地址映射本身就是很复杂的。

至此,你应该清楚地了解了现代基于 PCIe 系统的系统地址映射,并了解了系统地址映射的初始化过程(由平台固件执行)19. 深入分析 UEFI GetMemoryMap() 接口本系列的第一篇文章中,你学习了 BIOS E820h 接口。

本文中,我只会重申 UEFI 中与此函数对等的 UEFI GetMemoryMap() 函数此函数是 UEFI boot services 的一部分因此,你需要遍历 UEFI boot services 表来调用此函数。

调用此函数的简化算法为:定位到 EFI 系统表遍历 EFI 系统表的 EFI_BOOTSERVICES_TABLE遍历 EFI_BOOTSERVICES_TABLE 来定位 GetMemoryMap() 函数。

调用 GetMemoryMap() 函数GetMemoryMap() 函数会返回类似 legacy E820h 接口所返回的数据结构该数据结构称为 EFI_MEMORY_DESCRIPTOREFI_MEMORY_DESCRIPTOR 的定义如下:。

//*******************************************************//EFI_MEMORY_DESCRIPTOR//********************

***********************************typedef struct { UINT32 Type; EFI_PHYSICAL_ADDRESS PhysicalStart;

EFI_VIRTUAL_ADDRESS VirtualStart; UINT64 NumberOfPages; UINT64 Attribute;} EFI_MEMORY_DESCRIPTOR;

GetMemoryMap() 函数获取当前内存映射的信息该映射信息是一个内存描述符数组,每个描述符描述了一个连续的内存块此映射信息描述了所有的内存,无论该内存将作何用(译者注:这里的意思是只要是真实存在的内存区域都被会上报,PCI/PCIe 区域、RAM 区域之类的,但是并不会包括 reserve 区域)。

内存映射只描述在系统中确实存在的内存内存描述符不会被用来描述系统内存映射中的“洞”本文不会继续深入此接口读者可以自行参阅 UEFI 标准中关于此接口以及 EFI_MEMORY_DESCRIPTOR 的细节。

GetMemoryMap() 在 UEFI 标准的 "boot services" 一章,"memory allocation services" 一节20. 结语本文深入了 Haswell 系统地址映射及其初始化。

本文为对现代系统的理解提供了深厚的背景知识,对于比本文所述系统更复杂系统的理解应该也不在话下

免责声明:本站所有信息均搜集自互联网,并不代表本站观点,本站不对其真实合法性负责。如有信息侵犯了您的权益,请告知,本站将立刻处理。联系QQ:1640731186