baron · 2 天前

Arm gicv2 和 gicv3 的中断模型总结

GICV2

1、gicv2 的中断模型

在 SOC 中,中断产生后,怎么讲信息发送给 CPU 的呢,如下图所示,画了一个简要说明:

image.png

ARM CORE 只有 4 根线用于接受中断,nIRQ、nFIQ、nvIRQ、nvFIQ. 未 enable hypevisor 时,我们只看 nIRQ、nFIQ 就可以了;
SOC 中的所有中断都接到 gic 上,然后 gic 再输出 nIRQ、nFIQ、nvIRQ、nvFIQ 四根信号给 ARM core;
ARM CORE 在收到中断信号后,会通过 AXI 总线去读写 GIC 的寄存器(软件上是通过 memory-map 的方式去读写),继而获取是哪个中断号产生的中断。

当一个 device 需要使用中断,该 device 会输出一个中断信号线,该中断线会接到 gic 上,gic 中的 cpu interface 组件中的 GICC_IAR 寄存器就会跟着发生变化, 例如:
指纹模组产生了一个中断,该中断线接到了 gic 上,gic 收到该中断后,相应的 GICC_IAR 寄存器就会发送变化。这些都是由 ASIC 来设计的,例如我们由一个指纹中断,中断号是 59,所谓的该中断号是 59,其实就是该引脚产生中断信号后,相应的 GICC_IAR 寄存器会被写入 59 数值。

2、gicv2 寄存器

(1)、Distributor register

image.png

(2)、CPU interface register

image.png

3、中断处理的过程

在中断产生后,ARM Core 接收到 IRQ 或 FIQ 信号会跳转到 ARM 的 IRQ/FIQ 异步异常向量表.
该向量表是在 linux kernel 开机初始化时设置,在该向量表的处理中,会跳转到 gicv2 的 hander 函数:gic_handle_irq()

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqstat, irqnr;
    struct gic_chip_data *gic = &gic_data[0];
    void __iomem *cpu_base = gic_data_cpu_base(gic);

    do {
        irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
        irqnr = irqstat & GICC_IAR_INT_ID_MASK;

        if (likely(irqnr > 15 && irqnr < 1021)) {
            if (static_key_true(&supports_deactivate))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            handle_domain_irq(gic->domain, irqnr, regs);
            continue;
        }
        if (irqnr < 16) {
            writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            if (static_key_true(&supports_deactivate))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
            /*
             * Ensure any shared data written by the CPU sending
             * the IPI is read after we've read the ACK register
             * on the GIC.
             *
             * Pairs with the write barrier in gic_raise_softirq
             */
            smp_rmb();
            handle_IPI(irqnr, regs);
#endif
            continue;
        }
        break;
    } while (1);
}

我们剖析一下这段代码:

irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);  
irqnr = irqstat & GICC_IAR_INT_ID_MASK;  
// GIC_CPU_INTACK 是 0X0C,对应 gicv2 文档中的 GICC_IAR,也就是读取gic 中的物理中断号

if (likely(irqnr > 15 && irqnr < 1021)) // 如果是 PPI/SPI 中断,则执行 handle_domain_irq()

if (irqnr < 16) // 如果是 SGI 中断,则会执行后面的 handle_IPI()

GICV3

1、gicv3 的中断模型

image.png

在 gicv3 中,发生了很大的变化,原 gicv2 组件中的 cpu interface 移到了 ARM Core 中,
ARM Core 通过系统寄存器的方式访问 cpu interface 寄存器(msr/mrs 指令),不再是 memory-map 方式了。
当然了,访问 destributor/redestributor 还是通过 memory-map 方式.
gic 同 ARM Core 的接口,也变成了 AXI stream.

当有中断进来时,gic 组件会通过 AXI Stream 传输信息给 cpu interface, cpu interface 仲裁后,再将 FIQ/IRQ 信号发送给真正的 ARM CORE.

2、gicv3 的寄存器

gicv3 有组件中:detributor、redetributor、its 再 gicv3 中, cpu interface 挪到了 ARM Core 中
gicv3 的寄存器有两种访问方式:一类是通过 memory-map 方式访问、一类是通过系统寄存器方式访问;

  • GICD 开头的寄存器是 detributor 寄存器,只能通过 memory-map 方式
  • GICR 开头的寄存器是 redetributor 寄存器,只能通过 memory-map 方式
  • GITS 开头的寄存器是 its 寄存器,只能通过 memory-map 方式
  • GICC 开头的寄存器是 cpu interface 寄存器,可以通过 memory-map 方式
  • ICC 开头的寄存器是 cpu interface 寄存器,可以通过系统寄存器方式访问 —— 对于 cpu
    interface 寄存器,我们一般采取这种访问
    image.png

3、中断处理的过程

在中断产生后,ARM Core 接收到 IRQ 或 FIQ 信号会跳转到 ARM 的 IRQ/FIQ 异步异常向量表.
该向量表是在 linux kernel 开机初始化时设置,在该向量表的处理中,会跳转到 gicv2 的 hander 函数:gic_handle_irq()

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqnr;

    do {
        irqnr = gic_read_iar();

        if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
            int err;

            if (static_key_true(&supports_deactivate))
                gic_write_eoir(irqnr);

            err = handle_domain_irq(gic_data.domain, irqnr, regs);
            if (err) {
                WARN_ONCE(true, "Unexpected interrupt received!\n");
                if (static_key_true(&supports_deactivate)) {
                    if (irqnr < 8192)
                        gic_write_dir(irqnr);
                } else {
                    gic_write_eoir(irqnr);
                }
            }
            continue;
        }
        if (irqnr < 16) {
            gic_write_eoir(irqnr);
            if (static_key_true(&supports_deactivate))
                gic_write_dir(irqnr);
#ifdef CONFIG_SMP
            /*
             * Unlike GICv2, we don't need an smp_rmb() here.
             * The control dependency from gic_read_iar to
             * the ISB in gic_write_eoir is enough to ensure
             * that any shared data read by handle_IPI will
             * be read after the ACK.
             */
            handle_IPI(irqnr, regs);
#else
            WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
            continue;
        }
    } while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}

我们剖析一下这段代码:
irqnr = gic_read_iar() 使用 mrs 指令读取 cpu interface 寄存器中的 ICC_IAR_EL1 寄存器,该寄存器记录着物理中断号;
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) 如果是 PPI、SPI、LPI 中断 if (irqnr < 16)如果是 SGI 中断,则会执行后面的 handle_IPI()

推荐阅读
关注数
9463
内容数
210
以易懂、渐进、有序的方式,深入探讨ARMv8/ARMv9架构的核心概念。我们将从基础知识开始,逐步深入,覆盖最新的架构,不再纠缠于过时技术。本系列内容包含但不限于ARM基础、SOC芯片基础、Trustzone、gic、异常和中断、AMBA、Cache、MMU等内容,并将持续更新。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息