当前位置:网站首页>GICv3/v4-软件概述
GICv3/v4-软件概述
2022-08-02 14:08:00 【tupelo-shen】
目录内容
- 2 介绍→主要介绍GIC架构历史
- 3 GICv3基础→概念理解,尤其是编程模型的理解
- 4 GIC配置→如何配置GIC的各种寄存器,使其正常工作
- 5 处理中断→讲解中断的处理流程
- 6 LPI配置→理解ITS服务和基于消息的中断
- 7 SGI中断→如何发送接收软中断
- 8 虚拟化→如何在虚拟化环境下管理虚拟中断
- 9 GICv4→虚拟LPI的直接注入
2 介绍
2.1 范围
GICv3可以支持许多不同的配置和使用场景。为了简化,本文只关注其中的一个子集,该场景下:
存在2个安全状态
2个安全状态都使能中断路由
所有异常级都能访问系统寄存器
所有的处理器遵循
ARMv8-A架构,实现所有的异常级并且都运行在AArch64状态下
本文档不包括:
旧操作(除了本章中提到的)
运行在
AArch32状态下的情况
2.2 GIC架构的历史
GICv3添加了几个新特性。下表列出了不同版本的GIC架构的关键特性比较。
| 版本 | 关键特性 | 搭配的CPU核 |
|---|---|---|
| GICv1 | 最多支持8个PE 最多支持 1020中断ID 支持两种安全状态 | Cortex-A5 MPCore Cortex-A9 MPCore Cortex-R7 MPCore |
| GICv2 | GICv1所有特性支持虚拟化 | Cortex-A7 MPCore Cortex-A15 MPCore Cortex-A53 MPCore Cortex-A57 MPCore |
| GICv3 | GICv2所有特性支持超过8个PE 支持基于消息的中断 支持超过 1020中断IDCPU interface是系统寄存器增强安全模型,独立的安全Group1中断和非安全Group1中断 | Cortex-A53 MPCore Cortex-A57 MPCore Cortex-A72 MPCore |
| GICv4 | GICv3所有特性虚拟中断的直接注入 | Cortex-A53 MPCore Cortex-A57 MPCore Cortex-A72 MPCore |
GICv2m是GICv2的一个扩展,用来支持基于消息的中断,更多信息请联系ARM公司咨询。
2.3 GICv3架构的实现
ARM CoreLink GIC-500是GICv3的实现。ARM Cortex-A53, ARM Cortex -A57和ARM Cortex-A72等实现需要的CPU接口。
2.4 旧版本支持
GICv3对编程模型做了许多改变。为了支持基于GICv2的旧软件,GICv3支持旧操作。
编程模型可以通过GICD_CTRL寄存器中的亲和力路由使能(ARE)标志位进行控制:
ARE == 0,亲和力路由被禁止(旧操作);ARE == 1,亲和力路由被使能。
注意:为了可读性,在本文档中,
GICD_CTLR.ARE_S和GICD_CTLR.ARE_NS统称为ARE。
在系统的两个安全状态下,亲和力路由可以分别控制。只有特定的组合是允许的,如下图所示:
图-支持的ARE组合
本文档着重关注GICv3编程模型,也就是两种安全状态下ARE=1的情况。旧方式ARE==0不讨论。
对旧方式的支持是可选的。当实现对旧方式的支持时,复位选择旧方式。
3 GICv3基础知识
本章描述了兼容GICv3架构的中断控制器的基本操作。另外,包括不同的编程接口(也就是寄存器)。
3.1 中断类型
GICv3中断类型:
SPI-共享外设中断:全局外设中断,可以路由到某个PE,也可以是一组PE。PPI-私有外设中断:单个PE私有的中断。一个例子就是PE的通用定时器产生的中断。SGI-软件产生中断:通常用于核间通信,通过写GIC中的SGI寄存器产生。LPI(Locality-specific Peripheral Interrupt)-基于消息的中断:这是GICv3新引入的,它的方式不同于其它类型的中断。也就是说,这类中断是通过写内存产生的,而不是传统的使用中断线的方式。详细的描述在第6章。注意:
LPI需要GICD_CTLR.ARE_NS==1。
3.1.1 中断ID
每个中断源使用一个中断号(ID)标识,称之为INTID。每种类型的中断都有一段可用的中断号范围,如下表所示:
| INTID | 中断类型 | 注解 |
|---|---|---|
0-15 | SGI | 每个PE都会备份 |
16-31 | PPI | 每个PE都会备份 |
32-1019 | SPI | - |
1020-1023 | 特殊中断号 | 用于表示特殊情况,参考第5.3小节 |
1024-8191 | 保留 | - |
>=8192 | LPI | 具体上限由实现厂商定义 |
3.1.2 中断如何给中断控制器发送信号
通常,中断使用专用硬件信号从外设发送信号给中断控制器。

GICv3支持这种模型,并且支持基于消息的中断。基于消息的中断是通过写中断控制器的寄存器进行设置和清除操作的。
通过互联协议实现的基于消息的中断
使用消息转发外设到GIC的中断,移除了每个中断源必须一个专用信号的要求限制。这对于大型系统的硬件设计人员来说是一个优势,在大型系统中,可能有数百甚至数千个信号汇聚到中断控制器上。
GICv3中,SPI可以是基于消息的中断,但是LPI总是基于消息的中断。它们分别使用不同的寄存器进行设置。
| 中断类型 | 寄存器 |
|---|---|
SPI | GICD_SETSPI_NSR声明中断GICD_CLRSPI_NSR清除中断 |
LPI | GITS_TRANSLATER |
3.1.3 基于消息的中断对软件的影响
至于使用消息,还是使用一个专用信号发送中断,对软件处理中断的方式几乎没有什么影响。
但是可能需要对外设进行一些必要的设置,比如,指定中断控制器的地址。这超出了本文档的范围,故不在此描述。
3.2 中断状态机
中断控制器为每一个SPI、PPI和SGI中断维护着一个状态机。包含4种状态:
Inactive:中断还未发生Pending:中断已经发生,但是中断还没有被PE应答Active:中断已经发生,也已经被PE应答过Active和Pending:一个中断被应答过,另一个中断被挂起中
注意:LPI中断没有
active或active&pending状态。更多信息,参考第6.2节。
下图描述了状态机的状态转换:
中断的生命周期依赖于它被配置为电平触发还是边沿触发的。第3.2.1和3.2.2小节提供了采样序列。
3.2.1 电平触发

AP代表active和pending。
Inactive→Pending当中断源产生信号时,就会从
Inactive转变为Pending状态。从此刻开始,GIC发送中断信号给PE(如果中断被使能并且有足够的优先级)。Pending→Active & Pending当PE通过读取IAR寄存器(中断应答寄存器)应答该中断时,就会由
Pending转变为Active & Pending状态。这种读取操作一般是中断处理程序的一部分。当然,软件也可以轮询IAR寄存器。此时,GIC解除给PE的中断信号。
Active & Pending→Active当外设解除给GIC的中断信号后,由
Active & Pending转变为Active状态。这通常发生在PE上的中断处理程序写外设的状态寄存器时(作为写状态寄存器的响应)。Active→Inactive当PE写EOIR寄存器(中断结束寄存器)时,由
Active转变为Inactive状态。这表明PE已经完成中断的处理。
3.2.2 边沿触发

边沿触发中断的生命周期
Inactive→Pending当中断源产生信号时,就会从
Inactive转变为Pending状态。从此刻开始,GIC发送中断信号给PE(如果中断被使能并且有足够的优先级)。Pending→Active当PE通过读取IAR寄存器应答了中断后,中断的状态
Pending转变为Active。这种读取操作通常是中断处理程序(中断异常发生后)的一部分。当然,软件也可以轮询IAR寄存器。此时,GIC解除了给PE的中断信号。
Active→Active & Pending如果外设重新产生该中断信号,则会由
Active状态转变为active & pending。Active & Pending→Pending当PE对某个EOIR寄存器进行写操作时,中断会由
Active & Pending状态转变为Pending状态。这表明,PE已经处理完了第一次中断。此时,
GIC重新给PE发送中断信号。
3.3 亲和力路由
GICv3使用亲和力路由识别连接的PE,并将中断路由到某个PE或某组PE上。PE的亲和力通过4个8位字段表示:
<亲和力3>.<亲和力2>.<亲和力1>.<亲和力0>
下图是一个亲和力层次结构
图-一个亲和力层次架构的示例
亲和力0对应于Redistributor。也就是说,每个Redistributor连接到一个单独的CPU接口上。Redistributor可以控制SGI、PPI和LPI,参见第4章。
该亲和力方案与ARMv8-A中的相一致,与MPIDR_EL1寄存器中保存的PE的亲和力匹配。系统设计者必须保证MPIDR_EL1中的亲和力值等于GICR_TYPER中的值,后者是Redistributor和连接PE的对应关系。
不同级别的亲和力其真实意义由具体的处理器或SoC定义。下面是一个示例:
<group of groups>. <group of processors>.<processor>.<core>
<group of processors>.<processor>.<core>.<thread>
将所有可能节点在单个SoC都实现,往往不太现实。下面是一个移动设备SoC的布局:
0.0.0.[0:3] Cortex-A53处理器的0到3核
0.0.1.[0:1] Cortex-A57处理器的0到1核
在ARMv8-A中,AArch64状态支持4级亲和力。AArch32状态和ARMv7架构只支持3级亲和力。意思就是亲和力3上只有一个节点(0.x.y.z)。GICD_TYPER.A3V表明中断控制器是否支持多个亲和力3上的节点。
虽然每个L1级亲和力的节点上,最多可以承载256个
redistributor(L0级),但实际上,一般是16个或者更少。这是因为SGI中断的目标PE的编码方式,具体参考第7章。
3.4 安全模型
GICv3架构支持ARM的TrustZone技术。每个INTID必须分配到一个组(group)中,并且设置安全状态,如下表所示。
表 - 安全和分组
| 中断类型 | 示例使用 |
|---|---|
| Secure Group 0 | EL3中断(安全固件) |
| Secure Group 1 | Secure EL1中断(可信OS) |
| Non-secure Group 1 | 非安全状态的中断(OS和/或hypervisor) |
Group 0中断总是以FIQ的形式发送信号。Group 1既可以IRQ的形式,也可以以FIQ的形式发送信号,依赖于PE当前安全状态和异常等级。
表 - 安全设置和异常类型以及中断的对应关系(EL3使用AArch64)
| PE的异常等级和安全状态 | Secure 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 |
这些规则被设计用来补充ARMv8-A安全状态和异常级路由控制。下图展示了一个简化的软件栈,以及当在EL0执行不同类型的中断时发生的情况:
在本示例中,IRQ被路由到EL1(SCR_EL3.IRQ==0),FIQ被路由到(SCR_EL3.FIQ==1)。按照上表描述的规则,当在EL1或EL0执行当前安全状态的group 1中断时被视为一个IRQ。
非当前安全状态的中断一律视为FIQ,被EL3捕获。这样的设计,允许EL3的软件执行必要的上下文切换。这种情况的详细示例请参考第5.3节。
3.4.1 对软件的影响
在配置中断控制器时,软件负责将INTID分配给某个中断组。只有执行在安全状态的代码能够分配INTID给中断组。
通常只有执行在安全状态的软件能够访问安全中断的设置和状态(Group 0和Secure Group 1)。
从非安全状态设置安全中断并访问其状态是能够使能的。可以使用GICD_NSACRn和GICR_NSACR寄存器对每一个INTID单独进行控制。
注意1:在复位时,
INTID所属的中断组是SoC实现时定义的。注意2:LPI总是被视为非安全组1中断。
3.4.2 单个安全状态的支持
对于ARMv8-A和GICv3,两种安全状态是可选择的。所以,SoC在实现的时候,可以选择实现单个安全状态,也可以选择实现两个安全状态。
在GICv3实现中,支持两种安全状态。可以通过GICD_CTLR.DS控制使用几个安全状态。
GICD_CTLR.DS == 0:支持两个安全状态(Secure和Non-secure)。GICD_CTLR.DS == 1:只支持一个安全状态。在只实现了一个安全状态的实现中,该位是RAO/WI。
当只支持单个安全状态时,中断分为2组,Group 0和Group 1。
当然,本文档主要描述支持两种安全状态的实现。
注意:如果设置
GICD_CTLR.DS为1,则只能在复位时才能清除。
3.5 编程模型
GICv3中断控制器的寄存器主要分为3组:
Distributor接口Redistributor接口CPU接口
3.5.1 Distributor(GICD_*)
Distributor寄存器是内存寄存器,包含影响所有PE的全局设置。包括:
SPI中断优先级和分发
使能、禁止SPI中断
配置各个SPI中断的优先级
每个SPI中断的路由信息
配置各个SPI中断的触发方式(电平或边沿)
产生基于消息的SPI中断
控制SPI中断的
active和pending状态确定各个安全状态中使用的编程模型(亲和力路由还是旧方式)
3.5.2 Redistributor(GICR_*)
每个PE对应一个redistributor。redistributor提供的功能:
使能、禁止SGI和PPI中断
配置SGI和PPI中断的优先级
配置PPI中断的触发方式(电平或边沿)
给每一个SGI和PPI中断分配一个中断组
控制SGI和PPI中断的状态
控制LPI中断属性和挂起状态的数据结构在内存中的基地址
每个PE的电源管理
3.5.3 CPU接口(ICC_*_ELn)
每个redistributor连接到CPU接口。CPU接口提供的功能:
使能中断处理的控制和配置
应答中断
去掉中断的优先级并失效中断
为PE配置中断优先级掩码
为PE配置抢占策略
为PE确定最高优先级的挂起中断
GICv3中,CPU interface寄存器都是系统寄存器(ICC_*_ELn)。
软件必须在使用这些寄存器之前使能这些寄存器的访问。这是由ICC_SRE_ELn寄存器的SRE标志位控制的,在这儿,n表示异常级别(EL1-EL3)。
注意1:
GICv1和GICv2中,CPU接口寄存器是映射到内存上的(GICC_*)。注意2:可以通过读取寄存器
ID_AA64PFR0_EL1查看是否支持GIC系统寄存器,具体可以参考ARMv8-A架构规范。
4 配置GIC
本章描述如何在裸机环境下使能和配置兼容GICv3的中断控制器。详细的寄存器描述请参考《ARM Generic Interrupt Controller Architecture Specification GIC architecture version 3.0 and 4》
LPI中断的配置和SPI、PPI和SGI存在着较大差异,因此,专门在第6章描述。
使用GICv3中断控制器的大部分是多核系统,或是多处理器系统。有些配置是全局的,也就是说,会影响所有连接的PE。其它的配置是专门针对单个PE的。
我们首先看一下全局配置,然后是每个PE的设置。
4.1 全局配置
Distributor的控制寄存器(GICD_CTLR)可以控制中断分组的使能,并设置路由模式。
使能亲和力路由(ARE标志位)
GICD_CTLR的ARE标志位控制是否使能亲和力路由。如果没有使能,GICv3使用旧操作方式。亲和力路由的使能,在安全和非安全状态下是分开控制的。分发器使能
GICD_CTLR包含Group 0、Secure Group 1和Non-secure Group 1的独立使能位:GICD_CTLR.EnableGrp1S使能Secure Group 1中断的分发;GICD_CTLR.EnableGrp1NS使能Non-secure Group 1中断的分发;GICD_CTLR.EnableGrp0使能Non-secure Group 0中断的分发;
4.2 单个PE配置
4.2.1 Redistributor配置
复位时,Redistributor将其连接的PE视为休眠状态。唤醒是通过GICR_WAKER控制的。要将连接的PE标记为唤醒状态,软件必须:
将
GICR_WAKER.ProcessorSleep清零;轮询
GICR_WAKER.ChildrenAsleep,直到读到0;
使能和配置LPI在第6章。
当GICR_WAKER.ProcessorSleep==1或GICR_WAKR.ChildrenAsleep==1时,写CPU interface寄存器(除了ICC_SRE_ELn),会导致不可预知的行为。
4.2.2 CPU interface配置
CPU interface负责将中断传送到连接的PE上。为了使能CPU interface,软件必须进行下面的配置:
使能系统寄存器访问
通过设置
ICC_SRE_ELn的SRE位进行使能。设置优先级掩码和
binary point寄存器CPU接口包含优先级掩码寄存器(
ICC_PMR_EL1)和Binary Point寄存器(ICC_BPRn_EL1)。优先级掩码寄存器设置中断最小优先级(转发给PE靠此判断)。Binary Point寄存器用于优先级分组和抢占。这些寄存器的更多使用可以参考第5章。设置
EOI模式ICC_CTLR_EL1和ICC_CTLR_EL3中的EOImode标志位控制如何处理中断的完成。详细的描述参见第5.5小节。各个中断分组发送信号使能
必须在某个中断组的中断转发到相应的CPU接口之前,使能中断分组发送信号的功能。为了使能中断信号的发送,可以通过
ICC_IGRPEN1_EL1设置Group 1中断,通过ICC_IGRPEN0_EL1设置Group 0中断。ICC_IGRPEN1_EL1在安全状态下有一个备份。这意味着ICC_GRPEN1_EL1可以控制安全状态下的Group 1中断。在EL3,软件即可以通过ICC_IGRPEN1_EL3,访问安全Group 1中断和非安全Group 1中断的使能。
4.2.3 PE配置
PE还需要一些配置,以允许它接收和处理中断。详细的描述超出了本文的范围。这里只描述遵循ARMv8-A的PE在AArch64状态下所需执行的基本步骤就足够了。
路由控制
中断路由控制的标志位在PE的
SCR_EL3和HCR_EL2寄存器中。路由控制位决定了中断被路由到哪一个异常等级。复位时,这些标志位的状态未知,所以,软件必须进行初始化。中断掩码
PE在PSTATE中也有异常掩码位。设置了这些位,中断会被屏蔽。复位时被重置。
中断向量表
PE的中断向量表的地址保存到
VBAR_ELn寄存器中。复位时,VBAR_ELn的值未知。软件必须设置VBAR_ELn指向内存中的正确的向量表。
更多信息,参考ARMv8-A架构参考手册。
4.3 SPI、PPI和SGI配置
SPI是通过Distributor使用GICD_*寄存器配置的。PPI和SGI通过单独的Redistributor使用GICR_* 寄存器进行配置。
对于每一个INTID,软件必须配置以下内容:
优先级(
GICD_IPRIORITYn、GICR_IPRIORITYn)每个
INTID拥有一个相关的优先级,用8位无符号数表示。0x00是最高优先级,0xFF是最低优先级。第5章描述了GICD_IPRIORITYn和GICR_IPRIORITYn中的优先级是如何屏蔽掉低优先级的中断的,以及它如何控制抢占。中断控制器不需要实现所有的8个优先级表示位。如果GIC支持两个安全状态,最小实现5个标志位。如果仅支持一个安全状态,最小实现4个标志位即可。
分组(
GICD_IGROUPn、GICD_IGRPMODn、GICR_IGROUP0、GICR_IGRPMOD0)正如第3.4节中描述的,一个中断可以被指定为3个不同分组中的一个(
Group 0、Secure Group 1和Non-secure Group 1)。边沿触发、电平触发(
GICD_ICFGRn、GICR_ICFGRn)如果中断作为物理信号发送,则必须配置其触发方式(边沿触发或电平触发)。
SGI总是边沿触发,因此,对于软件中断,GICR_ICFGR0总是读为1,而忽略写操作(RAO/WI)。中断使能(
GICD_ISENABLERn、GICD_ICENABLER、GICR_ISENABLER0、GICR_ICENABLER0)每个中断(
INTID)都有一个使能位。设置使能寄存器(set-enable)和清除使能寄存器(clear-enable)寄存器消除了读-修改-写的竞态问题。ARM推荐:在使能INTID之前,应该配置本节概括的这些设置。
对于裸机环境,初始化配置之后通常不需要更改设置。但是,如果确实要重新配置中断,比如更变分组设置,建议首先禁止该INTID。
大部分配置寄存器的复位值都是实现定义的。也就是说,中断控制器的设计者决定这些值是多少,这些值可能因系统而已。
4.3.1 为SPI中断设置目标PE
对于SPI中断,必须配置中断的目标。由GICD_IROUTERn控制。每个SPI都有一个GICD_IROUTERn寄存器,Interrupt_Routing_Mode位控制路由策略,如下所示:
GICD_IROUTERn.Interrupt_Routing_Mode == 0SPI被传送到指定的PE(
A.B.C.D),寄存器中指定的亲和力值。GICD_IROUTERn.Interrupt_Routing_Mode == 1SPI可以被传送到中断分组中的任一个PE。Distributor选择合适的目标PE,每次发送中断信号的时候这可能会变化。这种路由类型称为1对N模式。
PE可以选择不接收1-of-N中断。这由GICR_CTLR中的DPG1S、DPG1NS和DPG0位控制。
5 处理中断
5.1 中断挂起后会发生什么
第3.2节,描述了外设产生一个专用中断信号后,中断控制器中由inactive转换为pending状态。
当中断变为pending状态,中断控制器决定是否将中断发送给某个PE。中断控制器选择PE,依赖于下面的条件:
分组使能
第3.4节描述了如何将
INTID指定给某个中断分组(Group 0、Secure Group 1、或Non-secure Group 1。对于每个中断分组,在Distributor和CPU Interface中有对应的控制位。如果一个中断属于被禁止的中断分组,则不能被转发给PE。中断使能
单独被禁止的中断可以被挂起(
pending),但是不会被转发给PE。路由控制
依赖于中断类型,中断控制器决定哪些PE可以接收该中断。
对于
SPI,路由行为由寄存器GICD_IROUTERn控制。一个SPI可以指定一个特定PE作为目标,也可以指定所有连接PE中的任一个作为目标。对于
LPI,路由信息来自于ITS(如果实现了ITS,参考第6.1节)。对于
PPI,是PE私有的,所以只能由该PE进行处理。对于
SGI,发起软件中断的PE定义目标PE的列表。将在第7章展开进一步的描述。
中断优先级&优先级掩码
每个PE有一个优先级掩码寄存器(
ICC_PMR_EL1),该寄存器属于CPU interface类寄存器。该寄存器设置将中断转发到对应的PE上所需要的最小优先级。只有优先级大于该值的中断才能被发送给对应的PE。运行优先级
第5.4节讲述了
运行优先级这个概念,以及它如何影响抢占。如果相应的PE还没有处理中断(此时已经挂起),运行优先级就是空闲优先级(0xFF)。只有比当前运行优先级更高的中断可以抢占当前中断。
5.2 中断应答
CPU interface拥有两个应答寄存器(IAR)。如果读取应答寄存器(IAR),则返回INTID,并更改中断状态机。在典型的中断处理程序中,第一步往往就是先读取IAR寄存器。具体寄存器描述如下表所示:
| 寄存器 | 用途 |
|---|---|
| ICC_IAR0_EL1 | 用于应答Group 0中断 |
| ICC_IAR1_EL1 | 用于应答Group 1中断 |
5.3 伪中断
第3.1.2小节描述了中断号1020→1023是为特殊目的保留的。这些中断ID可以通过读取IAR寄存器获取,用以标识异常处理中的一些特殊情况。
| ID | 意义 | 使用场景 |
|---|---|---|
| 1020 | 只有读取ICC_IAR0_EL1才返回;最高挂起中断属于 Secure Group 1;只有作为FIQ发送到EL3时才能看见该中断; | 当PE运行在非安全状态时,产生了可信OS的中断。会被当作一个FIQ发送到EL3,以便Secure Monitor能够快速切换到可信OS中 |
| 1021 | 只有读取ICC_IAR0_EL1才返回;最高挂起中断属于 Non-secure Group 1;只有作为FIQ发送到EL3时才能看见该中断; | 当PE运行在安全状态时,产生了rich-OS的中断。会被当作一个FIQ发送到EL3,以便Secure Monitor能够快速切换到rich-OS中 |
| 1022 | 旧操作使用 | 本文档不讨论旧操作 |
| 1023 | 伪中断。 没有使能的 INTID处于pending状态,或所有处于 pending状态中的INTID没有足够的优先级被处理。 | 轮询IAR寄存器时,该值表明没有可用中断需要应答。 |
5.3.1 示例
在下面的示例中,展示了移动系统,因为即将来临的电话,产生了一个modem中断信号。本中断打算是由运行在非安全状态的rich-OS处理:
图-使用中断号1021的示例
当
PE正在Secure EL1上执行可信OS时,modem中断会被挂起(pending)。因为modem中断属于非安全Group 1,所以它会以FIQ信号发送到PE。因为SCR_EL3.FIQ==1,异常陷入到EL3。执行在
EL3的Secure Monitor软件,读取IAR寄存器,返回1021。该值表明中断本应在非安全状态下处理。于是,Secure Monitor执行必要的上下文切换操作。现在
PE处于非安全状态,中断以IRQ的形式被发送,由运行在非安全EL1上的rich-OS进行处理。
在上面的示例中,非安全Group 1中断导致立即退出安全OS。但这并不总是需要的。下图展示了另一个模型,中断首先陷入到Secure EL1。
当
PE在Secure EL1上执行可信OS时,modem中断会被挂起(pending)。因为modem中断属于非安全Group 1,所以它会以FIQ信号发送到PE。因为SCR_EL3.FIQ==0,中断陷入到EL1。可信OS保存好自身的状态。然后,调用SMC指令切换到非安全状态。SMC异常陷入到EL3。执行在EL3的Secure Monitor执行必要的上下文切换操作。现在
PE处于非安全状态,中断以IRQ的形式被发送,由运行在非安全EL1上的rich-OS进行处理。
5.4 运行优先级和抢占
PMR优先级掩码寄存器可以设置中断被转发到PE所需要的最小优先级。GICv3架构还有一个运行优先级的概念。当PE应答了中断后,PE的运行优先级就会成为中断的优先级。当PE写EOI寄存器时,运行优先级返回到它之前的值。
图-运行优先级随时间的变化
CPU interface寄存器ICC_RPR_EL1报告当前运行优先级。
当考虑抢占的时候,运行优先级的概念就很重要了。抢占一般发生在PE正在处理一个低优先级的中断时,一个高优先级中断信号被发送到该PE上。抢占给软件带来了复杂性,但是它阻止了低优先级中断阻塞高优先级中断的处理。
图-没有抢占的情况
图-有抢占的情况
上图只是展示了一层抢占的情况。事实上,可能存在多层抢占的情况。
在某些情况下,可能不需要抢占。GICv3架构允许将可抢占的优先级进行分组,通过Binary Point寄存器(ICC_BPRn_EL1)实现。
Binary Point寄存器将优先级分为两个域:group优先级和subpriority优先级。

图-8位优先级表示位被分为了
group和subpriority
抢占的时候,只考虑group优先级,而忽略subpriority优先级。比如,考虑下面3个中断(N=4,分组后的寄存器如下图所示):

假设,
- 中断A的优先级为
0x10 - 中断B的优先级为
0x20 - 中断C的优先级为
0x21
该场景下:
- A能够抢占B或C。
- B不能抢占C,因为B和C具有相同的优先级。
有了这种划分,B和C被认为具有相同的抢占优先级。但是,A仍然具有较高的优先级,可以抢占B或C。
抢占要求中断处理程序必须支持嵌套。详细的内容超出本文章的范围,暂时不讨论。
5.5 中断结束
当中断被处理完后,软件必须通知中断控制器,以便其中断状态机转向下一个状态。GICv3架构将该任务分为两部分:
优先级降落(Priority drop)这意味着将
运行优先级降落回中断被处理之前的值。失效(Deactivation)这意味着更新当前处理中断的状态机。通常,从
active转换到inactive状态。
GICv3架构中,Priority drop和Deactivation即可以一起发生,也可以分开发生。这由ICC_CTLR_ELn.EOImode设置决定:
EOImode = 0写
ICC_EOIR0_EL1(Group 0)或ICC_EOIR1_EL1(Group 1),执行优先级降落和失效两个动作。这种模式通常用于简单的裸机程序。EOImode = 1写
ICC_EOIR0_EL1(Group 0)或ICC_EOIR1_EL1(Group 1),只执行优先级降落。单独写ICC_DIR_EL1寄存器执行失效操作。这种模式通常用于虚拟化场景。
当操作这些寄存器时,INTID也必须写入。
5.6 检查系统的当前状态
5.6.1 最高优先级挂起中断和运行优先级
正如名称所表示的,最高优先级挂起中断寄存器(ICC_HPPIR0_EL1 & ICC_HPPIR1_EL1)报告该PE上挂起的最高优先级中断的中断号(INTID)。不同的PE可能不同,比如,为SPI中断设置的不同路由目标。运行优先级在ICC_RPR_EL1寄存器中。
5.6.2 单个中断号的状态
Distributor提供了表示每个SPI中断当前状态的寄存器。相似的,Redistributor也提供了表示PPI和SGI中断当前状态的寄存器。
通过设置这些寄存器,也可以将中断转换到某种状态。这在没有外设的情况下,测试配置是否正确的时候非常有用。
有单独的寄存器分别报告active状态和pending状态。下表列出了active寄存器。pending状态寄存器拥有相同的格式。
| 寄存器 | 描述 |
|---|---|
GICD_ISACTIVERn | 设置SPI的active状态。每个INTID一位。读取某一位,返回值为: * 1 – 该中断处于active状态;* 0 – 该中断处于非active状态;对某一位写 1,则激活相应的中断;写0没有影响。 |
GICD_ICACTIVERn | 清除SPI的active状态。每个INTID一位。读取某一位,返回值为: * 1 – 该中断处于active状态;* 0 – 该中断处于非active状态;对某一位写 1,则失效相应的中断;写0没有影响。 |
GICR_ISACTIVER0 | 设置SGI和PPI的active状态。每个INTID一位(0-31)。读取某一位,返回值为: * 1 – 该中断处于active状态;* 0 – 该中断处于非active状态;对某一位写 1,则激活相应的中断;写0没有影响。 |
GICR_ICACTIVER0 | 清除SGI和PPI的active状态。每个INTID一位(0-31)。读取某一位,返回值为: * 1 – 该中断处于active状态;* 0 – 该中断处于非active状态;对某一位写 1,则失效相应的中断;写0没有影响。 |
注意1:当亲和力使能时,
GICD_ISACTIVER0和GICD_ICACTIVER0被看作RES0。这是因为GICD_ISACTIVER0和GICD_ICACTIVER0在该情况下对应的中断号是0-31,而它们在每个PE中都有备份,通过每个PE的redistributor报告。注意2:运行在非安全状态的软件,不能看见
Group 0或Secure Group 1中断的状态,除非通过GICD_NASCRn或GICR_NASCRn允许访问。
6 配置LPI
LPI只有在亲和力路由使能时支持,只是它们的配置方式不同。
配置LPI涉及到:
Redistributor。可选的
ITS(中断翻译服务)。
LPI总是基于消息的中断,并且可以由ITS支持。ITS负责接收外设中断,将其转换为LPI并转发给合适的Redistributor。一个系统可以包含多个ITS,每一个都能够单独配置。
外设也可以直接发送LPI给Redistributor,从而绕过ITS。但是,ITS提供了许多特性,允许高效地处理大量的中断源。
注意:对
LPI的支持是可选的,由GICD_TYPER.LPIS标志位控制。如果至少存在一个ITS,外设是否可以绕过ITS,直接发送LPI给Redistributor是中断控制器实现时定义的。
6.1 ITS
6.1.1 ITS的操作
外设通过写ITS的GITS_TRANSLATER可以产生LPI中断。写操作提供给ITS以下信息:
EventID写入到
GITS_TRANSLATER的值。EventID标识外设正在发送的中断。EventID可能与INTID相同,或者可以通过ITS翻译成INTID。DeviceIDDeviceID标识外设。产生外设标识符DeviceID的方法是实现时定义的。比如,可以使用AXI用户信号。
ITS将外设写入到GITS_TRANSLATER的EventID转换成INTID。如何将EventID转换成INTID取决于具体的外设,这就是为什么需要DeviceID的原因。
LPI中断的INTID集合在一起。集合中的所有INTID都路由到相同的Redistributor。软件分配LPI INTID给集合,允许它有效的将中断从一个PE迁移到另一个PE。
ITS使用三种类型的表处理LPI的翻译和路由。分别是:
设备表
将
DeviceID映射到中断翻译表。中断翻译表
包含与
DeviceID相关的EventID和INTID映射关系。还包含成员是INTID的集合。集合表
将集合映射到
Redistributor。

图-
ITS转发LPI到Redistributor
当外设写GITS_TRANSLATER时,ITS需要:
使用
DeviceID从设备表中选择合适的项。该项标识使用哪个中断翻译表。使用
EventID从中断翻译表中选择合适的项。该项提供INTID和集合ID。使用
集合ID从集合表中选择想要的项,它会返回路由信息。转发中断到目标
Redistributor。
注意:
ITS可以选择支持大量的硬件集合。硬件集合是ITS内部保存配置的地方,而不是将其存储在内存中。GITS_TYPER.HCC报告了支持的硬件集合数量。
6.1.2 命令队列
ITS通过内存中的命令队列进行控制。该命令队列是一个环形缓冲区,由3个寄存器定义。
GITS_CBASER这个寄存器指定了命令队列的基地址和大小。命令队列必须是
64k大小对齐的,大小必须是4K的倍数。队列中的每一项是32字节。GITS_CBASER还指定了ITS在访问命令队列时使用的缓存性和可共享性设置。GITS_CREADR指向
ITS将要处理的下一个命令。GITS_CWRITER指向下一个新命令将要写入的位置。

图-简化的
ITS循环命令队列
<ARM Generic Interrupt Controller Architecture Specification GIC architecture version 3.0 and 4.0>提供了ITS支持的所有命令的详细信息,以及编码方式。
6.1.3 ITS的初始化配置
为了在系统启动的时候,配置ITS服务,软件必须:
为设备和集合表申请内存。
GITS_BASER[0..7]寄存器指向ITS设备和集合表的基地址和大小。软件使用这些寄存器发现ITS支持的表数量和类型。然后,软件申请所需内存,设置GITS_BASERn指向这些分配的内存表。为命令队列申请内存。软件必须为命令队列分配内存,然后设置
GITS_CBASER和GITS_CWRITER指向这些内存的起始地址。使能
ITS。当这些表和命令队列申请内存成功,就可以使能ITS。通过设置GITS_CTLR.Enable为1实现。一旦该标志位设置成功,则GITS_BASERn和GITS_CBASER寄存器就变成了只读的。
6.1.4 集合和设备表的大小和布局
设备和集合表的位置和大小是通过GITS_BASERn寄存器配置的。软件必须分配足够的内存给这些表,并在使能ITS之前配置好GITS_BASERn。
软件可以分配平面(单级)表,或两级表,通过GITS_BASERn.Indirect标志位指定。
注意:两级表的支持是可选的。如果
ITS仅支持平面表,则GITS_BASERn.Indirect是RAZ/WI。
RAZ/WI:read as zero, write ignored。
平面表
使用平面表,则是分配一块连续的物理内存给
ITS,用以记录映射关系。在使能ITS之前,软件需要对这段内存全部填充0。之后,ITS在处理命令队列的命令时填充该表。
一个平面设备或集合表
表的大小依赖于
DeviceID或集合ID的宽度(位数,即ID数量)。大小计算如下:Size(单位:字节) = 2^ID_width * entry_size在这儿,
entry_size是每个表项的字节数,由GITS_BASERn.Entry_Size字段表示。配置
GITS_BASERn寄存器时,表的大小是按照page页数量进行指定的。page页的大小由GITS_BASERn.Page_Size控制,可以是4K、16K、64K。因此,上面公式计算出的结果必须是page页的大小的倍数,不足一页的按照一页分配。例如,如果实现的是8位
DeviceID,每个表项的大小是8个字节,页大小是4K,则:2^8 * 8 = 2048 字节 => 按页向上取整的算法,则表大小应该是`4K`两级表
使用两级表时,软件申请分配一个1级表,和一些2级表。

1级表由软件填充,每一项即可以指向一个2级表,也可以标记为
invalid。2级表在被分配给ITS之前,应该将其填充0,在ITS处理命令队列的命令时再将具体内容写入该表。ITS使能时(GITS_CTLR.Enabled==1),软件可能会申请分配新的2级表,并更新1级表的表项,以便指向这些新添加的2级表。当ITS使能时,软件不能删除已有的分配表,或更改已经存在的分配表。每个2级表的大小是一个
page页。和平面表一样,page页通过GITS_BASERn.Page_Size进行设置。所以,它包含的项数是page_size/entry_size。也就是说,每个1级表项表示(
page_size / entry_size)个ID,同时它可以指向一个2级表或被标记为无效。任何使用了无效ID的ITS命令(该ID对应无效表项)都会被放弃。1级表的大小可以通过下面公式进行计算:
Size(单位:字节) = (2^ID_width / (page_size / entry_size)) * 8和平面表类似,1级表的大小也是按页指定的。因此,上面公式向上按页取整。
6.1.5 添加一个新命令到命令队列中
为了添加新命令到命令队列,软件需要:
写新命令到队列中。
GITS_CWRITER指向没有包含合法命令的队列项。软件必须写命令到该项中,并保证全局可见性。更新
GITS_CWRITER软件必须更新
GITS_CWRITER,让其指向没有包含新命令的下一个队列项。更新动作会告知ITS,已经添加了一个新命令。当然,软件能够同时写多个命令到队列中,只要还有足够的空间,并且相应更新
GITS_CWRITER寄存器。等待命令被
ITS读取软件可以轮询
GITS_CREADR,判断命令是否被读取。当GITS_CWRITER.Offset == GITS_CREADR.Offset时,说明所有的命令已经被ITS读取。另外,还可以添加一个
INT命令,产生一个中断信号,告知一组命令已经被ITS读取。ITS按照顺序读取命令队列中的命令。但是,这些命令对于Redistributor的影响是乱序的。SYNC命令保证前面发出的命令的效果是可见的。
当
GITS_CWRITER指向GITS_CREADR前面的一个位置时,说明命令队列满了。在尝试添加新命令之前,软件必须检查队列中是否还有足够空间。
6.1.6 将中断映射到Redistributor
将
DeviceID映射到翻译表每个能够发送中断到
ITS的外设都有自己的DeviceID。每个DeviceID都有自己的中断翻译表(ITT),该表保存着EventID到INTID的映射关系。软件必须为ITT分配内存,然后,使用MAPD命令将DeviceID映射到ITT。MAPD <DeviceID>, <ITT_Address>, <Size>将
INTID映射到集合,将集合映射到Redistributor当外设的
DeviceID已经映射到ITT,那么该设备所发送的不同EventID必须映射到INTID,而这些INTID必须被映射到不同的集合。每个集合被映射到一个目标Redistributor上。中断号(
INTID)映射到集合,可以使用MAPTI和MAPI命令。当EventID和INTID相同时,使用MAPI命令:MAPI <DeviceID>, <EventID>, <Collection ID>当
EventID和INTID不同时,使用MAPTI命令:MAPTI <DeviceID>, <EventID>, <INTID>, <Collection ID>将集合映射到
Redistributor时,使用MAPC命令:MAPC <Collection ID>, <Target Redistributor>目标
Redistributor的标识依赖于GITS_TYPER.PTA:GITS_TYPER.PTA==0:使用ID标识Redistributor,该ID可以从GICR_TYPER.Processor_Number读取。GITS_TYPER.PTA==1:使用物理地址指定Redistributor。
示例
定时器的设备ID(
DeviceID)是5,且使用2个比特位的事件ID(EventID)。我们将EventID=0映射为INTID(8725)。为定时器分配的ITT的地址是0x84500000。假设集合的编号为
3,目标Redistributor的物理地址是0x78400000。命令序列如下所示:
MAPD 5, 0x84500000, 2 Map DeviceID 5 to an ITT MAPTI 5, 0, 8725, 3 Map EventID 0 to INTID 8725 and collection 3 MAPC 3, 0x78400000 Map collection 3 to Redistributor at address 0x78400000 SYNC 0x78400000该示例假设之前没有建立任何映射,且
GITS_TYPER.PTA==1。
6.1.7 在Redistributor之间迁移中断
有2种方法将中断从一个Redistributor迁移到另一个:
重映射集合
软件可以通过重映射整个集合,将所有中断从一个
Redistributor迁移到另外一个Redistributor上。这通常发生在与Redistributor绑定的PE关闭电源,且所有的中断必须迁移到另一个Redistributor上时。迁移的命令序列如下所示:MAPC <Collection ID>, <RDADDR2> # 重映射集合到新的Redistributor SYNC <RDADDR2> # 同步操作,保证映射完成 MOVALL <RDADDR1>, <RDADDR2> # 将所有处于挂起状态的中断迁移到新Redistributor SYNC <RDADDR1> # 同步操作,保证中断状态迁移完成在这段命令序列中,
RDADDR1是之前的目标Redistributor,RDADDR2是新Redistributor。如果有多个集合的目标是
RDADDR1,我们可能需要多个MAPC命令,每一个命令对应一个集合。这儿,假设所有的集合都将重映射到相同的新目标Redistributor上。将中断映射到不同的集合里
单个中断可以被重映射到另一个集合中。通过下面的命令序列完成:
MOVI <DeviceID>, <EventID>, <新集合ID> SYNC <RDADDR1>在该命令序列中,
RDADDR1是中断被重新映射之前,原来指定的那个集合对应的Redistributor。
6.1.8 移除中断映射
为了重新映射中断,或删除中断映射关系,软件需要:
禁止当前映射中断的物理中断号(
INTID)。这是在LPI配置表中完成的,参考第6.2.2节。发送
DISCARD命令。这将移除中断映射关系,并清除对应INTID的挂起状态。发送
SYNC命令,并等待直到该命令完成。
该命令完成后,不会再有中断被发送到中断原先映射的Redistributor上了。
6.1.9 重映射或删除设备映射关系
为了改变或删除设备映射关系,软件需要:
对于当前映射外设的每个
EventID,按照第6.1.8节执行一遍。发送
MAPD命令重新映射设备。或者使用有效位为0的MAPD命令删除映射关系。发送
SYNC命令,并等待直到该命令完成。
6.2 Redistributor
Redistributor掌握着所有物理LPI中断的控制、优先级和挂起状态信息,这些保存在内存表中。
LPI中断的配置信息存储在内存的一张表上。这就是LPI配置表,地址存储在GICR_PROPBASER寄存器中。LPI的配置是全局的,也就是说,所有的Redistributor看见的是相同的配置。通常,一个系统使用一张LPI配置表。
同样,LPI中断的状态信息也存储在内存表上,不过是多张表。这就是LPI挂起状态表,地址存储在GICR_PENDBASER寄存器中。每个Redistributor拥有自己的LPI挂起状态表,也就是说,该表是私有的。

图-
LPI配置和挂起状态表
6.2.1 初始化Redistributor的配置
初始化Redistributor的步骤如下:
为
LPI配置表分配内存,并使用每个LPI中断合适的配置信息初始化该表。设置
GICR_PROPBASER,使其都指向该LPI配置表。为每个
Redistributor的LPI挂起状态表分配内存,并分别初始化每个表。系统启动时,将表所在内存填充0,这意味着所有的LPI INTID都处于未激活状态(inactive)。设置每个
Redistributor自己的GICR_PENDBASER,使其指向自己私有的LPI挂起状态表。设置每个
Redistributor自己的GICR_CTLR.EnableLPIs为1,使能LPI中断。该标志位设为1后,GICR_PENDBASER和GICR_PROPBASER变为只读。
LPI配置表LPI配置表中,每个INTID占用一个字节。下图展示了该字节中各个字段的内容。

我们知道,`SPI`、`PPI`、`SGI`使用8位表示优先级,但是`LPI`只使用`6`位表示优先级,其最低两位总是`0b00`。
而且没有字段记录安全状态。因为`LPI`中断总是被视为非安全`Group 1`中断。
`LPI`配置表的大小,也就是其将要占用的内存依赖于`LPI`中断的数量。GIC支持的`SPI`、`PPI`、`SGI`和`LPI`中断的最大中断号数量是由`GICD_TYPER.IDbits`表示的。`LPI`配置表中使用的中断号大于`8191`。因此,为了支持所有可能的`LPI`中断,配置表大小的计算公式如下:
Size(单位:字节) = 2^(GICD_TYPER.IDbits+1) – 8192
但是,有时候可能需要支持更少的中断号,而`GICR_PROPBASER`中包含一个字段`IDbits`,表示`LPI`配置表支持的中断号数量。这个数量必须小于等于`GICD_TYPER`的值。软件必须为这些中断号分配足够的配置表内存。在这种情况下,配置表大小的计算公式如下:
Size(单位:字节) = 2^(GICR_PROPBASER.IDbits+1) – 8192
中断控制器必须能够读取为`LPI`配置表分配的内存,否则,它无法对这段内存进行写操作。
LPI挂起状态表前面已经讲过,
LPI中断的状态信息也存储在内存中。LPI只有两个状态,inactive和pending。
图-
LPI中断的状态机当
LPI中断被应答后,由pending转变为inactive状态。因为LPI只有两种状态,所以LPI挂起状态表中每个LPI中断只有1位表示。因此,为了支持所有可能的中断号,该表的大小为:Size(单位:字节) =2^(GICD_TYPER.IDbits+1) / 8不像
LPI配置表,LPI挂起状态表的大小没有考虑从中断号8192开始计算。因为该表的前1k大小(对应的中断号是0-8191)存储的是中断控制器在实现时的定义状态。正如前面描述的,硬件可以支持更小范围的中断号。
GICR_PROPBASER.IDBits控制中断号的范围。因此,它既影响了LPI配置表的大小,也影响了挂起状态表的大小。所以,挂起状态表的大小也可以通过下面公式计算:Size(单位:字节) = 2^(GICR_PROPBASER.IDbits+1)/8同样,中断控制器也必须能够读写该段内存。通常,
Redistributor内部可以缓存最高优先级的挂起中断,所以,一般情况下,会在无法再缓存更多状态信息或者进入低功耗模式时,将状态信息写入到LPI挂起状态表中。
6.2.2 重新配置LPI
LPI的配置信息位于内存,而不是寄存器中。Redistributor允许缓存LPI配置信息。这意味,为了重新配置LPI,软件需要:
- 更新
LPI配置表中的项。 - 同步更新操作。
- 失效
Redistributor中的缓存配置信息。
失效配置信息的缓存,可以通过发送INV或INVALL命令给ITS实现。INV失效单个中断,所以常用于重新配置少量LPI中断的情况。INVALL命令失效指定集合中所有中断。
如果没有实现ITS,软件必须写GICR_INVLPIR或GICR_INVALLR寄存器失效缓存。
7 发送和接收SGI中断
软件中断SGI,是由软件写中断控制器的特定寄存器触发的中断。
7.1 产生软件中断
写CPU Interface中的任何一个SGI寄存器,就会产生软件中断。
下表是使能了系统寄存器访问的前提下,可以使用的SGI寄存器:
| 系统寄存器接口 | 描述 |
|---|---|
| ICC_SGI0R_EL1 | 产生Secure Group 0中断 |
| ICC_SGI1R_EL1 | 为PE当前安全状态,产生Group 1中断 |
| ICC_ASGI1R_EL1 | 为PE的其它安全状态,产生Group 1中断 |
SGI寄存器的各个字段如下所示:

SGI寄存器的格式(SRE=1)
SGI ID控制产生的软件中断号。如第3.1.2节描述,软件中断的ID是
0-15。IRMIRM(Interrupt Routing Mode)该字段控制软件中断的目标PE(可能是多个),有两个选项:IRM = 0目标PE由
<aff3>.<aff2>.<aff1>.<Target List>决定,在这儿,<target list>中,每个aff0上的节点(也就是PE)占用一位。这意味着,该中断最多可以发送给16个PE,可能包含产生中断的PE。IRM = 1该中断被发送到除了产生中断的PE之外的所有连接的PE上。
正如第3.3节描述的,分层的亲和力真实的意义依赖于特定的设计。通常,亲和力1表示多核处理器,亲和力0表示该处理器上的PE单元(也就是我们常说的
CPU Core)。
安全状态和中断分组的控制:
ICC_SGI0R_EL1、ICC_SGI1R_EL1或ICC_ASGIR_EL1控制安全状态(产生软件中断的PE的寄存器)
GICR_IGROUPR0和GICR_IGRPMODR0控制中断分组(目标PE的寄存器)
执行在安全状态的软件可以发送安全和非安全SGI软件中断。非安全状态的代码是否可以产生安全SGI中断是由寄存器GICR_NSACR控制的。这个寄存器只能由运行在安全状态下的软件访问。下表展示了源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 1 | 是 | ||
| Non-Secure Group 1 | 否 | ||
| ICC_ASGI1R_EL1 | Secure Group 0 | 否 | |
| Secure Group 1 | 否 | ||
| Non-secure Group 1 | 是 | ||
| Non-secure EL2/EL1 | ICC_SGI0R_EL1 | Secure Group 0 | GICR_NSACR配置决定(*) |
| Secure Group 1 | 否 | ||
| Non-secure Group 1 | 否 | ||
| ICC_SGI1R_EL1 | Secure Group 0 | GICR_NSACR配置决定(*) | |
| Secure Group 1 | GICR_NSACR配置决定 | ||
| Non-secure Group 1 | 是 | ||
| ICC_ASGI1R_EL1 | Secure Group 0 | GICR_NSACR配置决定(*) | |
| Secure Group 1 | GICR_NSACR配置决定 | ||
| Non-secure Group 1 | 否 |
注意:上表假设
GICD_CTLR.DS==0(也就是支持两个安全状态)。如果GICD_CTLR.DS==1,被标记为(*)的SGI可以转发。
7.2 GICv3和GICv2对比
在GICv2中,源PE和目标PE都备份SGI中断号。这意味着,系统中,一个给定的PE可以备份8次相同SGI中断号的pending状态(因为GICv2最多可以支持8个核)。
在GICv3中,只有目标PE备份SGI中断号。这意味着给定PE只有一个SGI中断号挂起(pending)。
下面使用一个例子来阐述这种差异。假设,PE-A和PE-B同时发送SGI中断号5给PE C:

C能够看见几个中断呢?
GICv2:2个。它将同时看到来自A和B的中断。两个中断的顺序依赖于具体的设计和精确的采样时间。
GICC_IAR寄存器中的INTID的前缀是源PE的ID,可以通过这个事实区分这两个中断。GICv3:1个。因为源PE不备份
SGI,所以,相同的中断不能在两个源PE上挂起(处于pending状态)。因此,C只能看见一个中断(中断号为5)。
该例子假设同时(或几乎同时)发送两个中断。如果C能够在第二个SGI中断到达之前,能够应答第一个,则在GICv3架构中也能看见两个中断。
如果设置支持旧操作,也就是当
GICD_CTLR.ARE=0,SGI中断的行为与GICv2相同。
8 虚拟化
ARMv8-A支持虚拟化。为了实现虚拟化,GICv3也应该支持虚拟化。为此,GICv3需要添加以下功能:
CPU interface寄存器的硬件虚拟化虚拟中断
维护中断
GIC架构没有考虑
Distributor、Redistributor或ITS的硬件虚拟化。所以,这些接口的虚拟化需要软件进行处理。这些内容不在本文档的讨论范围之内。
8.1 术语
Hypervisor负责创建、控制和调度虚拟机(VM)。一个虚拟机从功能上说等价于一个物理系统,包含一个或多个虚拟处理器。每个虚拟处理器包含一个或多个vPE。

上图是虚拟机、虚拟处理器和vPE的关系。本章讨论的大部分控制工作都在vPE层。
8.2 接口
CPU Interface被分为3组:
- 物理
CPU interface寄存器 - 虚拟化控制寄存器
- 虚拟
CPU interface寄存器

上图为支持虚拟化的CPU interface寄存器
物理CPU接口(
ICC_*_ELn)hypervisor软件使用常规的ICC_*_ELn寄存器处理物理中断。虚拟控制寄存器(
ICH_*_EL2)hypervisor软件使用这类寄存器控制虚拟化特性。这些特性包括:- 使能、禁止
vCPU interface。 - 用于上下文切换时保存、恢复虚拟寄存器的状态。
- 配置维护中断。
- 控制虚拟中断。
这些寄存器只能控制访问它们的物理PE的虚拟化特性,不能访问其它PE的状态。也就是说,
PEx的软件不能访问PEy的状态。- 使能、禁止
虚拟CPU接口(
ICV_*_ELn)虚拟机内部软件(运行在
EL1)使用ICV_*_EL1寄存器处理中断。这些寄存器的格式和功能与ICC_*_EL1寄存器相同。ICV*和ICC*寄存器拥有相同的指令编码。在EL2、EL3和Secure EL1,都能访问ICC*寄存器。在非安全EL1,是否能够访问ICV*和ICC*寄存器是由HCR_EL2寄存器中的路由标志位决定的。ICV*寄存器分为3组:Group 0用于处理
Group 0中断,比如ICC_IAR0_EL1/ICC_IAR0_EL1。当HCR_EL2.FMO==1时,非安全EL1访问ICV*寄存器,而不是ICC*寄存器。Group 1用于处理
Group 1中断,比如ICC_IAR1_EL1/ICC_IAR1_EL1。当HCR_EL2.FMO==1时,非安全EL1访问ICV*寄存器,而不是ICC*寄存器。通用
用于处理
Group 0或1中断,比如ICC_DIR_EL1/ICV_DIR_EL1和ICC_PMR_EL1/ICV_PMR_EL1。当HCR_EL2.IMO==1或HCR_EL2.FMO==1,非安全EL1访问ICV*寄存器,而不是ICC*寄存器。
下图,展示了同一个指令如何基于
HCR_EL2的路由控制访问ICC*或ICV*寄存器的示例。
8.3 管理虚拟中断
hypervisor使用List列表寄存器ICH_LRn_EL2产生虚拟中断。每个寄存器表示一个虚拟中断,并记录:
虚拟中断号(
vINTID)这是报告给虚拟机内部的中断ID号。
虚拟中断状态
虚拟中断状态(
Pending、Active、Active&Pending或Inactive。随着虚拟机中的软件和GIC的交互,状态机自动更新。比如,hypervisor创建一个新的虚拟中断,首先设置中断状态为pending。当执行在vPE上的软件,读取ICV_IARn_EL1,中断控制器中,该中断的状态机更新为Active。中断分组(
group)虚拟机只有一个安全状态(也就是
GICD_CTLR.DS==1)。因此,虚拟中断分为两组:Group 0和Group 1。Group 0中断以vFIQ信号的形式发送,Group 1中断以vIRQ信号的形式发送。物理中断号(
pINTID)虚拟中断使用物理中断的
INTID进行标记(可选)。如果vINTID的状态机更新时,pINTID的状态也就更新了。
8.3.1 一个物理中断被转发到vPE的例子
下图展示了把一个物理中断转发到vPE的过程:

一个物理非安全
Group 1中断从Redistributor转发到物理CPU接口;物理CPU接口检查是否将物理中断转发到对应的PE。这个过程已经在第5.1小节描述过。假设,检查通过,物理中断生效。
该中断被
EL2捕获。hypervisor读取IAR寄存器,返回物理中断号(pINTID)。该物理中断(pINTID)现在处于active状态。hypervisor决定将该中断转发给正在运行的vPE。hypervisor将物理中断号(pINTID)写入到ICC_EOIR1_EL1寄存器。此时,ICC_CTLR_EL1.EOImode==1,只执行优先级降落,并没有将该物理中断失效。hypervisor写一个List寄存器,产生一个虚拟中断,并将状态设置为pending。List寄存器中会指定虚拟中断号(vINTID),同时记录原始的物理中断号(pINTID)。然后,hypervisor通过执行ERET指令,从异常中返回,重新将执行权限交给vPE。vCPU接口会检查是否将虚拟中断转发给vPE。这些过程与物理中断的处理一样,只不过它们使用的是ICV寄存器。本示例中,假设检查通过,虚拟中断生效。虚拟中断被非安全的
EL1捕获。当软件读取IAR,返回虚拟中断号(vINTID),虚拟中断此时处于active状态。Guest OS处理该虚拟中断。当它完成中断的处理,写EOIR寄存器,执行优先级降落,并失效该中断。因为List寄存器记录了物理中断号(pINTID),所以,同时将虚拟中断和物理中断失效。
8.4 维护中断
如果虚拟CPU接口中的某些条件为真,则可以配置CPU接口,产生物理中断。
这类中断是PPI中断,中断号是25。通常配置为非安全Group 1中断,由运行在EL2上的hypervisor处理。是否产生维护中断,由ICH_HCR_EL2控制,产生的中断可以通过ICH_MISR_EL2寄存器读取。
如果vPE清除了vCPU接口中的Group使能位中的一个,就可以产生维护中断。一旦收到该中断,hypervisor就可以从List寄存器的项中,删除属于被禁用Group中,挂起的虚拟中断。
8.5 旧虚拟机
使用了GICv3系统寄存器的hypervisor(ICC_SRE_EL2.SRE==1),照样可以托管使用旧方式(ICC_SRE_EL1(NS)==0)的虚拟机。在这种情况下,运行在虚拟机中的软件使用内存映射的GICV寄存器,就跟在GICv2中一样。
8.6 上下文切换
当在vPE之间切换上下文时,hypervisor软件保存一个vPE的状态,加载另一个的上下文内容。vCPU interface寄存器的状态是vPE上下文的一部分,这些状态包括:
ICV寄存器的状态有效的虚拟优先级
处于
pending、active或active&pending状态的虚拟中断
ICV寄存器可以被EL2使用ICH寄存器进行访问。下图,展示了ICH_VMCR_EL2的各个位域对ICV寄存器状态的映射关系。

切换vPE时,必须保存和恢复有效虚拟优先级。当前vPE的有效优先级可以通过ICV_APnR_EL2寄存器访问。
正如第8.3节描述,虚拟中断是通过List寄存器管理的。这些寄存器的状态特定于当前vPE。因此,在上下文切换的时候,必须保存和恢复这些寄存器。
9 GICv4:虚拟LPI的直接注入
GICv4添加了对直接注入虚拟LPI(vLPI)的支持。此功能允许软件向ITS描述物理事件(EventID和DeviceID的组合)如何映射成虚拟中断。如果中断的目标vPE正在运行,则直接转发虚拟中断,而无需先进入hypervisor。这可以减少虚拟化中断带来的开销。
9.1 Redistributor、vLPI状态和配置
为了支持vLPI的直接注入,Redistributor添加了两个寄存器:
GICR_VPROPBASER
设置
vLPI配置表地址。和物理LPI配置表一样,vLPI配置表记录了vLPI中断的配置。该配置对于同一个虚拟机中所有的vPE都是可见的。ARM期望VM中的所有vPE都将使用虚拟配置表的相同副本。GICR_VPENDBASER
设置
vLPI挂起状态表(简称VPT)。与物理LPI挂起表一样,VPT记录了vLPI的挂起状态。每个vPE都有自己私有的VPT。
9.1.1 调度vPE
多个vPE可能都托管在单个物理PE上,hypervisor在它们之间执行上下文切换。正在运行的vPE被称为正在被调度。当GICR_VPENDBASER被设置指向一个vPE的VPT时,该vPE被定义为正在被调度。
对于被调度的vPE,可以直接注入虚拟中断。如果目标vPE没有被调度,虚拟中断在正确的VPT表中被记录为pending状态。
当在vPE之间执行上下文切换时,hypervisor必须更新Redistributor相关寄存器。这意味着hypervisor必须:
清除
GICR_VPENDBASER.Valid标志位清除
Valid标志位,告知Redistributor,正在执行上下文切换。Redistributor将会检索vCPU接口中的任何挂起虚拟中断,保证内存中的VPT表是正确的。轮询
GICR_VPENDBASER.Dirty标志位,直到读到0Dirty标志位反映了Redistributor已经完成了VPT的更新。在该值为0之前,不能调度新vPE。更新
GICR_VPROPBASER如果是同一个VM的不同
vPE,不需要这一步。更新
GICR_VPENDBASER,设置Valid==1设置
Valid标志位为1,告知Redistributor,新vPE是合法的,此时,该vPE的虚拟中断可以被转发到vCPU接口上了。
VPT的前1K内容是实现时定义的。ARM期望实现将使用此空间来记录信息,从而在上下文切换时更快地解析VPT。 当一个vPE被调度时,必须通知Redistributor该区域是否包含有效数据。软件使用GICR_VPENDBASER.IDAI指示空间是否有效:
GICR_VPENDBASER.IDAI==1(无效)保留的1K空间不是合法的,
Redistributor必须解析整个VPT。下列情况必须设置IDAI:vPE被迁移到不同GIC中断控制器的Redistributor时VPT被分配后,第一次调度对应的vPE时,并且在分配整个表时未填充0
GICR_VPENDBASER.IDAI==0(有效)保留的1K空间是合法的,
Redistributor能够依赖该空间的值。ARM期望这是最常用的方式。当发生以下情况时,可以清除IDAI标志位:- 调度的
vPE位于相同的Redistributor上 - 调度的
vPE位于不同的Redistributor上,但是被连接到相同的GIC控制器 VPT被分配后,第一次调度对应的vPE时,并且在分配整个表时填充0(意味着没有挂起的中断)
注意:限制是
VPT在分配时是全零,而不是在第一次被调度时全零。如果vPE的ITS映射存在,可能会在创建到第一次驻留期间设置虚拟中断。
- 调度的
9.2 GICv4中的ITS操作
GICv4添加了许多新命令,添加了新的表类型给ITS使用。允许软件:
为某个PE,将
EventID-DeviceID组合映射成虚拟中断号(vINTID)- (可选)可以指定一个门铃中断。这是当中断产生,但是
vPE没有被调度的话,产生的一个物理中断(pINTID)。
- (可选)可以指定一个门铃中断。这是当中断产生,但是
将
vPE映射到物理Redistributor。
下图展示了ITS转发虚拟中断时遵循的流程,将虚拟LPI中断直接注入虚拟机:

外设写GITS_TRANSLATER:
ITS使用设备ID(DeviceID),从设备表中选择正确的项。该项可以标识将要使用的中断翻译表。ITS使用事件ID(EventID),从中断翻译表中选择正确的项。这会得到:a. 物理中断号(
pINTID)和集合ID,正如第6.1.1节描述的。b. 虚拟中断号(
vINTID)和vPE ID,(可选)一个物理中断号(pINTID)用于门铃中断。ITS使用vPE ID选择vPE表中想要的项,vPE表返回目标Redistributor和vPE的VPT地址。ITS转发虚拟中断号(vINTID)、门铃中断和VPT地址到目标Redistributor。Redistributor比较来自ITS的VPT地址与当前GICR_VPENDBASER中的值:a. 如果
VPT地址和GICR_VPENDBASER匹配,则调度vPE,并将虚拟中断号(vINTID)转发到虚拟CPU接口。b. 如果不匹配,则
vPE不被调度。相应的中断(vINTID)在VPT表中设为挂起状态。如果提供了门铃中断,则物理中断号(pINTID)被转发到物理CPU接口。
9.3 vPE和vINTID的映射关系
EventID-DeviceID组合被映射到虚拟中断号(vINTID)和虚拟PE(vPE)。当EventID和vINTID相同时,使用VMAPI命令。
VMAPI <DeviceID>, <EventID>, <Doorbell pINTID>, <vPE ID>
当EventID和vINTID不同时,使用VMAPTI命令。
VMAPTI <DeviceID>, <EventID>, <vINTID>, <pINTID>, <vPE ID>
在这些命令中:
<DeviceID>和<EventID>一起标识正在被重映射的中断。<vPE ID>是虚拟PE的ID。对于包含多个ITS的系统,必须为一个给定PE在所有ITS上指定相同的vPE ID。<pINTID>是门铃中断的物理中断号,是因为vPE没有被调度而产生的。指定1023意味着没有门铃中断。<vINTID>是虚拟LPI中断的中断号。对于VMAPI命令,EventID和vINTID具有相同的值。
ITS必须知道vPE将在哪个物理PE上调度执行。VMAPP命令将vPE映射到物理Redistributor:
VMAPP <vPE ID>, <RDADDR>, <VPT>, <VPT size>
在该命令中:
<vPE ID>是vPE的标识符。<RDADDR>是目标Redistributor的地址。<VPT>和<VPT size>标识了vPE相应的虚拟LPI挂起状态表。如第9.1.1节描述的,当GICR_VPENDBASER指向它的VPT时,vPE被调度。转发虚拟中断给Redistributor时,ITS包含VPT地址。这允许Redistributor判断vPE是否被调度执行,如果没有调度,则更新VPT,这样中断不会丢失。
9.3.1 例子
假设,定时器的设备ID是5。它产生两个事件ID:0和1。两个事件ID都映射成vPE的虚拟中断号,vPE ID为6:
EventID 0– 虚拟中断号为8725,门铃中断号为8192EventID 1– 虚拟中断号为9000,没有门铃中断
vPE(6)被映射到地址为0x78400000的Redistributor,同时,它的VPT表的地址是0x97500000。
命令序列是:
VMAPTI 5, 0, 8725, 8192, 6
VMAPTI 5, 1, 9000, 1023, 6
VMAPP 6, 0x78400000, 0x97500000, 12 VSYNC 6
注意:该示例假设
GITS_TYPER.PTA==1(即使用物理地址标识目标Redistributor),并且之前已经发出MAPD命令来映射ITT。
9.4 将vPE映射到不同的Redistributor
如果hypervisor将vPE映射到不同的物理PE上,ITS映射关系必须被更新,以便虚拟中断能够发送到正确的物理PE上。更新ITS映射关系使用VMOVP命令,使用VSYNC命令同步内容。
一个系统可以包含多个ITS。如果有不止一个ITS有vPE的映射关系,任何更改都必须反映到所有的ITS中。GICv4支持两个模型实现这个操作,GITS_TYPER.VMOVP标志位控制使用哪个模型。
9.4.1 GITS_TYPER.VMOVP==0
必须给所有的ITS(有该vPE的映射关系)发送VMOVP命令:
VMOVP <vPE ID>, <RDADDR>, <ITS List>, <Sequence Number>
在该命令中:
<vPE ID>是虚拟PE的标识符。<RDADDR>是vPE被重映射到的Redistributor的地址。<ITS List>是所有ITS的列表。每个ITS占用一位,bit 0对应ITS 0。ITS的编号由GITS_CTLR.ITS_Number寄存器报告。<Sequence Number>是同步点。软件在向不同的ITS发出VMOVP时必须使用相同的值,并且在所有ITS上的命令完成之前不得重复使用相同的值。
9.4.2 GITS_TYPER.VMOVP==1
VMOVP命令只能被发送给一个ITS,不管有多少个ITS有该vPE的映射关系。更改的传送与同步的处理需要硬件完成。这意味着不需要ITS List和SequenceNumber两个字段了。
VMOVP <vPE ID>, <RDADDR>
9.5 重映射或删除vPE/vINTID之间的关系
VMOVI重映射一对EventID-DeviceID组合到不同的vINTID或vPE。
VMOVI <DeviceID>, <EventID>, <vPE ID>, <Doorbell pINTID>
在该命令中:
<DeviceID>-<EventID>一起标识将要被重映射的中断。<vPE ID>是中断将要迁移到的虚拟PE的ID。<Doorbell pINTID>是vPE将要被重映射到的Redistributor。
9.6 改变vLPI配置
同物理LPI中断一样,允许Redistributor缓存vLPI的配置。如果一个vLPI的配置发生改变,则缓存的备份必须失效。有两个ITS命令用于该操作。
INV命令通常用于改变单个或小范围的vLPI中断时使用。
VINVALL命令通常用于失效指定vPE上所有的vLPI中断。也就是,修改大范围的vLPI时使用。
9.7 混合GICv3和GICv4使用
支持GICv3的CPU接口和支持GICv4的CPU接口可能混合连接到一个GIC中断控制器上。下图就展示了这样的一个示例:

图-
GICv4中断控制器,附加了支持GICv3架构的CPU接口
只有实现了GICv4的CPU接口能够接收直接注入的虚拟LPI中断。在支持GICv3的CPU接口上调度vPE(也就是设置GICR_VPENDBASER.Valid==1)是不可预测的。软件可以读取ICH_VTR_EL2.nV4判断是否支持直接注入的虚拟中断。

边栏推荐
- IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but
- AAPT: error: duplicate value for resource ‘attr/xxx‘ with config ‘‘, file failed to compile.
- boost库智能指针
- LLVM系列第二十四章:用Xcode编译调试LLVM源码
- LLVM系列第六章:函数返回值Return
- NDK报错问题分析方案(一)
- RN开发时遇到的问题
- Redis持久化机制
- checkPermissions Missing write access to /usr/local/lib
- 可以拖拽的ViewGroup,仿微信拖拽缩放关闭
猜你喜欢
随机推荐
利用红外-可见光图像数据集OTCBVS打通图像融合、目标检测和目标跟踪
PyTorch(14)---使用现有的模型及其修改
App signature in flutter
The problem that UIWindow's makeKeyAndVisible does not call viewDidLoad of rootviewController
关系代数、SQL与逻辑式语言
NER(命名体识别)之 FLAT模型
详解RecyclerView系列文章目录
IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but
PostgreSQL 性能谜题
深度学习之 卷积网络(textCNN)
6. How to use the CardView production card layout effect
flutter中App签名
MySQL知识总结 (十一) MySql 日志,数据备份,数据恢复
MySQL知识总结 (八) InnoDB的MVCC实现机制
牛客刷题汇总(持续更新中)
Redis持久化机制
ConstraintLayout from entry to abandonment
统计偏科最严重的前100名学生
redis基础
LLVM系列第十七章:控制流语句for










