ARM GIC 架构概览:GIC v2/v3/v4 演进、Distributor 与 Redistributor、CPU Interface
通用中断控制器(Generic Interrupt Controller, GIC)是 ARM 架构中管理中断的核心硬件单元,负责接收外设、软件触发或处理器间的中断信号,经过优先级仲裁、分发和屏蔽后,将中断事件路由到指定的 CPU 核心。GIC 的版本演进直接反映了多核、虚拟化和大规模系统对中断处理能力的需求变化。
2.1 GIC 版本演进与核心差异
GIC 从 v1 发展到 v4,每一代都在中断数量、路由灵活性、虚拟化支持和消息传递机制上做了显著增强。下表总结了各版本的关键特性:
| 特性 | GIC v2 | GIC v3 | GIC v4 |
|---|---|---|---|
| 中断线数量 | 最多 1020 个 SPI | 最多 1024 个 SPI + 16 个 LPI | 同 v3,LPI 扩展 |
| CPU 接口 | 内存映射寄存器(GICC 基址) | 系统寄存器(ICC_*_ELn) | 同 v3 |
| Redistributor | 无(Distributor 直连 CPU) | 每个 CPU 对应一个 Redistributor | 同 v3,增加 vLPI 支持 |
| 虚拟化支持 | 基础虚拟中断(vgic v2) | 增强虚拟中断(vgic v3) | 直接注入虚拟中断(vLPI) |
| 中断消息传递 | 仅通过内存映射写 | 支持 MSI(基于写请求) | 支持直接消息传递(Doorbell) |
| 典型应用 | Cortex-A7/A15 多核 | Cortex-A53/A72 服务器/移动 | 虚拟化密集场景(如 NFV) |
实践提示: 在设备树中,GIC v2 使用
compatible = "arm,cortex-a15-gic",而 GIC v3 使用 compatible = "arm,gic-v3"。驱动代码中通过读取 GICD_TYPER 寄存器的位域可判断硬件版本。
2.2 GIC v2 架构:Distributor 与 CPU Interface
GIC v2 采用经典的两级结构:Distributor 和 CPU Interface。Distributor 集中管理所有中断的使能、优先级、触发方式和目标 CPU,而每个 CPU 核心拥有独立的 CPU Interface,负责向核心提交中断信号。
GIC v2 中断类型:
- SGI(Software Generated Interrupt): 软件触发,用于核间通信,ID 0-15。
- PPI(Private Peripheral Interrupt): 私有外设中断,每个 CPU 独享,ID 16-31。
- SPI(Shared Peripheral Interrupt): 共享外设中断,可路由到任意 CPU,ID 32-1019。
2.3 GIC v3/v4 架构:Redistributor 的引入
GIC v3 对架构进行了重大重构,引入了 Redistributor 组件,每个 CPU 核心对应一个 Redistributor,负责管理该核心的 PPI、SGI 和 LPI(Locality-specific Peripheral Interrupt)。Distributor 不再直接与 CPU 接口交互,而是通过 Redistributor 进行间接通信。GIC v4 在此基础上增加了对虚拟中断的直接注入支持(vLPI)。
驱动开发注意: GIC v3 的 CPU Interface 不再使用内存映射寄存器(GICC 基址),而是通过系统寄存器(如 ICC_IAR0_EL1、ICC_EOIR0_EL1)访问。设备树中不再需要
reg 属性描述 GICC 区域,只需描述 Distributor 和 Redistributor 的基址。
2.4 Distributor、Redistributor 与 CPU Interface 职责对比
下表从寄存器接口、中断类型管理和路由策略三个维度,对比 GIC v3 中三个核心模块的职责:
| 模块 | 寄存器前缀 | 管理的中断类型 | 路由策略 | 内存映射区域 |
|---|---|---|---|---|
| Distributor | GICD_ | SPI(共享外设中断) | 全局路由表(GICD_IROUTER) | GICD 基址(设备树 reg 第一个区域) |
| Redistributor | GICR_ | PPI、SGI、LPI | 每个 CPU 独立管理私有中断 | GICR 基址(设备树 reg 第二个区域) |
| CPU Interface | ICC_*_ELn | 所有已使能中断的提交 | 系统寄存器直接访问,无内存映射 | 无(GIC v3 废弃 GICC 区域) |
2.5 设备树中 GIC 节点的典型描述
以下代码展示 GIC v3 在设备树中的标准描述方式,注意 reg 属性包含 Distributor 和 Redistributor 两个地址范围:
gic: interrupt-controller@2f000000 {
compatible = "arm,gic-v3";
#interrupt-cells = <3>;
#address-cells = <2>;
#size-cells = <2>;
interrupt-controller;
reg = <0x0 0x2f000000 0x0 0x10000>, /* GICD */
<0x0 0x2f100000 0x0 0x200000>; /* GICR */
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
};
字段说明:
#interrupt-cells = <3>:中断描述符包含中断类型(0=SPI, 1=PPI)、中断号、触发标志。reg第一个区域:Distributor 寄存器基址(0x2f000000,大小 64KB)。reg第二个区域:Redistributor 寄存器基址(0x2f100000,大小 2MB,按 CPU 核心数扩展)。interrupts:GIC 自身维护中断(如维护中断)使用 PPI 9。
2.6 中断处理流程:从外设到 CPU
以 GIC v3 为例,一个 SPI 中断从外设触发到 CPU 核心处理的完整路径如下:
- 外设断言中断线:SPI 中断信号到达 Distributor 的输入引脚。
- Distributor 仲裁:读取 GICD_ISPENDR 确认挂起状态,根据 GICD_IPRIORITYR 进行优先级比较,选择最高优先级中断。
- 路由决策:查询 GICD_IROUTER 寄存器,确定目标 CPU 核心(支持亲和性路由)。
- Redistributor 接收:目标 CPU 对应的 Redistributor 接收中断,更新 GICR_ISPENDR 和 GICR_ISACTIVER。
- CPU Interface 提交:CPU 核心通过系统寄存器 ICC_IAR0_EL1 读取中断 ID,硬件自动将中断提交到异常向量表。
- 中断服务例程执行:Linux 内核的 GIC 驱动调用
handle_domain_irq()进入中断处理流程。 - EOI 确认:中断处理完成后,写入 ICC_EOIR0_EL1 发送结束信号,Redistributor 清除挂起状态。
调试技巧: 在 Linux 内核中,可以通过
/proc/interrupts 查看每个 CPU 核心的中断分布,确认 GIC 路由是否按预期工作。如果中断全部集中在 CPU0,检查 GICD_IROUTER 寄存器配置或内核 irqbalance 服务状态。