1.概述
本文介绍:
- GICv3中断控制器的相关操作
- 如何在裸机环境下配置GICv3中断控制器
本文是_Arm CoreSight Generic Interrupt Controllers_相关指南的三部曲其一:
- Arm CoreLink Generic Interrupt Controller v3 and v4: Overview (this guide)
- Arm CoreLink Generic Interrupt Controller v3 and v4: Locality-specific Peripheral Interrupts
- Arm CoreLink Generic Interrupt Controller v3 and v4: Virtualization
1.1 背景
GIC两个至关重要的作用:优先级排序和路由。
GIC可以提高处理器效率以及使能中断虚拟化。
GICs的实现基于Arm GIC架构。这个架构由GICv1发展到最新的 GICv3 和 GICv4 版本。Arm设计了若干通用中断控制器,向Arm Cortex多处理器系统提供一系列的中断管理方案。这些控制器的范围从用于CPU核数较少的系统的最简单的GIC-400到用于高性能和多芯片系统的GIC-600。GIC-600AE为高性能ASIL B系统增加了额外的安全功能。
看完本文,你将学会:
- 不同类型的中断
- 能够编写代码,使能FIC以及配置不同的中断类型
2.开始之前
本指南涵盖了GICv3和v4的基本操作,以及共享外围中断(spi)、私有外围中断(PPIs)和软件生成中断(SGIs)的使用。本文是对Arm Generic Interrupt Controller Architecture Specification GIC architecture version 3.0 and 4.0的补充。
GICv3和GICv4允许不同的配置和用例。为了简单起见,本指南集中于这些配置和用例的子集,其中:
- 存在两种安全状态
- 每一种安全状态的Affinity路由使能
- 所有异常等级的系统寄存器访问使能
- 处理器兼容Armv8-A,所有异常等级都实现且每种异常等级都使用AArch64
本文不包括:
- Legacy operation
- 使用AArch32
前置知识:异常模型
3.什么是通用中断控制器(Generic Interrupt Controller)
GIC处理来自外设的中断,根据优先级排序,将中断分发到合适的处理器核。下图显示了一个GIC接收来自n个不同外设的中断,并将它们分配给两个不同的处理器。
GIC是Arm Cortex-A和Arm Cortex-R处理器的标准中断控制器。
3.1 Arm CoreLink GIC简史
主要特征 | |
---|---|
GICv1 | 支持8个处理单元 支持1020个中断号 支持两种安全状态 |
GICv2 | 所有GICv1的主要特征 支持虚拟化 |
GICv3 | 所有GICv2的主要特征 支持超过8个处理单元 支持消息信号中断 系统寄存器访问CPU接口寄存器 安全模型增强:分离了安全和非安全的Group 1中断 |
GICv4 | 所有GICv3的主要特征 直接注入虚拟中断 |
本文涵盖了Arm CoreLink GICv3和GICv4,它们被大多数Armv8-A和Armv8-R设计所使用。
版本小更新:
- GICv3.1:
- 增加了支持额外的有线中断,安全虚拟化和内存系统资源
- 分区和监视(MPAM)
- GICv4.1:
- 扩展的虚拟化支持,包括直接注入虚拟软件生成的中断(SGIs)
4.Arm CoreLink GIC基本原理
在本节中,我们将看看Arm CoreLink GICv3和v4中断控制器的基本操作。
4.1 中断类型
GIC可以处理四种不同类型的中断源:
- SPI:可以转发给任意相关的核
- PPI:属于某个核私有。例如通用计时器
- SGI(Software Generated Interrupt):典型用于处理器间通信。向GIC中的SGI寄存器写值将生成SGI。
- LPI(Locality-specific Peripheral Interrupt)(不太懂):Arm CoreLink Generic Interrupt Controller v3 and v4: Locality-specific Peripheral Interrupts一文中介绍了LPI的配置
每个中断源都由一个ID号标识,这个ID号被称为INTID。上述列表中引入的中断类型是根据intid范围定义的:
INTID | 中断类型 | 注释 |
---|---|---|
0 - 15 | SGIs | Banked per PE |
16 – 31 1056 – 1119 (GICv3.1) |
PPIs | Banked per PE |
32 – 1019 4096 – 5119 (GICv3.1) |
SPIs | |
1020 - 1023 | 特殊的中断号 | 详情见5.2 |
1024 - 8191 | Reserved | |
8192~ | LPIs | 上线由具体厂家定义 |
4.1.1 中断如何通知中断控制器
传统上,中断使用专用硬件信号从外设发送到中断控制器,如下图所示:
Arm CoreLink GICv3增加了一种信号机制:message-signaled interrupts (MSI)。MSI通过向中断控制器的寄存器写值发送。如下:
通过消息发送中断,中断源无需专门的信号。
这两种发送中断的方式对于中断如何处理几乎没有影响。可能需要一些对外设的配置。例如,可能需要指定中断控制器的地址。外设的配置不在本文范畴之内。
Arm CoreLink GICv3中,SPI可以是message-signaled中断。LPI总是message-signaled中断。不同的寄存器用于不同的中断类型,如下所示:
中断类型 | 寄存器 |
---|---|
SPI |
GICD_SETSPI_NSR 断言中断GICD_CLRSPI_NSR 解除中断 |
LPI | GITS_TRANSLATER |
4.2 中断状态机
中断控制器为每个SPI、PPI和SGI中断源维护一个状态机。这个状态机包含四个状态:
- Inactive:中断源当前没有断言
- Pending:中断源已经断言,但是没有PE接收。
- Active:中断源已经断言,且已被PE接收
- Active and Pending:一个中断已被接收,另一个中断在排队。
注意:LPIs有着类似的状态机。详情请查看6.2节。
状态机如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-syx4n2lg-1610113945096)(https://image-1304693124.cos.ap-shanghai.myqcloud.com/img/image-20210107213122412.png)]
中断的生命周期取决于它是被配置为电平触发还是边沿触发:
- 对于电平敏感的中断,在中断输入上的上升边缘会导致中断成为挂起,并且中断被保持,直到外围设备解除中断信号。
- 对于边缘敏感中断,在中断输入上的上升边缘会导致中断成为挂起,但是中断不会被保持。
4.2.2 边沿触发中断
下图显示了中断状态转换与中断信号的对应关系:
依次考虑每个状态转换:
-
从Inactive到pending:发生在中断源被断言的时候。如果中断使能且优先级适当,那么GIC就向PE断言了中断信号
-
从Pending到active:发生在PE通过读取CPU接口中的IAR寄存器接收中断的时候。这个读取通常发生在中断异常发生之后,属于中断处理过程的一部分。但是,软件也可以轮询IAR。这种时候GIC将解封(de-asserts)中断信号。
-
从Active到active and pending:发生在外设重新断言中断信号的时候。
-
从Active and pending到pending:发生在PE写某一个CPU接口中的EOIR寄存器的时候。此时PE已经处理完了第一个中断。此时GIC重新断言了一个中断信号。
4.3 Target interrupts
Arm架构中每个PE被分配了一个层级标识符,称作affinity。GIC通过affinity的值来确定中断将转发给哪一个特定的核。
affinity共32位,被分为四个区域:
<affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0>
每一个PE的affinity记录在MPIDR_EL1中。
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>
注意,AArch32状态,和Armv7-A智能支持三层affinity。GICD_TYPER.A3V指示断控制器是否支持多个level 3节点。
4.4 安全模型
Arm CoreLink GICv3架构支持Arm TrustZone技术。每一个INTID都要被软件指定一个组和安全度。GICv3支持三种设置的组合,如下所示。
中断类型 | 示例 |
---|---|
Secure Group 0 | EL3的中断(安全固件) |
Secure Group 1 | 安全EL1的中断(可信操作系统) |
Non-secure Group 1 | Non-secure状态的中断(OS或Hypervisor) |
Group 0的中断总是FIQ。
Group 1的中断可以是IRQ也可以是FIQ,取决于目前的安全状态和异常等级。如下所示:
EL和安全状态 | Group 0 | Secure Group 1 | Non-secure Group 1 |
---|---|---|---|
Secure EL0/1 | FIQ | IRQ | FIQ |
Non-secure EL0/1/2 | FIQ | FIQ | IRQ |
EL3 | FIQ | FIQ | FIQ |
这些规则是为了补充AArch64安全状态和异常级别路由控制而设计的。下图展示了一个简单的软件栈,以及当在EL0执行时,不同类型的中断被触发 会发生什么
在这个例子中,IRQ被路由到EL1(SCR_EL3.IRQ==0),FIQ被路由到EL3(SCR_EL3.FIQ==1)。考虑到上面描述的规则,当执行EL1或EL0时,当前安全状态的Group 1中断被视为IRQ。
用于其他安全状态的中断会触发FIQ,并将异常传递给EL3。这允许在EL3执行的软件执行必要的上下文切换。
4.4.1 对于软件的影响
在配置中断控制器时,软件控制给中断组分配INTID。只有在安全状态下执行的软件才能为中断组分配INTID。
通常,只有在安全状态下执行的软件才能访问安全中断的设置和状态:Group 0和Secure Group 1。
从非安全状态访问安全中断设置和状态能够被使能。使用GICD_NSACRn和GICR_NSACR寄存器分别为每个INTID控制。
注意:LPI总是被视为Non-secure Group 1中断。
4.4.2 支持单一安全状态
GICv3支持Arm TrustZone技术,但是TrustZone的使用是可选的。你可以选择配置以决定是使用单独的安全状态还是两个安全状态:
-
GICD_CTLR.DS == 0
两个安全状态
-
GICD_CTLR.DS == 1
只有一个安全状态
将GIC配置成与PE使用相同数目的安全状态。
4.5 编程模型
GICv3中断控制器的寄存器接口被分成三组:
- Distributor interface
- Redistributor interface
- CPU interface
这些接口如下图所示:
通常,Distributor和Redistributors用于配置中断,CPU接口处理中断。
4.5.1 Distributor(GICD_*)
Distributor寄存器是内存映射的,用于配置SPI。Distributor提供编程接口用于:
- 中断优先级排序、分发SPI
- 使能或禁止SPI
- 对每一个SPI设置优先级
- 为每个SPI路由
- 设置每一个SPI是电平触发还是边沿触发
- 生成message-signaled SPIs
- 控制SPI的活动和挂起状态
- 确定每一个安全状态下的编程模型:affinity routing或legacy
4.5.2 Redistributors (GICR_*)
每个相接的核都有一个Redistributor。Redistributor提供编程接口实现以下功能:
- 使能和关闭SGI和PPI
- 设置SGIs和PPIs的优先级
- 设置每个PPI为电平触发或边缘触发
- 将每个SGI和PPI分组
- 控制SGI和PPI的状态
- 控制内存中支持LPI的相关中断属性和挂起状态的数据结构的基址
- 为所连接的PE提供电源管理支持
4.5.3 CPU interfaces (ICC_*_ELn)
每个内核包含一个CPU接口,它是在中断处理期间使用的系统寄存器。CPU接口提供了一个编程接口实现以下功能:
- 提供通用控制和配置来启用中断处理
- 接收中断
- 实现中断的优先级删除和停用
- 为PE设置中断优先级mask
- 定义PE的抢占策略
- 为PE确定最高优先级的pending中断
在Arm CoreLink GICv3中,CPU接口寄存器作为系统寄存器来访问:ICC_*_ELn。
在使用这些寄存器之前,软件必须启用系统寄存器接口。这是由ICC_SRE_ELn寄存器中的SRE位控制的,其中“n”指定了异常级别:EL1-EL3。
5.配置Arm CoreLink GIC
本章讲述怎么在裸机环境下使能和配置GICv3中断控制器。
配置LPI与SPI、PPI和SGI有很大的不同。本文不会描述,可以参考Arm CoreLink Generic Interrupt Controller v3 and v4: Locality-specific Peripheral Interrupts一文。
使用GICv3控制器的大多为多核甚至多处理器系统。有些设置是全局的,有一些是针对于单个PE。
5.1 全局设置
分配器控制寄存器Distributor control register (GICD_CTLR)必须按照下面的原则启用中断组以及设置路由模式:
- Enable Affinity routing (ARE bits):GICD_CTLR中的ARE位控制GIC是在GICv3模式还是legacy模式下运行。legacy向后兼容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不支持legacy操作,故ARE位始终为1。
5.2 单个PE设置
5.2.1 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的中断被转发。
5.2.2 CPU接口配置
CPU接口负责向它所连接的PE发送中断异常。为了使能CPU接口,软件必须进行如下配置:
-
使能系统寄存器访问
4.5.3节描述了CPU接口寄存器,以及它们如何作为GICv3中的系统寄存器访问。软件必须使能访问CPU接口寄存器,通过设置ICC_SRE_ELn寄存器的SRE位。
注意:很多近期的Arm Cortex不支持legacy操作,所以SRE位固定为被设置。在这些处理器上这一步可以跳过。
-
设置优先级Mask和Binary Point寄存器
CPU接口包括Priority Mask寄存器(
ICC_PMR_EL1
)和Binary Point寄存器(ICC_BPRn_EL1)。优先级掩码 设置 为了转发到PE中断必须具有的 最小优先级。
二进制点寄存器用于优先级分组和抢占。
第六章进一步讲述了两个寄存器的用法。
-
设置EOI模式
CPU接口中的
ICC_CTLR_EL1
和ICC_CTLR_EL3
中的EOImode位控制了一个中断的完成是怎么处理的。6.4节有进一步的描述
-
使能每个中断组的信号
每个中断组的信号必须开启后,该中断组的中断才会通过CPU接口转发到PE。
为了使能信号,软件必须写ICC_IGRPEN1_EL1寄存器以实现Group 1中断,写ICC_IGRPEN0_EL1寄存器以实现Group 0中断。
ICC_IGRPEN1_EL1
属于安全状态。在EL3,软件可以使用ICC_IGRPEN1_EL3访问Group 1。
5.2.3 PE配置
我们将描述在AArch64状态下执行Armv8-A兼容PE所需的基本步骤。
-
路由控制
中断路由控制信息在SCR_EL3和HCR_EL2中。路由控制位决定了中断将发往那个异常的等级。路由位在复位时值未知,所以需要被软件初始化。
-
中断掩码
PSTATE中有异常掩码位。当置位时,中断被屏蔽。复位时将置位。
-
向量表
由VBAR_ELn寄存器设置PE向量表所在位置。VBAR_ELn复位时的值不确定。软件必须将VBAR_ELn寄存器设置为指向内存中适当的向量表。
5.2.4 SPI, PPI和SGI的配置
我们将讨论各个中断源的配置。
哪些寄存器用于配置中断取决于中断的类型:
- spi是通过Distributor配置的,使用GICD_*寄存器。
- PPIs和SGIs使用GICR_*寄存器通过单独的Redistributors进行配置
这些不同的配置机制如下图所示:
对于每个INTID,软件必须配置如下:
-
Priority: GICD_IPRIORITYn, GICR_IPRIORITYn
每个INTID都有一个相关的优先级,用8位无符号值表示。0x00是可能的最高优先级,0xFF是可能的最低优先级。
6.3节描述了ICD_IPRIORITYn和GICR_IPRIORITYn中的优先级值如何屏蔽低优先级中断,以及如何控制抢占。中断控制器不需要实现所有8个优先级位。如果GIC支持两种安全状态,则必须至少实现5位。如果GIC只支持单一的安全状态,则必须实现至少4位。
-
Group: GICD_IGROUPn, GICD_IGRPMODn, GICR_IGROUPn, GICR_IGRPMODn
可以通过这些寄存器设为三种Group中的一个
-
Edge-triggered or level-sensitive: GICD_ICFGRn, GICR_ICFGRn
对于PPIs和SPI,软件必须指定中断是边缘触发还是电平触发。SGIs总是被视为边沿触发的,因此对于这些中断,GICR_ICFGR0表现为Read-As-One, Writes Ignored (RAO/WI)。
-
Enable: GICD_ISENABLERn, GICD_ICENABLER, GICR_ISENABLERn, GICR_ICENABLERn
每个INTID都有一个使能位。设置启用寄存器和清除启用寄存器消除了执行读-修改-写例程的要求。Arm建议在启用INTID之前配置本节中概述的设置。
对于裸机环境,通常在初始配置后不需要更改设置。但是,如果一个中断必须被重新配置,例如改变组设置,你应该在改变它的配置之前先禁用中断。
大多数配置寄存器的reset值是厂家定义的。这意味着中断控制器的设计者决定这些值是什么,这些值在不同的系统中可能会有所不同。
5.2.5 Arm CoreLink GICv3.1和扩展的INTID范围
Arm CoreLink GICv3.1增加了对其他SPI和PPI INTID的支持。配置这些中断的寄存器与原始中断范围相同,只是它们有一个E后缀。如:
GICR_ISENABLERn | 原PPI的范围的使能位 |
---|---|
GICR_ISENABLERnE | 额外的PPI的使能位 |
5.2.6 设置SPI对应的PE
对于spi,必须配置中断的目标。由GICD_IROUTERn
或GICv3.1扩展的GICD_IROUTERnE
进行控制。每个SPI有一个GICD_IROUTERn
寄存器,且Interrupt_Routing_Mode
位控制着路由策略。如下:
-
GICD_IROUTERn.Interrupt_Routing_Mode == 0
SPI被交付给PE A.B.C.D,这是在寄存器中指定的 affinity co-ordinates。
-
GICD_IROUTERn.Interrupt_Routing_Mode == 1
SPI可以发送给任何相关的PE,这些PE参与了中断分组的分发。由Distributor而不是软件选择目标PE。因此,每次发出中断信号的时候,目标都可以改变,这种类型的路由称为1-of-N。
PE可以选择不接收1-of-N中断。这是由
GICR_CTLR
中的DPG1S、DPG1NS和DPG0位控制的。
6.处理中断
本节描述中断发生时发生的事情:例如,中断如何路由到PE,中断如何对彼此进行优先排序,以及在中断结束时发生了什么。
6.1 路由一个挂起的中断到PE
4.2节描述了中断的源被断言时,中断是如何从inactive状态转换到pending状态的。当中断挂起的时候,中断控制器根据下面的测试情况决定是否将中断发送到相连的PE上。下面的测试决定将中断发给哪一个PE:
-
检查与中断相关的组是否启用。
4.4描述了每个INTID是怎么分配给Group中的。对于每个组,在Distributor和每个CPU接口中都有一个Group使能位。
中断控制器检查是否为与该中断的INTID相关联的组设置了组使能位。
如果所在的Group被禁用了,那么中断不能向PE发送信号。这些中断将被挂起知道相应的Group使能。
-
检查中断是否启用。
单独禁用的中断可能会挂起但是不会发往PE。
-
检查路由控制以决定哪个PE可以接收中断。
哪些PE能接收到中断取决于正在发送哪种类型的中断:
- 对于SPI,路由由
GICD_IROUTERn
控制。一个SPI可以发往特定的PE,或者是相连的任何PE。 - 对于LPI而言,路由信息来自ITS。
- PPI只能发往一个PE且只能被该PE所处理。
- 对于SGI,源PE决定可以发往的PE。在第7章将进一步描述。
- 对于SPI,路由由
-
检查中断优先级和优先级掩码,以决定哪些PE适合处理中断
每个PE在其CPU接口中都有一个优先级掩码寄存器
ICC_PMR_EL1
。这个寄存器设置中断被转发到那个PE所需要的最小优先级。只有优先级高于掩码的中断才会通知PE。 -
检查运行优先级,以决定哪些PE可用来处理中断
6.3节包括运行优先级,以及它如何影响抢占。如果PE还没有处理中断,运行优先级是空闲优先级:0xFF。只有优先级高于运行优先级的中断才能抢占当前中断。
如果中断通过了所有这些测试,它将作为IRQ或FIQ异常转发到适当的核。可以回顾下5.2.6节。
6.2 获取中断
当进入异常处理程序时,软件不知道它所接受的是哪个中断。处理程序必须读取一个Interrupt Acknowledge Registers(IARs)来获取中断的INTID。
有两个IAR:
寄存器 | 用途 |
---|---|
ICC_IAR0_EL1 | 用于获取Group 0中断。通常在FIQ处理程序中读取。 |
ICC_IAR1_EL1 | 用于确认第一组中断。通常用于IRQ处理程序中。 |
读取IAR将返回已获取的中断的INTID,并推进中断的状态机。通常,IARs是在中断处理程序进入时读取的。然而,软件在任何时候都可以*读取寄存器。
有时,IAR不能返回一个有效的INTID。例如,读取ICC_IAR0_EL1,得知Group 0发生中断,但是挂起的中断属于Group 1。这种情况下,将读到一个保留的INTID,如下所示:
ID | 意义 | 场景案例 |
---|---|---|
1020 | 仅通过读取ICC_IAR0_EL1返回 最高挂起的中断是Secure Group 1。 只能被EL3查看。 |
当PE在非安全状态下执行时,一个受信任操作系统的中断被通知。这被当作到EL3的FIQ,这样安全监视器就可以上下文切换到受信任的操作系统。 |
1021 | 仅通过读取ICC_IAR0_EL1返回。 最高挂起的中断是Non-secure Group 1。 只能被EL3查看。 |
当PE在安全状态下执行时,一个属于rich OS的中断信号发生。这将被视为到EL3的FIQ,这样安全监视器就可以上下文切换到rich OS。 |
1022 | 仅用于legacy操作 | 遗留操作超出了本文的范围。 |
1023 | 虚假的中断。 没有所启用的INTID处于挂起状态,或者该挂起状态中的所有intid都没有足够的优先级需要获取。 |
当轮询IARs时,这个值表示没有中断可以确认。 |
读取IAR返回这些值的话,并不获取中断。
6.2.1 中断处理的例子
下图展示了一个手机系统。当有一个来电时,发生了一个中断。这个中断将由非安全状态下的Rich OS进行处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-id0dp2EX-1610113945109)(https://image-1304693124.cos.ap-shanghai.myqcloud.com/img/image-20210108202204721.png)]
处理中断的步骤如下:
- 当PE在Secure EL1执行Trusted OS时,中断被挂起。因为中断被配置为Non-secure Group 1,它将作为FIQ信号。因为SCR_EL3.FIQ==1,异常被发向EL3。
- EL3的安全监控软件读取IAR,返回值为1021。这个值表示中断希望在非安全状态下处理。安全监控软件切换上下文。
- PE现在是非安全状态,中断将被视作IRQ,并发向Non-secure EL1,被Rich OS处理。
在本例中,Non-secure Group 1中断导致立刻退出Secure OS。可能有些情况下我们不希望发生这种情况。下面是一个替代的模型案例,中断先被发向Secure EL1:
处理中断的步骤如下:
- 当PE在Secure EL1上执行的时候,中断挂起。因为中断被配置为Non-secure Group 1,它将被视作FIQ信号。因为SCR_EL3.FIQ==0,异常将发往Secure EL1。
- Trusted OS执行清理其内部状态的操作。就绪后,Trusted OS使用SMC指令转向Non-secure状态。
- SMC异常被发往EL3,EL3的安全监控软件执行必要的上下文切换。
- 现在PE处于非安全状态,中断被作为IRQ发出的信号,被Rich OS处理
6.3 运行优先级和抢占
Priority Mask寄存器设置了中断要发往PE所需的最低优先级。GICv3架构具有运行优先级的概念。当PE获取中断,它的运行优先级将和这个中断的优先级一样高。当PE写一个EOI寄存器时,运行优先级返回到它之前的值。下图显示了一个PE的运行优先级随时间变化的示例:
当前的运行优先级在CPU接口的Running Priority寄存器ICC_RPR_EL1
中上显示。当一个高优先级中断被通知给已经在处理一个低优先级中断的PE时,抢占发生。抢占给软件带来了额外的复杂性,但是它可以防止低优先级中断阻塞处理高优先级中断。
下面的图表显示了如果不允许抢占会发生什么:
高优先级中断被阻塞,直到先前发出信号的低优先级中断被占用。现在考虑相同的情况,但是启用了抢占:
Arm CoreLink GICv3架构允许软件通过指定抢占发生所需的优先级差异来控制抢占。由Binary Point寄存器来控制: ICC_BPRn_EL1
。
Binary Point寄存器将优先级划分为两个区域,group priority 和 sub-priority:
抢占时,只考虑组优先级位。次优先级位被忽略。
例如,考虑下面三种中断:
- INTID A has priority 0x10.
- INTID B has priority 0x20.
- INTID C has priority 0x21.
在这个例子中,我们决定:
- A能抢占B、C
- B不能抢占C,B,C优先级相同
为了实现这一点,组和次优先级之间的划分可以设置为N=4,如下:
Binary Point只影响抢占。即,在处理另一个中断的时候是否要发送当前中断的信号。Binary Point不用于在挂起的中断中进行选择。
注意:抢占机制要求中断处理支持嵌套。
6.4 中断结束
一旦中断被处理,软件必须通知中断控制器中断已经被处理,以便状态机可以转换到下一个状态。Arm CoreLink GICv3架构将此视为两个任务:
-
Priority drop
这意味着将优先级调低至中断前的优先级
-
Deactivation
这意味着更新当前正在处理的中断的状态机。通常将导致Active state到the Inactive state的状态切换。
GICv3架构中,priority drop和deactivation可一起发生或是单独发生,由ICC_CTLR_ELn.EOImode决定:
-
EOImode = 0
写ICC_EOIR0_EL1和ICC_EOIR1_EL1都将一起执行priority drop和deactivation。这种模式通常在裸机环境执行。
-
EOImode = 1
写ICC_EOIR0_EL1和ICC_EOIR1_EL1将导致优先级回落。Deactivation需要写ICC_DIR_EL1。虚拟化通常采用这种模式。
大多数软件将使用EOIMode==0。EOImode==1最常被hypervisor使用。
6.5 检查当前系统的状态
6.5.1 挂起中断的最高优先级和运行优先级
Highest Priority Pending Interrupt寄存器ICC_HPPIR0_EL1
和ICC_HPPIR1_EL1
,传递优先级最高的挂起的中断的INTID。
ICC_RPR_EL1
传递运行优先级。
6.5.2 个体INTID的状态
Distributor提供每个SPI的当前状态的寄存器。类似的,Redistributor指示相连的PPI和SGI的状态的寄存器。
这些寄存器能够将中断移动到特定的状态。例如,用于在没有外设断言中断的情况下,测试配置是否正确。
有单独的寄存器来报告活动状态和挂起状态。下表列出了活动状态寄存器。挂起状态寄存器有相同的格式:
寄存器 | 描述 |
---|---|
GICD_ISACTIVERn | 设置SPI的活动状态 每个INTID占用一个bit 返回值表示INTID状态: 1表示INTID active 0表示INTID非激活 写1表示激活相应的INTID 写0没有任何用处 |
GICR_ISACTIVERn | 设置SGIs和PPI的激活状态 每个INTID占用一个bit 返回值表示INTID状态: 1表示INTID active 0表示INTID非激活 写1表示激活相应的INTID 写0没有任何用处 |
GICR_ICACTIVERn | 清除SGIs和PPIs的激活状态 每个INTID占用一个bit 返回值表示INTID状态: 1表示INTID active 0表示INTID非激活 写1表示解激活 写0没有用处 |
注意:非安全状态下执行的软件不能看到Group 0和Secure Group 1的中断的状态,除非由GICD_NASCRn 或GICR_NASCRn允许。
7.发送和接收Software Generated Interrupts
软件生成中断(SGIs)是软件可以通过写入中断控制器中的寄存器来触发的中断。
7.1 生成SGI
一个SGI是通过在CPU接口中写入以下SGI寄存器来生成的:
系统寄存器接口 | 描述 |
---|---|
ICC_SGI0R_EL1 | 生成Secure Group 0中断 |
ICC_SGI1R_EL1 | 在PE当前的安全状态下,生成Group 1中断 |
ICC_ASGI1R_EL1 | 在其他PE的安全状态下,生成Group 1中断 |
你可以在下图中看到SGI寄存器的基本格式:
7.1.1 控制SGI ID
SGI ID字段控制生成哪一个INTID。参考4.1,SGI的INTID范围为0-15。
7.1.2 控制目标
IRM(中断路由模式)字段在SGI寄存器控制一个SGI被发送到哪些PE。有两种选择:
-
IRM = 0
中断被发向:<aff3>.<aff2>.<aff1>.<Target List>,其中每个<aff1>下的affinity 0节点在<target list>被编码为1位。这意味着中断最多可发往16个PE。
-
IRM = 1
中断被发往所有相连的PE,除了源PE。
7.1.3 控制安全状态和分组
SGI的安全状态和分组通过下面进行控制:
- SGI寄存器:ICC_SGI0R_EL1, ICC_SGI1R_EL1, 或 ICC_ASGIR_EL1。由源PE上的软件进行写操作
- 目标PE的
GICR_IGROUPR0
和GICR_IGRPMODR0
寄存器。
在安全状态下执行的软件可以发送安全的和不安全的SGI。在非安全状态下运行的软件是否能够生成安全的SGIs由GICR_NSACR
控制。该寄存器只能被在安全状态下执行的软件访问。下表显示GIC通过以下检查来决定中断是否被转发:
- 发起PE的安全状态
- 中断所针对的PE的中断处理的配置
- SGI寄存器
发送方PE的安全状态 | 写入的SGI寄存器 | 接收方PE的配置 | 发送? |
---|---|---|---|
Secure EL3/EL1 | ICC_SGI0R_EL1 | Secure Group 0 Secure Group 1 Non-secure Group 1 |
是 否 否 |
ICC_SGI1R_EL1 | Secure Group 0 Secure Group 0 Non-Secure Group 1 |
否(*) 是 否 |
|
ICC_ASGI1R_EL1 | Secure Group 0 Secure Group 0 Non-Secure Group 1 |
否 否 是 |
|
Non-secure EL2/EL1 | ICC_SGI0R_EL1 | Secure Group 0 Secure Group 0 Non-Secure Group 1 |
由GICR_NSACR配置 (*) 否 否 |
ICC_SGI1R_EL1 | Secure Group 0 Secure Group 0 Non-Secure Group 1 |
由GICR_NSACR配置 (*) 由GICR_NSACR配置 是 |
|
ICC_ASGI1R_EL1 | Secure Group 0 Secure Group 0 Non-Secure Group 1 |
由GICR_NSACR配置 (*) 由GICR_NSACR配置 否 |
这张表假设GICD_CTLR.DS==0。当GICD_CTLR.DS==1时,(*)标记的总是转发。
7.2 比较GICv3和GICv2
在Arm CoreLink GICv2中,SGI的INTID好归属于源PE和目的PE。这意味着一个给定的PE中,相同的SGI INTID可能同时存在,最多8个。
Arm CoreLink GICv3中,SGI只属于目的PE。
让我们用一个例子来说明这一区别。
PE A和B同时发送INTID为5的SGI给PE C,如下:
C会收到多少中断?
-
GICv2:2个
GIC将同时接收来自A和B的中断。两个中断的顺序取决于设计和到达的时间。可以通过以下方法区分这两个中断:GICC_IAR中返回的值中,PE的ID是INTID的前缀。
-
GICv3:1个。因为源PE不存储SGI,相同的中断不能挂起在两个PE上。因此,C只接收到一个ID为5的中断,没有前缀。
该示例假设两个中断是同时或几乎同时发送的。如果C能够在第二个SGI到达之前确认第一个SGI,那么C将在GICv3中看到两个中断。
注意:在legacy操作中,即当GICD_CTLR.ARE=0时,SGIs的行为与Arm CoreLink GICv2相同。
参考
https://developer.arm.com/architectures/learn-the-architecture/arm-corelink-generic-interrupt-controller-v3-and-v4-overview