0. 写在前面
由于移植一个 pcie 设备驱动时,需要处理该 pcie 设备的 msi 中断(message signaled interrup
)。
在 ARM 中, ARM 建议 msi 中断实现方式为: pcie 设备往 cpu 的一段特殊内存(寄存器)写某一个值,触发 LPI 中断,来实现 pcie 的 MSI 中断。
本篇笔记是学习 《ARM® Generic Interrupt Controller Architecture Specification - GIC architecture version 3.0 and version 4.0》第 6 章所做的笔记。
1. LPI 中断
只有 GICv3/v4 支持 LPI(Locality-specific Peripheral Interrupts: 局部外设中断) 。
LPI 可以理解为非共享外设中断(Non-Shared Peripheral Interrupts):只能发送给一个核处理(类比 SPI 不设置为 1- N 模式,可以由多个核处理)
。
2. LPI 与 ITS(Interrupt Translation Service)
2.1 LPIs
LPI 是使用消息机制、边沿触发的中断。LPI 可以使用 ITS 来生成。ITS 可以生成一个 LPI 并将该 LPI 中断路由到一个指定的处理器核的 Redistributor 处理。
在 GICv3/v4 中,支持两种生成 LPI 的方式:
- 使用 ITS 将一个设备写入的 EventID 翻译为指定的 LPI 中断;
- 设备直接写
GICR_SETLPIR
, 将 LPI 中断向量,发送给指定核的 Redistributor.
任一 SOC 芯片实现,只能使用上述两种方式之一。
如果一个 SOC 使用第二种方式生成 LPI(不实现 ITS),那么该 SOC 的 Redistributor 必须实现如下的寄存器:
寄存器 | 描述 |
---|---|
GICR_SETLPIR | 设置某一 LPI 向量为挂起状态,并生成 LPI 中断 |
GICR_CLRLPIR | 清除某一 LPI 向量的挂起状态 |
GICR_INVLPIR | 使无效某一 LPI 向量的已缓存配置信息,并从物理内存中的配置信息表中,重新缓存该 LPI 向量的配置信息 |
GICR_INVALLR | 使无效所有 LPI 向量的已缓存配置信息,并从物理内存中的配置信息表中,重新缓存所有 LPI 向量的配置信息 |
GICR_SYNCR | 指示 Redistributor 的某一写操作是否完成,比如写 GICR_CLRLPIR 、GICR_INVLPIR 、GICR_INVALLR |
如果一个 SOC 使用第一种方式生成 LPI(实现 ITS),那么至少可以生成 8192 个 LPI。因为 ITS 支持生成的 LPI 向量过多,所以 LPI 中断的配置信息、pending 状态信息被保存在物理内存中,而不是寄存器中。配置信息表与状态信息表地址存储在 Redistributor 寄存器中。下图展示了使用 ITS 生成 LPI 的流程框图:
LPI 在 SOC 中存在一片物理内存,每个核的 Redistributor 都可以接受某一指定 LPI 向量。
对于某一指定的 Redistributor,LPI 的配置与 pending 状态信息使用两种内存中的表维护:
- LPI 配置表: 配置 LPI 向量的属性与使能位,LPI 配置表的基地址被定义在
GICR_PROPBASER
中,GICR_PROPBASER 应该在GICR_CTLR.EnableLPIS
置 1 之前被更新。 - LPI pending 状态表:LPI pending 状态信息表中的 LPI 向量 pending 位支持内存备份,这个表特定于某一指定 Redistributor(每个 Redistributor 都有自己的 Pending 状态表?),表基地址被定义在
GICR_PENDBASER
中。
GICR_PROPBASER.IDBits
指定的支持的 LPI 中断数。
GICR_CTLR.EnableLPIS
置 1 表示使能 LPI。
2.1.1 LPI 配置表
LPI 配置表是全局的(所有的 Redistributor 使用同一份 LPI 配置表),GIC 是否支持 Redistributor 指向不同的 LPI 配置表是实现定义的。
一个 LPI 配置表的表项包含一个 LPI 中断向量的配置信息:
GICR_PROPBASER
指定一个 4KB 大小对齐的物理内存基地址- 对于 LPI 向量 N, 配置信息定义在 baseaddr + (N - 8192) 地址处(LPI 中断从 8192 开始?每一个 LPI 中断使用 1 字节保存配置信息),配置信息如下表:
配置信息字节中的位域定义如下: - Priority: 优先级定义
- Enable: 当前的 LPI 中断是否使能
Redistributor 可以将LPI 配置表缓存到本地。
2.1.2 LPI pending 状态表
由软件写 GICR_PENDBASER
指定 LPI pending 状态表基地址。每一个 Redistributor 都维护自己的 LPI pending 状态表,用单独的位表示某一个 LPI 的状态信息:
- 0 :表示某一 LPI 没有 Pending;
- 1 :表示某一 LPI 处于 Pending 状态。
LPI 挂起信息表使用 1KB 对齐的物理内存,如果挂起信息表全 0,那么表示没有 LPI 处理 Pending 状态。
对于某一指定的 LPI:
- Pending 状态表对应的 Pending 字节位置为 baseaddr + (N /8)
- Pending 状态表对应的 Pending 位为 (N MOD 8)
2.2 ITS(Interrupt Translation Service)
ITS 使用某一 DeviceID 的设备写入内存的 EventID,翻译为:
- 对应的 INTID
- 目标处理器的 Redistributor,用于处理转换而来的 INTID
ITS 将 EventID 翻译为 LPI,ITS 翻译流程如下:
- DeviceID 选择 Device table 中的某一表项,表项保存该设备使用的中断翻译表 - Interrupt translation table(ITT) 的地址。
- EventID 选择中断翻译表的一个表项,用于确定输出的 LPI 中断向量号,中断集合 ID 号(ICID)
- ICID 从集合表 - Collection table 中,选择一个表项,用于确定处理 LPI 的目标 CPU 的 Redistributor.
总结:
所以,ITS 翻译过程会用到下列的表:
- Device Table: 每一个 DeviceID 的设备都有一个表项,用于保存该设备使用的中断翻译表(ITT)。
- 中断翻译表:ITT 保存 EventID 与 LPI ID 的对应关系。
- 集合表:Collection table 保存中断集合 ID 号(ICID)与目标 Redistributor 的对应关系。
2.2.1 ITS 表
由于使用软件为 ITS 私有表提供内存,GIC提供了一组寄存器 - GITS_xxxR
,这些寄存器用于实现下列功能:
- ITS 需要的私有表的数量
- 每一个表的表项大小
- 每一个表的类型
GITS_BASER<n>
寄存器用于指定 ITS 表的基地址与大小,并且必须在 ITS 使能前被提供。
ITS 表拥有一级表结构或二级表结构,表结构通过 GITS_BASER<n>.Indirect
指定.
- 0 : 一级表结构,当前表包含连续的 block 结构,block 格式是实现定义的。
- 1 : 二级表结构,二级表结构格式是实现定义的,一级表的表项是 64 bit大小,并且拥有如下格式:
- Bit[63] - Valid 位,置1表示有效,指向二级表基地址。
- Bit[62 : 53] - 保留
- Bit[51 : N] - 二级表结构基地址,N 表示 page size 大小
- Bit[N-1 : 0] - 保留
下图显示了 ITS 生成 LPI 的流程(只关注物理 LPI):
2.2.2 中断集合- Interrupt Collections
ITS 认为所有生成的物理 LPI 是一个 Collections 的成员,Collection Table 基地址通过 ITS 寄存器指定,GITS_BASER<n>.Type == 0b100
.
集合表的表项如何?手册没写
2.2.3 设备表 - Device Table
设备表保存 DeviecID 与 ITT 的映射关系,设备表的每一个表项保存一个 ITT 的基地址。
DeviceID 是分配给每个设备的唯一标识符,这个设备可以创建一系列的 EventID。比如 ARM 期望来自 PCIe Root Complex 的16位 Request ID 作为 ITS 使用的 DeviceID。DeviceID 提供设备表的索引值。
设备表的表项位域分配实例如下:
6.2.4 中断翻译表 - interrupt translation table
每个设备都有自己的中断翻译表(ITT),翻译表可以对应多个事件的 LPI 中断翻译。
ITT 表的大小由设备表的表项中的 ITT Range
位域决定,占用内存为 2^(ITT Range + 1) * GITS_TYPER.ITT_entry_size。
ITT 拥有与设备表相同的共享属性与 cache 属性。
ITT 的作用如下:
- 描述 EventID 与 LPI ID 的对应关系;
- 描述 EventID 与 ICID 的对应关系,ICID 用于在 Collection Table 中查找目标 Redistributor.
EventID 用作 ITT 的索引值。
ITT 表项的示例位域分配如下:
2.2.5 集合表 - Collection Table
集合表(CT) 保存中断集合号 - ICID 与目标 Redistributor 地址的映射关系。
每一个 ITS 都有一个 CT。ICID 提供 CT 使用的索引值。
CT 的表项位域分配实例如下:
其中: RDbase 位域格式由 GITS_TYPER.PTA
决定。
2.2.6 vPE Table
虚拟 LPI 中断使用的表,不看。
2.2.7 ITS 私有表的控制与配置
ITS 提供内存映射的寄存器,用于配置 ITS:
寄存器 | 描述 |
---|---|
GITS_IIDR / GITS_PIDR2 | 提供版本信息 |
GITS_TYPER | 提供 ITS 支持的功能信息 |
GITS_CTLR | ITS 配置 |
GITS_TRANSLATER | 用于接收 EventID |
GITS_BASER | 用于提供 ITS 私有表的地址、大小、访问属性 |
GITS_CBASER / GITS_CREADR / GITS_CWRITER | 提供 ITS 命令队列的地址信息 |
2.2.8 ITS 命令接口
下图显示了 ITS 如何提供命令队列使用的基址和大小。为什么会有命令队列这个东西?
GITS_CBASER
: 提供命令队列基地址与大小以及访问属性。GITS_CREADR
: 指向 ITS 将要处理的下一个命令。GITS_CWRITER
: 指向软件可写入的下一个命令地址。
ITS 命令队列的队列项是 32 字节的,也就是 4KB 页大小的命令队列支持 128 个命令。
ITS 命令队列基地址是 64KB 对齐的,队列大小 4KB 对齐。
当第一个命令完成后,ITS开始处理下一个命令。读指针GITS_CREADR
的移动由 ITS 硬件维护 。如果GITS_CREADR
到达 GITS_CBASER
中指定的内存顶部,则跳回 GITS_CBASER
中指定的基址。GITS_CWRITER
由软件维护。
INT
命令可以产生中断?不是由设备写 EventID 产生中断?测试使用
答:
ITS 命令被 ITS 硬件用于维护 ITS 私有表,因为初始化时所有私有表结构都是0,只能通过 ITS 命令队列中的命令去维护 ITS 私有表,比如创建一个 DeviceID 与 ITT 的映射关系,已经 EventID 与 LPI 中断号的映射关系。
2.2.9 ITS 命令输出与 ITS 中断翻译
ITS 的命令由 ITS 硬件自动执行,在 SYNC
或 VSYNC
命令完成后发起的中断翻译请求将使用与命令执行后的状态一致的ITS状态进行翻译。
2.2.10 中断映射规则的限制
- 不可将多个 DeviceID - EventID 组合映射为同一个 LPI 中断
- 任一 LPI 中断只能由一个 Redistributor 处理
2.3 ITS 命令
ITS 命令用于维护 ITS 私有表,创建 ITS 中断翻译过程中使用的映射关系。
必须在 ITS 中断使能之前,完成各私有表的映射关系初始化 (为各 DeviceID 分配 EventID 与 LPI ID?)。