学习体系结构 - Arm 通用中断控制器 v3 和 v4
Learn the architecture - Arm Generic Interrupt Controller v3 and v4
Version 3.2
借助DeepL翻译 + 个人补充一些内容
建议提前阅读: arm 的 异常模型
1、Overview
本指南概述了 Arm 通用中断控制器 (GIC) v3 和 v4 的功能,并介绍了兼容 GICv3 的中断控制器的操作。它还介绍了如何配置 GICv3 中断控制器以便在裸机环境中使用。
Background
中断是向处理器发出的信号,表明发生了需要处理的事件。中断通常由外设产生。
例如,一个系统可能使用通用异步接收器/发送器(UART)接口与外界通信。当 UART 接收到数据时,它需要一种机制来告诉处理器新数据已经到达并准备好进行处理。UART 可以使用的一种机制是产生中断,向处理器发出信号。
小型系统可能只有几个中断源和一个处理器。然而,大型系统可能有更多潜在的中断源和处理器。GIC 执行中断管理、优先级排序和路由选择等重要任务。GIC 汇聚来自整个系统的所有中断,对其进行优先级排序,并将其发送到内核进行处理。GIC 主要用于提高处理器效率和实现中断虚拟化。
arm GIC功能:中断管理、优先级排序、路由选择等!
在 x86 架构中 ,中断控制器主要是通过两个芯片来处理的:8259A 芯片(也称为 PIC - Programmable Interrupt Controller)和 APIC(Advanced Programmable Interrupt Controller)。
对于 RISC-V 架构 ,中断控制器被称为 PLIC(Platform-Level Interrupt Controller),用于处理外部中断,它负责管理和分配中断给不同的处理器核心。
**GIC 是基于 Arm GIC 架构实现的。**该架构从 GICv1 发展到最新版本 GICv3 和 GICv4。Arm 拥有多个通用中断控制器,可为所有类型的 Arm Cortex 多处理器系统提供一系列中断管理解决方案。这些控制器的范围很广,从适用于 CPU 内核数量较少的系统的最简单的 GIC-400 到适用于高性能多芯片系统的 GIC-600。GIC-600AE 增加了额外的安全功能,适用于高性能 ASIL B 到 ASIL D 系统。
在本指南的最后,您可以检查自己掌握的知识。您将了解到不同类型的中断 ,并能编写启用 GIC 和配置这些不同中断类型的软件。
2、 Before you begin
本指南涵盖 GICv3 和 v4 的基本操作 以及共享外设中断 (SPI)、专用外设中断 (PPI) 和软件生成中断 (SGI) 的使用。
本指南是对 Arm 通用中断控制器架构规范 GIC 架构 3.0 和 4.0 版本 的补充。它不是替代品或备选方案。有关寄存器和行为的详细说明,请参阅 Arm 通用中断控制器体系结构规范 GIC 体系结构版本 3.0 和 4.0。
GICv3 和 GICv4 允许多种不同的配置和使用情况。为简单起见,本指南将集中介绍这些配置和用例中的一部分,其中包括:
-
存在两种安全状态
-
两种安全状态都启用了亲和路由( Affinity routing)选择
-
系统寄存器访问在所有异常级别都已启用
-
连接的处理器符合 Armv8-A 或 Armv9-A 标准,实现了所有异常级别,并在所有异常级别使用 AArch64
两种安全状态:即 Secure 和 Non-secure 两个安全状态,GICv3、GICv4允许中断根据其安全级别进行管理和路由
亲和路由是 GICv3 和 GICv4 中的一个特性,它允许系统更精细地控制中断传递到哪个处理器 。这是通过使用 MPIDR_EL1(多处理器亲和性寄存器)来实现的,它提供了处理器的唯一标识。
在所有这些异常级别都启用系统寄存器访问意味着无论在哪个级别,软件都可以访问 GIC 的系统寄存器。
本指南不包括:
- 传统操作(Legacy operation)
- 从使用 AArch32 的异常级别使用
本指南假定您熟悉 Arm 异常模型。如果您想了解 Arm 异常模型,可以阅读 Learn the Architecture: Exception model guide。
3、 What is a Generic Interrupt Controller?
**通用中断控制器(GIC)接收来自外设的中断,对其进行优先级排序,并将其发送到相应的处理器内核。**下图显示了一个通用中断控制器从 n 个不同的外设接收中断,并将其分配给两个不同的处理器。
GIC 是 Arm Cortex-A 和 Arm Cortex-R 配置文件处理器的标准中断控制器。 GIC 为中断管理提供了灵活、可扩展的方法,可支持从单核系统到拥有数百个内核的大型多芯片设计。
A brief history of the Arm CoreLink GIC
与 Arm 架构一样,GIC 架构也随着时间的推移而不断发展。下表总结了 GIC 规范的主要版本及其通常使用的处理器。
本指南涵盖大多数 Armv9-A、Armv8-A 和 Armv8-R 设计所使用的 Arm CoreLink GICv3 和 GICv4。
GICv3 和 GICv4 自发布以来也进行了小幅更新:
- GICv3.1 增加了对额外有线中断、安全虚拟化和内存系统资源分区与监控 (MPAM) 的支持
- GICv3.2 增加了对 Armv8-R AArch64 的支持
- GICv3.3 增加了对不可屏蔽中断的支持
- GICv4.1 扩展了虚拟化支持,涵盖直接注入虚拟软件生成中断 (SGI)
- GICv4.2 增加了对直接注入不可屏蔽虚拟中断的支持
GICv2m 是 GICv2 的扩展,增加了对消息信号中断的支持。 如需更多信息,请联系 Arm 支持部门。
4、 Arm GIC fundamentals
本节将介绍 Arm GICv3 和 v4 中断控制器的基本操作。
Interrupt types(重要!)
GIC 可处理四种不同类型的中断源:
- 共享外设中断 (SPI):可向任何连接的内核发送的外设中断
- 专用外设中断 (PPI):一个内核专用的外设中断。通用定时器中断就是 PPI 的一个例子
- 软件生成中断 (SGI):SGI 通常用于处理器间通信,由写入 GIC 中的 SGI 寄存器产生
- 本地特定外设中断(LPI) :LPI 在 GICv3 中首次引入,其编程模型与其他三种类型的中断截然不同。Arm CoreLink 通用中断控制器 v3 和 v4:本地特定外设中断指南 中介绍了 LPI 的配置
**每个中断源都有一个 ID 编号,称为 INTID。**前面列表中介绍的中断类型是根据 INTID 的范围定义的:
表中可以看到,0 - 15号 为软件生成中断 SGIs
16 - 31号 为专用外设中断 PPIs
32 - 1019号 为共享外设中断 SPIs
1020 - 1023号 为特殊中断号
1024 - 8191号 保留
8192 - greater号 为 本地特定外设中断 LPIs
How interrupts are signaled to the interrupt controller
传统上,中断信号是通过专用硬件信号从外设发送到中断控制器的,如下图所示:
Arm CoreLink GICv3 支持这种模式,但也提供了一种额外的信号机制:消息信号中断(MSI) 。如图所示,MSI 通过写入中断控制器中的寄存器进行传输:
使用消息将中断从外设转发到中断控制器,就无需为每个中断源设置专用信号。这对于大型系统的设计者来说是一个优势,因为在大型系统中,可能会有数百甚至数千个信号穿过 SoC 并汇聚到中断控制器。
中断是以 报文形式 发送还是使用 专用信号 发送,对中断处理代码处理中断的方式影响不大。可能需要对外设进行一些配置。例如,可能需要指定中断控制器的地址。这些外设配置超出了本指南的范围。
**在 Arm CoreLink GICv3 中,SPI 可以是消息信号中断。LPI 始终是消息信号中断。**不同的中断类型使用不同的寄存器,如下表所示:
GICD_SETSPI_NSR
是 ARM GIC 中的一个寄存器操作或者指令,用于设置特定的中断为非共享(Non-Secure)状态并触发(Assert)中断。
Interrupt state machine
**中断控制器为每个 SPI、PPI 和 SGI 中断源维护一个状态机。**该状态机由四种状态组成:
- Inactive:中断源当前未断言
- Pending:中断源已被断言,但 PE 尚未确认该中断
- Active:中断源已被断言,中断已被 PE 确认
- Active and Pending:一个中断实例已被确认,另一个实例正在等待处理
Inactive(未激活) :表示特定中断源当前没有发送中断信号给中断控制器。这意味着没有任何中断事件发生。
Pending(待处理) :意味着中断源已经发送了中断信号,但是处理器核心(PE)还没有确认该中断。中断控制器知道有中断请求发生了,但处理器核心尚未开始处理这个中断。
Active(激活) :指中断源已经发送了中断信号,并且处理器核心(PE)已经确认了该中断。处理器已经开始处理这个中断事件。
Active and Pending(激活且待处理) :描述了两个中断实例,其中一个已被确认,另一个实例正在等待处理。可能是在处理一个中断时,另一个中断也已经发生,但处理器还没有处理第二个中断。
LPI 的状态机更简单。更多信息,请参阅 "获取中断"。
状态机如下图所示:
中断的生命周期取决于将其配置为电平敏感型还是触发型:
- 对于电平敏感型中断,中断输入上的上升沿会导致中断挂起,并且中断会一直保持,直到外设取消中断信号。
- 对于边缘敏感型中断,中断输入上的上升沿会导致中断挂起,但中断不会保持断言状态
电平敏感型 :当中断信号的电平变化(比如上升沿)被探测到时,中断被触发并保持在挂起状态。
边缘敏感型 :当中断信号的边缘(比如上升沿)被检测到时,中断被触发但不会保持持续状态。
**当说中断源被"断言"时,意味着中断源发出了一个中断请求信号。**这个信号告诉中断控制器或处理器核心(CPU)有一个特定的事件需要被处理。在这个情况下,中断源产生了一个中断信号并通知系统需要对某个事件进行处理。
Level sensitive interrupts
下图显示了中断状态转换与中断信号的对应关系:
依次考虑每个状态转换:
- Inactive 到 Pending。当中断源被断言时,中断会从 Inactive 状态转为 Pending 状态。此时,如果中断已启用且具有足够的优先级,GIC 会向 PE 发出中断信号。
- Pending 到 Active and Pending。当处理器元件(PE)通过读取 CPU 接口 中的**中断确认寄存器(IAR)**之一来确认中断时,中断就会从 Pending 状态转换为 Active and Pending 状态。这种读取通常是中断异常发生后执行的中断处理例程的一部分。此时,GIC 会向 PE 取消中断信号。
- Acitve and Pending 到 Active。当外设取消中断信号时,中断会从 Active and Pending 状态转换为 Active 状态。这种情况通常发生在软件写入外设状态寄存器时。
- Active 到 Inactive。当 PE 写入 CPU 接口中的一个**中断结束寄存器(EOIR)**时,中断从 Active 状态转为 Inactive 状态。这表明 PE 已完成中断处理。
Pending 到 Active and Pending 是因为处理器确认中断,并且外设此时仍是高电平,被认为还在触发中断,所以到了 Active and Pending 状态而不是 Active 状态。
Edge-triggered interrupts
下图显示了中断状态转换与中断信号的对应关系:
依次考虑每个状态转换:
- Inactive 到 Pending。当中断源被断言时,中断会从 Inactive 状态转为 Pending 状态。此时,如果中断已启用且优先级足够高,GIC 会向 PE 发出中断信号。
- Pending 到 Active。**当 PE 通过读取 CPU 接口中的一个 IAR 确认中断时,中断就会从 Pending 状态转为 Active 状态。**这种读取通常是中断异常发生后执行的中断处理例程的一部分。不过,软件也可以轮询 IAR。此时,GIC 会向 PE 取消中断信号。
- Active 转为 Active and Pending。如果外设重新断言中断信号,则中断会从 Active 状态转为 Active and Pending 状态并待处理。
- Active and Pending 到 Pending。**当 PE 写入 CPU 接口中的一个 EOIR 时,中断将从 Active and Pending 状态转为 Pending 状态。**这表明 PE 已处理完第一个中断实例。此时,GIC 会重新向 PE 推送中断信号。
Target interrupts
Arm 架构为每个 PE 分配一个分层标识符,称为亲和值。GIC 使用亲和值将中断锁定在特定内核上。
亲和力是一个 32 位值,分为四个字段:
<affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0>
PE 的亲和力用 MPIDR_EL1
表示。
不同亲和度的确切含义由特定处理器和 SoC 确定。 例如,Arm Cortex-A53 和 Arm Cortex-A57 处理器使用:
<group of groups>.<group of processors>.<processor>.<core>
后来的设计,如在 Arm Cortex-A55 和 Arm Cortex-A76 处理器中使用的设计,都采用了:
<group of processors>.<processor>.<core>.<thread>
所以,要注意了,不同 SoC 的
MPIDR_EL1
寄存器读出来的内容不一样。
在单个实现中存在所有可能节点的可能性很小。例如,移动设备 SoC 的布局可能是这样的:
0.0.0.[0:3] Cores 0 to 3 of a Cortex-A53 processor
0.0.1.[0:1] Cores 0 to 1 of a Cortex-A57 processor
AArch32 状态和 Armv7-A 只能支持三级亲和度。这意味着使用 AArch32 状态的设计只能在亲和层 3(0.x.y.z)上使用一个节点。GICD_TYPER.A3V 表示中断控制器是否可以支持多个第 3 级节点。
Security model
Arm GICv3 架构支持 Arm TrustZone 技术。**必须通过软件为每个 INTID 分配 一个组 和 安全设置。**如下表所示,GICv3 支持三种设置组合:
在 ARM 架构中的中断控制器中,通常有多个中断组(Group)。这些组可以用于不同类型的中断分类和管理,而 Group 0 和 Group 1 是其中两个常见的组。
Secure Group 0:EL3的中断(安全固件)
Secure Group 1:Secure EL1 的中断(可信OS)
Non-secure Group 1:Non-secure的中断(OS 或 Hypervisor)
Group 0 中断总是作为 FIQ 发出信号。Group 1中断则根据 PE 当前的安全状态和异常级别,以 IRQ 或 FIQ 发出信号,如图所示:
Group 0 的中断总是FIQ,Group 1的中断根据当前的安全状态和异常级别,当前的是IRQ中断,其他的是FIQ中断
这些规则旨在补充 AArch64 安全状态和异常级路由控制。下图显示了简化的软件堆栈,以及在 EL0 执行时不同类型的中断信号发生时的情况:
**在本例中,IRQ 被路由到 EL1 (SCR_EL3.IRQ==0),FIQ 被路由到 EL3 (SCR_EL3.FIQ==1)。**考虑到上述规则,当在 EL1 或 EL0 执行时,当前安全状态的 Group 1 中断将被视为 IRQ。
其他安全状态的中断会触发 FIQ,并将异常带到 EL3。这样,在 EL3 执行的软件就可以执行必要的上下文切换。
在 ARM 架构中,SCR_EL3 是一个系统控制寄存器,其中的 IRQ 位是用来控制是否允许普通中断(IRQ)路由到 EL1(操作系统级别)的标志位。
当 SCR_EL3.IRQ 位设置为 0 时,这意味着在EL3级别,常规的IRQ(中断请求)不会被处理。IRQ会被路由到低于EL3的异常级别,例如EL2或EL1。
SCR_EL3 中的 FIQ 位是用来控制快速中断(FIQ)是否路由到 EL3(安全级别)的标志位。
**当 SCR_EL3.FIQ 位设置为 1 时,表示允许快速中断(FIQ)被发送到 EL3 处理。**FIQ 中断可能具有更高的优先级和特殊的处理要求。
Impact on software
在实施 Armv9-A Realm Management Extensions 时,GIC 会将 Realm 状态视为非安全状态的扩展。
在配置中断控制器时,软件可控制 INTID 到 中断组 的分配。只有在安全状态下执行的软件才能为中断组分配 INTID。
通常情况下,只有在 安全状态 下执行的软件才能访问安全中断的设置和状态:Group 0 和 Secure Group 1。
LPI 始终被视为 Non-Secure Group 1 中断。
Support for single Security state
GICv3 支持 Arm TrustZone 技术,但 TrustZone 的使用是可选的。这意味着,您可以将实施配置为单一安全状态或两种安全状态:
- GICD_CTLR.DS == 0 支持 Secure 和 Non-Secure 两种安全状态
- GICD_CTLR.DS == 1 只支持单一安全状态
配置 GIC 以使用与所连接 PE 相同数量的安全状态。通常情况下,这意味着 GIC 在连接到 Arm Cortex-A 配置文件处理器时支持两种安全状态,而在连接到 Arm Cortex-R 配置文件处理器时支持一种安全状态。
GICD_CTLR.DS 的DS为 Disable Security,用于控制中断的安全性特性。
当
GICD_CTLR.DS
被设置时(即设置为1),它会禁用GIC分发器的安全状态视图。这意味着中断将不再区分安全(Group 0)和非安全(Group 1)状态。启用
DS
位会使得所有中断都被视为非安全,无论它们原本被配置为安全还是非安全中断。
Programmer's model
GICv3 中断控制器的寄存器接口分为三组:
- Distributor interface
- Redistributor interface
- CPU interface
这些接口如下图所示:
一般来说,Distributor 和 Redistributor 用于配置中断 ,CPU interface 用于处理中断。
从这个图中,可以看出,GIC的三个组件中,CPU interface 位于处理器内部,而 Distributor 和 Redistributor 位于处理器外部,Redistributor 比较靠近处理器核心。
这也就解释了为什么下面的 Distributor 和 Redistributor 使用内存映射访问,而 CPU interface 可以使用系统寄存器访问。
Distributor
Distributor 寄存器采用内存映射(memory-mapped),用于配置 SPI。分配器提供了一个编程接口,用于:
- 中断优先级和 SPI 分配
- 启用和禁用 SPI
- 设置每个 SPI 的优先级
- 每个 SPI 的路由信息
- 将每个 SPI 设置为电平敏感型或边沿触发型
- 生成报文信号 SPI
- 控制 SPI 的 active 和 pending 状态
- 确定每个安全状态下使用的程序员模型:亲和路由或传统路由
Redistributor
每个连接的核心有一个 Redistributor。Redistributor 为以下设备提供编程接口:
- 启用和禁用 SGI 和 PPI
- 设定 SGI 和 PPI 的优先级别
- 将每个 PPI 设置为电平敏感型或边缘触发型
- 为每个 SGI 和 PPI 分配一个中断组
- 控制 SGI 和 PPI 的状态
- 控制内存中支持相关中断属性和 LPI 待处理状态的数据结构的基地址
- 为连接的 PE 提供电源管理支持
CPU interfaces
每个内核都包含一个 CPU 接口,即在处理中断时使用的系统寄存器。CPU 接口提供了一个编程接口,用于:
- 提供一般控制和配置,以实现中断处理
- 确认中断
- 执行优先级下调并停用中断
- 为 PE 设置中断优先级掩码
- 定义 PE 的抢占策略
- 确定 PE 的最高优先级待处理中断
在 Arm CoreLink GICv3 中,CPU 接口寄存器作为系统寄存器访问:ICC_*_ELn。
在使用这些寄存器之前,软件必须启用系统寄存器接口。这由 ICC_SRE_ELn 寄存器中的 SRE 位控制,其中 n 指定异常级别:EL1-EL3。
ICC 表示 Interrupt Controller Control Register(中断控制器控制寄存器) ,SRE 表示 System Register Enable(系统寄存器使能)。
对于 CPU 接口,GICv3 提供了两种访问模式:一种是通过系统寄存器(默认情况下),另一种是通过内存映射。哪种模式被使用取决于 ICC_SRE 寄存器的设置。如果 ICC_SRE 寄存器的 SRE 位被设置为 1,则使用系统寄存器模式;如果为 0,则可能使用内存映射模式。
5、 Configuring the Arm GIC
本节介绍如何在裸机环境中启用和配置兼容 GICv3 的中断控制器。有关详细寄存器说明,请参阅 Arm Generic Interrupt Controller Architecture Specification GIC architecture version 3.0 and 4。
本地特定外设中断 (LPI) 的配置与 共享外设中断 (SPI) 、 专用外设中断 (PPI) 和 软件生成中断 (SGI) 的配置有很大不同 ,超出了本指南的范围。要了解更多信息,请参阅我们的指南 Arm CoreLink Generic Interrupt Controller v3 and v4: Locality-specific Peripheral Interrupts 。
LPI(Local Peripheral Interrupts)是在 ARM GICv3 和 GICv4 中引入的一种新型中断类型,专门用于处理大量的中断源。LPI 旨在解决当中断源数量非常庞大时传统的 SPI(Shared Peripheral Interrupts)和 PPI(Private Peripheral Interrupts)的限制。
LPI 提供了一种高效的方式来管理大量中断源,特别是在那些需要处理数以千计中断源的高性能计算和虚拟化环境中。
大多数使用 GICv3 中断控制器的系统都是多核系统,也可能是多处理器系统。有些设置是全局性的,这意味着会影响所有连接的 PE。其他设置则针对单个 PE。
让我们先看看全局设置,然后再看看每个 PE 的设置。
Global settings
必须配置 Distributor 控制寄存器 (GICD_CTLR) 以启用中断组 并设置路由模式,如下所示:
- Enable Affinity routing(ARE 位) :GICD_CTLR 中的 ARE 位控制 GIC 是在 GICv3 模式还是传统模式下运行。传统模式向后兼容 GICv2。本指南假定 ARE 位设置为 1,因此使用的是 GICv3 模式
- Enables :GICD_CTLR 包含 Group 0、Secure Group 1 和 Non-secure Group 1 的单独使能位 :
- EnableGrp1S :启用 Secure Group 1 中断的分配
- EnableGrp1NS :启用 Non-secure Group 1 中断的分配
- EnableGrp0 :启用 Group 0 中断分配
Arm CoreLink GIC-600 不支持传统操作,ARE 位永久设置为 1。
当 ARE 位设置为 1 时,GIC 使用 Affinity Routing 模式。这意味着中断可以根据处理器的 Affinity 信息(包括其集群ID和处理器ID)来路由,从而支持更复杂的多核处理器拓扑。
Enables 允许对不同安全级别和组的中断进行独立的启用和控制。在 GICv3 架构中,引入这些不同的中断组是为了提供更精细的安全和优先级控制。它允许系统根据中断的安全级别和优先级进行更灵活的配置和管理。
Settings for each PE
本节涉及单个内核或 PE 的特定设置。
Redistributor configuration
每个 core 都有自己的 Redistributor,如图所示:
**Redistributor 包含一个名为 GICR_WAKER 的寄存器,用于记录所连接的 PE 是在线还是离线。只有 GIC 认为在线的 PE 才会转发中断。**重置时,所有 PE 都被视为离线。
要将连接的 PE 标记为在线,软件必须:
- 将 GICR_WAKER.ProcessorSleep 清零
- 轮询 GICR_WAKER.ChildrenAsleep,直至读数为 0
在配置 CPU 接口之前,软件必须执行这些步骤,否则会出现无法预测的行为。
当 PE 处于离线状态(GICR_WAKER.ProcessorSleep==1)时,针对 PE 的中断将导致唤醒请求信号被断言。通常情况下,该信号将传递给系统的电源控制器。然后,电源控制器打开 PE。唤醒时,PE 上的软件将清除 ProcessorSleep 位,从而允许唤醒 PE 的中断被转发。
GICR_WAKER 是 Redistributor 中的一个重要寄存器,用于控制和监视处理器的休眠状态。
ProcessorSleep
位用于控制处理器的休眠状态。当这个位被设置时,它表明与这个 Redistributor 关联的处理器核心处于休眠状态。- **在处理器休眠时,它不会响应除了特定唤醒中断之外的任何中断。**这有助于节能和降低功耗。
- 通常,操作系统在进入低功耗模式时会设置这个位,并在唤醒处理器时清除它。
ChildrenAsleep
位是一个状态位,用于指示与该 Redistributor 相关联的处理器核心或其它 Redistributor 是否处于休眠状态。- 当
ChildrenAsleep
位被设置时,表示至少有一个与该 Redistributor 关联的处理器核心或下层 Redistributor 处于休眠状态。- 这个位通常由硬件自动管理,反映了处理器核心或者 Redistributor 群组的休眠状态。
区别:
ProcessorSleep :这个位是一个控制位,由软件设置和清除,允许软件控制处理器核心的休眠和唤醒
ChildrenAsleep :这个位是一个状态位,由硬件自动更新,硬件根据系统的实际状态自动设置
CPU interface configuration
CPU 接口负责向所连接的 PE 发送中断异常。要启用 CPU 接口,软件必须配置以下内容:
-
启用系统寄存器访问
CPU 接口 (ICC_*_ELn) 部分介绍了 CPU 接口寄存器,以及如何在 GICv3 中作为系统寄存器访问这些寄存器。软件必须通过设置 ICC_SRE_ELn 寄存器中的 SRE 位,启用对 CPU 接口寄存器的访问。
-
设置优先级掩码和二进制点寄存器
CPU 接口包含优先级掩码寄存器(ICC_PMR_EL1)和二进制点寄存器(ICC_BPRn_EL1) 。优先级掩码寄存器设置中断必须具有的最低优先级,以便转发给 PE。二进制点寄存器用于优先级分组和抢占。关于这两个寄存器的使用详情,请参阅 Handling Interrupts。
-
设置 EOI 模式
CPU 接口 ICC_CTLR_EL1 和 ICC_CTLR_EL3 中的 EOImode 位 控制如何处理中断结束。有关详情,请参阅 End of interrupt。
-
启用每个中断组的信号
**在 CPU 接口将某个中断组的中断转发给处理器元素(PE)之前,必须启用该中断组的信令。**为了启用信令,软件必须对 Group 1 中断写入 ICC_IGRPEN1_EL1 寄存器,对 Group 0 中断写入 ICC_IGRPEN0_EL1 寄存器。ICC_IGRPEN1_EL1 寄存器是按安全状态分隔的。这意味着 ICC_IGRPEN1_EL1 控制当前安全状态下的 Group 1 中断 。在 EL3,软件可以通过 ICC_IGRPEN1_EL3 访问两个 Group 1 的使能寄存器。
优先级掩码寄存器(ICC_PMR_EL1):这个寄存器的值定义了一个"最低优先级"。只有优先级高于(数值更小)这个设定值的中断才会被转发给处理器。
二进制点寄存器(ICC_BPRn_EL1) :用于控制中断的优先级分组和抢占行为,通过设置二进制点,可以将优先级空间分成几个组。这允许在同一优先级组内的中断互不抢占,而不同组间的中断可以相互抢占。调整二进制点的值可以改变优先级分组的粒度,从而影响中断的抢占行为。较低的值意味着更粗粒度的分组,而较高的值提供了更细粒度的控制。
ICC_CTLR_EL1 和 ICC_CTLR_EL3 中的 EOImode 位:
- ICC_CTLR_EL1:这个寄存器主要用于控制非安全状态(Non-secure state)下的中断行为。
- ICC_CTLR_EL3:这个寄存器控制安全状态(Secure state)下的中断行为。
- EOImode 为 0 :当设置为 0 时,采用的是传统的 EOI 处理方式。在这种模式下,处理器通过对一个特定的 GIC 寄存器写入来标记中断处理的结束。这通常涉及到更新 GIC 的 EOI 寄存器。
- EOImode 为 1 :当设置为 1 时,启用了新的 EOI 处理方式,称为"优先级降低机制 "(Priority Drop mechanism)。在这种模式下,中断被标记为结束的同时,其优先级也被动态降低。这允许处理器处理嵌套中断,即在处理当前中断的同时,能够响应更高优先级的新中断。
必须启用每个中断组的信令,CPU 接口才能将该组的中断转发给 PE
尽管寄存器的名字相同(
ICC_IGRPEN1_EL1
),但根据 CPU 当前的安全状态,它实际上代表两个不同的寄存器实例。
PE configuration
还需要对 PE 进行一些配置,使其能够接收和处理中断。详细说明不在本指南的范围之内。在本指南中,我们将介绍在 AArch64 状态下执行兼容 Armv8-A 的 PE 所需的基本步骤。
-
Routing controls
**中断的路由控制位于 PE 的 SCR_EL3 和 HCR_EL2。**路由控制位决定了中断的异常级别。这些寄存器中的路由控制位在重置时具有未知值,因此必须通过软件进行初始化。
-
Interrupt masks
PE 的 PSTATE 中还有异常屏蔽位。当设置这些位时,中断将被屏蔽。这些位在复位时设置。
-
Vector table
PE 矢量表的位置由 VBAR_ELn 寄存器设置。与 SCR_EL3 和 HCR_EL2 寄存器一样,VBAR_ELn 寄存器在复位时具有未知值。软件必须设置 VBAR_ELn 寄存器,使其指向内存中相应的矢量表。
要了解有关这些步骤的更多信息,请参阅 Learn the Architecture: Exception model guide。
SPI, PPI, and SGI configuration
到目前为止,我们已经了解了中断控制器本身的配置 。现在我们将讨论各个中断源的配置。
哪些寄存器用于配置中断取决于中断类型:
- 通过Distributor使用 GICD_ 寄存器配置 SPI*
- PPI 和 SGI 通过各个Redistributor使用 GICR_ 寄存器进行配置*
下图说明了这些不同的配置机制:
只是一个索引
软件必须为每个 INTID 配置以下内容:
-
优先权:GICD_IPRIORITYn, GICR_IPRIORITYn
每个 INTID 都有一个相关的优先级,用 8 位无符号值表示 。0x00 代表最高优先级,0xFF 代表最低优先级 。运行优先级和抢占描述了 GICD_IPRIORITYn 和 GICR_IPRIORITYn 中的优先级值如何屏蔽低优先级中断,以及如何控制抢占。中断控制器不需要实现全部 8 个优先级位。如果 GIC 支持两种安全状态,则必须至少实现 5 位。如果 GIC 仅支持单个安全状态,则必须至少实现 4 个位。
-
Group: GICD_IGROUPn, GICD_IGRPMODn, GICR_IGROUPn, GICR_IGRPMODn
如安全模型所述,中断可配置为属于三个中断组之一。这些中断组分别是 Group 0 、安全Group 1 和 Non-secure Group 1。
-
Edge-triggered or level-sensitive: GICD_ICFGRn, GICR_ICFGRn
对于 PPI 和 SPI,软件必须指定中断是边缘触发还是电平敏感 。SGI 始终被视为边缘触发,因此 GICR_ICFGR0 对这些中断的处理方式为 Read-As-One, Writes Ignored(RAO/WI)。
-
Enable: GICD_ISENABLERn, GICD_ICENABLER, GICR_ISENABLERn, GICR_ICENABLERn
每个 INTID(中断标识符)都有一个使能位。设置使能寄存器(Set-enable registers)和清除使能寄存器(Clear-enable registers)消除了执行读-修改-写操作的需求。Arm 建议,在启用 INTID 之前配置本节中概述的设置。
-
Non-maskable:配置为不可屏蔽的中断比属于同一组的所有其他中断具有更高的优先级。也就是说,不可屏蔽的 Non-secure Group 1 中断的优先级高于所有其他 Non-secure Group 1 中断。
- 不可屏蔽属性是在 GICv3.3 中添加的,需要在 PE 中提供匹配支持
- 只有 Secure Group 1 和 Non-secure Group 1 中断可标记为不可屏蔽
对于裸机环境,通常无需在初始配置后更改设置。 不过,如果必须重新配置中断,例如更改组设置,则应在更改其配置前首先禁用中断。
大多数配置寄存器的重置值是由实施定义的。这意味着中断控制器的设计者决定其值是多少,不同系统的值可能会有所不同。
Arm GICv3.1 and the extended INTID ranges
Arm GICv3.1 增加了对额外 SPI 和 PPI 的 INTID 的支持。配置这些中断的寄存器与原来的中断范围相同,只是后缀为 E。例如:
- GICR_ISENABLERn - 原始 PPI 范围的启用位
- GICR_ISENABLERnE - GICv3.1 中引入的附加 PPI 的启用位
Setting the target PE for SPIs
**对于 SPI,必须配置中断目标。**这由 GICD_IROUTERn 或 GICv3.1 扩展 SPI 的 GICD_IROUTERnE 控制。每个 SPI 都有一个 GICD_IROUTERn 寄存器,中断路由模式(Interrupt_Routing_Mode)位控制路由策略。选项包括:
-
GICD_IROUTERn.Interrupt_Routing_Mode == 0
SPI 发送到 PE A.B.C.D,即寄存器中指定的亲和坐标。
-
GICD_IROUTERn.Interrupt_Routing_Mode == 1
SPI 可以发送到任何参与中断组分配的已连接 PE 。选择目标 PE 的是 Distributor,而不是软件 。因此,每次发出中断信号时,目标都会不同。这种路由类型被称为 1-of-N。
PE 可以选择不接收 1-of-N 中断。这由 GICR_CTLR 中的 DPG1S、DPG1NS 和 DPG0 位控制。
GICD_IROUTERn.Interrupt_Routing_Mode 控制中断的路由策略
DPG1S(Deactivate Priority Group 1 Secure):当设置为1时,安装状态下的Group 1中断都不会被发送到该PE。
DPG1NS (Deactivate Priority Group 1 Non-Secure) :当
DPG1NS
设置为 1 时,非安全状态下的所有 Group 1 中断都不会被发送到该 PE。DPG0 (Deactivate Priority Group 0) :当
DPG0
设置为 1 时,所有 Group 0 中断都不会被发送到该 PE。
6、 Handling interrupts
本节将介绍中断发生时的情况:例如,中断如何路由到 PE、中断之间的优先级关系以及中断结束时的情况。
Routing a pending interrupt to a PE
中断状态机部分描述了当中断源断言时,中断如何从 inactive 状态过渡到 pending 状态。当中断成为 pending 状态时,中断控制器会根据以下测试决定是否将中断发送到所连接的某个 PE。这些测试将决定向哪个 PE(如果有)发送中断:
-
检查与中断相关的组是否已启用
Security model 一节介绍了 如何将每个 INTID 分配给一个组: Group 0、Secure Group 1 或 Non-secure Group 1。对于每个组,Distributor 和每个 CPU interface 都有一个组启用位
Redistributor 也有启用位,也记录分组,管理 SGI 和 PPI。
中断控制器检查与该中断 INTID 相关联的组的组使能位是否已设置
属于禁用组的中断不能向 PE 发出信号。这些中断会一直处于待处理状态,直到组被启用
-
检查中断是否已启用
单独禁用的中断可能成为待处理中断,但不会转发给 PE
-
检查路由控制,决定哪些 PE 可以接收中断
哪些 PE 可以接收中断取决于发送的中断类型:
- 对于共享外设中断 (SPI),路由由 GICD_IROUTERn 控制。SPI 可以针对一个特定的 PE,也可以针对连接的任何一个 PE
- 对于本地特定外设中断 (LPI),路由信息来自 ITS
- 专用外设中断 (PPI) 专用于一个 PE,只能由该 PE 处理
- 对于软件生成的中断 (SGI),发端 PE 定义目标 PE 的列表。 Sending and receiving Software Generated Interrupts 中对此有进一步描述
-
检查中断优先级和优先级掩码,以决定哪些 PE 适合处理中断
每个 PE 的 CPU 接口中都有一个优先级屏蔽寄存器 ICC_PMR_EL1。该寄存器设置了将中断转发至该 PE 所需的最低优先级。只有优先级高于掩码的中断才会向 PE 发出信号
-
检查运行优先级,以决定哪些 PE 可用来处理中断
Running priority and preemption covers 运行优先级以及运行优先级对抢占的影响。如果 PE 尚未处理中断,则运行优先级为空闲优先级:0xFF。只有优先级高于运行优先级的中断才能抢占当前中断。
如果中断通过了所有这些测试,就会作为 IRQ 或 FIQ 异常转发到相应的内核。要了解更多信息,请参阅 Setting the target PE for SPIs。
Taking an interrupt
在进入异常处理程序时,软件并不知道发生了哪个中断。处理程序必须读取一个中断确认寄存器(IAR),以获得中断的 INTID。
有两个 IARs:
**读取 IAR 会返回所发生中断的 INTID,并推进中断的状态机。**通常情况下,IAR 是在进入中断处理程序时读取的。不过,软件也可以随时读取寄存器。
有时,IAR 无法返回有效的 INTID。例如,软件读取 ICC_IAR0_EL1,确认 Group 0 中断,但待处理的中断属于 Group 1 。在这种情况下,读取将返回一个保留的 INTID,如下表所示:
当软件从
ICC_IAR0_EL1
寄存器读取时,它期望获得一个当前待处理的 Group 0 中断的 INTID。如果此时没有任何 Group 0 中断处于待处理状态,但存在待处理的 Group 1 中断,软件尝试读取
ICC_IAR0_EL1
寄存器将无法获得有效的 Group 0 中断 INTID。**在这种情况下,读取操作通常会返回一个保留的 INTID 值。**这个保留值不对应于任何实际的中断源,而是表示没有符合当前读取条件的有效中断。
为了处理 Group 1 中断,软件需要检查和读取相应的 Group 1 中断确认寄存器(如
ICC_IAR1_EL1
)。
读取返回这些保留值之一的 IAR 时,如果存在中断,则不会确认中断。
INTID 含义 示例场景 1020 只有在读取 ICC_IAR0_EL1 时返回 。最高优先级待处理的中断是Secure Group 1 的 。只在 EL3 看到。 当处理器元素(PE)在非安全状态执行时,触发了一个针对可信操作系统的中断。这会作为 FIQ(快速中断请求)传递到 EL3,以便安全监视器可以上下文切换到可信操作系统。 1021 只有在读取 ICC_IAR0_EL1 时返回 。最高优先级待处理的中断是Non-secure Group 1 的 。只在 EL3 看到。 当 PE 在安全状态执行时,触发了一个针对富操作系统的中断。这会作为 FIQ 传递到 EL3,以便安全监视器可以上下文切换到富操作系统。 1022 在读取 ICC_IAR1_EL1 时返回 。最高优先级待处理的中断是非安全组 1 的,具有不可屏蔽属性。 具有不可屏蔽属性的中断通过不同的寄存器来确认。 1023 伪造的中断。没有启用的 INTID 处于待处理状态,或所有待处理的 INTID 优先级不足以被处理。 当轮询 IAR(中断确认寄存器)时,这个值表明没有可用于确认的中断。
Example of interrupt handling
下图显示了一个移动系统的示例,该系统带有一个调制解调器中断,发出来电信号。富操作系统将在非安全状态下处理该中断。
处理中断的步骤如下:
- 当 PE 在安全 EL1 执行可信操作系统时,调制解调器中断成为待处理中断。由于调制解调器中断被配置为 Non-secure Group 1,它将作为 FIQ 发出信号。 如果 SCR_EL3.FIQ==1,异常将被带到 EL3。
- 在 EL3 执行的安全监控软件读取 IAR,返回 1021 。该值表明预计将在非安全状态下处理该中断。然后,安全监控程序会执行必要的上下文切换操作。
- 现在处理器元素(PE)处于非安全状态,中断被重新标记为 IRQ,并被传递到非安全的 EL1 异常级别,由富操作系统处理。
在本例中,Non-secure Group 1 中断导致立即退出安全操作系统。 这可能并非总是需要或想要的。该示例的另一种模式如下图所示,即中断最初进入安全 EL1:
处理中断的步骤如下:
- 当 PE 在安全 EL1 下执行可信操作系统时,调制解调器中断成为待处理中断。由于调制解调器中断被配置为 Non-secure Group 1,因此将作为 FIQ 发出信号。当 SCR_EL3.FIQ==0 时,异常会被带到安全 EL1。
- 可信操作系统会执行一些操作来整理其内部状态。准备就绪后,可信操作系统使用 SMC 指令转入非安全状态。
- SMC 异常被带到 EL3。在 EL3 执行的安全监控软件执行必要的上下文切换操作。
- 现在处理器元素(PE)处于非安全状态,中断被重新标记为 IRQ,并被传递到非安全的 EL1 异常级别,由富操作系统处理。
Running priority and preemption
**优先级掩码寄存器设置了将中断转发给 PE 的最低优先级。**GICv3 架构也有运行优先级的概念。当一个 PE 确认一个中断时,其运行优先级将与中断的优先级相同。 **当 PE 写入一个中断结束(EOI)寄存器时,运行优先级将恢复到原来的值。**下图显示了 PE 的运行优先级随时间变化的示例:
当前的运行优先级在 CPU 接口的运行优先级寄存器中报告:ICC_RPR_EL1。
在考虑抢占时,运行优先级的概念非常重要。当高优先级中断向已在处理低优先级中断的 PE 发出信号时,就会发生抢占。 抢占会给软件带来一些额外的复杂性,但它可以防止低优先级中断阻塞对高优先级中断的处理。
下图显示了不允许抢占时的情况:
高优先级中断被阻断,直到先前发出信号的低优先级中断被处理为止。现在考虑同样的情况,但启用抢占功能:
当优先级较高的中断挂起时,它会抢占先前发出信号的低优先级中断。上图显示的是一级抢占。但也可以有多级抢占。
**Arm CoreLink GICv3 架构允许软件通过指定发生抢占所需的优先级差异来控制抢占。**这可通过二进制点寄存器进行控制:ICC_BPRn_EL1。
如图所示,二进制点寄存器将优先级分为 组优先级 和 子优先级 两个字段:
对于抢占,只考虑组优先级位。子优先级位被忽略。
例如,考虑以下三个中断:
- INTID A 的优先级为 0x10
- INTID B 的优先级为 0x20
- INTID C 的优先级为 0x21
在这个例子中,我们确定:
- A 可以抢先 B 或 C
- B 不能抢先 C,因为 B 和 C 的优先级相似
为此,可将 组 和 次优先级 之间的分隔设置为 N=4,如图所示:
分割后,就抢占而言,B 和 C 现在具有相同的优先级。但是,A 的优先级仍然更高,这意味着它可以抢占 B 或 C。
二进制点寄存器只影响抢占,即在处理另一个中断时是否应发出中断信号。在选择待处理中断时,不使用二进制点寄存器。
抢占要求编写的中断处理程序支持嵌套。如何编写此类中断处理程序的细节不在本指南的讨论范围之内。
End of interrupt
**一旦中断被处理,软件必须通知中断控制器中断已被处理,以便状态机过渡到下一状态。**Arm CoreLink GICv3 架构将其视为两项任务:
-
Priority drop
这意味着将运行优先级降回到中断发生前的值
-
Deactivation
这意味着更新当前正在处理的中断的状态机,通常是从活动状态过渡到非活动状态
在 GICv3 架构中,Priority drop 和 Deactivation可以同时进行,也可以分开进行。这取决于 ICC_CTLR_ELn.EOImode 的设置:
-
EOImode = 0
对 Group 0 中断写入 ICC_EOIR0_EL1,或对 Group 1 中断写入 ICC_EOIR1_EL1,都会执行Priority drop和Deactivation操作。这种模式通常用于简单的裸机环境。
-
EOImode = 1
对 Group 0 中断写入 ICC_EOIR0_EL1,或对 Group 1 中断写入 ICC_EOIR1_EL1 会导致Priority drop。Deactivation时需要单独写入 ICC_DIR_EL1。该模式通常用于虚拟化目的。
大多数软件会使用 EOIMode==0,管理程序(hypervisor)最常使用 EOImode==1。
在
EOImode = 0
的情况下,优先级降级和去激活是一步完成的,适用于简单环境。在
EOImode = 1
的情况下,优先级降级和去激活是分开的,提供更细致的控制,适用于虚拟化环境。
Checking the highest priority pending interrupt and running priority
顾名思义,最高优先级待处理中断寄存器 ICC_HPPIR0_EL1 和 ICC_HPPIR1_EL1 报告 PE 的最高优先级待处理中断的 INTID。
HPPIR(Highest Priority Pending Interrupt) :
ICC_HPPIR0_EL1
用于获取 Group 0 的最高优先级挂起中断的 INTID(中断标识符)
ICC_HPPIR1_EL1
用于获取 Group 1 的最高优先级挂起中断的 INTID。Group 1 中断分为两类:Secure Group 1 和 Non-secure Group 1,分别用于处理安全状态和非安全状态下的中断。
运行优先级在 运行优先级和抢占 中引入,由运行优先级寄存器 (ICC_RPR_EL1) 报告。
Checking the state of individual INTIDs
**Distributor 提供寄存器,显示每个 SPI 的当前状态。**同样,Redistributor 也提供寄存器,显示其所连接 PE 的 PPI 和 SGI 的状态。
这些寄存器还可以将中断移至特定状态。例如,这对于测试配置是否正确非常有用,而无需外设断言中断。
Active 状态和 Pending 状态分别由不同的寄存器报告。下表列出了 Active 状态寄存器。Pemdomg状态寄存器的格式相同:
除非 GICD_NASCRn 或 GICR_NASCRn 允许访问,否则在非安全状态下执行的软件无法查看 Group 0 或 Secure Group 1 中断的状态。
7、 Sending and receiving Software Generated Interrupts
软件生成中断(SGI)是软件通过写入中断控制器中的寄存器来触发的中断。
生成 SGI 写入 CPU 接口中的下列 SGI 寄存器之一即可生成 SGI:
下图显示了 SGI 寄存器的基本格式:
Controlling the SGI ID
SGI ID 字段控制 INTID 的生成。如中断类型所述,INTID 0-15 用于 SGI。
Controlling the target
SGI 寄存器中的 IRM(中断路由模式)字段控制向哪个或哪些 PE 发送 SGI。有两个选项:
-
IRM = 0
中断被发送到
<aff3>.<aff2>.<aff1>.<Target List>
,其中<target list>
被编码为<aff1>
下每个亲和 0 节点的 1 位。这意味着中断最多可发送到 16 个 PE,其中可能包括始发 PE。 -
IRM = 1
该中断将发送给所有连接的 PE,但发起中断的 PE(自身)除外。
Controlling the Security state and grouping
SGI 的安全状态和分组由以下方面控制:
- 软件在源 PE 上写入的 SGI 寄存器 ICC_SGI0R_EL1、ICC_SGI1R_EL1 或 ICC_ASGIR_EL1。
- 目标 PE 或 PE 的 GICR_IGROUPR0 和 GICR_IGRPMODR0 寄存器。
在安全状态下执行的软件可以发送安全和非安全 SGI。在非安全状态下执行的软件能否生成安全 SGI 由 GICR_NSACR 控制。
只有在安全状态下执行的软件才能访问该寄存器。下表显示了 GIC 通过检查来确定是否转发中断:
- 发端 PE 的安全状态。
- 中断针对的 PE 的中断处理配置。
- SGI 寄存器。
- 本表假定 GICD_CTLR.DS==0。当 GICD_CTLR.DS==1 时,标有 (*) 的 SGI 也会被转发。
Comparison of GICv3 and GICv2
在 Arm GICv2 中,SGI INTID 由初始 PE 和目标 PE 保存。这意味着一个给定的 PE 最多可以有 8 次相同的 SGI INTID 待处理,系统中的每个 PE 都有一次。
**在 Arm GICv3 中,SGI 仅由目标 PE 存储。**这意味着一个给定的 PE 只能有一个 SGI INTID 实例待处理。
让我们举例说明这种差异。如图所示,PE A 和 B 同时向 PE C 发送 SGI INTID 5:
C 会收到多少个中断?
-
For GICv2: Two interrupts
GIC 将同时接收来自 A 和 B 的中断。这两个中断的顺序取决于具体的设计和精确的时间安排。这两个中断可以通过 GICC_IAR 返回的 INTID 前缀有发端 PE 的 ID 这一事实来区分。
-
For GICv3: One interrupt
由于发起中断的 PE 没有将 SGI 存入银行,因此同一个中断不可能在两个 PE 上挂起。因此,C 只收到一个 ID 为 5 的中断,没有前缀。
该示例假定两个中断同时或几乎同时发送。 如果 C 能够在第二个 SGI 到达之前确认第一个 SGI,那么 C 将在 GICv3 中看到两个中断。
在传统运行期间,即 GICD_CTLR.ARE=0 时,SGI 的行为与 Arm CoreLink GICv2 相同。
9、 Check your knowledg
问:Arm GICv3 中的四种中断类型是什么,如何识别?
答:有四种类型:本地特定外设中断(LPI)、共享外设中断(SPI)、专用外设中断(PPI)和软件生成中断(SGI)。类型可通过 INTID(中断源 ID 号)识别。
问:中断可以分配给哪些不同的组,每个组通常有什么用途?
答:Group 0 通常用于针对 EL3 固件的中断。Secure Group 1 通常用于针对安全状态软件的中断。Non-secure Group 1 通常用于针对非安全内核或管理程序的中断。
问:中断处理程序会读取哪些寄存器,以找出它已执行的中断?
A:ICC_IARn_EL1 寄存器之一。
问:重置后,GIC 会将所有连接的 PE 视为离线或睡眠状态。软件如何将 PE 标记为在线?
答:清除该 PE 的Redistributor中的 GICR_WAKER.ProcessorSleep,然后轮询直到 ChildrenAsleep 读数为 0。
问:GIC 为每个中断源存储什么配置?
答:Enable 、Priority 、 Edge-triggered vs Level-sensitive 、 Group 、 Target (SPIs only)
问:PE 能否向自己发送 SGI?
答:是
问:如果一个 PE 正在处理一个中断,是什么在控制另一个中断能否抢占它的位置?
答:第二个中断的优先级和二进制点寄存器的值:ICC_BPRn_EL1。