中断处理和GIC-V2
中断引脚:
在 ARM 体系结构里,中断是属于异步异常的一种,其处理过程与异常处理很类似。
ARM64 处理器有两个与中断相关的引脚——nIRQ 和 nFIQ(如下图所示)。这两个引脚直接连接到 ARM64 处理器内核上。ARM 处理器把中断请求分成普通 IRQ(Interrupt Request)和FIQ(Fast Interrupt Request)两种。
PSTATE 寄存器里面有两位与中断相关,它们相当于 CPU 内核的中断总开关。
I:用来屏蔽和打开 IRQ。
F:用来屏蔽和打开 FIQ。
中断控制器:
随着 SoC 越来越复杂,需要支持的中断源越来越多,需要支持的中断类型也越来越多,通常 ARM64 处理器内置了中断控制器,如下图 所示。例如,GIC400
中断处理过程:
下图中,假设有一个正在运行的程序,这个程序可能运行在内核态,也可能运行在用户态:
中断处理过程如下:
(1)CPU 面对中断会自动做一些事情,例如,把当前的 PC 值保存到 ELR 中,把 PSTATE寄存器的值保存到 SPSR 中,然后跳转到异常向量表里面。
(2)在异常向量表里,CPU 会跳转到对应的汇编处理函数。对于 IRQ,若中断发生在内核态,则跳转到 el1_irq 汇编函数;若中断发生在用户态,则跳转到 el0_irq 汇编函数。
(3)在上述汇编函数里保存中断现场。
(4)跳转到中断处理函数。例如,在 GIC 驱动里读取中断号,根据中断号跳转到设备中断处理程序。
(5)在设备中断处理程序里,处理这个中断。
(6)返回 el1_irq 或者 el0_irq 汇编函数,恢复中断上下文。
(7)调用 ERET 指令来完成中断返回。CPU 会把 ELR 的值恢复到 PC 寄存器,把 SPSR 的值恢复到 PSTATE 寄存器。
(8)CPU 继续执行中断现场的下一条指令。
中断处理过程是处理器在执行程序时,突然被一个外部事件(如设备输入/输出完成、定时器到期等)打断而转而执行另一段代码的过程。下面是中断处理过程的一般步骤:
- 中断请求(Interrupt Request):外部设备或其他系统组件向处理器发送中断请求,请求处理器停止当前正在执行的任务,并执行与该中断相关的处理程序。
- 中断响应(Interrupt Acknowledge):处理器接收到中断请求后,会暂停当前执行的任务,并执行中断响应过程。在中断响应过程中,处理器会确定触发中断的设备或事件,并获取相关的中断向量或中断号。
- 保存上下文(Context Saving):在执行中断处理程序之前,处理器会保存当前任务的上下文(例如程序计数器、寄存器状态等)到堆栈或其他存储区域中,以便在中断处理结束后能够恢复到中断之前的状态。
- 中断处理(Interrupt Handling):处理器根据中断向量或中断号,跳转到相应的中断处理程序。中断处理程序是事先定义好的,用于处理特定类型的中断。它可能包括一系列的操作,如处理设备数据、更新数据结构、通知其他任务等。
- 恢复上下文(Context Restoring):在中断处理程序执行完成后,处理器会从堆栈或其他存储区域中恢复之前保存的上下文。这包括恢复程序计数器、寄存器状态等信息。
- 中断返回(Interrupt Return):处理器根据恢复的上下文信息,返回到中断发生时的执行点,继续执行之前被中断的任务。
- 清除中断标志(Clear Interrupt Flag):在中断处理结束后,处理器可能需要清除中断标志位,以便允许其他中断继续被响应。
GIC-V2:
GIC发展史:
中断状态、中断触发方式和硬件中断号:
每一个中断支持的状态有以下 4 种。
不活跃(inactive)状态:中断处于无效状态。
等待(pending)状态:中断处于有效状态,但是等待 CPU 响应该中断。
活跃(active)状态:CPU 已经响应中断。
活跃并等待(active and pending)状态:CPU 正在响应中断,但是该中断源又发送中断过来。
外设中断支持两种中断触发方式。
边沿触发(edge-triggered):当中断源产生一个上升沿或者下降沿时,触发一个中断。
电平触发(level-triggered):当中断信号线产生一个高电平或者低电平时,触发一个中断。
对于 GIC 来说,为每一个硬件中断源分配的中断号就是硬件中断号。GIC 会为支持的中断类型分配中断号范围,如
不同类型中断的作用:
SGI:SGI 通常用于多核之间的通信。GIC-V2 最多支持 16 个 SGI,硬件中断号范围为 0~15。SGI通常在Linux内核中被用作处理器之间的中断(Inter-Processor Interrupt,IPI),并会送达系统指定的 CPU 上。
PPI :每个处理器内核私有的中断。GIC-V2 最多支持 16 个 PPI,硬件中断号范围为 16~31。PPI 通常会送达指定的 CPU 上,应用场景有 CPU 本地定时器(local timer)。
SPI :公用的外设中断。GIC-V2 最多可以支持 988 个外设中断,硬件中断号范围为 32~1019。
SGI 和 PPI 是每个 CPU 私有的中断,而 SPI 是所有 CPU 内核共享的。
GIC-V2内部结构:
GIC-V2 是由两个硬件单元组成的,一个是分发器,另一个是 CPU 接口(CPU interface),如下图所示。分发器主要用来做仲裁和分发,CPU 接口是与 CPU 内核连接的模块。分发器只
有一个,是共用的,但是每个 CPU 内核有一个 CPU 接口,它们通过 nIRQ 与 nFIQ 这两个引脚和 CPU 内核连接在一起。
中断流程:
GIC 检测中断的流程如下。
(1)当 GIC 检测到一个中断发生时,会将该中断标记为等待状态。
(2)对于处于等待状态的中断,分发器会确定目标 CPU,将中断请求发送到这个 CPU。
(3)对于每个 CPU,分发器会从众多处于等待状态的中断中选择一个优先级最高的中断,发送到目标 CPU 的 CPU 接口。
(4)CPU 接口会决定这个中断是否可以发送给 CPU。如果该中断的优先级满足要求,GIC会发送一个中断请求信号给该 CPU。
(5)CPU 进入中断异常,读取 GICC_IAR 来响应该中断(一般由 Linux 内核的中断处理程序来读寄存器)。寄存器会返回硬件中断号(hardware interrupt ID)。对于 SGI 来说,返回源 CPU
的 ID(source processor ID)。当 GIC 感知到软件读取了该寄存器后,根据如下情况处理。
如果该中断处于等待状态,那么状态将变成活跃。
如果该中断又重新产生,那么等待状态将变成活跃并等待状态。
如果该中断处于活跃状态,将变成活跃并等待状态。
(6)处理器完成中断服务,发送一个完成信号结束中断(End Of Interrupt,EOI)给 GIC。
GIC 支持中断优先级抢占功能。一个高优先级中断可以抢占一个处于活跃状态的低优先级中断,即 GIC 的分发器会找出并记录当前优先级最高的且处于等待状态的中断,然后抢占当前中断,并且发送这个最高优先级的中断请求给 CPU,CPU 应答了高优先级中断,暂停低优先级中断服务,转而处理高优先级中断。总之,GIC 的分发器总会把等待状态中优先级最高的中断请求发送给 CPU。
GIC-V2寄存器:
GIC-V2 寄存器也分成两部分:一部分是分发器的寄存器;另一部分是 CPU 接口的寄存器。分发器寄存器(以 GICD_为开头)包含了中断设置和配置,如下表 所示。CPU 接口的寄存器(以 GICC_为开头)包含 CPU 相关的特殊设置,如下表所示:
分发器的寄存器:
CPU接口的寄存器:
中断路由:
GIC-V2 可以配置 SPI 外设中断的路由,如下图:
GICD_ITARGETSRn 寄存器用来配置分发器,把某个中断分发到哪个 CPU 上。
GICD_ITARGETSRn 寄存器使用 8 位来表示一个中断源,每位代表一个 CPU 编号,因为 GIC-V2 控制器最多支持 8 个 CPU。
某个中断源的位被设置,说明该中断源可以路由到这些位对应的 CPU 上。
前 32 个中断源的路由配置是硬件配置好的,它们是为 SGI 和 PPI 中断准备的,软件不能配置路由。
第 33~1019 号中断可以由软件来配置其路由。