OP-TEE驱动与安全中断处理协同调试 - 第4节

4.1 中断模型概述:安全中断 vs 普通中断

在 OP-TEE 系统中,ARM GIC(Generic Interrupt Controller)将中断分为三类:安全中断(Secure Interrupt)、普通中断(Non-secure Interrupt)和信任中断(TrustZone Interrupt)。理解这三者的区别是进行安全中断处理协同调试的基础。

中断类型 目标安全状态 路由目标 典型来源 调试关注点
安全中断(S) 安全世界(Secure World) OP-TEE 内核 / 安全驱动 安全定时器、安全外设、TEE 内部事件 中断是否被安全 GIC 正确捕获,FIQ 处理路径是否完整
普通中断(NS) 非安全世界(Normal World) Linux 内核 / 普通驱动 网卡、USB、MMC、普通定时器 中断是否被正确路由到非安全侧,IRQ 处理是否被安全中断抢占
信任中断(TrustZone) 安全世界(Secure World) OP-TEE 安全监控器(Monitor) 安全监控调用(SMC)、安全电源管理 中断是否触发 Monitor 切换,SMC 处理是否完整
调试提示:在调试初期,建议先确认 GIC 配置寄存器中每个中断的 Group 和 Security 位是否正确设置。一个常见的错误是将安全中断误配为 Group 0(FIQ)但 Security 位未置位,导致中断被路由到非安全世界。

4.1.1 中断路由与 GIC 控制器架构

GICv2/v3 通过 Distributor 和 CPU Interface 两个逻辑单元实现中断路由。安全世界拥有独立的 GIC 配置视图,非安全世界无法修改安全中断的配置。

核心概念:GIC 的 GICD_IGROUP 和 GICD_ISENABLER 寄存器共同决定中断的安全属性。当 Group = 0 且 Security = 1 时,中断为安全 FIQ;当 Group = 1 且 Security = 0 时,中断为非安全 IRQ。
GIC 中断路由架构图 安全外设中断 普通外设中断 SMC 信任中断 GIC Distributor GICD_IGROUP / GICD_ISENABLER 中断优先级 / 目标 CPU 安全视图(Secure View) 非安全视图(NS View) 中断分组(Group 0/1) CPU Interface GICC_CTLR / GICC_PMR 中断确认(IAR) 中断结束(EOI) 安全 CPU 接口 非安全 CPU 接口 CPU Core(安全/非安全) 中断路由规则: • 安全中断(Group 0)→ 安全 FIQ → OP-TEE 安全中断处理 • 普通中断(Group 1)→ 非安全 IRQ → Linux 中断处理

4.2 安全中断处理流程

当安全中断触发时,ARM Core 进入 FIQ 模式,OP-TEE 内核接管控制权。整个处理流程分为三个阶段:中断捕获 → 中断分发 → 中断完成。

4.2.1 安全中断处理步骤

  1. 中断触发:安全外设向 GIC Distributor 发送中断信号。
  2. GIC 路由:Distributor 根据 GICD_IGROUP 和 GICD_ISENABLER 判断中断为安全中断,将其路由到安全 CPU 接口。
  3. FIQ 异常:CPU Interface 向 Core 发送 FIQ 信号,Core 切换到 FIQ 模式,跳转到 monitor_fiq_handler。
  4. 中断确认:OP-TEE 读取 GICC_IAR 寄存器获取中断 ID,确认中断源。
  5. 中断处理:根据中断 ID 查找安全中断处理函数表,执行对应的安全驱动处理逻辑。
  6. 中断结束:处理完成后写入 GICC_EOIR 寄存器,通知 GIC 中断处理完毕。
  7. 返回:执行 eret 指令,从 FIQ 模式返回被中断的上下文。
调试陷阱:在步骤 4 中,如果 GICC_IAR 返回的中断 ID 为 1023(伪中断),说明 GIC 配置有误或中断已被其他 CPU 处理。此时应检查 GICD_ITARGETSR 寄存器是否将中断正确路由到当前 CPU。

4.2.2 安全中断处理代码路径

OP-TEE 中安全中断处理的核心代码位于 core/arch/arm/kernel/thread.c 和 core/arch/arm/kernel/thread_a64.S。以下为关键处理路径:

// 安全中断 FIQ 入口(汇编)
FUNC thread_fiq_handler , :
    /* 保存上下文 */
    sub sp, sp, #THREAD_CTX_SIZE
    store_xregs sp, THREAD_CTX_X0, x0-x29, x30

    /* 读取中断 ID */
    mrs x0, gicc_iar
    and w0, w0, #0x3FF

    /* 调用 C 层处理函数 */
    bl itr_handle

    /* 写 EOI 结束中断 */
    msr gicc_eoir, x0

    /* 恢复上下文并返回 */
    load_xregs sp, THREAD_CTX_X0, x0-x29, x30
    add sp, sp, #THREAD_CTX_SIZE
    eret
END_FUNC thread_fiq_handler

4.3 普通中断与安全中断的协同

在混合系统中,安全中断和普通中断可能同时发生。OP-TEE 通过 中断优先级 和 中断屏蔽 机制实现协同处理。

场景 当前 CPU 状态 中断到达 处理行为
场景 1 非安全世界(Linux) 安全中断 立即触发 FIQ,切换到安全世界处理,处理完成后返回非安全世界
场景 2 安全世界(OP-TEE) 普通中断 OP-TEE 屏蔽 IRQ,安全中断处理完成后,通过 SMC 返回非安全世界处理
场景 3 非安全世界(Linux) 普通中断 Linux 直接处理 IRQ,不涉及安全世界切换
场景 4 安全世界(OP-TEE) 安全中断 OP-TEE 直接处理 FIQ,不涉及世界切换
安全中断与普通中断协同处理时序 时间 Linux 运行 安全中断 OP-TEE 处理 FIQ 普通中断 屏蔽 IRQ 继续处理 FIQ SMC Linux 处理 IRQ 协同处理关键点: • 安全中断具有最高优先级,可以抢占非安全世界的普通中断处理 • 安全世界处理期间,普通中断被硬件屏蔽(IRQ mask),直到返回非安全世界

4.4 GIC 控制器配置实战

在 OP-TEE 启动过程中,安全中断的 GIC 配置由 core/drivers/gic.c 中的 gic_init() 函数完成。以下为关键配置步骤:

4.4.1 安全中断配置流程

  1. 禁用 Distributor:写入 GICD_CTLR 寄存器,清除 Enable 位。
  2. 配置中断安全属性:遍历所有中断,设置 GICD_IGROUP 寄存器,将安全中断设为 Group 0。
  3. 配置中断优先级:写入 GICD_IPRIORITYR 寄存器,安全中断优先级通常设为最高(0x00)。
  4. 配置中断目标 CPU:写入 GICD_ITARGETSR 寄存器,将安全中断路由到当前 CPU。
  5. 使能中断:写入 GICD_ISENABLER 寄存器,使能安全中断。
  6. 配置 CPU Interface:设置 GICC_CTLR 寄存器,使能 FIQ 信号。
  7. 使能 Distributor:写入 GICD_CTLR 寄存器,设置 Enable 位。
// OP-TEE GIC 初始化代码片段
void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base)
{
    unsigned int i;

    /* 1. 禁用 Distributor */
    io_write32(gd->gicd_base + GICD_CTLR, 0);

    /* 2. 配置所有中断为安全中断(Group 0) */
    for (i = 0; i < gd->max_it; i += 32) {
        io_write32(gd->gicd_base + GICD_IGROUP(i / 32), 0);
    }

    /* 3. 设置安全中断优先级为最高 */
    for (i = 0; i < gd->max_it; i += 4) {
        io_write32(gd->gicd_base + GICD_IPRIORITYR(i / 4), 0x00000000);
    }

    /* 4. 配置中断目标为 CPU0 */
    for (i = 0; i < gd->max_it; i += 4) {
        io_write32(gd->gicd_base + GICD_ITARGETSR(i / 4), 0x01010101);
    }

    /* 5. 使能 CPU Interface,允许 FIQ */
    io_write32(gd->gicc_base + GICC_CTLR, GICC_CTLR_ENABLEGRP0);

    /* 6. 使能 Distributor */
    io_write32(gd->gicd_base + GICD_CTLR, GICD_CTLR_ENABLEGRP0);
}
调试检查点:在 gic_init() 执行后,可以通过读取 GICD_IGROUP 寄存器验证安全中断配置。如果某个中断的对应位为 0,表示该中断为安全中断(Group 0);如果为 1,则为非安全中断(Group 1)。

4.5 中断调试常用方法

在协同调试过程中,以下方法可以帮助快速定位中断处理问题:

  • GIC 寄存器转储:在 OP-TEE 控制台执行 gic dump 命令,查看所有中断的配置状态。
  • 中断计数统计:在 itr_handle() 函数中添加计数器,统计每种安全中断的触发次数。
  • FIQ 延迟测量:使用 ARM Generic Timer 测量从中断触发到 FIQ 处理函数入口的延迟。
  • 中断嵌套检测:在 FIQ 处理函数入口和出口设置断点,检查是否存在中断嵌套。
中断调试检查清单 检查 GICD_IGROUP 确认中断分组正确 检查 GICC_IAR 确认中断 ID 有效 检查 GICC_EOIR 确认 EOI 写入 检查中断优先级 GICD_IPRIORITYR 检查中断使能状态 GICD_ISENABLER 检查 CPU 掩码 GICD_ITARGETSR
实战建议:在调试初期,建议使用 printk 在 itr_handle() 函数中打印每次中断的 ID 和时间戳。这可以帮助你快速建立中断触发模式,识别异常中断或丢失的中断。