基本逻辑
物理SGI通过写ICC_SGI0R_EL1/ICC_SGI1R_EL1/ICC_ASGI1R_EL1触发。虚拟化下,vCPU写对
应的寄存器会触发trap到KVM。GICv3/v4.0需要通过list register向vCPU注入vSGI,GICv4.1
支持vCPU在线的时候通过GITS_SGIR寄存器直接向vCPU注入vSGI。
如上的三个寄存器用来区分group和security,具体又和所在的EL级别有关系。ICC_SGI0R_EL1
表示触发一个group0的SGI,在各个特权级下,group0都是FIQ;ICC_SGI1R_EL1表示触发一个
group1的SGI,security类型和当前PE相同;ICC_ASGI1R_EL1也是触发group1的SGI,只是
security类型和当前PE不一样。
如上各个寄存器的标记SGI的目标CPU、中断号以及是否做广播。
1 | 55 48 40 39 32 27 24 23 16 15 0 |
使用affinity3/2/1以及target list标记系统里的CPU,这个和MPIDR_EL1寄存器标记CPU的
语意是一致的。IRM为1表示向所有CPU广播,IRM为0才用如上affinity/target list域段指定
目标CPU。
其中,如上寄存器的target list和MPIDR_EL1的affinity0有所不同,前者16个bit表示具体
affinity1下的16个CPU,所以GIC支持向一个affinity1下的多个CPU发SGI多播。而MPIDR_EL1
的affinity0用8bit的位宽表示一个数。这里的设计看起来是对不上的。
ICH_HCR_EL2.TC控制访问相关寄存器是否trap到EL2。
GICv3/v4.0 vSGI需要使用list register由KVM注入vCPU。todo: …
GICv4.1时,KVM通过写GITS_SGIR寄存器直接注入vSGI,GITS_SGIR使用vPEID和vINTID表示
目标vCPU和SGI中断号。KVM需要把收到的affinity3/2/1+target list转换成vPEID,然后使
用GITS_SGIR发送vSGI。
ARM KVM下,vCPU中MPIDR_EL1由vcpu_id在KVM中转换计算得到对应的MPIDR_EL1的值,KVM保
存MPIDR_EL1和vPEID的映射关系。注意,vcpu_id在每个VM里,从0开始依次编号;MPIDR是
ARM下硬件标记CPU编号的方式;vPEID是GIC视角的vCPU编号,在host上这个是vCPU的唯一编
号。
直接使用GITS_SGIR还需要哪些前置的配置?KVM配置ITS的寄存器把信息传给硬件,硬件需
要知道vCPU当前在哪个物理CPU上,才能转发给对应的GICR;GICR需要知道vCPU当前是否在线,
在线直接注入,不在线就把信息记录下,触发doorbell中断,doorbell中断处理里触发vCPU
上线响应vSGI中断。所以,这里至少需要这两个对应的前置配置,第一个配置复用VMAPP/VMOVP
等配置,其实就是在vPE table里找见对应vPE对应的GICR,第二是否在线的配置也是复用vPE
是否在线的检测,即vPE table里保存的LPI pending table是否和GICR上配置的一样。除此
之外,增加VSGI命令:
1 | VSGI vPEID, vINTID, enable, group, priority, clear |
这个命令配置一个vSGI中断的基本信息。这些基本信息是保存在LPI pending table的空余
空间里。
另外,GICv4.1的SGI直通是个半虚拟化的特性。GICD_TYPE2.nASSGIcap表示是否支持SGI直通,
guest内核驱动配置GICD_CTL.nASSGIrep可以使能这个特性。GICD_TYPE2.nASSGIcap在host读
的时候是0。kvm在模拟gicv3的时候把GICD_TYPE2.nASSGIcap暴露给了guest系统。这样guest
内核gic驱动就可以检测并配置GICD_TYPE2.nASSGIrep。
KVM代码分析
具体到代码层面,vSGI大概可以分为”数据流”和”控制流”,前者是vCPU发起一个vSGI的流程,
后者是之前为整个通路做的准备。
“数据流”即为ICC_SGI1R_EL1 trap的逻辑。
1 | kvm_handle_sys_reg |
下面看整个通路是怎么准备好的。
GIC里有很多irq_chip和irq_domain,irq-gic-v3-its.c里有:”GICv4-vpe”,”GICv4.1-vpe”,
“GICv4.1-sgi”以及”ITS”对应的chip和domain,irq-gic-v3.c里有:”GICv3”对应的chip和domain。
GICv3/ITS是非虚拟化相关的,其它类型的chip/domain都和虚拟化有关系。irq_chip封装特
定中断控制器的回调函数,irq_domain封装linux内核中断分配、中断映射等逻辑,内核给
它管理的每个中断创建一个irq_desc,其中保存逻辑中断号、irq_data以及irq_chip。
这里只看和SGI虚拟化相关的逻辑。
1 | its_init |
KVM里模拟GIC,初始化GIC的逻辑。这里会使用如上的vpe_domain_ops和sgi_domain_ops。
1 | vgic_v4_init |
vgic_irq里的host_irq获取逻辑如下,这个irq应该不是一个真实的irq,而是借助这个irq
直接调用到了GICv4.1-sgi irq_chip中的vSGI操作函数。对vSGI使能/关闭/配置优先级等
操作归根到底是在host上使用VSGI。
1 | kvm_vcpu_ioctl KVM_RUN |
SGI内核代码分析
Linux内核SGI相关逻辑可以参考这里。