ARM GIC(三) GIC V3 Handling Interrupts学习笔记。

目录

Handling Interrupts

5.1 What happens when an interrupt becomes pending(中断什么时候变成挂起状态)

当中断被挂起时,中断控制器决定是否发送中断连接到其中一个已连接的PE。中断控制器选择的PE(如果有)取决于以下设置:
ARM GIC(三) GIC V3 Handling Interrupts学习笔记。

5.2 Interrupt acknowledge(中断响应)

CPU接口有两个IAR。读取IAR返回INTID在典型的中断处理程序中,处理中断的第一步是获取IAR。
ARM GIC(三) GIC V3 Handling Interrupts学习笔记。

5.3 Spurious interrupts(伪中断)

1020到1023是为特殊目的保留的。这些INTID可以通过读取IAR返回,并指示异常处理中的特殊情况。
ARM GIC(三) GIC V3 Handling Interrupts学习笔记。

在下面的例子中,一个移动系统有一个调制解调器中断,它向来电发出信号。此中断将由REE业务操作系统在非安全状态下处理。
ARM GIC(三) GIC V3 Handling Interrupts学习笔记。
1、当PE在安全EL1上 TEE 安全操作系统工作时,此时中断变为挂起。中断被配置为非安全组non-secure Group1时,它将作为FIQ发出信号。此时EL3 SCR_EL3.FIQ==1,IRQ == 0。

2、在EL3执行的安全监控软件读取IAR,IAR返回1021。1021指示中断预计将在非安全状态下处理,那么EL3需要执行必要的上下文切换操作。

3、现在PE处于非安全状态,中断被作为IRQ发出信号,并被带到非安全状态EL1将由REE操作系统异常向量表中处理。

上图中示例中,非安全组1中断将会导致CPU立即退出安全操作系统。这可能并不总是需要或想要的。图11显示了一个替代模型,其中中断最初用于保护EL1,也是目前TF-A主流推荐配置。

ARM GIC(三) GIC V3 Handling Interrupts学习笔记。

1、当PE在安全EL1上执行受信任的操作系统时,调制解调器中断变为挂起。当调制解调器中断被配置为非安全组1时,它将作为FIQ发出信号。但是此时SCR_EL3.FIQ==0,采取异常以保护EL1。
2、受信任的操作系统执行完其内部所有操作之后。当它准备好时,TEE-OS系统将使用SMC指令,切回非安全状态,此时CPU回到REE系统中
3、SMC例外情况适用于EL3。在EL3执行的安全监视器软件执行以下操作:必要的上下文切换操作。
4、现在PE处于非安全状态,中断被作为IRQ发出信号,并被路由到当前层级异常向量表中处理。

5.4 Running priority & preemption(优先级和抢占)

PMR设置中断必须转发到特定PE的最小优先级。这个GICv3体系结构具有运行优先级的概念。当PE确认一个中断时,它的运行优先级变成了中断的优先级。当PE写入时,运行优先级返回到它以前的值其中一个EOI寄存器。当前运行优先级报告在CPU接口的运行优先级寄存器中(ICC_RPR_EL1)。在考虑抢占时,运行优先级的概念很重要。抢占发生在优先级中断被发信号给已经在处理低优先级中断的PE。先发制人为软件带来了一些额外的复杂性,但它可以防止低优先级中断阻塞处理更高优先级的中断。

在 distributor 和 CPU interface 中都存在中断优先级的概念,高优先级的中断信号自然是优先处理,优先处理分为两种概念,一种是抢占优先,高优先级的中断可以直接抢占低优先级中断执行,另一种是并发的优先级,也就是当两个中断同时触发时,才会使用根据优先级来判断谁先执行,而如果低优先级中断正在处理,也只能等着。

在 gic 中,优先级由 8 位表示,总共 256 个等级,具体的实现可以不使用这么多,最少可以使用 4 bits 来表示,即 16 个优先级,优先级值的 value 越低,对应的执行优先级越高,因此,优先级为 0 总是表示最高优先级的中断,而最低优先级对应的 value,根据使用的优先级 bits 而定。

在 gic 中,存在两种类型的优先级,一种就是上面说的每个中断源都可配置为一个静态的中断优先级(由 distributor 管理),这个优先级可通过软件动态地修改,另一种是 CPU interface 的 runtime 优先级,即当前正在处理的优先级,当前中断的优先级高于正在处理的优先级时,中断才会将当前中断发送到 CPU 核,不同优先级的中断之间可以配置抢占,也就是在 CPU 正在处理低优先级中断时,再次发送一个高优先级中断,不过发送归发送,具体的实现中 CPU 是不是会受理该新的中断,根据实现而定,比如 linux 中,即使配置 gic 支持抢占,也会因为 linux 处理中断时会关闭中断而禁止中断的抢占。

在 GIC 的初始化阶段就已经为每个外部中断初始化了优先级,在外部中断传递的过程中,优先级和抢占的功能体现在下面的情况中

  • distributor 总是会将最高优先级的外部中断传递给 CPU interface,而 CPU interface 会维护当前正在处理或者正在 assert CPU IRQ 线的中断优先级。

  • CPU interface assert CPU 的 IRQ 线,CPU 再通过寄存器的读操作获取外部中断 ID.但是,在 CPU 的 IRQ 线被 assert 到 CPU 读取寄存器的过程中,产生了更高优先级的中断,会发生什么? distributor 会将新发生的高优先级的中断传递给 CPU interface,因此执行到 CPU 读取中断 ID 寄存器时,读取的也就是新中断的 ID 号,这种情况下,实现了高优先级中断的优先处理,从 CPU interface 的角度来说,这算是中断抢占,因为旧的中断已经提交,而新的高优先级中断横插一脚,而对于 CPU 来说,这并不算中断抢占,毕竟 CPU 还没有真正开始执行旧的中断,算是合理的优先级排序,毕竟抢占的概念通常用在正在执行的对象上。

  • 当 CPU 读取中断 ID 寄存器时,同时意味着向 CPU interface ack 了该中断,并开始处理中断,如果这时候产生了更高优先级的中断,此时这个中断在满足以下条件的情况下就会抢占当前的中断。

  • GIC 配置中断可抢占,中断的优先级配置中可以对中断进行分组,目前支持两个组:grp0 和 grp1,grp0 通常是针对 secure 模式,grp0 的中断可以传递搭配 CPU 的 IRQ 和 FIQ 线,而 grp1 通常应用在 nonsecure 模式,GIC 在初始化的时候可以为每个中断进行分组. 产生中断抢占的条件之一就是新的 pending 中断优先级要比当前 active 中断的优先级高,也就是抢占其实是以组为单位的. 在 linux 默认的 GIC 驱动中,默认运行在 nonsecure 模式,而且不使用 FIQ 引脚,因此默认是不配置抢占功能的。

  • 硬件上的抢占是一回事,软件上是否响应又是另一回事,当 CPU ack 了一个外部中断时,这时候 CPU interface 会 deassert CPU IRQ 中断线,如果产生了中断抢占的条件 ,新的中断就会重新 assert CPU IRQ 线,对于 arm 来说,在中断处理的时候关闭了 CPU 的中断,即使 IRQ 被重新 assert 也不会理会,当然, linux 系统是完全开放的,如果你是一个有想法又技术不错的 hacker,完全可能通过自行配置在 linux 中支持中断抢占。

5.5 End of interrupt(中断处理)

当中断已被处理时,软件必须通知中断控制器中断已被处理已被处理,以便状态机可以转换到下一个状态。当 CPU 中断处理完之后,需要将中断处理完的消息通知 GIC,这个消息通知包含两个部分

  • 优先级下降 —— 降低 CPU interface 的上报优先级
    这意味着将运行优先级降低到中断之前的值。

  • 失活 —— deactivate 处理完的中断,切换中断的状态
    这意味着更新当前正在处理的中断的状态机。通常是这样将从活动状态转换为非活动状态。

在GICv3体系结构中,优先级下降和停用可以同时发生,也可以单独发生。这是由ICC的设置决定。ICC_CTLR_ELn.EOImode。
ARM GIC(三) GIC V3 Handling Interrupts学习笔记。
在具体的实现中,这两个过程可配置为统一操作和分开操作的模式,由寄存器 GICC_CTLR 的 EOI 配置位进行配置,在 GIC 中被称为 EOI(end of interrupt) mode,当 EOI mode 被使能时,写寄存器 GICC_EOIR 将会触发中断的 priority drop,而 deactivate 操作需要通过写 GICC_DIR 寄存器实现,当 EOI mode 被 disable (对应上图中EOImode = 0)时,直接写 GICC_EOIR 将会同时触发 priority drop 和中断的 deactivate。

中断如何上报呢?

前面几篇博文主要结合手册梳理了中断一些基础概念,以及中断路由,中断嵌套的概念。下面以一个常见的低电平触发的中断为例,下面是一个中断的完整上报流程

  • 外设产生中断,拉低连接到 gic 的中断线,gic 中对应中断线的 pending bit 被置位,表示中断产生,这是 gic 的硬件自动完成的。

  • 如果该中断和全局中断传递使能,distributor 根据该中断设置的 CPU mask 将中断传递到对应的单个或多个 CPU interface,一般来说,只会将中断传递给一个 CPU 处理,如果为一个中断设置多个 target CPU,中断源的触发会导致多个 CPU 的 IRQ 引脚被拉低从而进入中断模式,实际上又只会有一个 CPU 处理该中断。但是在 GIC 手册中,是支持这种做法的,也就是一个中断源配置多个 target CPU(1-N中断),第一个 CPU ack 了该中断之后,后续的 CPU 查询中断 ID 寄存器返回无效的中断号 1023,退出中断处理,尽管我暂时没见过哪些中断需要这么干,但这种做法确实是被允许的。 如果存在多个中断,distributor 会将最高优先级的中断发送给 CPU interface。

  • CPU interface 决定是否将中断发送给 CPU 核,也就是触发对应 CPU 的 irq 引脚,取决于中断传递是否使能以及中断是否有足够的优先级,如果 CPU 当前没有正在处理中断,自然是可以传递的,如果 CPU 正在处理中断,那就看新中断的优先级是否更高同时还取决于是否配置了中断的抢占

  • 在 arm linux 中,CPU interface 将中断信号传递给 CPU,实际上就是 assert CPU 的 IRQ 中断线,如果 CPU 没有屏蔽 irq 中断线,就会跳转到中断模式,处理中断,经过一系列的现场保存工作等准备工作之后,CPU 会读取 GICC_IAR 寄存器获取中断 ID,读这个寄存器也意味着 CPU ack 了该中断,该中断在 gic 中记录的状态切换为 active and pending,因为即使 CPU ack 了中断,当前中断线的电平并没有被拉高,还是处于触发状态。 如果是边沿触发的中断,在 CPU ack 了之后,就会切换为 active 状态

  • 系统的中断代码会调用到用户自定义的中断处理程序,在自定义处理程序中,用户需要将该外部中断 deassert,也就是将中断线重新置为高,该 gic 的状态也就从 active and peding 切换为 active,如果没有这一个步骤,中断会被反复触发。

  • 中断处理完之后就是收尾工作,在这阶段,CPU 需要告诉 GIC 当前中断的处理已经完成,把当前中断源状态设置为 inactive,同时将 CPU interface 的优先级降低,linux 内核中也会重新开启中断(操作 CPSR 的 i bit),gic 也就可以重新发送其它的 pending 中断到 CPU 上了。

这是一个 SPI 中断的处理流程,对于 SGI 来说,不同之处在于:

  • 中断是软件触发的,通过向对应的寄存器中写入需要触发的中断号(0-15)以及对应的 CPU ID。
    distributor 并不再需要通过读取 CPU mask 寄存器来决定该中断需要发送给哪个 CPU,SGI 的 target CPU 在触发中断时已经决定了。

  • SGI 中断可以发送给多个 CPU,而且经常作为 CPU 之间通信的方式,各个 CPU 之间处理 SGI 中断是独立的,比如对于 8 号 SGI 中断,每个 CPU 都可以独立处理,而且在处理的过程中,每个 CPU 对应该中断的中断状态也是独立的。

上一篇:SystemVerilog学习笔记3——接口、采样、测试、调试


下一篇:usb驱动开发8——配置描述符