GICv3/v4-软件概述

目录内容

  • 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
GICv2GICv1所有特性
支持虚拟化
Cortex-A7 MPCore
Cortex-A15 MPCore
Cortex-A53 MPCore
Cortex-A57 MPCore
GICv3GICv2所有特性
支持超过8个PE
支持基于消息的中断
支持超过1020中断ID
CPU interface是系统寄存器
增强安全模型,独立的安全Group1中断和非安全Group1中断
Cortex-A53 MPCore
Cortex-A57 MPCore
Cortex-A72 MPCore
GICv4GICv3所有特性
虚拟中断的直接注入
Cortex-A53 MPCore
Cortex-A57 MPCore
Cortex-A72 MPCore

GICv2mGICv2的一个扩展,用来支持基于消息的中断,更多信息请联系ARM公司咨询。

2.3 GICv3架构的实现

ARM® CoreLink™ GIC-500GICv3的实现。ARM® Cortex®-A53, ARM® Cortex® -A57ARM® Cortex®-A72等实现需要的CPU接口。

2.4 旧版本支持

GICv3对编程模型做了许多改变。为了支持基于GICv2的旧软件,GICv3支持旧操作。

编程模型可以通过GICD_CTRL寄存器中的亲和力路由使能(ARE)标志位进行控制:

  • ARE == 0,亲和力路由被禁止(旧操作);
  • ARE == 1,亲和力路由被使能。

注意:为了可读性,在本文档中,GICD_CTLR.ARE_SGICD_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-15SGI每个PE都会备份
16-31PPI每个PE都会备份
32-1019SPI-
1020-1023特殊中断号用于表示特殊情况,参考第5.3小节
1024-8191保留-
>=8192LPI具体上限由实现厂商定义
3.1.2 中断如何给中断控制器发送信号

通常,中断使用专用硬件信号从外设发送信号给中断控制器。

在这里插入图片描述
GICv3支持这种模型,并且支持基于消息的中断。基于消息的中断是通过写中断控制器的寄存器进行设置和清除操作的。
在这里插入图片描述

通过互联协议实现的基于消息的中断

使用消息转发外设到GIC的中断,移除了每个中断源必须一个专用信号的要求限制。这对于大型系统的硬件设计人员来说是一个优势,在大型系统中,可能有数百甚至数千个信号汇聚到中断控制器上。

GICv3中,SPI可以是基于消息的中断,但是LPI总是基于消息的中断。它们分别使用不同的寄存器进行设置。

中断类型寄存器
SPIGICD_SETSPI_NSR声明中断
GICD_CLRSPI_NSR清除中断
LPIGITS_TRANSLATER
3.1.3 基于消息的中断对软件的影响

至于使用消息,还是使用一个专用信号发送中断,对软件处理中断的方式几乎没有什么影响。

但是可能需要对外设进行一些必要的设置,比如,指定中断控制器的地址。这超出了本文档的范围,故不在此描述。

3.2 中断状态机

中断控制器为每一个SPI、PPI和SGI中断维护着一个状态机。包含4种状态:

  • Inactive:中断还未发生

  • Pending:中断已经发生,但是中断还没有被PE应答

  • Active:中断已经发生,也已经被PE应答过

  • ActivePending:一个中断被应答过,另一个中断被挂起中

注意:LPI中断没有activeactive&pending状态。更多信息,参考第6.2节。

下图描述了状态机的状态转换:
在这里插入图片描述

中断的生命周期依赖于它被配置为电平触发还是边沿触发的。第3.2.1和3.2.2小节提供了采样序列。

3.2.1 电平触发

在这里插入图片描述

AP代表activepending

  • InactivePending

    当中断源产生信号时,就会从Inactive转变为Pending状态。从此刻开始,GIC发送中断信号给PE(如果中断被使能并且有足够的优先级)。

  • PendingActive & Pending

    当PE通过读取IAR寄存器(中断应答寄存器)应答该中断时,就会由Pending转变为Active & Pending状态。这种读取操作一般是中断处理程序的一部分。当然,软件也可以轮询IAR寄存器。

    此时,GIC解除给PE的中断信号。

  • Active & PendingActive

    当外设解除给GIC的中断信号后,由Active & Pending转变为Active状态。这通常发生在PE上的中断处理程序写外设的状态寄存器时(作为写状态寄存器的响应)。

  • ActiveInactive

    当PE写EOIR寄存器(中断结束寄存器)时,由Active转变为Inactive状态。这表明PE已经完成中断的处理。

3.2.2 边沿触发

在这里插入图片描述

边沿触发中断的生命周期

  • InactivePending

    当中断源产生信号时,就会从Inactive转变为Pending状态。从此刻开始,GIC发送中断信号给PE(如果中断被使能并且有足够的优先级)。

  • PendingActive

    当PE通过读取IAR寄存器应答了中断后,中断的状态Pending转变为Active。这种读取操作通常是中断处理程序(中断异常发生后)的一部分。当然,软件也可以轮询IAR寄存器。

    此时,GIC解除了给PE的中断信号。

  • ActiveActive & Pending

    如果外设重新产生该中断信号,则会由Active状态转变为active & pending

  • Active & PendingPending

    当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可以控制SGIPPILPI,参见第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 0EL3中断(安全固件)
Secure Group 1Secure EL1中断(可信OS)
Non-secure Group 1非安全状态的中断(OS和/或hypervisor

Group 0中断总是以FIQ的形式发送信号。Group 1既可以IRQ的形式,也可以以FIQ的形式发送信号,依赖于PE当前安全状态和异常等级。

表 - 安全设置和异常类型以及中断的对应关系(EL3使用AArch64

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

这些规则被设计用来补充ARMv8-A安全状态和异常级路由控制。下图展示了一个简化的软件栈,以及当在EL0执行不同类型的中断时发生的情况:
在这里插入图片描述

在本示例中,IRQ被路由到EL1(SCR_EL3.IRQ==0),FIQ被路由到(SCR_EL3.FIQ==1)。按照上表描述的规则,当在EL1EL0执行当前安全状态的group 1中断时被视为一个IRQ

非当前安全状态的中断一律视为FIQ,被EL3捕获。这样的设计,允许EL3的软件执行必要的上下文切换。这种情况的详细示例请参考第5.3节。

3.4.1 对软件的影响

在配置中断控制器时,软件负责将INTID分配给某个中断组。只有执行在安全状态的代码能够分配INTID给中断组。

通常只有执行在安全状态的软件能够访问安全中断的设置和状态(Group 0Secure Group 1)。

从非安全状态设置安全中断并访问其状态是能够使能的。可以使用GICD_NSACRnGICR_NSACR寄存器对每一个INTID单独进行控制。

注意1:在复位时,INTID所属的中断组是SoC实现时定义的。

注意2:LPI总是被视为非安全组1中断。

3.4.2 单个安全状态的支持

对于ARMv8-AGICv3,两种安全状态是可选择的。所以,SoC在实现的时候,可以选择实现单个安全状态,也可以选择实现两个安全状态。

GICv3实现中,支持两种安全状态。可以通过GICD_CTLR.DS控制使用几个安全状态。

  • GICD_CTLR.DS == 0:支持两个安全状态(SecureNon-secure)。

  • GICD_CTLR.DS == 1:只支持一个安全状态。在只实现了一个安全状态的实现中,该位是RAO/WI

当只支持单个安全状态时,中断分为2组,Group 0Group 1

当然,本文档主要描述支持两种安全状态的实现。

注意:如果设置GICD_CTLR.DS1,则只能在复位时才能清除。

3.5 编程模型

GICv3中断控制器的寄存器主要分为3组:

  • Distributor接口

  • Redistributor接口

  • CPU接口
    在这里插入图片描述

3.5.1 Distributor(GICD_*

Distributor寄存器是内存寄存器,包含影响所有PE的全局设置。包括:

  • SPI中断优先级和分发

  • 使能、禁止SPI中断

  • 配置各个SPI中断的优先级

  • 每个SPI中断的路由信息

  • 配置各个SPI中断的触发方式(电平或边沿)

  • 产生基于消息的SPI中断

  • 控制SPI中断的activepending状态

  • 确定各个安全状态中使用的编程模型(亲和力路由还是旧方式)

3.5.2 Redistributor(GICR_*

每个PE对应一个redistributorredistributor提供的功能:

  • 使能、禁止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:GICv1GICv2中,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中断的配置和SPIPPISGI存在着较大差异,因此,专门在第6章描述。

使用GICv3中断控制器的大部分是多核系统,或是多处理器系统。有些配置是全局的,也就是说,会影响所有连接的PE。其它的配置是专门针对单个PE的。

我们首先看一下全局配置,然后是每个PE的设置。

4.1 全局配置

Distributor的控制寄存器(GICD_CTLR)可以控制中断分组的使能,并设置路由模式。

  • 使能亲和力路由(ARE标志位)

    GICD_CTLRARE标志位控制是否使能亲和力路由。如果没有使能,GICv3使用旧操作方式。亲和力路由的使能,在安全和非安全状态下是分开控制的。

  • 分发器使能

    GICD_CTLR包含Group 0Secure Group 1Non-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==1GICR_WAKR.ChildrenAsleep==1时,写CPU interface寄存器(除了ICC_SRE_ELn),会导致不可预知的行为。

4.2.2 CPU interface配置

CPU interface负责将中断传送到连接的PE上。为了使能CPU interface,软件必须进行下面的配置:

  • 使能系统寄存器访问

    通过设置ICC_SRE_ELnSRE位进行使能。

  • 设置优先级掩码和binary point寄存器

    CPU接口包含优先级掩码寄存器(ICC_PMR_EL1)和Binary Point寄存器(ICC_BPRn_EL1)。优先级掩码寄存器设置中断最小优先级(转发给PE靠此判断)。Binary Point寄存器用于优先级分组和抢占。这些寄存器的更多使用可以参考第5章。

  • 设置EOI模式

    ICC_CTLR_EL1ICC_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_EL3HCR_EL2寄存器中。路由控制位决定了中断被路由到哪一个异常等级。复位时,这些标志位的状态未知,所以,软件必须进行初始化。

  • 中断掩码

    PE在PSTATE中也有异常掩码位。设置了这些位,中断会被屏蔽。复位时被重置。

  • 中断向量表

    PE的中断向量表的地址保存到VBAR_ELn寄存器中。复位时,VBAR_ELn的值未知。软件必须设置VBAR_ELn指向内存中的正确的向量表。

更多信息,参考ARMv8-A架构参考手册。

4.3 SPI、PPI和SGI配置

SPI是通过Distributor使用GICD_*寄存器配置的。PPISGI通过单独的Redistributor使用GICR_* 寄存器进行配置。

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

  • 优先级(GICD_IPRIORITYn GICR_IPRIORITYn

    每个INTID拥有一个相关的优先级,用8位无符号数表示。0x00是最高优先级,0xFF是最低优先级。第5章描述了GICD_IPRIORITYnGICR_IPRIORITYn中的优先级是如何屏蔽掉低优先级的中断的,以及它如何控制抢占。

    中断控制器不需要实现所有的8个优先级表示位。如果GIC支持两个安全状态,最小实现5个标志位。如果仅支持一个安全状态,最小实现4个标志位即可。

  • 分组(GICD_IGROUPnGICD_IGRPMODnGICR_IGROUP0GICR_IGRPMOD0

    正如第3.4节中描述的,一个中断可以被指定为3个不同分组中的一个(Group 0Secure Group 1Non-secure Group 1)。

  • 边沿触发、电平触发(GICD_ICFGRnGICR_ICFGRn

    如果中断作为物理信号发送,则必须配置其触发方式(边沿触发或电平触发)。SGI总是边沿触发,因此,对于软件中断,GICR_ICFGR0总是读为1,而忽略写操作(RAO/WI)。

  • 中断使能(GICD_ISENABLERnGICD_ICENABLERGICR_ISENABLER0GICR_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 == 0

    SPI被传送到指定的PE(A.B.C.D),寄存器中指定的亲和力值。

  • GICD_IROUTERn.Interrupt_Routing_Mode == 1

    SPI可以被传送到中断分组中的任一个PE。Distributor选择合适的目标PE,每次发送中断信号的时候这可能会变化。这种路由类型称为1对N模式。

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

5 处理中断

5.1 中断挂起后会发生什么

第3.2节,描述了外设产生一个专用中断信号后,中断控制器中由inactive转换为pending状态。

当中断变为pending状态,中断控制器决定是否将中断发送给某个PE。中断控制器选择PE,依赖于下面的条件:

  • 分组使能

    第3.4节描述了如何将INTID指定给某个中断分组(Group 0Secure Group 1、或Non-secure Group 1。对于每个中断分组,在DistributorCPU 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的示例

  1. PE正在Secure EL1上执行可信OS时,modem中断会被挂起(pending)。因为modem中断属于非安全Group 1,所以它会以FIQ信号发送到PE。因为SCR_EL3.FIQ==1,异常陷入到EL3

  2. 执行在EL3Secure Monitor软件,读取IAR寄存器,返回1021。该值表明中断本应在非安全状态下处理。于是,Secure Monitor执行必要的上下文切换操作。

  3. 现在PE处于非安全状态,中断以IRQ的形式被发送,由运行在非安全EL1上的rich-OS进行处理。

在上面的示例中,非安全Group 1中断导致立即退出安全OS。但这并不总是需要的。下图展示了另一个模型,中断首先陷入到Secure EL1
在这里插入图片描述

  1. PESecure EL1上执行可信OS时,modem中断会被挂起(pending)。因为modem中断属于非安全Group 1,所以它会以FIQ信号发送到PE。因为SCR_EL3.FIQ==0,中断陷入到EL1

  2. 可信OS保存好自身的状态。然后,调用SMC指令切换到非安全状态。

  3. SMC异常陷入到EL3。执行在EL3Secure Monitor执行必要的上下文切换操作。

  4. 现在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位优先级表示位被分为了groupsubpriority

抢占的时候,只考虑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 dropDeactivation即可以一起发生,也可以分开发生。这由ICC_CTLR_ELn.EOImode设置决定:

  • EOImode = 0

    ICC_EOIR0_EL1Group 0)或ICC_EOIR1_EL1Group 1),执行优先级降落和失效两个动作。这种模式通常用于简单的裸机程序。

  • EOImode = 1

    ICC_EOIR0_EL1Group 0)或ICC_EOIR1_EL1Group 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设置SPIactive状态。每个INTID一位。
读取某一位,返回值为:
* 1 – 该中断处于active状态;
* 0 – 该中断处于非active状态;
对某一位写1,则激活相应的中断;写0没有影响。
GICD_ICACTIVERn清除SPIactive状态。每个INTID一位。
读取某一位,返回值为:
* 1 – 该中断处于active状态;
* 0 – 该中断处于非active状态;
对某一位写1,则失效相应的中断;写0没有影响。
GICR_ISACTIVER0设置SGIPPIactive状态。每个INTID一位(0-31)。
读取某一位,返回值为:
* 1 – 该中断处于active状态;
* 0 – 该中断处于非active状态;
对某一位写1,则激活相应的中断;写0没有影响。
GICR_ICACTIVER0清除SGIPPIactive状态。每个INTID一位(0-31)。
读取某一位,返回值为:
* 1 – 该中断处于active状态;
* 0 – 该中断处于非active状态;
对某一位写1,则失效相应的中断;写0没有影响。

注意1:当亲和力使能时,GICD_ISACTIVER0GICD_ICACTIVER0被看作RES0。这是因为GICD_ISACTIVER0GICD_ICACTIVER0在该情况下对应的中断号是0-31,而它们在每个PE中都有备份,通过每个PE的redistributor报告。

注意2:运行在非安全状态的软件,不能看见Group 0Secure Group 1中断的状态,除非通过GICD_NASCRnGICR_NASCRn允许访问。

6 配置LPI

LPI只有在亲和力路由使能时支持,只是它们的配置方式不同。

配置LPI涉及到:

  • Redistributor

  • 可选的ITS(中断翻译服务)。

LPI总是基于消息的中断,并且可以由ITS支持。ITS负责接收外设中断,将其转换为LPI并转发给合适的Redistributor。一个系统可以包含多个ITS,每一个都能够单独配置。

外设也可以直接发送LPIRedistributor,从而绕过ITS。但是,ITS提供了许多特性,允许高效地处理大量的中断源。

注意:对LPI的支持是可选的,由GICD_TYPER.LPIS标志位控制。如果至少存在一个ITS,外设是否可以绕过ITS,直接发送LPIRedistributor是中断控制器实现时定义的。

6.1 ITS
6.1.1 ITS的操作

外设通过写ITSGITS_TRANSLATER可以产生LPI中断。写操作提供给ITS以下信息:

  • EventID

    写入到GITS_TRANSLATER的值。EventID标识外设正在发送的中断。EventID可能与INTID相同,或者可以通过ITS翻译成INTID

  • DeviceID

    DeviceID标识外设。产生外设标识符DeviceID的方法是实现时定义的。比如,可以使用AXI用户信号。

ITS将外设写入到GITS_TRANSLATEREventID转换成INTID。如何将EventID转换成INTID取决于具体的外设,这就是为什么需要DeviceID的原因。

LPI中断的INTID集合在一起。集合中的所有INTID都路由到相同的Redistributor。软件分配LPI INTID给集合,允许它有效的将中断从一个PE迁移到另一个PE。

ITS使用三种类型的表处理LPI的翻译和路由。分别是:

  • 设备表

    DeviceID映射到中断翻译表。

  • 中断翻译表

    包含与DeviceID相关的EventIDINTID映射关系。还包含成员是INTID的集合。

  • 集合表

    将集合映射到Redistributor

在这里插入图片描述

图-ITS转发LPIRedistributor

当外设写GITS_TRANSLATER时,ITS需要:

  1. 使用DeviceID从设备表中选择合适的项。该项标识使用哪个中断翻译表。

  2. 使用EventID从中断翻译表中选择合适的项。该项提供INTID集合ID

  3. 使用集合ID从集合表中选择想要的项,它会返回路由信息。

  4. 转发中断到目标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服务,软件必须:

  1. 为设备和集合表申请内存。GITS_BASER[0..7]寄存器指向ITS设备和集合表的基地址和大小。软件使用这些寄存器发现ITS支持的表数量和类型。然后,软件申请所需内存,设置GITS_BASERn指向这些分配的内存表。

  2. 为命令队列申请内存。软件必须为命令队列分配内存,然后设置GITS_CBASERGITS_CWRITER指向这些内存的起始地址。

  3. 使能ITS。当这些表和命令队列申请内存成功,就可以使能ITS。通过设置GITS_CTLR.Enable1实现。一旦该标志位设置成功,则GITS_BASERnGITS_CBASER寄存器就变成了只读的。

6.1.4 集合和设备表的大小和布局

设备和集合表的位置和大小是通过GITS_BASERn寄存器配置的。软件必须分配足够的内存给这些表,并在使能ITS之前配置好GITS_BASERn

软件可以分配平面(单级)表,或两级表,通过GITS_BASERn.Indirect标志位指定。

注意:两级表的支持是可选的。如果ITS仅支持平面表,则GITS_BASERn.IndirectRAZ/WI

RAZ/WIread 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控制,可以是4K16K64K。因此,上面公式计算出的结果必须是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 添加一个新命令到命令队列中

为了添加新命令到命令队列,软件需要:

  1. 写新命令到队列中。

    GITS_CWRITER指向没有包含合法命令的队列项。软件必须写命令到该项中,并保证全局可见性。

  2. 更新GITS_CWRITER

    软件必须更新GITS_CWRITER,让其指向没有包含新命令的下一个队列项。更新动作会告知ITS,已经添加了一个新命令。

    当然,软件能够同时写多个命令到队列中,只要还有足够的空间,并且相应更新GITS_CWRITER寄存器。

  3. 等待命令被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),该表保存着EventIDINTID的映射关系。软件必须为ITT分配内存,然后,使用MAPD命令将DeviceID映射到ITT

    MAPD <DeviceID>, <ITT_Address>, <Size>
    
  • INTID映射到集合,将集合映射到Redistributor

    当外设的DeviceID已经映射到ITT,那么该设备所发送的不同EventID必须映射到INTID,而这些INTID必须被映射到不同的集合。每个集合被映射到一个目标Redistributor上。

    中断号(INTID)映射到集合,可以使用MAPTIMAPI命令。当EventIDINTID相同时,使用MAPI命令:

      MAPI <DeviceID>, <EventID>, <Collection ID>
    

    EventIDINTID不同时,使用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是之前的目标RedistributorRDADDR2是新Redistributor

    如果有多个集合的目标是RDADDR1,我们可能需要多个MAPC命令,每一个命令对应一个集合。这儿,假设所有的集合都将重映射到相同的新目标Redistributor上。

  • 将中断映射到不同的集合里

    单个中断可以被重映射到另一个集合中。通过下面的命令序列完成:

    MOVI <DeviceID>, <EventID>, <新集合ID>
    SYNC <RDADDR1>
    

    在该命令序列中,RDADDR1是中断被重新映射之前,原来指定的那个集合对应的Redistributor

6.1.8 移除中断映射

为了重新映射中断,或删除中断映射关系,软件需要:

  1. 禁止当前映射中断的物理中断号(INTID)。这是在LPI配置表中完成的,参考第6.2.2节。

  2. 发送DISCARD命令。这将移除中断映射关系,并清除对应INTID的挂起状态。

  3. 发送SYNC命令,并等待直到该命令完成。

该命令完成后,不会再有中断被发送到中断原先映射的Redistributor上了。

6.1.9 重映射或删除设备映射关系

为了改变或删除设备映射关系,软件需要:

  1. 对于当前映射外设的每个EventID,按照第6.1.8节执行一遍。

  2. 发送MAPD命令重新映射设备。或者使用有效位为0MAPD命令删除映射关系。

  3. 发送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的步骤如下:

  1. LPI配置表分配内存,并使用每个LPI中断合适的配置信息初始化该表。

  2. 设置GICR_PROPBASER,使其都指向该LPI配置表。

  3. 为每个RedistributorLPI挂起状态表分配内存,并分别初始化每个表。系统启动时,将表所在内存填充0,这意味着所有的LPI INTID都处于未激活状态(inactive)。

  4. 设置每个Redistributor自己的GICR_PENDBASER,使其指向自己私有的LPI挂起状态表。

  5. 设置每个Redistributor自己的GICR_CTLR.EnableLPIs1,使能LPI中断。该标志位设为1后,GICR_PENDBASERGICR_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只有两个状态,inactivepending

    在这里插入图片描述

    图-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,软件需要:

  1. 更新LPI配置表中的项。
  2. 同步更新操作。
  3. 失效Redistributor中的缓存配置信息。

失效配置信息的缓存,可以通过发送INVINVALL命令给ITS实现。INV失效单个中断,所以常用于重新配置少量LPI中断的情况。INVALL命令失效指定集合中所有中断。

如果没有实现ITS,软件必须写GICR_INVLPIRGICR_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

  • IRM

    IRM(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_EL1ICC_SGI1R_EL1ICC_ASGIR_EL1控制安全状态(产生软件中断的PE的寄存器)
  • GICR_IGROUPR0GICR_IGRPMODR0控制中断分组(目标PE的寄存器)

执行在安全状态的软件可以发送安全和非安全SGI软件中断。非安全状态的代码是否可以产生安全SGI中断是由寄存器GICR_NSACR控制的。这个寄存器只能由运行在安全状态下的软件访问。下表展示了源PE的安全状态,目标PE的中断处理配置和SGI寄存器是如何决定中断是否转发的各种情况。

源PE的安全状态SGI寄存器目标PE的配置是否转发
Secure EL3/EL1ICC_SGI0R_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
ICC_SGI1R_EL1Secure Group 0否 (*)
Secure Group 1
Non-Secure Group 1
ICC_ASGI1R_EL1Secure Group 0
Secure Group 1
Non-secure Group 1
Non-secure EL2/EL1ICC_SGI0R_EL1Secure Group 0GICR_NSACR配置决定(*)
Secure Group 1
Non-secure Group 1
ICC_SGI1R_EL1Secure Group 0GICR_NSACR配置决定(*)
Secure Group 1GICR_NSACR配置决定
Non-secure Group 1
ICC_ASGI1R_EL1Secure Group 0GICR_NSACR配置决定(*)
Secure Group 1GICR_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-APE-B同时发送SGI中断号5PE 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=0SGI中断的行为与GICv2相同。

8 虚拟化

ARMv8-A支持虚拟化。为了实现虚拟化,GICv3也应该支持虚拟化。为此,GICv3需要添加以下功能:

  • CPU interface寄存器的硬件虚拟化

  • 虚拟中断

  • 维护中断

GIC架构没有考虑DistributorRedistributorITS的硬件虚拟化。所以,这些接口的虚拟化需要软件进行处理。这些内容不在本文档的讨论范围之内。

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*寄存器拥有相同的指令编码。在EL2EL3Secure 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_EL1ICC_PMR_EL1/ICV_PMR_EL1。当HCR_EL2.IMO==1HCR_EL2.FMO==1,非安全EL1访问ICV*寄存器,而不是ICC*寄存器。

    下图,展示了同一个指令如何基于HCR_EL2的路由控制访问ICC*ICV*寄存器的示例。

    在这里插入图片描述

8.3 管理虚拟中断

hypervisor使用List列表寄存器ICH_LRn_EL2产生虚拟中断。每个寄存器表示一个虚拟中断,并记录:

  • 虚拟中断号(vINTID

    这是报告给虚拟机内部的中断ID号。

  • 虚拟中断状态

    虚拟中断状态(PendingActiveActive&PendingInactive。随着虚拟机中的软件和GIC的交互,状态机自动更新。比如,hypervisor创建一个新的虚拟中断,首先设置中断状态为pending。当执行在vPE上的软件,读取ICV_IARn_EL1,中断控制器中,该中断的状态机更新为Active

  • 中断分组(group

    虚拟机只有一个安全状态(也就是GICD_CTLR.DS==1)。因此,虚拟中断分为两组:Group 0Group 1Group 0中断以vFIQ信号的形式发送,Group 1中断以vIRQ信号的形式发送。

  • 物理中断号(pINTID

    虚拟中断使用物理中断的INTID进行标记(可选)。如果vINTID的状态机更新时,pINTID的状态也就更新了。

8.3.1 一个物理中断被转发到vPE的例子

下图展示了把一个物理中断转发到vPE的过程:

在这里插入图片描述

  1. 一个物理非安全Group 1中断从Redistributor转发到物理CPU接口;

  2. 物理CPU接口检查是否将物理中断转发到对应的PE。这个过程已经在第5.1小节描述过。假设,检查通过,物理中断生效。

  3. 该中断被EL2捕获。hypervisor读取IAR寄存器,返回物理中断号(pINTID)。该物理中断(pINTID)现在处于active状态。hypervisor决定将该中断转发给正在运行的vPEhypervisor将物理中断号(pINTID)写入到ICC_EOIR1_EL1寄存器。此时,ICC_CTLR_EL1.EOImode==1,只执行优先级降落,并没有将该物理中断失效。

  4. hypervisor写一个List寄存器,产生一个虚拟中断,并将状态设置为pendingList寄存器中会指定虚拟中断号(vINTID),同时记录原始的物理中断号(pINTID)。然后,hypervisor通过执行ERET指令,从异常中返回,重新将执行权限交给vPE

  5. vCPU接口会检查是否将虚拟中断转发给vPE。这些过程与物理中断的处理一样,只不过它们使用的是ICV寄存器。本示例中,假设检查通过,虚拟中断生效。

  6. 虚拟中断被非安全的EL1捕获。当软件读取IAR,返回虚拟中断号(vINTID),虚拟中断此时处于active状态。

  7. 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系统寄存器的hypervisorICC_SRE_EL2.SRE==1),照样可以托管使用旧方式(ICC_SRE_EL1(NS)==0)的虚拟机。在这种情况下,运行在虚拟机中的软件使用内存映射的GICV寄存器,就跟在GICv2中一样。

8.6 上下文切换

当在vPE之间切换上下文时,hypervisor软件保存一个vPE的状态,加载另一个的上下文内容。vCPU interface寄存器的状态是vPE上下文的一部分,这些状态包括:

  • ICV寄存器的状态

  • 有效的虚拟优先级

  • 处于pendingactiveactive&pending状态的虚拟中断

ICV寄存器可以被EL2使用ICH寄存器进行访问。下图,展示了ICH_VMCR_EL2的各个位域对ICV寄存器状态的映射关系。

在这里插入图片描述

切换vPE时,必须保存和恢复有效虚拟优先级。当前vPE的有效优先级可以通过ICV_APnR_EL2寄存器访问。

正如第8.3节描述,虚拟中断是通过List寄存器管理的。这些寄存器的状态特定于当前vPE。因此,在上下文切换的时候,必须保存和恢复这些寄存器。

9 GICv4:虚拟LPI的直接注入

GICv4添加了对直接注入虚拟LPI(vLPI)的支持。此功能允许软件向ITS描述物理事件(EventIDDeviceID的组合)如何映射成虚拟中断。如果中断的目标vPE正在运行,则直接转发虚拟中断,而无需先进入hypervisor。这可以减少虚拟化中断带来的开销。

9.1 RedistributorvLPI状态和配置

为了支持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被设置指向一个vPEVPT时,该vPE被定义为正在被调度

对于被调度的vPE,可以直接注入虚拟中断。如果目标vPE没有被调度,虚拟中断在正确的VPT表中被记录为pending状态。

当在vPE之间执行上下文切换时,hypervisor必须更新Redistributor相关寄存器。这意味着hypervisor必须:

  • 清除GICR_VPENDBASER.Valid标志位

    清除Valid标志位,告知Redistributor,正在执行上下文切换。Redistributor将会检索vCPU接口中的任何挂起虚拟中断,保证内存中的VPT表是正确的。

  • 轮询GICR_VPENDBASER.Dirty标志位,直到读到0

    Dirty标志位反映了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在分配时是全零,而不是在第一次被调度时全零。如果vPEITS映射存在,可能会在创建到第一次驻留期间设置虚拟中断。

9.2 GICv4中的ITS操作

GICv4添加了许多新命令,添加了新的表类型给ITS使用。允许软件:

  • 为某个PE,将EventID-DeviceID组合映射成虚拟中断号(vINTID

    • (可选)可以指定一个门铃中断。这是当中断产生,但是vPE没有被调度的话,产生的一个物理中断(pINTID)。
  • vPE映射到物理Redistributor

下图展示了ITS转发虚拟中断时遵循的流程,将虚拟LPI中断直接注入虚拟机:

在这里插入图片描述

外设写GITS_TRANSLATER

  1. ITS使用设备ID(DeviceID),从设备表中选择正确的项。该项可以标识将要使用的中断翻译表。

  2. ITS使用事件ID(EventID),从中断翻译表中选择正确的项。这会得到:

    a. 物理中断号(pINTID)和集合ID,正如第6.1.1节描述的。

    b. 虚拟中断号(vINTID)和vPE ID,(可选)一个物理中断号(pINTID)用于门铃中断。

  3. ITS使用vPE ID选择vPE表中想要的项,vPE表返回目标RedistributorvPEVPT地址。

  4. ITS转发虚拟中断号(vINTID)、门铃中断和VPT地址到目标Redistributor

  5. Redistributor比较来自ITSVPT地址与当前GICR_VPENDBASER中的值:

    a. 如果VPT地址和GICR_VPENDBASER匹配,则调度vPE,并将虚拟中断号(vINTID)转发到虚拟CPU接口。

    b. 如果不匹配,则vPE不被调度。相应的中断(vINTID)在VPT表中设为挂起状态。如果提供了门铃中断,则物理中断号(pINTID)被转发到物理CPU接口。

9.3 vPE和vINTID的映射关系

EventID-DeviceID组合被映射到虚拟中断号(vINTID)和虚拟PE(vPE)。当EventIDvINTID相同时,使用VMAPI命令。

VMAPI <DeviceID>, <EventID>, <Doorbell pINTID>, <vPE ID>

EventIDvINTID不同时,使用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命令,EventIDvINTID具有相同的值。

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:01。两个事件ID都映射成vPE的虚拟中断号,vPE ID6

  • EventID 0 – 虚拟中断号为8725,门铃中断号为8192

  • EventID 1 – 虚拟中断号为9000,没有门铃中断

vPE(6)被映射到地址为0x78400000Redistributor,同时,它的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

如果hypervisorvPE映射到不同的物理PE上,ITS映射关系必须被更新,以便虚拟中断能够发送到正确的物理PE上。更新ITS映射关系使用VMOVP命令,使用VSYNC命令同步内容。

一个系统可以包含多个ITS。如果有不止一个ITSvPE的映射关系,任何更改都必须反映到所有的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 0ITS的编号由GITS_CTLR.ITS_Number寄存器报告。

  • <Sequence Number>是同步点。软件在向不同的ITS发出VMOVP时必须使用相同的值,并且在所有ITS上的命令完成之前不得重复使用相同的值。

9.4.2 GITS_TYPER.VMOVP==1

VMOVP命令只能被发送给一个ITS,不管有多少个ITS有该vPE的映射关系。更改的传送与同步的处理需要硬件完成。这意味着不需要ITS ListSequenceNumber两个字段了。

VMOVP <vPE ID>, <RDADDR>
9.5 重映射或删除vPE/vINTID之间的关系

VMOVI重映射一对EventID-DeviceID组合到不同的vINTIDvPE

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 混合GICv3GICv4使用

支持GICv3的CPU接口和支持GICv4的CPU接口可能混合连接到一个GIC中断控制器上。下图就展示了这样的一个示例:

在这里插入图片描述

图-GICv4中断控制器,附加了支持GICv3架构的CPU接口

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


在这里插入图片描述

  • 11
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
# 学习对象在全民造车、造芯的大时代,在努力去解决卡脖子的时代,ASIC硬件、SOC底层软件、Linux Kernel等操作系统软件(内核/驱动)、软硬件方面的系统架构师等的岗位需求也越来越明显,社会一直都是非常缺人的,缺的是核心的那一小撮、领头的那一小撮,社会所缺的更是能够软硬件融合的那一小撮人……总之,要想在这个时代,站稳自己的脚跟,能够在大公司或行业上拥有一席之地,就必需深入学习底层技术原理,核心技术才是您的看家本领。本课程设计之初,主要针对SOC底层软件开发的者、系统开发者,或者励志成为这样的人。既适合资深/高级工程师来查缺补漏,又适合初级工程师入门。(理论上该课程和ASIC硬件电路设计无关,该课程偏软件,但事实购买该课程的做ASIC的同学已然超过了15%)适用人群1、芯片开发者(包括底层软件、或做ASIC硬件的)。不限行业,例如车、云、物联网、移动端等领域;2、汽车行业开发者(主机厂、tier1、SOC厂家、各级供应商);3、嵌入式开发者、kernel开发者、驱动、软件工程师;4、学生。既适合学生从入门到精通,也适合资深工程师查缺补漏;您的收益:1、全体系的掌握ARMv8/ARMv9的核心知识点(ARM基础、异常中断GIC、MMU/Cache、architecture...);2、掌握ARM架构、掌握SOC架构、掌握常规IP(gic、smmu、timer、AXI/ACE/CHI、TZC400...);3、快速熟悉常规系统软件(bootrom、spl、ATF、TEE、bootloader、kernel...), Secureboot安全启动...4、技术水平提升N个level, 掌握快速的学习方法;# 学习什么在ARM蓬勃发展的年代,不仅仅涉及到物联网IOT、移动领域(如手机)、汽车电子领域,现在还涉及到PC、服务器的,简直就是各行各业。ARMv8出来已经有10年了,ARMv9也2年时间了。在技术不断更新迭代的背景下,此时再去学习十五年前的ARMv7、二十年前的ARMv5/v6显然不是明智的选择。本课程主要基于当前最新的架构,ARMv8的aarch64和ARMv9,如涉及具体的ARM Core IP主要还是以最新的ARM Core IP为主,软件架构也是以当前最主流的/未来所趋势的架构来讲解。以下也给大家列举初了一个ARM产品的timeline的总结(在本课程中有着大量的这种总结),从这张图中,您是可以清晰的看到本课程拥有独具一格的风格、拥有全网最新(且唯一)的资料总结或学习路线。# 本课程大纲和规划(课程持续更新中,课程总量统计:2022/10/02  当前是 61节课, 22小时)第一章:主要是快速学习: ARM简介、指令集、寄存器总结等。第二章:本系列视频的一大亮点,系统全面地讲解了arm异常中断gic等相关的软硬件知识,本人一直在倡导“学arm安全其实就是学arm架构,学arm架构其实就是学习arm的异常和中断”,异常中断是领着你进入架构的入门,是让你变成系统软硬件架构师的必走之路。第三章:安全专题,这也是本视频最核心的东西。因为你无论买书还是看博客等,你都很难找到讲解安全的教程,这里就是有和无的区别。本人系统的整理的安全的知识,带领你快速入门。第四章:mmu专题,透过事务看本质的讲解,白话式的演讲。在所有模块中,mmu也算是相对较简单模块。相信人人听得懂,人人学得会。第五章:cache专题,一切追求实事求是,不人云亦云,一切知识点都有迹可循,推翻了网络的很多观念。在众多模块中,cache算是一个比较难的模块。了解了cache后,才能算真正了解系统的软硬件架构。第六章:虚拟化,本人不擅长,会啥就随便讲点啥。(以后学会了再来补)第七章:architecture,就是零散和零碎的系统架构知识,如exclusive、arch timer、reset、系统启动、SOC设计、AMBA/AXI/ACE、DSU、WFE/WFI这样的。第八章: 新增的ARMv9 CCA/RME安全架构专题第九章:主要放置一些直播课。# 课程收益1、知道我学习什么,我要怎么去学习,从此之后有了一个明确的学习路线。2、认识一些共同目标的人,相互讨论问题,共同进步。勤学、共学、助学。3、ARM不再神秘,SOC不在神秘,让您短期内就能cover住全局4、熟悉ARM Architecture架构知识5、熟悉SOC架构知识6、熟悉主流的系统软件框架7、熟悉各项硬件原理和机制,如异常中断、MMU、cache、TLB、VMSA、Trustzone6、深入了解当前的系统架构、软硬件架构,能够看懂这些大家,将来也能够自己设计。7、熟悉系统的启动流程、Secureboot等8、熟悉各类标准和规范9、能够进入芯片厂商干活、能够在非芯片产生成为技术担当。10、学习资料的获取方法,会看11500多页的ARM手册,会看数以百计的ARM各项参考手册。 本课程会持续更新。也希望通过本课程的学习,能够让大家的ARMv8/ARMv9开发技术能有质的飞越,能找到自己心仪的工作。在购买之前,也建议大家看一看第一章第一节的课程介绍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值