如上图,为GIC里面的中断对应的状态,当没有任何中断的时候状态为inactive。外设触发中断,distributor会将状态改为pending。当ARM处理器响应中断服务(中断服务程序响应之前,由GIC驱动会产生一个ACK信号给控制器)的时候,cpu interface在收到ACK信号后,将状态改为active或者active+pending,什么情况下是active呢?比如,外部中断是一个gpio按钮产生,假如是低电平有效,当用户按下按钮时,电平变为低电平,此时在GIC上产生电信号变化,GIC将状态改为penging,如果此时,用户突然松开了手,那么cpu interface只将状态改为active。但是如果用户没有松开手,那么cpu interface则将状态改为active+pending。所以可以了解到如果外设持续产生中断信号,那么就会出现active+pending的状态。这就是为什么绝大部分外设驱动在中断服务程序里面都会有一个清除pending状态的动作,如果不清除,即使中断服务程序处理结束返回,也会继续触发新的无用中断。
另外,当中断服务程序处理结束后,通常GIC驱动还需要做一个动作叫做EOI,这个EOI一般是将两个操作合并成了一个(GICv1就是这样),但是到了GICv2,它允许将两个操作分开。那么是哪两个操作呢?即priority drop和deactivation。
priority drop叫做优先级还原,即把当前中断的优先级从running priority改为响应中断之前的优先级。比如,我们给中断配置了一个优先级5,running priority数字为0, 那么肯定0的优先级高于5。当这个优先级为5的中断被CPU响应时,CPU会临时将这个中断的优先级改为0(running priority),这样可以防止被同组内的其它高优先级抢占而破坏中断流程,影响关键数据读取。当这个中断服务处理完成后,GIC又通过EOI的步骤将这个优先级变回原来的优先级5。
deactivation叫做失效,即因为在GIC驱动ACK应答GIC控制器的时候,GIC控制器会把中断状态改为active或者active+penging,因此,这个deactivation可以将状态active改为inactive状态或者active+penging改为penging。
在GICv2当中,它允许将两个方法分开,由软件控制,通过配置GICC_CTLR寄存器的EOImode位达到目的。如果分开,软件在中断服务程序结束后,需要做两个动作:1、写EOIR寄存器,释放running优先级;2、写GICC_DIR寄存器,修改中断状态。如果不分开,那么久只需要做一个写写EOIR寄存器的动作就可以了。
中断控制器抢占实现
GIC有两种方式实现中断抢占:1、利用优先级组实现;2、利用EOI实现
EOI实现组内优先级抢占:
首先,我们提及一下,在GIC驱动ACK应答控制的时候,GIC控制器内部会自动为当前中断分配一个临时最高优先级叫做running priority,如上面所说,这个优先级因为是最高的,因此在一个中断优先级组内是不可能被抢占的,但是如果GIC驱动将EOI设置成分开模式,就可以达到组内优先级抢占的目的。如:在中断服务程序里面,在自己读完关键寄存器后,可以先设置priority drop(中断优先级会从最高优先级running priority变为自己原本的优先级),然后再去执行一些耗时动作,这样,这个中断就可以被抢占,嵌套了(依然只能是比当前优先级更高的中断来抢占)。
优先级组实现抢占:
优先级组的设计本来就是为了抢占而实现,这里不会过多说明,通常情况下,组优先级相同的中断是不能抢占同组内的中断服务的。只有组优先级高于中断服务程序所在中断的组优先级的中断才会产生抢占(不管子优先级有多高,只要相同组,就不会发生抢占(上述EOI方法除外))。
中断控制器虚拟化实现(hypervisor)
GIC控制器与hypervisor的说明,会在以后虚拟化的时候,单独讲解。
Linux的GICv2实现,以后讲解,这里不讲解。
附上一个来自于《gic_architecture_specification.pdf》的截图: