3、GICv2架构详解:GICv2的组成模块、中断类型、中断优先级与亲和性
GICv2(Generic Interrupt Controller version 2)是ARM Cortex-A系列处理器在ARMv7-A及部分早期ARMv8-A平台上广泛使用的中断控制器标准。理解GICv2的组成模块、中断类型、优先级与亲和性机制,是掌握Linux内核中断子系统初始化的基础。本节将从硬件架构层面深入剖析GICv2的核心设计。
3.1 GICv2的组成模块
GICv2由两大逻辑模块构成:Distributor(分发器)和CPU Interface(CPU接口)。这两个模块通过专用总线连接,共同完成中断的收集、优先级仲裁、分发和CPU核交付。
3.1.1 Distributor(分发器)
Distributor是GICv2的控制中心,主要职责包括:
- 中断使能与屏蔽: 控制每个中断源的使能/禁止状态。
- 优先级管理: 为每个中断分配优先级值,并基于优先级进行仲裁。
- 中断目标选择: 决定每个中断可以发送到哪个或哪些CPU核(亲和性)。
- 中断触发方式配置: 设置中断为电平触发或边沿触发。
- 中断分组: 将中断分为Group 0(安全中断)和Group 1(非安全中断),支持TrustZone。
- 软件触发中断(SGI): 提供寄存器用于软件生成SGI。
3.1.2 CPU Interface(CPU接口)
每个CPU核拥有一个独立的CPU Interface模块,负责:
- 中断优先级掩码: 设置当前CPU核能响应的最低优先级阈值。
- 中断抢占逻辑: 管理高优先级中断对低优先级中断的抢占。
- 中断应答(ACK): CPU读取GICC_IAR寄存器获取中断ID。
- 中断结束(EOI): CPU写入GICC_EOIR寄存器通知中断处理完成。
- 运行优先级跟踪: 维护当前CPU核的运行优先级状态。
0x2C001000(以某平台为例),CPU Interface基地址为 0x2C002000。驱动通过访问这些寄存器完成初始化配置。
3.2 中断类型
GICv2定义了四种中断类型,每种类型在中断ID范围、触发方式、目标CPU等方面有明确区分。
| 中断类型 | 中断ID范围 | 触发方式 | 目标CPU | 典型用途 |
|---|---|---|---|---|
| SGI(Software Generated Interrupt) | ID 0 ~ 15 | 边沿触发(软件写入) | 可指定一个或多个CPU | 核间通信(IPI)、CPU唤醒 |
| PPI(Private Peripheral Interrupt) | ID 16 ~ 31 | 电平或边沿触发 | 仅绑定到特定CPU核 | 本地定时器、性能监视单元(PMU) |
| SPI(Shared Peripheral Interrupt) | ID 32 ~ 1019 | 电平或边沿触发 | 可路由到任意CPU核 | 外设中断(UART、I2C、DMA等) |
| LPI(Locality-specific Peripheral Interrupt) | ID 8192 ~ 65535 | 消息中断(基于写操作) | 可路由到任意CPU核 | GICv3/v4引入,GICv2不支持 |
3.2.1 SGI(软件生成中断)
SGI通过Distributor的GICD_SGIR寄存器触发。Linux内核使用SGI实现IPI(Inter-Processor Interrupt),例如:
- IPI_RESCHEDULE: 触发CPU重新调度(SGI ID 0)。
- IPI_CALL_FUNC: 让目标CPU执行指定函数(SGI ID 1)。
- IPI_CPU_STOP: 停止目标CPU(SGI ID 2)。
3.2.2 PPI(私有外设中断)
PPI是每个CPU核私有的中断,不同CPU核的同ID PPI互不干扰。典型PPI包括:
- ARM Generic Timer: 物理定时器(PPI ID 29)、虚拟定时器(PPI ID 27)。
- Performance Monitors: PMU溢出中断(PPI ID 23)。
3.2.3 SPI(共享外设中断)
SPI是系统中所有外设共享的中断类型,通过Distributor路由到指定CPU核。SPI ID从32开始,每个外设占用一个唯一ID。例如:
- UART0中断通常映射为SPI ID 32 ~ 39(具体取决于SoC设计)。
- I2C控制器中断映射为SPI ID 40 ~ 47。
3.3 中断优先级
GICv2使用8位优先级值(实际实现可能只使用高4位或高5位),数值越小优先级越高。优先级管理涉及两个关键寄存器:
- GICD_IPRIORITYR(Distributor优先级寄存器): 每个中断源占用一个字节,设置该中断的优先级。
- GICC_PMR(CPU Interface优先级掩码寄存器): 设置当前CPU核能响应的最低优先级阈值。只有优先级高于PMR值的中断才能被CPU处理。
- Distributor收集所有挂起的中断,比较它们的优先级。
- 选择优先级最高的中断,将其发送到目标CPU Interface。
- CPU Interface检查该中断优先级是否高于当前运行优先级(由GICC_RPR寄存器维护)。
- 如果高于运行优先级,则触发CPU中断异常;否则等待。
3.3.1 优先级配置示例
Linux内核通常将优先级分为几个等级,例如:
| 优先级值(高4位) | 优先级等级 | 典型用途 |
|---|---|---|
| 0x00 | 最高 | 紧急错误中断(如硬件故障) |
| 0x10 ~ 0x20 | 高 | 定时器、DMA传输完成 |
| 0x40 ~ 0x60 | 中 | 网络、存储控制器 |
| 0x80 ~ 0xA0 | 低 | UART、I2C等慢速外设 |
| 0xFF | 最低 | 空闲中断或屏蔽 |
3.4 中断亲和性(Affinity)
中断亲和性是指将中断路由到特定CPU核的能力。GICv2通过Distributor中的GICD_ITARGETSR寄存器实现亲和性配置。每个中断源占用一个字节,每个bit对应一个CPU核(bit0对应CPU0,bit1对应CPU1,以此类推)。
- 将SPI中断路由到CPU0:写入
0x01到对应的GICD_ITARGETSR字节。 - 将SPI中断路由到CPU0和CPU1:写入
0x03。 - 将SPI中断路由到所有CPU:写入
0xFF(最多8个CPU)。
3.4.1 亲和性在Linux内核中的应用
Linux内核通过irq_set_affinity接口动态调整中断亲和性,实现负载均衡:
/* 将IRQ 42(假设为网卡中断)绑定到CPU2 */
struct cpumask mask;
cpumask_clear(&mask);
cpumask_set_cpu(2, &mask);
irq_set_affinity(42, &mask);
内核的irqbalance守护进程会周期性地重新分配中断亲和性,以均衡各CPU核的中断处理负载。
3.5 中断生命周期与状态机
GICv2中的每个中断源在硬件层面维护一个状态机,包含以下状态:
| 状态 | 描述 | 触发条件 |
|---|---|---|
| Inactive | 中断未触发 | 初始状态或EOI后 |
| Pending | 中断已触发,等待CPU响应 | 外设触发中断信号 |
| Active | CPU已应答中断,正在处理 | CPU读取GICC_IAR |
| Active and Pending | 中断处理中,又有新触发 | 处理期间中断再次到达 |
- 外设触发中断信号,Distributor将对应中断状态置为Pending。
- Distributor进行优先级仲裁,选择最高优先级中断。
- Distributor将中断发送到目标CPU的CPU Interface。
- CPU Interface检查优先级掩码,若允许则向CPU核发送IRQ/FIQ信号。
- CPU核响应异常,读取
GICC_IAR寄存器获取中断ID,状态变为Active。 - CPU执行中断服务程序(ISR)。
- ISR完成后,CPU写入
GICC_EOIR寄存器,状态回到Inactive。
3.6 本章小结
GICv2架构通过Distributor和CPU Interface的分工协作,实现了灵活的中断管理。Distributor负责全局中断收集、优先级仲裁和亲和性路由,CPU Interface负责与CPU核的直接交互和优先级掩码控制。四种中断类型(SGI、PPI、SPI、LPI)覆盖了核间通信、私有外设和共享外设的不同需求。优先级和亲和性机制为Linux内核实现中断负载均衡和实时性保障提供了硬件基础。理解这些概念,是后续分析GIC初始化代码和中断处理流程的关键前提。