17 中断支持(Interrupt Support)
上一章
上一章为系统电源管理的讨论提供了整体背景,并详细描述了与 PCI 总线电源管理接口规范和高级配置与电源接口(ACPI)规范兼容的 PCIe 电源管理。PCIe 定义了 PCI-PM 规范的扩展,主要侧重于链路电源和事件管理。同时还概述了 OnNow 计划、ACPI 以及 Windows 操作系统的参与情况。
本章
本章描述了 PCIe 功能生成中断的不同方式。传统的 PCI 模型使用引脚来实现此功能,但在串行模型中边带信号并不理想,因此强制支持带内 MSI(消息信号中断)机制。为了软件向后兼容性,仍可使用 PCIe INTx 消息模拟 PCI INTx# 引脚操作。本章将同时介绍传统的 PCI INTx# 方法以及较新的 MSI/MSI-X 机制。
下一章
下一章描述了为 PCIe 定义的三种复位类型:基本复位(包括冷复位和热复位)、热复位以及功能级复位(FLR)。讨论了使用边带复位信号 PERST#生成系统复位的方法,以及基于带内 TS1 的热复位机制。
17.1 中断支持背景(Interrupt Support Background)
17.1.1 概述
PCI 架构支持来自外围设备的中断,以此提升设备性能,并减轻 CPU 轮询设备以确定其是否需要服务的负担。PCIe 基本沿袭了 PCI 的这一支持,未作重大改动,从而实现了软件向后兼容 PCI。本章将介绍系统中断处理的背景知识,但希望深入了解中断细节的读者,建议参考以下资料:
- 关于 PCI 中断背景,请参阅 PCI 规范 3.0 版,或 MindShare 教材《PCI 系统架构》(www.mindshare.com)第 14 章。
- 如需了解更多关于本地 APIC 和 I/O APIC 的内容,请参阅 MindShare 教材《x86 指令集架构》。
17.1.2 中断传递的两种方法
PCI 使用边带中断线,这些中断线被路由至中央中断控制器。这种方法在简单的单 CPU 系统中运行良好,但存在一些缺陷,促使业界转向名为 MSI(消息信号中断)的新方法,并扩展出 MSI-X(扩展 MSI)。
传统 PCI 中断传递(Legacy PCI Interrupt Delivery)—— 这种为 PCI 总线定义的原始机制包含每个设备最多四个信号或 INTx#(INTA#、INTB#、INTC#和 INTD#),如图 17-1(第 795 页)所示。在该模型中,引脚通过线或(wire-OR)方式共享,最终连接至 8259 PIC(可编程中断控制器)的输入端。当某个引脚被置位时,PIC 会向 CPU 发出其中断请求引脚,这一过程详见第 796 页的”传统模型”章节。
PCIe 为了向后兼容而支持这种 PCI 中断功能,但串行传输的设计目标之一是尽量减少引脚数量。因此,INTx# 信号并未作为边带引脚实现。相反,功能设备可以生成带内中断消息包,以指示引脚的断言或取消断言。这些消息充当”虚拟导线”,并以系统中的中断控制器作为目标(通常在根复合体中),如第 796 页的图 17-2 所示。该图还说明了使用 INTx# 引脚的旧 PCI 设备如何在 PCIe 系统中工作;桥接器将引脚的断言转换为向上游发送至根复合体的中断仿真消息(INTx)。预期 PCIe 设备通常不需要使用 INTx 消息,但在撰写本文时,实践中它们经常使用,因为系统软件尚未更新以支持 MSI。
图 17-1:PCI 中断传递
MSI 中断传递(MSI Interrupt Delivery)—— MSI 通过使用内存写入来传递中断通知,从而消除了边带信号的需求。“消息信号中断”这个术语可能会令人困惑,因为其名称中包含”消息”一词,而”消息”在 PCIe 中是一种 TLP 类型,但 MSI 中断实际上是已发布的内存写入,而非消息事务。MSI 内存写入与其他内存写入的区别仅在于它们所针对的地址,这些地址通常由系统保留用于中断传递(例如,基于 x86 的系统传统上将 FEEx_xxxxh 地址范围保留用于中断传递)。
图 17-2 展示了各种类型 PCIe 设备的中断传递方式。所有 PCIe 设备都必须支持 MSI,但软件可能支持也可能不支持 MSI,在这种情况下,将使用 INTx 消息。图 17-2 还展示了 PCIe 到 PCI 桥接器如何将来自所连接 PCI 设备的边带中断转换为 PCIe 支持的 INTx 消息。
图 17-2:PCIe 系统中的中断传递选项
17.2 传统模型(The Legacy Model)
17.2.1 概述
为说明传统中断传递模型,请参考第 797 页的图 17-3,并考虑使用传统中断引脚方法进行中断传递时通常涉及的步骤:
设备通过向控制器断言其引脚来生成中断。在较旧的系统中,该控制器通常是 Intel 8259 PIC,具有 15 个 IRQ 输入和一个 INTR 输出。随后 PIC 会断言 INTR 以通知 CPU 有一个或多个中断待处理。
一旦 CPU 检测到 INTR 信号被置位并准备处理该中断,它必须确定哪个中断实际需要服务。为此,CPU 会在处理器总线上发出一个称为”中断确认”的特殊命令。
该命令由系统路由至 PIC(可编程中断控制器),PIC 会返回一个 8 位值——称为”中断向量”,用于报告当前待处理中断中优先级最高的那个。系统软件会事先为每个 IRQ 输入分配唯一的向量值。
随后,中断处理程序将该向量作为偏移量,访问中断向量表(由软件建立的存储所有中断服务程序起始地址的区域),并从该位置获取 ISR 的起始地址。
该地址将指向为处理该中断而设置的 ISR 的第一条指令。该处理程序将被执行,处理中断并通知其设备取消断言 INTx# 线,然后将控制权返回给先前被中断的任务。
图 17-3:传统中断示例
17.2.2 支持多处理器的变更
该模型在单 CPU 系统中运行良好,但在多 CPU 系统中存在局限性,使其无法达到最优性能。问题在于 INTR 引脚只能连接到一个 CPU。如果存在多个处理器,则只有一个处理器能看到中断并必须处理所有中断,而其他 CPU 则无法看到任何中断。为了获得最佳性能,此类系统实际上需要将系统任务均匀分布到所有处理器上,这被称为 SMP(对称多处理),但引脚模型无法支持这一点。
为了实现更好的 SMP,需要一种新模型,为此 PIC 被改进为 IO APIC(高级可编程中断控制器)。IO APIC 设计有一条独立的小型总线,称为 APIC 总线,通过该总线可以传递中断消息,如图 17-4(第 799 页)所示。在此模型中,消息包含中断向量号,因此 CPU 无需向 IO 层发送中断确认来获取该向量。APIC 总线连接到处理器内部新增的逻辑模块——本地 APIC。该总线由所有代理共享,任何代理都可以在其上发起消息,但就我们的目的而言,其关键用途在于外设的中断传递。这些中断现在可以由软件静态分配给不同 CPU 处理、由多个 CPU 共同处理,甚至由 IO APIC 动态分配。
图 17-4:中断传递的 APIC 模型
该模型被称为 APIC 模型,在数年内足以满足需求,但仍依赖外围设备的边带引脚才能工作。该模型的另一个限制是 IO APIC 的中断请求线(IRQ)数量。由于 IRQ 数量有限,外围设备不得不共享 IRQ,这意味着每当 IRQ 被触发时都会增加延迟,因为可能有多个设备同时触发该中断,软件必须逐一评估所有设备。这种将多个中断服务程序(ISR)串联的技术常被称为中断链。最终,由于这一问题及其他若干次要问题,又一项改进应运而生。
为何不让外围设备直接将中断消息发送至本地 APIC?所需通信路径已经存在,即 PCI 总线与处理器总线。因此,APIC 总线被取消,所有中断均以内存写入形式传递至本地 APIC,这种机制称为 MSI(Message Signaled Interrupt,消息信号中断)。这些 MSI 指向一个特殊地址,系统会将该地址识别为面向本地 APIC 的中断消息地址。对于基于 x86 的系统,传统上使用 FEEx_xxxxh 地址范围。甚至 IO APIC 也被编程为通过普通数据总线使用内存写入(MSI)发送其中断通知。现在,它只需通过数据总线发送一个针对目标处理器本地 APIC 内存地址的 MSI 内存写入,即可实现向处理器通知中断的效果。
该模型被称为 xAPIC 模型,由于它不依赖于进入具有有限输入数量的中断控制器的边带信号,因此几乎消除了共享中断的需求。有关此模型的更多信息,请参见第 827 页的“MSI 解决方案”。
PCI 多年前已将 MSI 支持作为可选功能加入,而 PCIe 则将其变为强制性要求。能够自主生成 MSI 事务的外设开辟了处理中断的新途径,例如允许每个功能生成多个独立中断,而非仅限一个。
17.2.3 传统 PCI 中断传递
本节将更详细地介绍传统 PCI 中断传递机制。熟悉 PCI 的读者可直接跳转至第 805 页的”虚拟 INTx 信令”章节,了解 PCIe 如何模拟这种传统模式;或参阅第 812 页的”MSI 模型”章节,深入了解该方法的细节。
使用中断的 PCI 设备有两种选择方案。它们可采用以下任一方式:
- INTx# 低电平有效信号,可共享,最初在规范中定义。
- 消息信号中断(MSI)作为可选功能在规范 2.2 版本中引入。MSI 在 PCIe 系统中使用时无需修改。
17.2.3.1 设备 INTx# 引脚
一个 PCI 设备最多可实现 4 个 INTx# 信号(INTA#、INTB#、INTC#和 INTD#)。之所以提供多个引脚,是因为 PCI 设备最多可支持 8 个功能,每个功能允许驱动一个(且仅一个)中断引脚。在 PCI 开发时期,典型系统采用包含 15 输入 8259 PIC 的芯片组,因此系统可用的 IRQ 数量(映射至中断向量)即为 15 个。然而,其中许多 IRQ 已被系统用途占用,例如系统定时器、键盘中断、鼠标中断等。此外,部分引脚需保留给仍可插入这些旧系统的 ISA 卡使用。因此,PCI 规范制定者认为其新总线仅能可靠使用 4 个 IRQ,故规范仅支持 4 个中断引脚。但众所周知,PCI 总线上通常存在超过 4 个 PCI 设备,甚至单个设备内部可能包含超过 4 个功能,每个功能都需要独立的中断资源。
这些原因促使 PCI 中断被设计为电平触发且可共享。这些信号可以通过简单的线或逻辑合并为少量输出信号,每个输出代表一个中断请求。由于信号是共享的,当中断被检测到时,中断处理程序软件需要遍历共享同一引脚的功能列表,逐一检测哪些功能需要服务。
17.2.3.2 确定 INTx# 引脚支持
PCI 功能通过其配置头中的信息表明对 INTx# 信号的支持。如图 17-5 所示的只读中断引脚寄存器,用于指示该功能是否支持 INTx# 信号,若支持,则标明在请求中断时将断言哪个中断引脚。
图 17-5:PCI 配置头中的中断寄存器
17.2.3.3 中断路由
图 17-5(第 801 页)所示的中断线寄存器提供了驱动程序所需的下一项信息:该引脚所连接的 PIC 输入引脚。系统软件为 PIC 的每个输入引脚(IRQ)分配了唯一的向量号。优先级最高的已断言中断所对应的向量会被报告给处理器,处理器随后使用该向量索引中断向量表中的对应条目。该条目指向中断设备的服务例程,处理器将执行该例程。
平台设计者负责分配设备上 INTx# 引脚的布线路径。这些引脚可以通过多种方式进行连接,但最终每个 INTx# 引脚都会连接到中断控制器的某个输入端。第 803 页的图 17-6 展示了一个示例,其中多个 PCI 设备的中断通过可编程路由器连接到中断控制器。所有连接到可编程路由器同一输入端的信号都将被导向中断控制器的特定输入。平台软件(通常是固件)会为那些中断被路由到同一中断控制器输入端的功能分配相同的中断线编号。在该示例中,IRQ15 连接了来自不同设备的三个 PCI INTx#输入。因此,使用这些 INTx# 线路的功能将共享 IRQ15,这意味着当被查询时,它们都会导致控制器发送相同的向量。该向量会将不同功能的三个 ISR 串联在一起。
17.2.3.4 将 INTx# 线路关联至 IRQ 编号
根据系统需求,路由器被编程将其四个输入连接到四个可用的 PIC 输入。完成此操作后,与每个功能关联的 INTx# 引脚的布线便已确定,软件会将中断线号写入每个功能。该值最终由功能的设备驱动程序读取,以便其了解被分配的中断表条目。该位置将写入其 ISR 的起始地址,这一过程称为“挂接中断”。当该功能后续生成中断时,CPU 将接收到与中断线寄存器中指定的 IRQ 相对应的向量号。CPU 使用该向量索引中断向量表,以获取与该功能设备驱动程序关联的中断服务例程的入口点。
图 17-6:INTx 信号路由因平台而异
17.2.3.5 INTx# 信号
INTx# 信号线是低电平有效信号,采用开漏极实现,系统为每条信号线提供上拉电阻。连接至同一 PCI 中断请求信号线的多个设备可同时驱动该信号线而不会造成损坏。
当功能模块发出中断信号时,它还会设置配置头状态寄存器中的中断状态位。系统软件可通过读取该位来判断当前是否有待处理的中断。(参见第 805 页图 17-8。)
17.2.3.5.1 中断禁用。
2.3 版 PCI 规范在配置头命令寄存器中新增了中断禁用位(第 10 位)。参见第 804 页图 17-7。该位在复位时被清除以允许 INTx# 信号生成,但软件可将其置位 以阻止中断生成。请注意,中断禁用位对消息信号中断(MSI)无效。MSI 通过 MSI 能力结构中的命令寄存器启用。启用 MSI 会自动产生禁用中断引脚或仿真的效果。
17.2.3.5.2 中断状态。
PCI 2.3 规范在配置状态寄存器(见第 805 页图 17-8)中新增了一个只读的中断状态位。当有中断挂起时,功能模块必须置位该状态位。此外,如果头部命令寄存器中的中断禁用位被清零(即中断使能),则当该状态位置位时,功能模块的 INTx# 信号将被置为有效。该状态位不受中断禁用位状态的影响。
图 17-7:配置命令寄存器——中断禁用字段
图 17-8:配置状态寄存器——中断状态字段
17.2.4 虚拟 INTx 信号
17.2.4.1 概述
如果在 PCIe 拓扑中无法使用 MSI,则将采用 INTx 信号模型。以下是需要使用 INTx 消息的两种设备示例:
PCIe 到 PCI/PCI-X 桥接器—— 大多数 PCI 设备将使用 INTx# 引脚,因为 MSI 支持对其而言是可选的。由于 PCIe 不支持边带中断信号,因此改用带内消息。中断控制器理解该消息,并向 CPU 发送包含预编程向量号的中断请求。
启动设备(Boot Devices)—— PC 系统在启动过程中通常使用传统中断模型,因为 MSI 通常需要操作系统级初始化。通常,启动至少需要三个子系统:面向操作员的输出设备(如显示器)、操作员的输入设备(通常是键盘),以及用于加载操作系统的设备(通常是硬盘)。参与系统初始化的 PCIe 设备被称为”启动设备”。在操作系统和设备驱动程序加载完成之前,启动设备将使用传统中断支持,之后则更倾向于使用 MSI。
17.2.4.2 虚拟 INTx 线路传递
第 806 页的图 17-9 展示了一个包含 PCIe 端点设备和 PCI Express 转 PCI 桥接器的系统。假设软件尚未在端点设备上启用 MSI,它将通过 INTx 消息传递中断请求。在此示例中,桥接器通过 INTx 消息传播来自所连接 PCI 设备的引脚中断。如图所示,桥接器发送 INTB 消息来通知其 INTB# 输入引脚在 PCI 总线上的断言和取消断言状态。PCIe 端点设备则通过仿真消息发送 INTA 信号。请注意,INTx# 信号传输涉及两条消息:
- Assert_INTx 消息表示虚拟 INTx# 信号从高电平到低电平(从非活动状态到活动状态)的转换。
- Deassert_INTx 消息表示从低电平到高电平的转换。
当某个功能(Function)发送 Assert_INTx 消息时,它也会在配置状态寄存器中设置其中断状态位,就像它断言物理 INTx# 引脚一样(参见第 805 页的图 17-8)。
图 17-9:用于虚拟化 INTA#-INTD# 信号转换的 INTx 消息示例
17.2.4.3 INTx 消息格式
第 807 页的图 17-10 展示了 INTx 消息头的格式。中断控制器是这些消息的最终目的地,但所采用的路由方式并非”路由至根复合体”,而是如图 17-10 所示的”本地路由:在接收端终止(Local - Terminate at Receiver)“。这有两个原因。首先,因为上游路径上的每个桥接器(包括交换端口和根端口)都可能将虚拟中断线映射到桥接器另一侧的不同虚拟中断线(例如,交换端口接收 Assert_INTA,但在向上游传播时将其映射为 Assert_INTB)。关于此 INTx 映射的更多信息,请参见第 808 页的”INTx 映射”。
这些消息采用本地路由类型的第二个原因,在于我们正在模拟基于引脚的电信号。如果一个端口接收到断言中断消息,该消息在其主侧映射为 INTA,而由于之前的中断,该端口已经向上游发送过 Assert_INTA 消息,那么就没有必要再发送另一个。INTA 已被视为已断言状态。关于此 INTx 消息合并的更多信息,请参见第 810 页的“INTx 合并”。
图 17-10:INTx 消息格式与类型
17.2.5 INTx 消息的映射与合并
17.2.5.1 INTx 映射
交换机必须遵循 PCI 规范定义的 INTx 映射,如表 17-1(第 809 页)所示。该映射定义了当中断通过 PCI 到 PCI 桥接器路由时存在的虚拟连接。映射基于 INTx 消息类型以及消息中Requester ID 字段的设备编号。
请参考第 810 页图 17-11 中的示例。两个下游交换端口接收到的断言中断消息均为 INTA 消息。每个入口端口的虚拟 PCI-to-PCI 桥接器会将两个 INTA 消息均映射为 INTA,即保持不变。这是因为两个源端点设备的设备号均为零(该设备号作为Requester ID 的一部分包含在中断消息中)。表 17-1 显示,来自设备 0 的中断消息在桥接器另一侧映射为相同的 INTx 消息(即在交换器内部,两个 INTA 消息均映射为 INTA)。因此,每个下游端口将中断消息向上游传播时不会改变其虚拟线路。然而,传播后的中断消息不再包含原始请求者的 ReqID,而是携带传播该中断消息的端口的 ReqID。
接下来,上游交换端口会接收到传播过来的中断消息。来自端口 2:1:0 的 INTA 中断在向上游传播时,会被映射为 INTB 消息,因为该中断消息表明它来自设备 1(ReqID 2:1:0)。而由端口 2:2:0 传播的另一个中断,在从上游交换端口发送到根端口时,会被映射为 INTC 消息。请参考表 17-1 确认这些映射关系。
这种中断映射的原因与 PCI 相同:尽可能避免多个功能共享同一个 INTx# 引脚。如前所述,单功能设备在使用传统中断时必须使用 INTA。因此,如果根端口下游的所有功能都使用 INTA,且桥接器之间没有映射,它们都将被路由到同一个 IRQ。这意味着每当某个功能断言 INTA 时,所有功能都必须被检查。这将导致列表末尾的功能出现显著的中断服务延迟。这种中断映射方法是一种粗略的尝试,旨在将中断(尤其是 INTA)分配到所有四个 INTx 虚拟线上,因为每个 INTx 虚拟线都可以映射到中断控制器上的独立 IRQ。
表 17-1:跨虚拟 PCI-to-PCI 桥接器的 INTx 消息映射
| 传递 INTx 的设备号 | 输入端 INTx 消息类型 | 输出端 INTx 消息类型 |
|---|---|---|
| 0、4、8、12 等 | INTA | INTA |
| 0、4、8、12 等 | INTB | INTB |
| 0、4、8、12 等 | INTC | INTC |
| 0、4、8、12 等 | INTD | INTD |
| 1、5、9、13 等 | INTA | INTB |
| 1、5、9、13 等 | INTB | INTC |
| 1、5、9、13 等 | INTC | INTD |
| 1、5、9、13 等 | INTD | INTA |
| 2、6、10、14 等 | INTA | INTC |
| 2、6、10、14 等 | INTB | INTD |
| 2、6、10、14 等 | INTC | INTA |
| 2、6、10、14 等 | INTD | INTB |
| 3、7、11、15 等 | INTA | INTD |
| 3、7、11、15 等 | INTB | INTA |
| 3、7、11、15 等 | INTC | INTB |
| 3、7、11、15 等 | INTD | INTC |
图 17-11:INTx 映射示例
17.2.5.2 INTx 合并
PCIe 交换机必须确保 INTx 消息以正确的方式向上游传递。具体而言,必须妥善处理传统 PCI 实现的中断路由,使软件能够确定哪些中断被路由到哪个中断控制器输入。INTx# 信号线可采用线或逻辑连接,并路由至中断控制器的同一 IRQ 输入。当多个设备在同一信号线上发出中断信号时,中断控制器仅能识别首次断言。同样,当其中一个设备取消其 INTx# 信号线断言时,该信号线将保持断言状态,直至最后一个设备关闭。这些原则同样适用于 PCIe INTx 消息。
然而,在某些情况下,两个重叠的 INTx 消息可能被出口端口的虚拟 PCI 桥映射到同一 INTx 消息,从而需要对这些消息进行合并。请参考图 17-12(第 811 页)所示的示例。
当上游交换机端口映射中断消息以通过上游链路传输时,两个中断都将根据下游交换机端口的设备编号映射为 INTB。请注意,由于这两个重叠消息相同,因此必须进行合并。
合并机制确保中断控制器永远不会收到针对共享中断的两个连续的 Assert_INTx 或 Deassert_INTx 消息。这相当于 INTx 信号通过线或逻辑连接。
图 17-12:交换机使用 INTx 消息的桥接映射
17.2.5.3 INTx 传递规则
与 INTx 消息传递相关的规则具有一些独特特性:
- Assert_INTx 和 Deassert_INTx 仅向上游方向发送。
- 进行中断合并的交换机仅在中断状态发生变化时才会向上游发送 INTx 消息。
- 链路两侧的设备必须跟踪 INTA-INTD 断言信号的当前状态。
- 交换机跟踪其每个下游端口的四条虚拟线路的状态,并可能在其上游端口上呈现合并后的虚拟线路集。
- 根复合体必须为每个下游端口跟踪四条虚拟线(A-D)的状态。
- 通过命令寄存器中的中断禁用位可禁用 INTx 信号。
- 若任何 INTx 虚拟线处于激活状态且设备中断随后被禁用,则必须发送相应的 Deassert_INTx 消息。
- 当下游交换端口进入 DL_Down 状态时,必须取消所有激活的 INTx 虚拟线,并相应更新上游端口(若该 INTx 处于激活状态,则需发送 Deassert_INTx 消息)。
17.3 MSI 模型(The MSI Model)
PCIe 功能通过 MSI 能力寄存器指示对 MSI 的支持。每个功能必须实现 MSI 能力结构或 MSI-X(扩展 MSI,参见第 821 页的“MSI-X 模型”)能力结构,或两者都实现。MSI 能力寄存器由配置软件设置,包括:
- 目标内存地址
- 要写入该地址的数据值
- 可以编码到数据中的唯一消息数量
关于内存写事务头的回顾,请参见第 188 页的“存储器请求头字段”。请注意,MSI 始终具有 1 DW 的数据载荷。
17.3.1 MSI 能力结构(The MSI Capability Structure)
MSI 能力结构位于 PCI 兼容配置空间区域(前 256 字节)。根据功能是否支持 64 位寻址,以及是否支持每向量屏蔽(Per-vector Masking),MSI 能力结构存在四种变体。原生 PCIe 设备必须支持 64 位寻址。四种变体如第 813 页图 17-13 所示。
图 17-13:MSI 能力结构变体
17.3.1.1 能力 ID(Capability ID)
能力 ID 值为 05h,表示 MSI 能力结构,且该字段为只读。
17.3.1.2 下一个能力指针(Next Capability Pointer)
该寄存器的第二个字节是只读值,给出从配置空间起始位置到链表中下一个能力结构的双字对齐偏移量;若该值为 00h,则表示能力链表结束。
17.3.1.3 消息控制寄存器(Message Control Register)
第 814 页的图 17-14 和表 17-2 展示了消息控制寄存器的布局与用法。
图 17-14:消息控制寄存器
表 17-2:消息控制寄存器的格式与用法
| 位 | 字段名称 | 描述 |
|---|---|---|
| 0 | MSI 使能 | 读/写。复位后为 0,表示设备的 MSI 功能被禁用。0 = 功能禁止使用 MSI,必须使用 MSI-X 或 INTx 消息;1 = 功能使用 MSI 请求服务,不会使用 MSI-X 或 INTx 消息。 |
| 3:1 | 多消息能力 | 只读。系统软件读取该字段,以确定功能希望使用多少条消息(中断向量)。请求数量必须是 2 的幂,因此若功能希望使用 3 条消息,则必须请求 4 条。编码含义:000b = 1,001b = 2,010b = 4,011b = 8,100b = 16,101b = 32,110b/111b = 保留。 |
| 6:4 | 多消息使能 | 读/写。系统软件读取“多消息能力”字段后,在此字段中写入实际分配给该功能的消息数量。分配数量可以等于或少于功能请求的数量。复位后为 000b。编码含义同“多消息能力”:000b = 1,001b = 2,010b = 4,011b = 8,100b = 16,101b = 32,110b/111b = 保留。 |
| 7 | 64 位地址能力 | 只读。0 = 功能未实现消息地址寄存器的高 32 位,仅能使用 32 位地址;1 = 功能实现了消息地址寄存器的高 32 位,能够生成 64 位内存地址。 |
| 8 | 支持每向量屏蔽 | 只读。0 = 功能未实现屏蔽位寄存器或待处理位寄存器,软件无法通过该能力结构屏蔽单个中断;1 = 功能实现了屏蔽位寄存器和待处理位寄存器,软件可以通过该能力结构屏蔽单个中断。 |
| 15:9 | 保留 | 只读,读取始终返回 0。 |
17.3.1.4 消息地址寄存器(Message Address Register)
32 位消息地址寄存器的低两位为 0 且不可更改,这会强制软件分配的地址必须按双字对齐。通常,该地址指向系统 CPU 中的本地 APIC。在基于 x86 的系统中(兼容 Intel 架构),该地址传统上为 FEEx_xxxxh 格式,其中低 20 位指示目标本地 APIC,以及与中断本身相关的其他信息。需要特别注意:地址如何解析由平台决定,PCI 或 PCIe 规范并不规定这一点。
包含消息地址 [63:32] 的寄存器对于原生 PCI Express 设备是必需的,但对于传统端点是可选的。若消息控制寄存器的第 7 位被置位,则该寄存器存在。此时,它是一个读/写寄存器,与消息地址 [31:0] 寄存器配合使用,使该功能能够使用 64 位内存地址进行中断传递。
17.3.1.5 消息数据寄存器(Message Data Register)
系统软件会将一个基本消息数据模式写入这个 16 位读/写寄存器。当该功能生成中断请求时,它会向消息地址寄存器指定的内存地址写入一个 32 位数据值。该数据值的高 16 位始终为 0,低 16 位来自消息数据寄存器。
如果该功能被分配了多条消息,它会修改消息数据寄存器值的低位,以形成与其希望报告的事件对应的适当值。可修改的低位数量取决于配置软件分配给该功能的消息数量。示例见第 820 页“生成 MSI 中断请求的基本原理”。
17.3.1.6 屏蔽位寄存器和待处理位寄存器(Mask Bits Register and Pending Bits Register)
如果该功能支持每向量屏蔽(由消息控制寄存器第 8 位指示),则会实现这些寄存器。使用 MSI 时,一个功能最多可请求并被分配 32 条中断消息(中断向量)。因此,这两个寄存器均为 32 位宽,每个潜在的中断消息都有独立的屏蔽位和待处理位。如果屏蔽位寄存器的第 0 位被置位,则中断消息 0 被屏蔽(它是该功能的基础向量);如果第 1 位被置位,则中断消息 1 被屏蔽(基础向量 + 1),依此类推。
当某条中断消息被屏蔽时,该向量对应的 MSI 无法发送,而是设置相应的待处理位。这样软件就可以屏蔽某个功能中的单个中断,并定期轮询该功能,以检查是否存在被屏蔽但处于待处理状态的中断。
如果软件清除了某个屏蔽位,且相应的待处理位已经置位,则该功能必须立即发送 MSI 请求。当中断消息发送完成后,该功能会清除待处理位。
17.3.2 MSI 配置基础
以下列表说明了软件为 PCI Express 设备配置 MSI 中断所执行的步骤。请参考第 819 页的图 17-15。
- 在启动时,枚举软件会扫描系统中的所有 PCI 兼容功能(关于枚举过程的讨论,请参见第 109 页的“单根枚举示例”)。
- 一旦发现某个功能,软件会读取能力列表指针,以找到链表中第一个能力结构的位置。
- 如果在链表中找到 MSI 能力结构(能力 ID 为 05h),软件会读取设备消息控制寄存器中的“多消息能力”字段,以确定设备支持多少个事件特定消息,以及它是否支持 64 位消息地址或仅支持 32 位。随后,软件会分配等于或少于该数量的消息,并将该值写入“多消息使能”字段。至少会为设备分配一个消息。
- 软件将基本消息数据模式写入设备的消息数据寄存器,并将一个双字对齐的内存地址写入设备的消息地址寄存器,作为 MSI 写入的目标地址。
- 最后,软件在设备的消息控制寄存器中设置 MSI 使能位,使其能够生成 MSI 写入操作,并禁用其他中断传递选项。
图 17-15:设备 MSI 配置流程
17.3.3 生成 MSI 中断请求的基本原理
第 821 页的图 17-16 展示了 MSI 内存写事务头部和数据字段的内容。关键要点包括:
- 对于原生功能,格式字段必须为 011b,表示带数据的 4 DW 报头(64 位地址);对于传统端点,该字段也可能为 010b,表示使用 32 位地址。
- No Snoop 和 Relaxed Ordering 属性位必须为 0。
- Length 字段必须为 01h,表示数据载荷长度为 1 DW。
- First DW Byte Enable 字段必须为 1111b,表示该 DW 的四个字节全部有效,尽管对于 MSI 而言高两个字节始终为 0。
- Last DW Byte Enable 字段必须为 0000b,表示这是一次单 DW 传输。
- 头部中的地址字段直接来自 MSI 能力寄存器中的消息地址字段。
- 数据载荷的低 16 位来自 MSI 能力寄存器中的消息数据字段。
图 17-16:原生设备 MSI 投递的内存写事务格式
17.3.4 多消息(Multiple Messages)
如果系统软件为功能分配了多条消息,则功能可通过修改已分配消息数据值的低位,为每个设备特定事件类型发送不同的消息。
例如,假设以下条件成立:
- 系统为设备分配了 4 条消息。
- 设备的消息数据寄存器被写入数据值 49A0h。
- 内存地址 FEEF_F00Ch 已被写入设备的消息地址寄存器。
当四个事件中的任意一个发生时,设备通过向内存地址 FEEF_F00Ch 执行双字写入来生成请求,数据值分别为 0000_49A0h、0000_49A1h、0000_49A2h 或 0000_49A3h。换言之,数据值的低两位会被修改,以指示具体发生了哪个事件。如果该功能被分配了 8 条消息,则低三位可以被修改。此外,设备始终使用 0000h 作为消息数据值的高两个字节。
17.4 MSI-X 模型(The MSI-X Model)
17.4.1 通用
PCI 规范 3.0 版本增加了对 MSI-X 的支持,该功能拥有独立的能力结构。MSI-X 的引入旨在解决 MSI 的三个不足:
- 每个功能仅支持 32 个向量,对某些应用而言数量不足。
- 单一目标地址导致难以在多 CPU 间静态分配中断。若能给每个向量分配唯一地址,则可实现最大灵活性。
- 在 x86 等平台中,中断的向量编号决定了其相对于其他中断的优先级。采用 MSI 时,单个功能可分配多个中断,但所有中断向量必须连续排列,这意味着优先级相近。若该功能的某些中断需要高优先级而其他中断需要低优先级,这种方案并不理想。更优的做法是由软件为分配给该功能的每个中断指定唯一向量(消息数据值),且无需保持连续性。
牢记这些目标,就不难理解为实现每个向量分配目标地址和消息数据值而实施的寄存器变更,从而提供更多向量。
17.4.2 MSI-X 能力结构(MSI-X Capability Structure)
如第 822 页图 17-17 所示,MSI-X 的消息控制寄存器与 MSI 存在显著差异。有趣的是,尽管 MSI-X 每个功能最多可支持 2048 个向量,而 MSI 仅支持 32 个,但 MSI-X 的配置寄存器数量实际上比 MSI 略少。这是因为向量相关信息不存放在该能力结构中,而是存放在由 Table BIR(Base Indicator Register,基地址指示寄存器)指向的内存映射位置(MMIO)中,如图 17-18(第 824 页)所示。
图 17-17:MSI-X 能力结构
表 17-3:MSI-X 消息控制寄存器的格式与用法
| 位 | 字段名称 | 描述 |
|---|---|---|
| 10:0 | Table Size(表大小) | 只读。该字段指示此功能支持的中断消息(向量)数量。该值采用 N-1 编码,因此值为 0 表示支持 1 个向量,值为 7 表示支持 8 个向量。每个向量在 MSI-X 表中拥有独立条目,并在待处理位数组中拥有独立位。 |
| 13:11 | Reserved(保留) | 只读,读取始终返回 0。 |
| 14 | Function Mask(功能屏蔽) | 读/写。系统软件可使用该位一次性屏蔽该功能的全部中断。如果该位清零,仍可通过设置各 MSI-X 表项中的向量屏蔽位来单独屏蔽某个中断。 |
| 15 | MSI‐X Enable(MSI‐X 使能) | 读/写。复位后为 0,表示设备的 MSI-X 功能被禁用。0 = 功能禁止使用 MSI-X,必须使用 MSI 或 INTx 消息;1 = 功能启用 MSI-X 请求服务,不会使用 MSI 或 INTx 消息。 |
图 17-18:MSI-X 表位置
17.4.3 MSI-X 表(MSI-X Table)
MSI-X 表本身是一个由向量地址和向量数据组成的数组,如图 17-19(第 825 页)所示。每个条目代表一个向量,包含四个双字(DW)。DW0 和 DW1 为该向量提供唯一的 64 位地址,DW2 提供唯一的 32 位数据模式。DW3 目前仅包含一个有效位:该向量的屏蔽位,用于按需独立屏蔽每个向量。
图 17-19:MSI-X 表项
MSI-X 表项可概括为:
| 双字 | 字段 | 说明 |
|---|---|---|
| DW0 | 消息地址低 32 位 | 该向量的目标地址低位。 |
| DW1 | 消息地址高 32 位 | 该向量的目标地址高位。 |
| DW2 | 消息数据 | 该向量写入目标地址的数据值。 |
| DW3 | 向量控制 | 位 0 为向量屏蔽位(Mask Bit),其余位保留。 |
17.4.4 待处理位数组(Pending Bit Array)
类似地,待处理位数组(Pending Bit Array,PBA)也位于内存地址空间中。它可以使用与 MSI-X 表相同的 BIR 值(即相同 BAR)但不同偏移量,也可以使用完全不同的 BAR。如图 17-20 所示,该数组为每个可能使用的向量提供一个待处理位。如果触发该中断的事件发生,但其屏蔽位已置位,则不会发送 MSI-X 事务,而是设置相应的待处理位。之后,如果该向量被取消屏蔽且待处理位仍处于置位状态,则此时将生成中断。
图 17-20:待处理位数组
待处理位数组可概括为:
| QW | 包含的待处理位 |
|---|---|
| QW 0 | 待处理位 0-63 |
| QW 1 | 待处理位 64-127 |
| QW 2 | 待处理位 128-191 |
| … | … |
| QW (N-1)/64 | 待处理位 N-1 所在的最后一个 QW |
17.5 进入中断处理程序时的内存同步(Memory Synchronization When Interrupt Handler Entered)
17.5.1 问题所在
任何中断方案在数据传输过程中都可能存在潜在问题。例如,若设备先前已发送数据并希望通过中断报告该事件,数据传输的意外延迟可能导致中断过早到达。这种情况可能发生在图 17-21(第 827 页)所示的桥接数据缓冲区中,从而引发竞态条件。其步骤与我们之前的讨论(参见第 796 页”传统模型”)类似:
- 该功能向内存写入一个数据块。该写入操作在本地总线上作为已发布事务完成,意味着发送方已完成所有必要操作,该事务被视为已完成。
- 中断被触发以通知软件某些请求的数据现已存在于内存中。然而,由于某种原因,该数据在桥接器中被延迟了。
- 中断向量按正常流程被获取。
- 中断服务程序的起始地址被获取,控制权随之移交至该地址。
- ISR 从目标内存缓冲区读取数据,但数据载荷尚未送达,因此获取到的是过期数据,可能导致错误。
图 17-21:内存同步问题
17.5.2 一种解决方案
缓解这一问题的一种方法是利用 PCI 事务排序规则。如果中断服务程序(ISR)在尝试获取数据之前,先向发起中断的设备发送一个读取请求,那么生成的读取完成数据包将沿着与该设备向内存写入数据时相同的路径返回 CPU。事务排序规则保证,桥接器中的读取结果不能超越同方向上的已发布写入操作,因此最终结果是:在读取结果被允许到达 CPU 之前,数据将先被写入内存。因此,如果 ISR 在继续执行前等待读取完成数据包到达,就可以确保所有数据已交付至内存,从而避免竞态条件。由于这种读取本质上被用作数据刷新机制,因此无需返回任何实际数据。在这种情况下,读取长度可以为零,返回的数据将被丢弃。正因如此,这种读取有时被称为“空读”。
17.5.3 MSI 解决方案
MSI 可以简化这一过程,尽管其正常工作需要满足一些要求(参见第 829 页图 17-22)。如果系统允许设备生成自己的 MSI 写入,而非通过 IO APIC 等中介设备,则可能发生以下示例:
- 设备将有效载荷数据写入内存,这些数据被桥接器中的写缓冲区吸收。
- 设备认为数据已成功送达,并通过发送中断信号通知 CPU。此时,MSI 中断被触发,且与数据采用相同路径传输。由于数据和 MSI 在桥接器看来均属于内存写入操作,常规事务排序规则将确保两者保持正确的执行顺序。
- 有效载荷数据被传送至内存,从而为 MSI 写入释放了通过桥接器的路径。
- MSI 写入被传送至 CPU 本地 APIC,此时软件便知晓有效载荷数据已就绪。
17.5.4 流量类别必须匹配
然而,这里必须强调一个关键点:要使此机制生效,数据和 MSI 必须使用相同的流量类别。回顾一下,被分配不同 TC 值的报文可能最终被映射到不同的虚拟通道中,而不同 VC 中的报文之间不存在排序关系。如果数据被映射到 VC0 而 MSI 被映射到 VC1,那么系统将无法感知它们之间的任何排序关系,也无法自动强制执行内存一致性。
如果无法为两个数据包分配相同的 TC,系统将需要改用“空读”方法,且读取请求的 TC 必须与数据写入数据包的 TC 匹配。显然,即使两者使用相同的 TC,也必须避免使用宽松排序位。我们依赖事务排序规则来实现内存同步,因此这些规则不能被放宽。
图 17-22:MSI 传递
17.6 中断延迟(Interrupt Latency)
从发出中断信号到软件响应设备服务的时间被称为中断延迟。尽管 MSI 具有诸多优势,但与其他中断传递机制一样,它也无法保证中断延迟的确定性。
17.7 MSI 可能导致错误(MSI May Result In Errors)
由于 MSI 以内存写入事务的形式传递,因此与 MSI 传递相关的错误将按照与其他内存写入错误条件相同的方式处理。以 ECRC 错误的处理为例,具体可参见第 657 页的”ECRC 生成与校验”部分。当然,令人担忧的是,如果错误导致 MSI 数据包无法被识别,处理器将无法接收到任何中断。这种情况的处理方式不在 PCIe 规范的涵盖范围内。
17.8 MSI 相关规则与建议(Some MSI Rules and Recommendations)
- 规范旨在由系统软件将互斥消息分配给功能,且每条消息在传递至处理器时将被转换为独占中断。
- 禁止每个功能拥有多个 MSI 能力寄存器组。
- 读取消息地址寄存器会产生未定义结果。
- 保留寄存器和位为只读,读取时始终返回零。
- 系统软件可以修改消息控制寄存器的位,但设备本身禁止进行此类操作。换言之,不允许通过”后门”机制修改这些位。
- 至少会为每个设备分配一条消息(假设系统软件支持并计划在系统中使用 MSI)。
- 系统软件不得写入包含消息数据寄存器的双字的高半部分。
- 如果设备多次写入相同的消息,则仅保证其中一条消息得到处理。若所有消息都必须得到处理,则设备在前一条消息处理完成之前不得再次生成相同的消息。
- 如果一个设备被分配了多条消息,并且它写入了一系列不同的消息,则可以保证这些消息全部得到处理。
17.9 基础系统外设的特殊考虑(Special Consideration for Base System Peripherals)
中断也可能来自嵌入式传统硬件,例如 I/O 控制器集线器或超级 I/O 设备。此类系统中常见的一些传统设备包括:
- 串行端口
- 并行端口
- 键盘和鼠标控制器
- 系统定时器
- IDE 控制器
这些设备通常需要一条特定的 IRQ 线路连接到 PIC 或 IO APIC,以便传统软件能够正确与其交互。
使用 INTx 消息并不能保证设备能获得所需的 IRQ 分配。以下示例展示了一个能够支持正确传统中断分配的系统。
17.9.1 示例:传统系统
第 831 页的图 17-23 展示了一个较旧的 PCI Express 系统,其中包含一个通过专有 Hub 链路连接到根复合体的 I/O 控制器集线器(ICH)。ICH 内嵌的 IO APIC 在接收到中断请求输入时,可以生成一个 MSI。在此类实现中,软件可以为每个输入分配传统向量号,以确保调用正确的传统软件。
这种方法的优势在于可以利用现有硬件来支持 PCIe 平台的遗留需求。该系统还要求在引导序列期间配置 MSI 子系统以供使用。所示示例消除了对 INTx 消息的需求,除非 PCIe 扩展设备集成了 PCI Express 转 PCI 桥接器。
图 17-23:采用基于 PCI 的 I/O 控制器集线器的 PCI Express 系统