Arm通用中断控制器GICv3和GICv4

2023-12-28 20:41:56

概述

本文介绍了 Arm 通用中断控制器(GIC)v3和v4的功能,描述了 GICv3 中断控制器的相关操作,同时也介绍了如何在裸机环境中配置GICv3 中断控制器。

本文是 Arm 通用中断控制器相关指南中的第一篇:

背景

中断是向处理器发出信号,表明发生了需要处理的事件,中断通常由外设产生。

例如,系统可能使用通用异步接收器/发送器 (UART) 接口与外界通信。当UART接收数据时,它需要一种机制来告诉处理器新数据已经到达。UART 可以使用的一种机制是生成中断,向处理器发出信号。

小型系统可能只有几个中断源和一个处理器。然而,大型系统可能有更多的中断源和处理器。GIC 执行中断管理、优先级和路由等关键任务。 GIC 管理来自整个系统的所有中断,确定优先级,并将它们发送到内核进行处理。GIC 主要用于提高处理器效率并开启中断虚拟化。

GIC 是基于Arm GIC 架构实现的。该架构已经从 GICv1 发展到最新版本 GICv3 和 GICv4。 Arm 拥有多个通用中断控制器,可为所有 Arm Cortex 多核处理器系统提供一系列中断管理解决方案,如用于小型CPU系统的 GIC?400,用于高性能多芯片系统的 GIC?600。 GIC?600AE 额外添加了功能安全特性,针对高性能 ASIL B 至 ASIL D 系统。

在本指南的最后,您可以检查您的知识。您将了解不同类型的中断,能启用 GIC 并配置不同的中断类型。

开始之前

本指南涵盖了 GICv3 和 v4 的基本操作,Shared Peripheral Interrupt(SPIs)、Private Peripheral Interrupt (PPIs) 和Software Generated Interrupt (SGIs)的使用。

本指南是Arm Generic Interrupt Controller Architecture Specification GIC architecture version 3.0 and 4.0的补充。有关寄存器和行为的详细说明,请参阅 Arm 通用中断控制器架构规范 GIC 架构 3.0 和 4.0 版本。

GICv3 和 GICv4 允许多种不同的配置和用例。为简单起见,本指南重点介绍以下场景的配置和用例:

  • 存在两种安全状态
  • 两种安全状态均启用 affinity 路由
  • 所有异常等级可以访问系统寄存器
  • 处理器符合 Armv8?A 或 Armv9?A 标准,实现 AArch64 所有异常等级

本文并未覆盖:

  • 遗留操作
  • 在 AArch32 异常等级下使用

本文假定您熟悉 Arm 异常模型。如果您想了解 Arm 异常模型,可以阅读Learn the Architecture: Exception model guide

什么是通用中断控制器?

通用中断控制器 (GIC) 从外设获取中断,确定优先级,然后将它们送入到处理器内核。下图展示了 GIC 从 n 个不同的外设源获取中断,并将它们分配给两个不同的处理器。
GIC示例

GIC 是针对 Arm Cortex?A 和 Arm Cortex?R 系列处理器的标准中断控制器。GIC 提供了一种灵活且可扩展的中断管理方法,支持单核系统到具有数百个核的大型多芯片设计。

Arm CoreLink GIC的历史

与 Arm 架构一样,GIC 架构也随着时间的推移而发展。下表总结了 GIC 规范的主要版本以及常运用的处理器。

GIC版本关键特性典型运用
GICv1最多支持8个PEArm Corte-A5 MPCore、Arm Corte-A9 MPCore、Arm Corte-R4、Arm Corte-R5 MPCore、Arm Corte-R7 MPCore、Arm Corte-R8 MPCore
GICv2GICv1的所有功能、支持虚拟化Arm Corte-A7 MPCore、Arm Corte-A15 MPCore、Arm Corte-A17 MPCore
GICv3GICv2的所有功能、支持8个以上PE、支持消息信号中断、支持超过1020个中断ID、系统寄存器可访问到CPU内部寄存器、一种增强的安全模型,将安全和非安全组中断隔离开Arm Corte-A3x MPCore、Arm Corte-A5x MPCore、Arm Corte-A7x MPCore
GICv4GICv3的所有功能、直接注入虚拟中断Arm Corte-A3x MPCore、Arm Corte-A5x MPCore、Arm Corte-A7x MPCore

本文涵盖了Arm CoreLink GICv3和GICv4,这些运用在大多数Armv-9A、Armv8-A及Armv8-R设计中。

GICv3和GICv4自发布以来,也进行了一些更新:

  • GICv3.1 添加支持线中断、安全虚拟化和内存系统资源分区和监控 (MPAM)
  • GICv3.2 添加了对Armv8?R AArch64 的支持
  • GICv3.3 添加了对不可屏蔽中断的支持
  • GICv4.1 扩展虚拟化支持,涵盖SGIs中断的直接注入
  • GICv4.2 支持对不可屏蔽虚拟中断直接注入

GIC基础知识

在本节中,我们将了解 Arm GICv3 和 v4 中断控制器的基本操作。

中断类型

GIC 可以处理四种不同类型的中断源:

  • 共享外设中断(SPI)。外设中断可以传入到任意连接的核。
  • 私有外设中断(PPI)。某一内核专有的外设中断。例如,通用定时器中断。
  • 软件触发中断(SGI)。SGI 通常用于处理器间通信,通过写入 GIC 中的 SGI 寄存器生成。
  • 特定外设中断(LPI)。LPI 首次在 GICv3 中引入,与其他三种中断类型的编程模型不同,LPI的配置将在后续进行介绍。

每个中断源由一个 ID 号标识,称为 INTID。前面介绍的中断类型是根据INTIDs的范围定义的:

INTID中断类型注意
0-15SGIs每个PE Banked
16-31、1056-1119(GICv3.1)PPIs每个PE Banked
32-1019、4096-5119(GICv3.1)SPIs-
1020-1023特殊中断号用于特殊情况,参考Setting for each PE章节
1024-8191保留-
8192及以上LPIs上限由实现定义

如何向中断控制器发送中断信号

中断通常是从外设向中断控制器发送专用硬件信号,如下:

专用中断信号

Arm CoreLink GICv3 支持此模型,但还提供了额外的信号机制:消息信号中断 (MSI)。 MSI 的传递是通过写中断控制器的寄存器实现,如下所示:

消息信号中断

使用消息将中断从外设转发到中断控制器,无需为每个中断源提供专用信号。这对于大型系统的设计人员来说是一个优势,因为在大型系统中,可能有数百甚至数千个信号在 SoC 上路由并汇聚到中断控制器上。

中断是作为消息发送还是使用专用信号对中断处理影响不大,可能需要对外设进行一些配置。例如,可能需要指定中断控制器的地址,这些已经超出本文的范围。

在 Arm CoreLink GICv3 中,SPI 可以是消息信号中断,而LPI 始终是消息信号中断。不同的中断类型使用不同的寄存器,如下表所示:

中断类型寄存器
SPIGICD_SETSPI_NSR
LPIGITS_TRANSLATER

中断状态机

对于每一个SPI,PPI和SGI中断源,中断控制器维护了一个状态机,由四个状态组成:

  • Inactive:中断处于无效状态。
  • Pending:中断已触发,但尚未被 PE 响应。
  • Active:中断已触发,并且中断已被 PE 响应。
  • Active and Pending:当一个中断处于Active状态,同一中断源又触发了中断,进入Pending状态。

中断状态机如下图所示。

中断状态机

中断的生命周期取决于其配置的是电平触发还是边沿触发:

  • 对于电平触发中断,中断输入的上升沿会导致中断变为挂起状态,并且一直保持有效状态,直到外设取消中断信号。
  • 对于边沿触发中断,中断输入的上升沿会导致中断变为挂起状态,但中断不会继续保持有效状态。

电平触发中断

下图展示了中断状态转换与中断信号间的对应关系:

电平触发时序

  • Inactive to pending:当中断发生时,中断从非活跃状态转换为挂起状态。此时,如果中断使能并且优先级较高,GIC会向PE发出中断信号。

  • Pending to active and pending:当PE读取CPU接口中的中断应答寄存器(IARs)后,中断从挂起状态转化为活跃及挂起状态。这个读取动作是中断处理程序的一部分,然后GIC取消向PE发出中断信号。

  • Active and pending to active:当外设取消置位中断信号时,中断将从活跃及挂起状态转换为活跃状态。这通常发生在软件写入外设的状态寄存器时。

  • Active to inactive:当 PE 写入 CPU 接口的 End of Interrupt Registers (EOIRs) 时,中断从活跃状态变为非活跃状态,这表明PE已经完成了中断处理。

边沿触发中断

下图展示了中断状态转换与中断信号间的对应关系:

边沿触发时序

  • Inactive to pending:当中断发生时,中断从非活跃状态转换为挂起状态。此时,如果中断使能并且优先级较高,GIC会向PE发出中断信号。

  • Pending to active:当PE读取CPU接口中的中断应答寄存器(IARs)后,中断从挂起状态转化为活跃状态。这个读取动作是中断处理程序的一部分,然而软件也可以轮训IARs。然后GIC取消向PE发出中断信号。

  • Active to active and pending:当外设又发出中断信号时,中断将从活跃状态转换为活跃及挂起状态。

  • Active and pending to pending:当 PE 写入 CPU 接口的 End of Interrupt Registers (EOIRs) 时,中断从活跃及挂起状态变为挂起状态,这表明PE已经完成了第一个中断实例的处理。此时GIC重新向PE发出中断信号。

目标中断

Arm架构为每个PE分配了一个分层标识符,称为affinity(亲和性)。GIC使用affinity值将中断路由到相应的core。

affinity 是一个32位值,分为4个字段:

<affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0>

每个PE的affnity值可以通过MPDIR_EL1寄存器获取(Affinity值表示一个中断被分配给哪个处理器核心)。

不同等级的affinity含义是由处理器和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 布局如下:

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

affinity level 0:表示一个core中的第几个thread,大多数是sigle-threaded,因此该值为0。

affinity level 1:表示一个cluster中的第几个core。

affinity level 2/3:表示系统中第几个cluster。

安全模型

Arm GICv3 架构支持 Arm TrustZone 技术。软件为每个 INTID 分配一个组和安全设置。GICv3 支持三种设置组合,如下:

中断类型使用示例
Secure Group 0EL3中断(Secure Firmware)
Secure Group 1Secure EL1中断(Trusted OS)
Non-secure Group 1No-secure状态中断(OS或Hypervisor)

Group 0 中断始终以 FIQ 形式发出信号,而 Group 1 中断以 IRQ 或 FIQ 形式,这取决于 PE 当前的安全状态和异常等级,如下:

PE的异常等级和安全状态Group 0Group 1Group 1
SecureNon-Secure
Secure EL0/1FIQIRQFIQ
Non-secure EL0/1/2FIQFIQIRQ
EL3FIQFIQFIQ

这些规则旨在补充 AArch64 安全状态和异常等级路由控制。下图是一个简单的软件栈,在EL0执行时,当触发不同的中断类型,会发生什么:

中断路由示例

上图 IRQ 路由到 EL1(SCR_EL3.IRQ=0),FIQ 路由到 EL3(SCR_EL3.FIQ=1)。考虑上述规则,在EL0或者EL1执行时,安全状态下的 Group 1 中断被视为 IRQ。

举例说明:当PE在 Secure EL0/1 (图右)下执行时,来了一个 Secure Group 1 中断,将发出 IRQ 信号;当PE在 Secure EL0/1 (图右)下执行时,来了一个 Non-secure Group 1 中断,将发出 FIQ 信号;当PE在 Non-secure EL0/1/2 (图左)下执行时,来了一个 Secure Group 1 中断,将发出 FIQ 信号;当PE在 Non-secure EL0/1/2 (图左)下执行时,来了一个 Non-secure Group 1 中断,将发出 IRQ 信号;而无论PE处于哪个异常等级和安全状态,来了一个 Group 0中断,始终发出 FIQ 信号。

对软件的影响

当配置中断控制器时,软件进行 INTID 中断组的分配。软件只有在安全状态下才能进行 INTID 的分配。

通常,软件只有在安全状态下才能访问安全中断状态和配置:Group0 和 Secure Group 1。

也可以使能非安全状态访问安全状态和配置的权限,这是由每个 INTID 单独控制的,使用 GICD_NASACRnGICR_NSACR 寄存器j进行配置。

支持一个安全状态

GICv3 支持 Arm TrustZone 技术,但是否使用 TrustZone 是可选的。这意味着你可以配置是实现一个安全状态还是两个安全状态:

  • GICD_CTLR.DS == 0:支持两种安全状态,安全或者非安全
  • GICD_CTLR.DS == 1:只支持一种安全状态

对于连接的所有PE,GIC须配置支持相同数量的安全状态。这意味着,如果连接 Arm Cortex-A系列处理器,GIC 将支持两种安全状态,而连接 Arm Cortex-R系列处理器,将只支持一种安全状态。

编程模型

GICv3 中断控制器的寄存器接口分为三组:

  • Distributor 接口
  • Redistributor 接口
  • CPU 接口

这些接口如下图所示:

编程接口

一般来说,Distributor 和 Redistributor 接口用于配置中断,CPU 接口用于处理中断。

Distributor

Distributor 寄存器是内存映射,用于配置 SPI。Distributor 提供编程接口用于:

  • SPI 的中断优先级及分配
  • 启用和禁止 SPI
  • 设置每个 SPI 的优先级
  • 每个 SPI 的路由信息
  • 设置 SPI 是电平触发还是边沿触发
  • 控制 SPI 的激活和挂起状态
  • 确定每个安全状态的编程模型:affinity routing 或者 leagacy

Redistributors

每个连接的 Core 都有一个 Redistributor。Redistributor 提供编程接口用于:

  • 启用和禁止 SGI 和 PPI
  • 设置 SGI 和 PPI 的优先级
  • 设置 PPI 是电平触发还是边沿触发
  • 为每个SGI 和 PPI 分配一个中断组
  • 控制 SGI 和 PPI 的状态
  • 控制内存中数据结构的基地址,这个数据结构支持相关的中断属性和每个LPI的挂起状态
  • 为连接的 PE 提供电源管理支持

CPU接口

每个 Core 都包含一个 CPU 接口,这些接口是在中断处理期间使用的系统寄存器。CPU 接口提供编程接口用于:

  • 提供通用控制和配置,以启用中断处理
  • 中断应答
  • 执行中断优先级降级和挂起
  • 配置 PE 的中断优先级
  • 决定 PE 的抢占策略
  • 决定 PE 的最高优先级挂起中断

在 Arm CoreLink GICv3 中,CPU 接口寄存器像系统寄存器一样进行访问:ICC_*_ELn

在使用这些寄存器之前,软件必须使能系统寄存器接口。这是由ICC_SRE_ELn寄存器中的 SRE 位控制的,其中 n表示异常等级:EL1-EL3。

配置 Arm GIC

本章介绍如何在裸机环境中启用和配置 GICv3 的中断控制器,详细寄存器配置参考Arm Generic Interrupt Controller Architecture Specification GIC architecture version 3.0 and 4

LPI 与 SPI、PPI 以及 SGI 的配置明显不同,更多信息请参考Locality-specific Peripheral Interrupts

大多数 GICv3 中断控制器都是用在多核系统,也有可能是多处理器系统。有些设置是全局的,即会影响所有连接的 PE,其他设置是针对单个PE。

Global settings

Distributor 控制寄存器(GICD_CTLR)必须启用中断组,以及设置路由模型:

  • 启用 Affinity 路由(ARE 位):GICD_CTLR寄存器中的 ARE 位决定是在 GICv3 模式或者 legacy 模式下运行。legacy 模式提供 GICv2 向后兼容性。本文假设 ARE 位设置为1,即使用 GICv3 模式。
  • GICD_CTLR 包含中断分组启用位:Group 0,Secure Group 1,和 Non-secure Group 1。
    • EnableGrp1S:使能 Secure Group 1 中断分发
    • EnableGrp1NS:使能 Non-secure Group 1 中断分发
    • EnableGrp0:使能 Group 0 中断分发

Settings for each PE

本节针对单个 Core 或者 PE 的配置。

Redistributor配置

每个Core都有自己的Redistributor,如下:

PE连接到redistributor

Redistributor 包含一个 GICR_WAKER 的寄存器,用于记录连接的 PE 是否在线或离线。中断只转发给 GIC 认为在线的 PE。复位时,所有 PE 均视为离线。

要将连接的 PE 标记为在线,软件必须:

  • GICR_WAKER.ProcessorsSleep清零
  • 轮询GICR_WAKER.ChildrenASleep,直至读到0

在配置 CPU 接口之前,软件必须执行这些步骤,否则可能工作异常。

当 PE 离线(GICR_WAKER.ProcessorsSleep==1),中断将触发唤醒请求信号。通常,该信号将发送至系统的电源控制器,然后电源控制器会打开PE。当唤醒后,PE会清零 ProcessorSleep 位,允许转发该中断。

CPU 接口配置

CPU 接口负责向与其连接的 PE 发送中断异常。为了使能CPU接口,软件必须配置:

  • 启用系统寄存器访问

    CPU 接口(ICC_*_ELn)部分描述了 CPU 接口寄存器,以及如何在 GICv3 中如何像系统寄存器一样进行访问。软件必须设置ICC_SRE_ELn寄存器中的 SRE 位,来启用对 CPU 接口寄存器的访问。

  • 设置 Priority Mask 和 Binary Point 寄存器

    CPU 接口包 Priority Mask(ICC_PMR_EL1)和 Binary Point(ICC_BPRn_EL1)。Priority Mask 设置中断必须具有的最低优先级,才能转发到 PE。Binary Point 寄存器用于优先级分组和抢占。下面处理中断章节中更详细地描述了这两个寄存器的使用。

  • 设置 EOI 模式。

    CPU 接口ICC_CTLR_EL1ICC_CTLR_EL3中的 EOI 模式位决定如何处理中断完成。下面中断结束章节对此进行了更详细的描述。

  • 使能每个中断组的信号。

    在 CPU 接口将每个中断组的中断转发到 PE 之前,必须使能该中断所在的中断组。软件必须配置ICC_IGRPEN1_EL1寄存器(用于Group 1中断)和ICC_IGRPEN0_EL1寄存器(用于Group 0中断)。ICC_IGRPEN1_EL1由安全状态存储,这意味着ICC_GRPEN1_EL1控制Group 1的安全状态。在 EL3 中,软件可以使用 ICC_IGRPEN1_EL3,同时访问用于Group 1的上述两个寄存器。

PE配置

PE的一些配置需要允许接收和处理中断,详细说明超出了本文的范围,这里我们只介绍在 AArch64 状态下执行所需的基本步骤。

  • 路由控制

    中断的路由控制位于PE 的SCR_EL3HCR_EL2寄存器中。路由控制位决定中断发生的异常等级。在复位时,这些路由控制位是未知值,因此软件必须进行初始化。

  • 中断屏蔽

    PSTATE寄存器中,PE 有一个异常屏蔽位。当置位时,中断被屏蔽,复位时这些是置位的。

  • 向量表

    PE 向量表的位置是由VBAR_ELn寄存器设置的。与SCR_EL3HCR_EL2寄存器一样,VBAR_ELn寄存器值复位时也是未知的。软件必须配置VBAR_ELn寄存器,指向内存中的向量表。

SPI、PPI和SGI配置

到目前为止,我们介绍了中断控制器本身的配置,现在看一下各个中断源的配置。

使用哪些寄存器来配置中断取决于中断的类型:

  • SPI 是通过 Distributor 进行配置的,即使用 GICD_*寄存器。
  • PPI 和 SGI 是通过 Redistributor 进行配置,即使用 GICR_*寄存器。

下图说明了这些不同的配置机制:

配置寄存器

对于每个 INTID,软件必须配置如下内容:

  • Priority:GICD_IPRIORITYn,GICR_IPRIORITYn

    每个 INTD 都有一个优先级,用8位无符号值表示。0x00 表示最高优先级,0xFF 表示最低优先级。运行优先级和抢占决定了GICD_IPRIORITYnGICR_IPRIORITYn中的优先级值如何屏蔽低优先级中断,如何控制抢占。中断控制器不需要实现所有 8 个优先级位,如果 GIC 支持两种安全状态,中断控制器至少需要实现5位; 如果 GIC 仅支持单个安全状态,则必须至少实现 4 位。

  • Group:GICD_IGROUPn, GICD_IGRPMODn, GICR_IGROUPn, GICR_IGRPMODn

    如安全模型章节中所述,中断可以配置:Group 0、Secure Group 1 和 Non-secure Group 1。

  • 边沿触发或者电平触发:GICD_ICFGRn,GICR_ICFGRn

    对于 PPI 和 SPI,软件必须指定中断是边沿触发还是电平触发。 SGI 始终被视为边沿触发,因此GICR_ICFGR0总是读为1、忽略写(RAO/WI)。

  • 使能:GICD_ISENABLERn, GICD_ICENABLER, GICR_ISENABLERn, GICR_ICENABLERn

    每个 INTID 都有一个使能位。Set-enable registers 和 Clear-enable registers 不需要了执行读取?修改?写入流程。Arm 建议在启用 INTID 之前按照本节所述进行配置。

  • 不可屏蔽:不可屏蔽中断比同一组的其他中断优先级更高。也就是说,不可屏蔽的 Non-secure Group 1 中断比其他 Non-secure Group 1 中断的优先级高。

对于裸机环境,通常初始配置后无需再修改。然而,如果必须重新配置中断,例如更改组设置,则应先禁用该中断,然后再更改其它配置。

大多数配置寄存器的复位值都是由实现定义的。这意味着中断控制器的设计者决定这些值是什么,并且这些值可能因系统而异。

Arm GICv3.1 和扩展的 INTID 范围

Arm GICv3.1 添加了对其他 SPI 和 PPI INTID 的支持。配置这些中断的寄存器与原始中断范围相同,只是它们有一个E后缀。例如:

  • GICR_ISENABLERn:使能原始 PPI 范围
  • GICR_ISENABLERnE:使能 GICv3.1 引入的其他 PPI

设置SPI中断的目标PE

对于 SPI,可以配置中断的目标。这由 GICv3.1 扩展 SPI 的GICD_IROUTERnGICD_IROUTERnE控制。每个SPI都有一个
GICD_IROUTERn寄存器,Interrupt_Routing_Mode位控制路由策略:

  • GICD_IROUTERn.Interrupt_Routing_Mode == 0

    SPI 路由到 PE A.B.C.D,这是寄存器中指定的affinity坐标。

  • GICD_IROUTERn.Interrupt_Routing_Mode == 1

    SPI 可以路由到参与中断组分配的任何 PE。Distributor(而不是软件)选择目标 PE。因此,每次发出中断信号时,目标都会有所不同。这种类型的路由称为 1?of?N。

PE 可以选择不接收 1?of?N 中断。这是由GICR_CTLR中的DPG1SDPG1NSDPG0位控制的。

处理中断

本节介绍中断生成时会发生什么,例如:中断如何路由到PE,中断如何确定彼此的优先级,中断结束会发生什么。

将挂起的中断路由到PE

上面中断状态机章节介绍了当中断源被置位时,中断如何从非活跃状态转换为挂起状态。当中断变为挂起状态时,中断控制器根据以下检查决定是否将中断发送到连接的 PE 。这些检查确定将中断发送到哪个 PE(如果有):

  • 检查与中断关联的组是否已启用。

    上面安全模型章节介绍了如何将 INTID 分配到一个组:Group 0,Secure Group 1,Non-secure Group 1。对于每个组,在 Distributor 和每个 CPU 接口中都有一个 Group 使能位。

    中断控制器检查是否为该中断所在的组设置了 Group 使能位。

    如果所在的组被禁用了,中断将无法发送到 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接口中都有一个优先级屏蔽(Priority Mask)寄存器 ICC_PMR_EL1。该寄存器设置将中断转发到该 PE 所需的最低优先级。只有优先级高于 mask 的中断才会发送给该 PE。

  • 检查运行优先级,决定哪些 PE 可以处理中断。

    下面 Running priority and preemption 介绍了运行优先级,以及这些如何影响抢占。如果PE尚未处理中断,则运行优先级为空闲优先级:0xFF。只有优先级高于运行优先级的中断才能抢占当前中断。

如果中断通过了这些检查,它将作为 IRQ 或 FIQ 异常转发到相应的Core。

获取中断

当进入异常处理程序时,软件不知道它获取的是哪个中断。处理程序必须读取中断应答寄存器 (IAR) ,获取中断的 INTID。有两个 IAR:

寄存器用途
ICC_IAR0_EL1用于确定 Group 0中断,通常在 FIQ 处理程序中读取
ICC_IAR1_EL1用于确定 Group 1中断,通常在 IRQ 处理程序中读取
ICC_NMIAR1_EL1用于确定不可屏蔽中断(仅限GICv3.3)

读取 IAR 得到 INTID,进入中断的状态机。通常,IAR 在进入中断处理程序时被读取。然而,软件可以随时读取该寄存器。

有时,IAR 无法返回有效的 INTID。例如,软件读取ICC_IAR0_EL1,确认 Group 0 中断,但挂起的中断属于 Group 1。在这种情况下,读取返回的一个保留值 INTID,如下表所示:

ID含义示例场景
1020仅读取ICC_IAR0_EL1返回、挂起的最高优先级中断是 Secure Group 1、仅在EL3出现。当 PE 在非安全状态下执行时,Trusted OS 收到中断信号。这被视为 EL3 的 FIQ,以便 Secure Monitor可切换到Trusted OS。
1021仅读取ICC_IAR0_EL1返回、挂起的最高优先级中断是 Non-secure Group 1、仅在EL3出现。当 PE 在安全状态下执行时,Rich OS 收到中断信号。这被视为 EL3 的 FIQ,以便 Secure Monitor可切换到Rich OS。
1022读取ICC_IAR1_EL1返回、挂起的最高优先级中断是 Non-secure Group 1、不可屏蔽属性。不可屏蔽中断使用其它寄存器进行确认。
1023虚假中断、没有启用的 INTID 处于挂起状态,或者处于挂起状态的所有 INTID 的优先级较低,不足以处理。当轮询 IAR 时,该值表示没有可供确认的中断。

如果存在上述情况,读取 IAR 返回的是这些保留值之一,并不确认中断。

中断处理示例

下图是一个手机系统示例,收到了电话,触发了 modem 中断。这个中断旨在由非安全状态的 Rich OS 处理。

处理中断

中断处理的步骤如下:

  1. 当 PE 正在 Secure EL1 下执行 Trusted OS 时,modem 中断变为挂起状态。由于该中断配置为 Non-secure Group 1,因此将发出 FIQ 信号。如果 SCR_EL3.FIQ=1,异常转移到 EL3。
  2. 在 EL3 上执行的 Secure Monitor 软件读取 IAR,返回1021。该值表示期望在非安全状态下处理中断。然后,Secure Monitor 执行必要的上下文切换操作。
  3. 现在 PE 处于非安全状态,中断重新发出 IRQ 信号,送入 Non-secure EL1,由 Rich OS 处理。

在这个示例中,Non-secure Group 1中断导致 PE 立即从 Secure OS 退出。这可能并不是我们想要的结果,下面是一个替代路由方案,中断传入 Secure EL1:

替代的路由模型

中断处理的步骤如下:

  1. 当 PE 正在 Secure EL1 下执行 Trusted OS 时,modem 中断变为挂起状态。由于该中断配置为 Non-secure Group 1,因此将发出 FIQ 信号。如果 SCR_EL3.FIQ=0,异常转移到 Secure EL1。
  2. Trusted OS 执行相关操作,整理其内部状态。当准备就绪后,Trusted OS 使用 SMC 指令进入非安全状态。
  3. SMC异常进入到 EL3,在 EL3 上执行的 Secure Monitor 软件执行必要的上下文切换操作。
  4. 现在PE 处于非安全状态,中断将作为 IRQ 发出信号,送入 Non-secure EL1,由 Rich OS 处理。

运行优先级和抢占

优先级屏蔽寄存器设置中断转发到PE的最低优先级,GICv3 架构也有运行优先级的概念。当 PE 接收一个中断时,其运行优先级也就变为与该中断优先级相同。当 PE 写入中断结束(EOI)寄存器时,运行优先级变为之前的值。下图展示了运行优先级随时间变化的过程:

运行优先级值随时间变化

当前运行优先级可以在 CPU 接口中的 Running Priority 寄存器中查看:ICC_RPR_EL1

在考虑抢占时,运行优先级的概念是很重要的。当 PE 正在处理较低优先级中断,又触发了高优先级中断信号时,就会
发生抢占。抢占使软件处理变得复杂,但可以防止低优先级中断阻塞较高优先级中断的处理。

下图展示了如果不允许抢占会发生什么:

无抢占情形

高优先级中断将被挂起,直到先前低优先级中断处理完成。 接下来看下允许抢占的情况:

允许抢占

当较高优先级中断变为挂起状态时,它会抢占先前的低优先级中断。上图是一级抢占,然而可能存在多级抢占。

通过配置优先级的不同,Arm CoreLink GICv3 架构允许软件控制抢占发生。这是通过 Binary Point 寄存器实现的:ICC_BPRn_EL1

Binary Point 寄存器将优先级划分为两个字段:组优先级和子优先级。

8位优先级值

对于抢占,只考虑组优先级,忽略子优先级。例如,考虑一下三个中断:

  • INTID A 优先级为 0x10。
  • INTID B 优先级为 0x20。
  • INTID C 优先级为 0x29。

我们想达到如下目的:

  • A 可以抢占 B 或者 C。
  • B 不能抢占 C,因为 B 和 C 优先级相似。

为了实现这一点,组优先级和子优先级之间的划分可以设置为 N=4,如下所示:

组优先级

通过这种拆分,B 和 C 现在具有相同的组优先级。而 A 仍然具有更高的组优先级,这意味着它可以抢占 B 或 C。

Binary Point 寄存器仅影响抢占,即当正在处理一个中断时,另外一个中断是否触发处理。在选择挂起的中断时,不使用 Binary Point 寄存器。

中断结束

一旦中断处理完成,软件必须通知中断控制器中断,以便状态机可以切换到下一个状态。Arm CoreLink GICv3 架构将此视为两个任务:

  • 优先级下降。这意味着运行优先级降为该中断发生之前的值。
  • 停用。这意味着更新当前正在处理中断的状态机,通常是从活跃状态转换到非活跃状态。

在 GICv3 架构中,优先级下降和停用可以一起发生,也可以单独发生。这是由ICC_CTLR_ELn.EOImode的配置决定的:

  • EOImode = 0。对于Group 0 中断写入ICC_EOIR0_EL1,对于Group 1 中断写入ICC_EOIR1_EL1,这些会执行优先级下降和停用。该模式常用于简单的裸机环境。
  • EOImode = 1。对于Group 0 中断写入ICC_EOIR0_EL1,对于Group 1 中断写入ICC_EOIR1_EL1,这些会执行优先级下降。停用需要单独写入ICC_DIR_EL1,这通常用于虚拟化。

大多数软件都会使用EOIMode==0EOImode==1通常用于虚拟机。

检查最高优先级挂起中断和运行优先级

顾名思义,最高优先级挂起中断寄存器ICC_HPPIR0_EL1ICC_HPPIR1_EL1指明 PE 挂起的最高优先级中断 INTID。

运行优先级是在上面介绍的,由运行优先级寄存器(ICC_RPR_EL1)指明 。

检查各个 INTID 的状态

Distributor 提供寄存器,指示每个 SPI 的当前状态。类似地,Redistributors 提供寄存器,指示其连接 PE 的 PPI 和 SGI 状态。

这些寄存器还可以将中断移动到特定状态。例如,这对于测试配置是否正确十分有用,而不需要外设去断言中断。

有单独的寄存器来报告活跃状态和挂起状态。下表列出了活跃状态寄存器,挂起状态寄存器具有相同的格式:

寄存器描述
GICD_ISACTIVERn设置SPI的活动状态,每个INTID一个位
读取一位会返回中断的当前状态:
- 1:INTID处于活动状态
- 0:INITID处于非活动状态
写 1 激活相应的INTID,写 0 无影响
GICD_ICACTIVERn清除SPI的活动状态,每个INTID一个位
读取一位会返回中断的当前状态:
- 1:INTID处于活动状态
- 0:INITID处于非活动状态
写 1 激活相应的INTID,写 0 无影响
GICR_ISACTIVERn设置SGI和PPI的活动状态,每个INTID一个位,该寄存器涵盖INTID是0到31,是每个PE私有的
读取一位会返回中断的当前状态:
- 1:INTID处于活动状态
- 0:INITID处于非活动状态
写 1 激活相应的INTID,写 0 无影响
GICR_ICACTIVERn清除SGI和PPI的活动状态,每个INTID一个位,该寄存器涵盖INTID是0到31,是每个PE私有的
读取一位会返回中断的当前状态:
- 1:INTID处于活动状态
- 0:INITID处于非活动状态
写 1 激活相应的INTID,写 0 无影响

发送和接收SGI

软件触发中断 (SGI) 是软件通过写入中断控制器中的寄存器来触发的中断。

生成SGI

SGI是通过写入下面CPU接口中的SGI寄存器生成的:

系统寄存器接口描述
ICC_SGI0R_EL1生成Secure Group 0中断
ICC_SGI1R_EL1生成Group 1中断,针对安全状态PE
ICC_ASGI1R_EL1生成Group 1中断,针对非安全状态PE

下图是SGI寄存器的基本格式:

SGI寄存器

控制SGI ID

SGI ID 字段控制生成哪个 INTID。如中断类型中所述,INTID 0?15 用于 SGI。

控制中断目标

IRM(中断路由模式)字段控制 SGI 发送到哪个 PE,有两个选择:

  • IRM = 0

    中断发生到 ... ,其中 对于下的每个affinity 0节点按1位编码。这意味着中断最多可以发送到 16 个PE,其中可能包括原始生成中断的 PE。

  • IRM = 1

    中断发送到所有连接的 PE,除了原始 PE(自身)之外。

控制安全状态和分组

SGI 的安全状态和分组由以下控制:

  • 原始 PE 软件配置的 SGI register,ICC_SGI0R_EL1, ICC_SGI1R_EL1, or ICC_ASGIR_EL1。
  • 目标 PE 的 GICR_IGROUPR0 和 GICR_IGRPMODR0。

在安全状态下执行的软件可以发送安全和非安全 SGI。在非安全状态执行的软件能否生成安全的 SGI 是由 GICR_NSACR寄存器决定的。

SGI 寄存器只能在安全状态下访问。GIC 通过检查以下内容决定是否转发中断:

  • 生成中断 PE 的安全状态
  • 中断目标 PE 的处理配置
  • SGI 寄存器
发送PE的安全状态SGI寄存器接收PE的配置是否转发
Secure EL3/EL1ICC_SGI0R_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
Yes
No
No
Secure EL3/EL1ICC_SGI1R_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
No(*)
Yes
No
Secure EL3/EL1ICC_ASGIR_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
No
No
Yes
Non-secure EL2/EL1ICC_SGI0R_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
由GICR_NSACR配置(*)
No
No
Non-secure EL2/EL1ICC_SGI1R_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
由GICR_NSACR配置(*)
由GICR_NSACR配置
No
Non-secure EL2/EL1ICC_ASGIR_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
由GICR_NSACR配置(*)
由GICR_NSACR配置(
No
  • 该表假设GICD_CTLR.DS==0。当GICD_CTLR.DS==1时,标有(*)的SGI也会转发。

GICv3和GICv2的比较

在 Arm GICv2 中,SGI INTID 由原始 PE 和目标 PE 进行存储。这就意味着对于同一个中断标识符,一个给定的处理器可能会有最多八个实例同时处于待处理状态,因为每个处理器都可以产生一次中断。

在 Arm GICv3 中,SGI 仅由目标 PE 存储。这意味着给定的 PE 只能有一个 SGI INTID 实例处于挂起状态。

举个例子说明一下这些差异,PE A 和 PE B 同时向 PE C发送 SGI INTID 中断。

多个发送者示例

C会接收多少个中断?

  • 对于GICv2:两个中断

    GIC 将同时接收来自 A 和 B 的中断。两个中断的顺序取决于设计和精确的时序。这两个中断可以通过以下区分:INTID 带了一个发出中断 PE ID 的前缀,由GICC_IAR 返回。

  • 对于GICv3:一个中断

    由于原始 PE 不存储 SGI,因此同一中断不能在两个 PE 上挂起。因此,C 只接收1个中断,ID为5,无前缀。

该示例假设两个中断同时或几乎同时发送。如果 C 能够在第二个 SGI 到达之前确认第一个 SGI,那么 C 将在 GICv3 中看到两个中断。

示例

本指南附带了一个简单的示例。该示例配置系统计数器以生成系统计数,然后使用两个定时器根据系统计数生成中断。当每个定时器触发时,中断处理程序将禁用相关的定时器,以清除中断。

该示例需要 Arm Development Studio。如果您还没有,可以下载评估版

请参阅 zip 文件中的 ReadMe.txt 文件,了解构建和运行该示例的完整说明。

检查你的知识

  1. Arm GICv3 中有哪四种中断类型?如何识别?

    有四种类型:LPI,SPI,PPI和SGI。通过中断源 ID号 INTID 识别。

  2. 中断可以分配到哪些组,每个组通常用于什么?

    • Group 0 中断通常用于 EL3 固件。

    • Secure Group 1 中断通常用于安全状态。

    • Non-secure Group 1 中断通常用于针对非安全内核或虚拟机。

  3. 中断处理程序读取哪个寄存器来找出中断?

    ICC_IARn_EL1寄存器。

  4. 复位后,GIC 将所有连接的 PE 视为离线,软件如何将 PE 标记为在线?

    通过清除该 PE 的 Redistributor 中的GICR_WAKER.ProcessorSleep,然后轮询直到 ChildrenAsleep 读取为 0。

  5. GIC 存储中断源的哪些配置?

    • 使能
    • 优先级
    • 边沿触发或者电平触发
    • 目标(仅限SPI)
  6. PE 可以向自己发送 SGI 吗?

    是的。

  7. 如果PE正在处理一个中断,如何控制其他中断进行抢占?

    第二个中断的优先级和 Binary Point 寄存器:ICC_BPRn_EL1

相关信息

下一步

本指南介绍了 Arm CoreLink GICv3 和 v4 架构以及如何在裸机环境中初始化 GIC。本指南简要介绍了局部特定外设中断 (LPI) 中断类型。在 Arm CoreLink 通用中断控制器 v3 和 v4 指南:局部特定外设中断中,介绍了如何处理和初始化 LPI。

Arm CoreLink GIC 架构支持虚拟化。要了解有关 GICv3 和 GICv4 中虚拟化功能的更多信息,请阅读指南 Arm CoreLink 通用中断控制器 v3 和 v4:虚拟化。

附录:Legacy操作

相比 GICv2,GICv3 编程模型有些变化。为了保留对 GICv2 系统的兼容,GICv3 可以支持 legacy 操作。支持 legacy 操作是可选的并已弃用,Arm 的几个最新 GIC 实现不再支持它。

编程模型是由 Affinity Routing Enable(ARE) 的GICD_CTRL位控制的:

  • 当 ARE==0 时,affinity routing 关闭(legacy operation) 。
  • 当 ARE==1 时,affinity routing 使能(GICv3 operation) 。

在具有两个安全状态的系统中,每个安全状态可以单独控制 affinity routing。仅允许特定组合,如下所示:

支持的组合

欢迎关注“安全有理”微信公众号。

安全有理

文章来源:https://blog.csdn.net/qq_16106195/article/details/135276562
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。