一、参考连接
gic-v3相关原理可参考https://zhuanlan.zhihu.com/p/520133301
本文主要通过开源测试工具kvm-unit-tests,针对GIC的中断进行一系列验证,这样可以直入中断底层,熟悉整个原理。
kvm-unit-tests官网为kvm-unit-tests / KVM-Unit-Tests · GitLab
armv8寄存器介绍官网为Documentation – Arm Developer
二、中断验证
GIC的中断主要为四类,即SGI(IPI)、PPI、SPI、LPI,本文主要针对这四类进行验证。具体原理将在验证中随意阐述。总体来看kvm-unit-tests源码的理解难度要低于kernel源码,所以方便引用,其实底层逻辑其实是一样的。
GIC总计结构:
GIC中断分布:
中断类型 | 中断号 | 说明 |
SGI | 0 - 15 | |
PPI | 16 – 31 | |
SPI | 32 - 1019 | |
特殊中断号 | 1020 - 1023 | |
保留中断号 | 1024 - 1055 | |
扩展PPI | 1056 -1119 | GICv3.1版本后才支持 |
保留中断号 | 1120 - 4095 | |
扩展SPI | 4096 - 5119 | GICv3.1版本后才支持 |
保留中断号 | 5120 - 8191 | |
LPI | 8192 - | 最大支持的中断号由实现确定 |
先来整体看下一个机器中断的整体分布,我是在VM中:
2.1 SGI(software generated interrupt)(IPI)[0-15] 中断验证
该类型中断并没有实际的物理连线,而是由软件通过写寄存器方式触发,它只支持边沿触发。通常用于处理器之间的通信,如linux内核电源管理模块中调用的ipi中断就是通过SGI实现的。
IPI即核间中断,arm的叫sgi。
kvm-unit-tests(本文以后简称k-u-t)中正好有专门的SGI检测项,可以通过总体测试然后log输出,也可以单独加参数测试验证:
2.1.1中断发送
发送中断的本质其实,从软件层面来讲,其实就是写寄存器。发送SGI中断的步骤归纳如下:
- 通过GICR_ISENABLER0寄存器设置中断使能
- 通过GICR_IPRIORITYR<n>设置优先级
- 写寄存器 GICD_SGI1R
- 写寄存器 ICC_SGI1R_EL1
kut中关于发送sgi中断的函数是lib/arm/gic-v3.c中的gicv3_ipi_send_mask()函数(可类比kernel-5.10中的gic_ipi_send_mask函数),
再看下写入到寄存器ICC_SGI1R_EL1中的值,以及spec中规定的ICC_SGI1R_EL1各字段含义:
嗯,只能说是一模一样。所以这就是软件层面发送sgi中断的最终操作,剩下的就是硬件层面的事了。
类比kernel-5.10中sgi中断发送代码的调用路径是:
gic_ipi_send_mask-->gic_send_sgi-->gic_write_sgi1r
其实除了逻辑更复杂了,整体框架都一样。
2.1.2中断接收处理
接收中断的处理函数是arm/gic.c中的irq_handler(),这个函数基本上总结了所以中断handler的模板:
所以中断处理的一般步骤就是:
- 读取iar寄存器,获取到描述irq的结构体irq_data;
- 通过irq_data获取到irq_num;
- 具体的中断处理;
- 回写eoi寄存器,中断结束。
kut因为只是模拟测试,所以中断handler比较简单,具体的sgi中断处理只是简单的记录信息,如ack[cpuid]++;表示本cpu接收到了中断。
2.1.3kut中sgi测试总体逻辑
由于讲述中断,所以直接开讲的中断发送以及中断处理,其实是倒叙看源码了。正序应该是类似gdb似的从头开始。kut中每个test-case都有一个main函数,当我们独立测试某一项时,如测试gic的sgi中断,当我们命令行输入-append ipi时,就已经进入到sgi测试的主入口了,程序逻辑依次是:
至此,SGI中断的逻辑以及原理差不多可以了,反正就是触发SGI中断就写ICC_SGI1R_EL1寄存器,接收中断handler就是按标准步骤操作寄存器:读取iar,获取irq,逻辑处理,回写eoi。
2.2 PPI(private peripheral interrupt)[16-31] 中断验证
该类型中断是每个处理器私有的,即一个特定的中断只会被路由到特定的处理器上。且其同一个中断号在每个处理器上都可以有不同的中断,如对于一个拥有两个PE的smp系统,中断号16的PPI中断可以分别被注册为PE0和PE1的私有中断,它们可以被独立触发并被特定的PE独立处理.
kut中没有显示测试ppi的用例,但其中有个timer的测试用例,通过/proc/interrupt可以看出,timer的中断其实就是ppi的中断,毕竟每个cpu都会独立收到timer。所以直接测试kut中的timer一项即可,其中的打印为我自己加的补丁,可以看到timer的irq正好属于[16-31]这个PPI区间。
2.2.1中断发送
发送PPI中断的步骤归纳如下
- 通过GICR_ISENABLER0寄存器设置中断使能
- 通过GICR_IPRIORITYR<n>设置优先级
- 写相应的寄存器(如timer的)
同样的,timer也有一大堆寄存器来控制,包括使能ctl、读写比较值cval、读写时间值tval、读count等。
kut中关于timer中断的发送