4. GICv3/v4 架构新特性

GICv2 在物理中断管理上已经成熟,但随着虚拟化、大核数(>8)和高性能计算需求的爆发,ARM 推出了 GICv3 和 GICv4 架构。本章深入剖析 GICv3 的 Redistributor 机制、LPI 中断、Direct Virtual LPI 以及 GICv4 的虚拟化增强,这些特性是 Linux 内核启动时中断控制器初始化的核心难点。

学习提示:GICv3/v4 与 GICv2 最大的区别在于中断路由从“全局分发”变为“每 CPU 重分发”,并且引入了基于消息的中断(LPI)。理解 Redistributor 是掌握整个 GICv3 初始化流程的关键。

4.1 GICv3 的 Redistributor 机制

GICv3 将传统 GICv2 中单一的 Distributor 功能拆分为 Distributor (D) 和 Redistributor (R)。每个 CPU 核心(或 PE)拥有一个专用的 Redistributor 实例,负责管理该 CPU 的私有中断(PPI、SGI)以及 LPI 中断的配置。

Redistributor 的核心作用包括:

  • 每 CPU 中断控制: 管理 PPI(16-31)和 SGI(0-15)的使能、优先级、触发方式。
  • LPI 中断处理: 接收来自 ITS(Interrupt Translation Service)转发的 LPI,并维护 LPI 的挂起与使能状态。
  • 电源管理接口: 支持 CPU 睡眠/唤醒时的中断上下文保存与恢复。
  • 物理地址映射: 每个 Redistributor 通过一组固定的 MMIO 页面(GICR_*)暴露给软件。
初始化要点:内核在启动时,通过读取 GICD_TYPER 寄存器中的 Redistributor 基数 字段,遍历所有 Redistributor 基地址,并为每个在线 CPU 初始化其对应的 Redistributor 实例。
GICv3 架构:Distributor 与 Redistributor 关系 Distributor (GICD) Redistributor 0 CPU 0 Redistributor 1 CPU 1 Redistributor N CPU N 说明: • Distributor 负责全局中断路由策略(如 SPI 路由到哪个 Redistributor)。 • 每个 Redistributor 独立管理其关联 CPU 的 PPI/SGI 和 LPI 挂起状态。 • 内核通过 GICR_BASE 和 GICR_STRIDE 遍历所有 Redistributor 页面。

4.2 LPI(Locality-specific Peripheral Interrupt)支持

LPI 是 GICv3 引入的一种新型中断类型,专为高密度、可扩展的外设中断场景设计。与传统的 SPI/PPI/SGI 不同,LPI 具有以下特征:

特性 传统中断(SPI/PPI/SGI) LPI 中断
中断号范围 0-1019(固定) 8192-(可扩展至 2^24)
配置方式 MMIO 寄存器(GICD/GICR) 内存表(GICR_PENDBASER / GICR_PROPBASER)
触发方式 电平/边沿 仅边沿触发(消息中断)
路由 Distributor 静态路由 ITS 动态路由(基于设备 ID 和事件 ID)
优先级 8 位(256 级) 8 位(256 级)

LPI 的配置不通过 MMIO 寄存器直接读写,而是通过 内存表 实现。每个 Redistributor 维护两张关键内存表:

  • 属性表(Property Table): 由 GICR_PROPBASER 指向,存储 LPI 的优先级和使能位。
  • 挂起表(Pending Table): 由 GICR_PENDBASER 指向,存储 LPI 的挂起状态。
初始化陷阱:内核在启用 LPI 前,必须确保 Redistributor 的 GICR_CTLR.EnableLPI 位被置位,并且属性表和挂起表所在的物理内存区域必须对齐到 64KB 边界,且不能被缓存(Normal Non-Cacheable 或 Device 属性)。
LPI 中断配置与内存表结构 ITS (Interrupt Translation) Redistributor 属性表 (PROPBASER) 优先级 + 使能位 挂起表 (PENDBASER) LPI 配置流程: 1. 外设通过 ITS 发送 MSI 消息(包含 Device ID + Event ID)。 2. ITS 将 Event ID 翻译为 LPI 中断号(INTID),并路由到目标 Redistributor。 3. Redistributor 查询属性表获取 LPI 优先级和使能状态。 4. 若 LPI 使能且优先级满足条件,Redistributor 在挂起表中标记挂起位。 5. Redistributor 向 CPU 核心发送中断信号(IRQ)。 6. CPU 读取 GICR_ISPENDR 或通过内存查询挂起表确认中断源。

4.3 Direct Virtual LPI

在虚拟化场景中,传统方式下 Guest OS 访问 GIC 需要经过 VM exit 进行 Trap-and-Emulate,性能开销巨大。GICv3 引入了 Direct Virtual LPI 机制,允许 Guest OS 直接操作 LPI 的配置和挂起状态,无需 VMM 介入。

Direct Virtual LPI 的核心组件:

  • GICR_VPENDBASER: 指向 Guest 的 LPI 挂起表,Redistributor 硬件直接读写该表。
  • GICR_VPROPBASER: 指向 Guest 的 LPI 属性表,硬件直接查询优先级和使能。
  • GICR_VGICR: 虚拟化控制寄存器,用于切换物理/虚拟中断上下文。
性能提升:Direct Virtual LPI 消除了 VM exit 的开销,使得 LPI 中断在虚拟化环境中的延迟从微秒级降至纳秒级,接近原生性能。
Direct Virtual LPI 硬件直通路径 Guest OS 直接读写 LPI 表 Redistributor (硬件) VPENDBASER / VPROPBASER Guest 物理内存 虚拟 LPI 属性表 虚拟 LPI 挂起表 Direct Virtual LPI 工作流程: 1. VMM 在 VM entry 前设置 GICR_VPENDBASER 和 GICR_VPROPBASER。 2. Guest OS 直接通过内存映射访问虚拟 LPI 表(无需 VM exit)。 3. 硬件 Redistributor 自动同步虚拟挂起表到物理中断状态。 4. 中断注入直接通过物理 IRQ 线到达 Guest,无需 VMM 模拟。

4.4 GICv4 的虚拟化增强

GICv4 在 GICv3 的基础上,进一步针对虚拟化场景进行了深度优化,主要解决 虚拟中断注入效率 和 多 vCPU 调度 问题。

GICv4 引入的关键特性:

特性 描述 优势
Direct Virtual SGI/PPI 允许 Guest 直接生成和处理 SGI/PPI,无需 VMM 干预 减少 VM exit,提升 IPI 性能
VM 中断优先级屏蔽 硬件支持基于 VMID 的中断优先级过滤 避免 VMM 软件优先级仲裁
GICR_VMCR 虚拟化 CPU 接口控制寄存器,保存 VM 的中断上下文 快速 VM 切换,无需保存/恢复大量 GIC 状态
LPI 中断的 VM 亲和性 ITS 支持将 LPI 直接路由到指定 vCPU 精准中断投递,减少 vCPU 迁移开销
兼容性注意:GICv4 硬件向后兼容 GICv3,但内核需要检测 GICD_TYPER 的 GICv4 标志位(bit 11)来启用虚拟化增强特性。如果硬件不支持,内核会回退到 GICv3 的虚拟化路径。
GICv4 虚拟化增强架构 物理硬件层 (GICv4 Distributor + Redistributor + ITS) 支持 VMID 过滤、Direct SGI/PPI、LPI 亲和性 VMM (Hypervisor) - 仅初始化时配置 VMID 和路由表 Guest OS (vCPU 0 ... vCPU N) 直接访问 GICR_V* 寄存器,硬件自动完成中断注入 GICv4 关键改进: • 运行时零 VM exit 中断处理路径,硬件直接完成中断投递和 EOI。

4.5 内核初始化中的 GICv3/v4 配置要点

在 Linux 内核启动流程中,中断控制器初始化阶段需要针对 GICv3/v4 执行以下关键步骤:

  1. 检测 GIC 版本: 读取 GICD_PIDR2 寄存器,确认是 GICv3 还是 GICv4。
  2. 枚举 Redistributor: 通过 GICD_TYPER 获取 Redistributor 基地址和步长,为每个 CPU 初始化 GICR 页面。
  3. 配置 LPI 内存表: 分配非缓存、对齐到 64KB 的物理内存,设置 GICR_PROPBASER 和 GICR_PENDBASER。
  4. 启用 LPI: 设置 GICR_CTLR.EnableLPI = 1。
  5. ITS 初始化: 如果存在 ITS,则初始化 ITS 设备,建立设备 ID 到 LPI 中断号的映射表。
  6. 虚拟化支持: 如果支持 GICv4,则初始化 GICR_VPENDBASER 等虚拟化寄存器,并注册虚拟中断处理函数。
关键代码路径:
// drivers/irqchip/irq-gic-v3.c
static int __init gic_init_bases(void __iomem *dist_base,
                                 struct redist_region *rdist_regs,
                                 u32 nr_redist_regions,
                                 void __iomem *cpu_base)
{
    // 1. 检测 GIC 版本
    u32 typer = readl_relaxed(dist_base + GICD_TYPER);
    
    // 2. 遍历 Redistributor 区域
    for (i = 0; i < nr_redist_regions; i++) {
        // 初始化每个 Redistributor
        gic_populate_rdist(rdist_regs[i].phys_base);
    }
    
    // 3. 设置 LPI 内存表
    gic_dist_init();
    gic_cpu_init();
    
    // 4. 初始化 ITS(如果存在)
    if (typer & GICD_TYPER_ESPI)
        its_init(typer);
    
    // 5. 启用虚拟化支持
    if (gicv4_enabled())
        gic_v4_init();
}

通过以上机制,GICv3/v4 为现代 ARM 服务器和虚拟化平台提供了高性能、可扩展的中断管理能力,Linux 内核的初始化代码必须精确适配这些硬件特性,才能保证系统稳定高效运行。