PCIe的中断机制

Peter 于 2020-12-05 发布

出于向下兼容的需要,PCIe完全继承了PCI的所有的中断特性(包括INTx,MSI/MSIx)。但是与PCI不同的是,PCIe使用串行总线尽量减少pin的使用,所以对于INTX类型的中断,它没有使用sideband pin的方式而是使用Message传递中断。另外对于PCI, MSI/MSIX是可选的但是PCIe设备必须支持MSI/MSIx的中断请求机制,可以不支持INTx中断消息。

PCIINT

INTx 中断:

在详细介绍PCIe的中断机制之前,我们先来回顾一下PCI时代传统的中断处理过程。下图是一个典型使用PCI设备的单处理器的系统。在这个系统中设备通过INTA#产生中断,INTA#链接到南桥PIC的IRQ上,PIC再通过INTR连接到CPU上。在这样的一个系统中,中断的传递流程如下所示:

  1. 设备通过连接到中断控制器PIC的INTA#请求中断,通常PIC有15个IRQ和1个INTR,它收到中断请求以后,它会拉INTR通知CPU有中断在等待处理中。

  2. CPU检测到INTR的信号以后,它会需要知道对应的中断向量,所以它会发一个中断响应命令给PIC。

  3. PIC 返回一个当前等待中最高优先级中断请求的8位的中断向量,该向量是由之前的系统软件设置的。

  4. CPU 按照中断向量找到对应的中断处理程序。

  5. 执行该中断处理程序并告诉设备释放INTx#信号线,程序返回到之前被中断的任务继续执行。

PCIINTFlow

PCI spec 2.3在Command Register中引入了一个新的Interrupt Disable bit用来控制中断的enable和disable,该bit只会控制INTx类型的中断,不会影响MSIx。另外在Configuration Status Register加了一个Interrupt Status bit用来反应中断的状。

PCICMD

PCIIST

在PCI总线中,设备的INTx pin最终要连接到中断控制器的IRQ pin。通常在一个系统中会有多个设备,而且我们还可以通过桥扩展多个设备。这种情况下设备的INTx和中断控制器的连接方式要妥善的处理已达到负载均衡的目的。如下图的一个系统中,共有3个PCI插槽这些PCI插槽与中断控制器的IRQ_PINx引脚 (分别为IRQW#、IRQX#、IRQY#和IRQZ#)可以按照下图所示的拓扑结构进行连接。 

PCISwizzle

PCI插槽 A、B、C 的 INTA#、INTB# 和 INTC# 信号将分散连接到中断控制器的 IRQW#、IRQX#和IRQY#信号,而所有INTD#信号将共享一个IRQZ#信号。采用这种连接方式时,整个处理器系统使用的中断请求信号,其负载较为均衡。而且这种连接方式保证了每一个插槽的INTA#信号都与一根独立的IRQx#信号对应,从而提高了PCI插槽中断请求的效率。在一个处理器系统中,多数PCI设备仅使用INTA#信号,很少使用INTB#和INTC#信号,而INTD#信号更是极少使用。在PCI总线中,PCI设备配置空间的Interrupt Pin寄存器记录该设备究竟使用哪个 INTx 信号。

PCICFH

x86处理器系统还经常使用PCI桥进行PCI总线扩展,扩展出来的PCI总线还可能挂接一些PCI插槽,这些插槽上INTx#信号仍然需要处理。PCI桥规范并没有要求桥片传递其下PCI设备的中断请求。事实上多数PCI桥也没有为下游PCI总线提供中断引脚INTx#,管理其下游总线的PCI设备。但是PCI桥规范推荐使用下面的表建立下游PCI设备的INTx信号与上游PCI总线INTx信号之间的映射关系。

PCIMAP

我们举例说明该表的含义。在PCI桥下游总线上的PCI设备,如果其设备号为0,那么这个设备的INTA#引脚将和PCI总线的INTA#引脚相连;如果其设备号为1,其INTA#引脚将和PCI总线的INTB#引脚相连;如果其设备号为2,其INTA#引脚将和PCI总线的INTC#引脚相连;如果其设备号为3,其INTA#引脚将和PCI总线的INTD#引脚相连。在x86处理器系统中,由BIOS通过APCI _PRT (PCI Routing Table)记录PCI总线的INTA~D#信号与中断控制器之间的映射关系,保存这个映射关系的数据结构也被称为中断路由表。大多数BIOS使用表中的映射关系,这也是绝大多数BIOS支持的方式。如果在一个x86处理器系统中,PCI桥下游总线的PCI设备使用的中断映射关系与此不同,那么系统软件程序员需要改动BIOS中的中断路由表。BIOS初始化代码根据中断路由表中的信息,可以将PCI设备使用的中断向量号写入到该PCI设备配置空间的Interrupt Line 寄存器中。

Name(AR00, Package()
{
    // SD Host #0 - eMMC
    Package() {0x0010FFFF, 0, 0, 16 },
    // SD Host #1 - SDIO
    Package() {0x0011FFFF, 0, 0, 17 },
    // SD Host #2 - SD Card
    Package() {0x0012FFFF, 0, 0, 18 },
})

Name(PRSA, ResourceTemplate()         // Link name: LNKA
{
    IRQ(Level, ActiveLow, Shared, LLKA) {3,4,5,6,10,11,12,14,15}
})
Alias(PRSA,PRSB)      // Link name: LNKB
Alias(PRSA,PRSC)      // Link name: LNKC
Alias(PRSA,PRSD)      // Link name: LNKD

Name(PR00, Package()
{
    // SD Host #0 - eMMC
    Package() {0x0010FFFF, 0, LNKA, 0 },
    // SD Host #1 - SDIO
    Package() {0x0011FFFF, 0, LNKB, 0 },
    // SD Host #2 - SD Card
    Package() {0x0012FFFF, 0, LNKC, 0 },
}

Method(_PRT,0)
{
    If(PICM) {Return(AR00)} // APIC mode
    Return (PR00) // PIC Mode
} // end _PRT

ACPI_ADR

_PRT method中的资源的描述格式如上表所示 Source Index指的是对应的IOAPIC的pin。 PCIe 虽然继承了PCI所有的中断类型,但是PCIe很少使用INTx,主要使用的MSIx去传递中断,INTx的存在主要是因为有些老的PCI设备通过PCIe to PCI bridge接入到PCIe总线中。因为PCIe并没有INTx的边带信号,所以它使用的基于Message的中断传递。其中Assert_INTx消息表示INTx信号的下降沿。Dessert_INTx消息表示INTx信号的上升沿, 当一个Assert_INTx消息发出来,对应中断状态寄存器也会被设置起来。

PCIINTX

INTx的消息格式如下所示:

INTXFORMAT

PCIe桥下面的设备和总线的map关系和PCI类似

INTXV

下图是一个简单的例子。3:0:0 Assert_INTA 消息因为它的设备号是0,所以2:1:0收到之后还是使用INTA,但是会讲ReqID改为2:1:0;因为2:1:0的设备号是1,所以1:0:0收到的消息会转化为 Assert_INTB (ReqID 1:0:0); 1:0:0接下来会把消息送给中断控制器。

ExampleINTX

MSI/MSI-X中断

PCIe的MSI/MSI-X继承自PCI,这个新的机制的引入同样是为了消除INTx的边带信号,而且可以更加合理的使用PCIe总线的序。目前绝大多数的PCIe设备使用MSI/MSI-X中断机制提交中断请求。MSI和MSI-X基本原理相同。MSI最多支持32个中断,而且要求中断向量连续。MSI-X可以支持更多的中断请求而且不要求中断向量连续。MSI/MSI-X本质上是一种Memory Write。PCIe设备中包含两个Capability结构,一个MSI另一个是MSI-X。MSI Cap ID是0x05,MSI有4种Capability组合,32位和64位的Message结构,32位和64位带中断Masking的结构。

MSICap

我们可以通过Message Control Register控制MSI的开启和关闭以及是否支持64bit、是否支持多个消息,Masking等。

MCR

在X86系统中PCIe设备使用的Message Address 字段仍然保存PCI总线域的地址格式:

MsgAdr

  1. 第31~20位,存放FSB Interrupts存储器空间的基地址,其值为0xFEE。当PCIe设备对0xFEEX-XXXX这段“PCI总线域”的地址空间进行写操作时,桥片会将会首先进行“PCI总线域”到 “存储器域”的地址转换,之后将这个写操作翻译为系统总线的Interrupt Message总线事务,从而向CPU内核提交中断请求。
  2. Destination ID字段保存目标CPU的ID号,目标CPU的ID与该字段相等时,目标CPU将接收这个Interrupt Message。系统总线Interrupt Message总线事务可以向不同的CPU提交 中断请求。
  3. RH(Redirection Hint Indication)位为0时,表示Interrupt Message将直接发向与Destination ID字段相同的目标CPU;如果RH为1时,将使能中断转发功能。
  4. DM(Destination Mode)位表示在传递优先权最低的中断请求时,Destination ID字段是否被翻译为Logical或者Physical APIC ID。在x86处理器中APIC ID有三种模式,分别为 Physical、Logical和Cluster ID模式。
  5. 如果RH位为1且DM位为0时,Destination ID字段使用Physical模式;如果RH位为1且DM位为1,Destination ID字段使用Logical模式;如果RH位为0,DM位将被忽略。

Message Data字段的格式如下图所示:

MsgD

  1. Trigger Mode字段为0b0x时,PCIe设备使用边沿触发方式申请中断;为0b10时使用低电平触发方式;为0b11时使用高电平触发方式。MSI/MSI-X中断请求使用边沿触发方式,但是 系统总线 Interrupt Message总线事务还支持Legacy INTx中断请求方式,因此在Message Data字段中仍然支持电平触发方式。但是对于PCIe设备而言,该字段为0b0x。

  2. Vector字段表示这个中断请求使用的中断向量。系统总线Interrupt Message总线事务在提交中断请求的同时,将中断向量也通知给处理器。因此使用系统总线Interrupt
    Message总线事务时,处理器不需要使用中断响应周期通过读取中断控制器获得中断向量号。

  3. Delivery Mode字段表示如何处理来自PCIe设备的中断请求。

    a. 该字段为0b000时,表示使用“Fixed Mode”方式。此时这个中断请求将被Destination ID字段指定的CPU处理。

    b. 该字段为0b001时,表示使用“Lowest Priority”方式。此时这个中断请求将被优先权最低的CPU处理。当使用“Fixed Mode”和“Lowest Priority”方式时,如果Vector字段有 效,CPU接收到这个中断请求之后,将使用Vector字段指定的中断向量处理这些中断请求;而当Delivery Mode字段为其他值时,Message Data字段中所包含的Vector字段效。

    c. 该字段为0b010时,表示使用SMI方式传递中断请求,而且必须使用边沿触发,此时Vector字段必须为0。这个中断请求将被Destination ID字段指定的CPU处理。

    d. 该字段为0b100时,表示使用NMI方式传递中断请求,而且必须使用边沿触发,此时Vector字段和Trigger字段的内容将被忽略。这个中断请求将被Destination ID字段指定的 CPU处理。

    e. 该字段为0b101时,表示使用INIT方式传递中断请求,Vector字段和Trigger字段的内容将被忽略。这个中断请求将被Destination ID字段指定的CPU处理。

    f. 该字段为0b111时,表示使用INTR信号传递中断请求且使用边沿触发。此时MSI中断信息首先传递给中断控制器,然后中断控制器在通过INTR信号向CPU传递中断请求,之后CPU 在通过中断响应周期获得中断向量。

为了能够使用MSI, 系统软件(BIOS)在启动的时候要做一些基本的配置。

  1. 首先BIOS枚举所有的PCI兼容的设备。

  2. 发现设备以后软件读取Capabilities list指针找到MSI CAP的位置。

  3. 软件读取设备的消息控制寄存器的Mulitple Message Capable栏位获得设备支持的消息数量以及是否支持64bit消息地址。然后使能对应的enable bit。

  4. 软件分配base message data pattern以及Message Address。

  5. 最后使能MSI enable bit并关闭其它的中断选项。

MSI内存写传输数据包格式如下:

MSITF

MSI-X的Capability 结构如下所示:

MSIXCAP

MSIXCTL

MSI-X支持多达2048个中断向量,但是MSI-X的相关寄存器在配置空间中占用的空间却更小。这是因为中断向量通过在BIR指向的mmio地址处。

LMSIX

中断消息向量表格式如下:

MSIXE

Pending bit Array格式如下:

PBA

使用MSI传递中断请求的过程:

  1. PCIe 设备在发送 MSI/MSI-X中断请求之前,系统软件需要合理设置PCIe设备MSI/MSI-X Capability 寄存器,使 Message Address寄存器的值为0xFEExx00y,同时合理地设置 Message Data寄存器Vector字段。
  2. PCIe设备提交MSI/MSI-X中断请求时,需要向0xFEExx00y地址写Message Data寄存器中包含的数据,并以存储器写TLP的形式发送到RC。当桥片收到这个TLP后,发现这个TLP的目 的地址在系统总线Interrupts存储器空间中,则将PCIe总线的存储器写请求转换为系统总线Interrupt Message总线事务,并在系统总线上广播。
  3. 系统总线上的CPU,根据APIC ID信息,选择是否接收这个Interrupt Message总线事务,并进入中断状态,之后该CPU将直接从这个总线事务中获得中断向量号,执行相应的中断服 务例程,而不需要从APIC中断控制器获得中断向量。

Refer:

1. PciTree.asl

  1. 《PCI Express Technology》

  2. 《PCI Express 体系结构导读》