Arm Generic Interrupt Controller v3 and v4(GICv3v4)学习(一)

提示

该博客主要为个人学习,通过阅读官网手册整理而来(个人觉得阅读官网的英文文档非常有助于理解各个IP特性)。若有不对之处请参考参考文档 ,以官网参考文档为准。

Arm Generic Interrupt Controller v3 and v4学习一共分为三章,这是第一章

  • 第一章:讲解GIC,主要为基础知识,SPI、PPI与SGI配置
  • 第二章:讲解LPI
  • 第三章:讲解Virtualization

1 Background

中断是发送给处理器的一个信号,表明已经发生了需要处理的事件。中断通常是由外围设备产生的。

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

GIC是基于Arm GIC架构实现的。这个体系结构已经从GICv1发展到最新版本的GICv3和GICv4。Arm有几个通用的中断控制器,它们为所有类型的Arm Coretex多核处理器系统提供了一系列的中断管理解决方案。这些控制器的范围从具有具有小CPU内核数量的系统的最简单的GIC-400到具有高性能和多芯片系统的GIC-600。GIC-600AE为ASIL D系统增加了针对高性能ASIL B的额外安全特性。

2 Before you begin

本章涵盖了GICv3和v4的基本操作,以及共享外设中断(SPIs)、私有外设中断(PPIs)和软件生成中断(SGIs)的使用。

GICv3和GICv4允许几种不同的配置和用例。为简单起见,本章集中介绍这些配置和用例中的一个子集,其中:

  • 目前有两个安全(Security)状态
  • 对这两个安全状态都启用了Affinity routing
  • 在所有异常级别上都使能了系统寄存器访问权限
  • 连接的处理器或多个处理器符合Armv8-A或Armv9-A,实现所有异常级别,并在所有异常级别上使用AArch64
    该文档不包含:
  • Lagacy operation
  • 使用异常级别在AArch32中

3 What is a Generic Interrupt Controller

通用中断控制器(GIC)从外设中获取中断,对它们进行优先排序,并将它们传递到适当的处理器core。下图显示了一个GIC从n个不同的外设获取中断,并将它们分发到两个不同的处理器。

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

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

本文章涵盖了大多数Armv9-A、Armv8-A和Armv8-R设计中使用的Arm CoreLink GICv3和GICv4。

自发布后,GICv3和GICv4也有一些小的更新。

  • GICv3.1增加了支持额外的有线中断、安全虚拟化和内存系统资源分区和监控(MPAM)
  • GICv3.2增加了对Armv8-R AArch64的支持
  • GICv3.3增加了对不可屏蔽中断的支持。
  • GICv4.1扩展了虚拟化支持,以覆盖虚拟软件生成的中断(SGIs)的直接注入
  • GICv4.2增加了对直接注入不可掩蔽的虚拟中断的支持

GICv2m是对GICv2的一个扩展,用于添加对消息信号中断的支持。

4 Arm GIC fundamentals

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

4.1 Interrupt types

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

  • 共享外设中断(Shared Peripheral Interrupt, SPI)。可传输到任何已连接的core的外围设备中断。
  • 私有外设中断(Private Peripheral Interrupt, PPI)。对一个core私有的外围中断。PPI的一个例子是来自通用定时器(Generic Timer)的中断。
  • 软件生成中断(Software Generated Interrupt, SGI)。SGI通常用于处理器间的通信,并通过对GIC中的SGI寄存器的写入来生成。
  • 特定外设中断(Locality-specific Peripheral Interrupt, LPI)。LPI首先在GICv3中引入,它与其他三种类型的中断有一个非常不同的编程模型。

LPI配置详见:Locality-Specific Peripheral Interrupts Arm Generic Interrupt Controller v3 and v4

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

4.1.1 Special interrupt number

GIC体系结构为特殊目的保留的中断号如下:

  • 1020:GIC响应于在EL3上的ICC_IAR0_EL1或ICC_HPPIR0_EL1的读取而返回此值,以表示被确认的中断是预期将在Secure EL1或Secure EL2上处理的中断。只有当PE在EL3上使用AArch64状态执行时,或者当PE在监视器模式(Monitor mode)下以AArch32状态执行时,才会返回此INTID。
    当ICC_CTLR_EL3.RM == 1时,也可以在EL3处读取ICC_IAR1_EL1或ICC_HPPIR1_EL1返回这个值
  • 1021: GIC响应于在EL3上的ICC_IAR0_EL1或ICC_HPPIR0_EL1的读取返回这个值,以表示被确认的中断是预期在Non-secure EL1或EL2上处理的中断。只有当PE在EL3上使用AArch64状态执行时,或者当PE在监视器模式(Monitor mode)下以AArch32状态执行时,才会返回此INTID。
    当ICC_CTLR_EL3.RM == 1时,也可以在EL3的ICC_IAR1_EL1或 ICC_HPPIR1_EL1读取返回这个值
  • 1022:在GICv3.3中,当SCLTR_ELx.NMI设置为1,GIC响应ICC_IAR1_EL1返回这个值,以表示被确认的中断是NMI。
  • 1023:对于发送给PE的中断信号,如果该中断信号没有足够优先级,或者最高优先级中断(Pending状态)对于当前的Security state或者与系统寄存器器关联的中断分组不适配,则响应中断返回此值。

4.1.2 How interrupts are signaled to the interrupt controller

传统上,系统使用专用硬件信号从外围设备到中断控制器发出中断信号,如下图所示:

Arm CoreLink GICv3支持这个模型,但也提供了一个额外的信号机制:消息信号中断(message-signaled interrupts,MSI)。MSI通过写入传输到中断控制器中的一个寄存器,如下图所示:

使用消息将中断从外围设备转发到中断控制器,消除了对每个中断源的专用信号的要求。这对于大型系统的设计者来说可能是一个优势,在那里,数百甚至数千个信号可能通过SoC路由并收敛到中断控制器上。

无论中断是作为消息发送的,还是使用专用信号,对中断处理代码处理都没有什么影响。可能需要一些外设的配置。例如,可能需要指定中断控制器的地址。

在Arm CoreLink GICv3中,SPI可以是发出消息信号的中断。LPI总是具有消息信号的中断。不同的中断类型用于不同的寄存器,如下表所示:

4.1.3 Interrupt state machine

中断控制器为每个SPI、PPI和SGI中断源维护一个状态机。此状态机包括以下四种状态:

  • Inactive:当前并未断言(asserted)该中断源。
  • Pending:中断源已被断言,但该中断尚未被PE响应(acknowledged)。
  • Active:该中断源已被断言,并且该中断已被一个PE响应。
  • Active and Pending:一个中断被PE认可,另外一个中断处于Pending状态(注意:这两个中断都是同一个编号)。
    状态机状态如下图所示:

中断的生命周期取决于它是被配置为电平敏感还是边缘触发:

  • 对于电平敏感中断,中断输入的上升边缘,中断变为Pending状态,中断会一直断言(asserted)直到外围设备取消断言(de-asserts)中断信号。
  • 对于边缘敏感中断,中断输入上的上升边缘会导致中断变为Pending状态,但中断不会断言。

4.1.4 Level sensitive and Edge-triggered

4.1.4.1 Level sensitive
  • Inactive to Pending: 中断源置起,此时 GIC 置起中断信号到 PE(前提是:中断被使能并且具有足够的优先级)
  • Pending to Active & Pending: PE 认可中断(读IAR寄存器),GIC 置低到PE的中断信号当PE读取CPU接口(CPU interface)中的一个中断确认寄存器(Interrupt Acknowledge Registers,IAR)来确认中断时,中断从Pending状态转换为Pending to Active状态。此读取通常是在发生中断异常后执行的中断处理例程的一部分。此时,GIC取消断言(de-asserts)中断信号。
  • Active and Pending to Active: 外设置低中断信号(清中断)
  • Active to Inactive: PE 完成中断处理(PE写入CPU接口中的中断结束寄存器(End of Interrupt Registers,EOIR))
4.1.4.2 Edge-triggered

Inactive to Pending: 中断源置起,此时 GIC 置起中断信号到 PE(前提是中断使能并且优先级够)

Pending to Active: PE 认可中断(读IAR寄存器),中断从Pending状态转换为Active状态。此读取通常是在发生中断异常后执行的中断处理例程的一部分。然而,软件也可以轮询IAR。此时,此时,GIC取消断言(de-asserts)中断信号。

Active to Active and Pending: 外设再次置起中断信号

Active and Pending to Pending: PE完成上一个中断处理(CPU interface写EOIR寄存器),GIC再次置起中断信号到PE。

4.1.5 nterrupt signaling in the GIC-400 with physical interrupts only

下图显示了GIC-400如何处理两个具有不同优先级的物理中断。在本示例中为中断N和M(M优先级低于N):

  • 都是Level sensitive
  • 都是SPI,使用active-HIGI IRQS输入
  • 同样的目标PE(PE = CPU)
  • 被配置为Group 0,并且目标CPU interface的GICC_CTLR设置了FIQEn位,因此它们被作为FIQs的信号发送给该CPU。

下面开始解释该图:

  • T1:Distributor检测到Group 0中断M的断言
  • T2:Distributor设置中断M Pending
  • T17:CPU interface断言nFIQCPU[n]
    在中断M pending的几个周期后,nFIQCPU[n]断言,在中断M之后发生一些。在图中,物理接口上的延迟是tph = 15个时钟周期。

Distributor需要几个周期来计算未决中断的最高优先级。如果在计算进行时中断,它只会影响下一次计算的结果。这意味着中断延迟可能会发生变化。因此,虽然tph通常是12个周期,但它通常可能在10到20个周期之间。

  • T42:Distributor检测到更高优先级Group 0中断N的断言
  • T43:Distributor用中断N替换中断M,作为待决中断的最高优先级,并将N设置为待定。
  • T58:tph时钟周期后,中断N变成Pending,CPU interface断言nFIQCPU[n]。nFIQCPU[n]的状态没有变化,因为nFIQCPU[n]是在T17处断言的
    CPU interface更新GICC_IAR中的中断ID字段,以包含中断N的ID值
  • T61:处理器读取GICC_IAR,确认最高优先级的等待中断N
  • T61-T131:处理服务中断N
    • T64:在确认中断N后的3个时钟周期后,CPU interface取消断言nFIQCPU[n]
    • T126:外设取消断言会中断N
    • T128:Pending状态状态从N中移除
    • T131:处理器写入中断寄存器结束寄存器(End of Interrupt Register,GICC_EOIR),其ID为中断N,Distributor deactives中断N
  • T146:在GICC_EOIR写入N后的tph时钟周期,Distributor将新的最高优先级待决中断M转发到CPU interface,该接口断言nFIQCPU[n]
  • T211:处理器读取GICC_IAR,响应最高优先级的待定中断M,并且Distributor将中断M设置为Active and Pending
  • T214:在响应中断M后的3个时钟周期后,CPU interface取消断言nFIQCPU[n]。

上图中的时间仅供说明。它们是典型的值,不能得到保证,也不能被依赖。

4.1.6 Target interupts(Affinity routing)

Arm架构为每个PE分配了一个称为亲和性(Affinity)的层次结构标识符。GIC使用亲和性值(Affinity values)来定位特定core上的中断。

Affinity是一个32位的值,它被分为四个字段:

c 复制代码
<affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0>

下图显示了一个Affinity级别层次结构的示例。

在Affinity级别0处,有一个重新分配器(Redistributor)。每个Redistributor都连接到单个CPU interface。Redistributor控制SGI、PPI和LPI。

Affinity方案在ARMv8-A中,每个PE的affinity存储在MPIDR_EL1寄存器中。系统设计人员必须确保MPIDR_EL1指示的Affinity value与连接到PE的Redistributor中的GICR_TYPER寄存器指示的Affinity value相同。

不同Affinity级别的确切含义是由特定的处理器和SoC来定义的。例子如下:

c 复制代码
<group of groups>. <group of processors>.<processor>.<core>
<group of processors>.<processor>.<core>.<thread>

所有可能的节点都不太可能存在于单个实现中。例如,移动设备的SoC可以有类似的布局:

c 复制代码
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

在ARMv8-A中,AArch64支持4层Affinity路由。但是AArch32和ARMv7只支持3层中断路由(0, x, y, z)。GICD_TYPER.A3V表示中断控制器是否支持level 3。为1表示支持Level 3。

尽管level 0理论最大可以支持256个PE,但是实际上最大可支持16个或者更少。因为PE要适应SGI的编码。

4.1.7 Security model

  1. AArch64

Arm GICv3架构支持Arm TrustZone技术。每个INTID必须由软件分配一个组(group)和安全(security)设置。GICv3支持三种设置组合,如下表所示:

Group 0的中断总是被标记为FIQ。group 1的中断被标记为IRQ或FIQ,这取决于PE的当前安全状态和异常级别,您可以在这里看到:

Group0/EL3总是FIQ,Seure切换(Secure到Non-secure,Non-secure到Secure)为FIQ

这些规则旨在补充AArch64安全状态和异常级路由控制。下图显示了一个简化的软件堆栈,以及当在EL0执行时发出不同类型的中断信号时会发生什么:

在本例中,IRQ被路由到EL1 (SCR_EL3.IRQ0)和FIQ路由到EL3 (SCR_EL3.FIQ1) .考虑到上述规则,当在EL1或EL0处执行时,针对当前安全状态的Group 1中断被视为IRQ。

控制PE状态切换其实由三个因素决定:

  1. 中断产生的时候中断类型,中断类型的意思是中断期望在哪一个状态下得到处理。如上图左上角Non-secure Group1,表示该中断希望在Non-secure的状态下得到处理。Secure Group1,表示该中断希望在Secure状态下得到处理(这里特指Secure EL1状态下得到处理)。Group0表示表示该中断希望在EL3状态下得到处理。
    2.目前PE所处的状态,是在Non-secure还是Secure状态下或者是EL3状态
    3.SCR寄存器中FIQ和IRQ位,这两位分别控制中断发出来的行为。如果FIQ等于1,表示不管发出来是什么,直接切换到EL3。如果FIQ等于0,那么如果发出来的中断是FIQ,那么会去找一个最适合处于该请求的状态,然后再切换到该状态下。同理IRQ也是一样的,如果发出来是IRQ(如左上角第一个信号),如果SCR_EL3.IRQ等于1,则只要发出请求是IRQ,直接切换到EL3处理。如果SCR_EL3.IRQ等于0,则会去找一个最适合处于该请求的状态,然后再切换到该状态下进行处理。

现在,分析该图:

◦ 左上角Non-secure Group1:该中断希望自己在Non-secure状态下得到处理,并且现在PE处于Non-secure状态。按照之前理论,不需要EL3介入。因此IRQ中断发出来之后,由于SCR_EL3.IRQ等于0,PE会找一个可以对IRQ进行处理的状态,然后切换到该状态。显然,因为PE是Non-secure状态,在Rich OS kernel的状态下就可以对该状态处理。(如果SCR_EL3.IRQ等于1,直接切换到EL3处理。)

  • 左上角Secure Group1:该中断希望自己在Secure状态下得到处理,并且现在PE处于Non-secure状态。显然,此时需要EL3介入处理,将PE从Non-secure切换到Secure,然后在对Secure Group1进行处理。所以由于EL3介入,信号首先要发FIQ,查看SCR_EL3.FIQ等于1,此时,直接切换到EL3(Secure Monitor状态下)进行执行,然后找到FIQ中断处理Vector,由它完成状态的切换,切到Secure状态下,在Trusted OS下处理(左上角)Secure Group1的中断源,处理完以后再原路返回。如果SCR_EL3.FIQ等于0,当然此场景下不能设为SCR_EL3等于0,让该信号在Rich OS状态下执行(即让Secure状态中断在Non-secure状态下执行)。这是一个无效的中断路由模型。
  • 左上角Group0:该中断希望自己在EL3状态下得到处理,显然发出来就是FIQ,在EL3下处理之后返回即可。
  • 右上角Secure Group1:该中断希望自己在Secure状态下得到处理,并且现在PE处于Secure状态。按照之前理论,不需要EL3介入。因此IRQ中断发出来之后,由于SCR_EL3.IRQ等于0,PE会找一个可以对IRQ进行处理的状态,然后切换到该状态。显然,因为PE是Non-secure状态,在Rich OS kernel的状态下就可以对该状态处理。(如果SCR_EL3.IRQ等于1,直接切换到EL3处理。)
  • 右上角Non-secure Group1:该中断希望自己在Non-secure状态下得到处理,并且现在PE处于Secure状态。显然,此时需要EL3介入处理,将PE从Secure切换到Non-secure,然后在对Non-secure Group1进行处理。所以由于EL3介入,信号首先要发FIQ,查看SCR_EL3.FIQ等于1,此时,直接切换到EL3(Secure Monitor状态下)进行执行,然后找到FIQ中断处理Vector,由它完成状态的切换,切到Non-secure状态下,在Rish OS下处理(右上角)Secure Group1的中断源,处理完以后再原路返回。
  • 右上角Group0:该中断希望自己在EL3状态下得到处理,显然发出来就是FIQ,在EL3下处理之后返回即可。
  1. AArch32
    3. Single Security
    在未实现EL3的系统或当GICD_CTLR.DS == 1时,仅支持单一安全状态的系统中的中断信令

4.1.8 Impact on software

当实现Armv9-A领域管理扩展(Realm Management Extensions, RME)时,GIC将领域状态视为Non-secure state的扩展。

在配置中断控制器时,软件控制对中断组(interrupt groups)的分配。只有在安全状态(Secure state)下执行的软件才能分配INTIDs来中断组。

通常,只有在安全状态下(Secure state)执行的软件才应该能够访问安全中断(Secure interrupts)的设置和状态:Group 0和Secure Group 1。

也可以使能从非安全状态到安全中断设置和状态的访问。这使用GICD_NSACRn和GICR_NSACR寄存器对每个INTID进行单独控制。

LPIs总是被视为Non-secure Group 1中断

4.1.9 Support for single Security state

GICv3支持Arm TrustZone技术,但使用TrustZone是可选的。这意味着您配置为一个安全状态或两个安全状态:

  • GICD_CTLR.DS == 0 :Secure and Non-secure都支持。
  • GICD_CTLR.DS == 1:只支持一个安全状态。这可以是安全状态或非安全状态。
    将GIC配置为使用与附加的PEs相同数量的安全状态。通常,这意味着当连接到Arm Coretx-A配置文件处理器时,GIC将支持两种安全状态,当连接到ArmCoretx-R配置文件处理器时,GIC将支持一种安全状态。

4.1.10 Programmer's model

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

  • Distributor interface
  • Redistributor interface
  • CPU interface
    通常,Distributor和Redistributor 用于配置中断,而CPU接口用于处理中断。
4.1.10.1 Distributor(GICD_*)

Distributor寄存器为Memory-mapped。并且包括了全局设置:影响所有的PE。Distributor提供了以下编程接口:

◦ Interrupt prioritization and distribution of SPIs.SPI中断优先级和分发

◦ Enabling and disabling SPIs.

◦ Setting the priority level of each SPI.

◦ Routing information for each SPI.(与GICR最大差别)

◦ Setting each SPI to be level-sensitive or edge-triggered.

◦ Generating message-based SPIs.

◦ Controlling the active and pending state of SPIs.

◦ Controls to determine the programmers'model that is used in each Security state (affinity routing or legacy).设置GIC为GICv2还是GICv3(ARE)

4.1.10.2 Redistributors(GICR_*)

每个Redistributor连接一个PE。Redistributors提供了以下编程接口:

◦ Enabling and disabling SGIs and PPIs.

◦ Setting the priority level of SGIs and PPIs.

◦ Setting each PPI to be level-sensitive or edge-triggered. SGI默认为边缘触发

◦ Assigning each SGI and PPI to an interrupt group. group: group0, security group1, non-security group1

◦ Controlling the state of SGIs and PPIs.

◦ Base address control for the data structures in memory that support the associated interrupt properties and pending state for LPIs.控制内存中支持相关中断属性和LPI的挂起状态的数据结构的基本地址

◦ Power management support for the connected PE

4.1.10.3 CPU interface(ICC_*_ELn)

每个Redistributor连接一个CPU interface。CPU interface提供了以下编程接口:

◦ General control and configuration to enable interrupt handling.

◦ Acknowledging(应答) an interrupt.

◦ Performing(执行) a priority drop(恢复) and deactivation(下拉) of interrupts.

◦ Setting an interrupt priority mask for the PE.

◦ Defining the preemption policy(抢占策略) for the PE.

◦ Determining the highest priority pending interrupt for the PE.

GICv3中CPU interface register是以System registers(ICC_*_ELn)访问。

在使用这些寄存器前,软件必须使能 System register interface. ICC_SRE_ELn.SRE ==1 ,n表示Exception Level(EL1-EL3)

GICv1和GICv2中CPU interface register是以memory mapped(GICC_*)访问。

对于PE而言,可以通过读取ID_AA64PFR0_EL1来判定是否支持GIC system register

5 Configuring the Arm GIC

该节讲述SPI,PPI和SGI配置。

5.1 Global setting

打开Distributor control register(GICD_CTLR)中断组(interrupt groups)和路由模型(routing mode)

  • Enable Affinity routing(ARE bits)
    • GICD_CTLR.ARE == 1: GICv3
    • GICD_CTLR.ARE == 0: GICv2

Secure和Non-Secure模式下都可以设置

  • Enable
    GICD_CTLR可以独立使能Group0, Secure Group1 和 Non-secure Group1
    • GICD_CTLR.EnableGrp1S使能Secure Group1 interrupts Distributor
    • GICD_CTLR.EnableGrp1NS使能Non-Secure Group1 interrupts Distributor
    • GICD_CTLR.EnableGrp0使能Group0 interrupts Distributor

5.2 Settings for each PE

5.2.1 Redistributor configuration

每个core都有自己的Redistributor,如下图所示

Redistributor包含一个名为GICR_WAKER的寄存器,它用于记录所连接的PE是online/wake-up还是offline/sleeping。中断只被转发到GIC认为是在线的PE。

reset时,Redistributor认为连接PE是睡眠模式(sleeping)。需要使用GICR_WAKER寄存器唤醒(Wake-up)

  • GICR_WAKER.ProcessorSleep = 0
  • while(!GICR_WAKER.ChildrenAsleep)

软件在配置CPU接口之前执行上述步骤是很重要的,否则行为可能是不可预测的。

写CPU interface寄存器时,除了ICC_SRE_ELn(4.1.10.3节)。当GICR_WAKER.ProcessorSleep == 1或者GICR_WAKER.ChildrenAsleep == 1时,会导致不可预知情况发生。

当PE离线时(GICR_WAKER.ProcessorSleep ==1),一个针对PE的中断将导致一个唤醒请求信号被断言。通常,这个信号将转到系统的电源控制器。然后,电源控制器打开PE。在醒来时,该PE上的软件会清除进程或睡眠位,允许唤醒PE的中断被转发。

5.2.2 CPU interface configuration

CPU接口负责传递中断到连接的PE。若要启用CPU接口,软件必须配置以下内容

  • Enable System register access(使能寄存器访问方式)
    • 在GICv3中,使能ICC_SRE_ELn.SRE

许多最近的Arm Cortex处理器不支持之前版本操作,并且SRE位被固定下来。在这些处理器上,可以跳过此步骤。

  • Set priority mask and binary point registers
    CPU interface包含了priority mask寄存器(ICC_PMR_EL1)和binary point寄存器(ICC_BPRn_EL1)
    • priority mask寄存器:设置最小优先级,即如果该中断想要被PE识别,则优先级要大于该最小优先级
    • binary point寄存器:中断优先级分组和抢占
  • Set EOI mode
    在CPU interface控制器中有ICC_CTRL_EL1和ICC_CTRL_EL3寄存器中含有EOImode bits。用于控制中断处理是怎么完成的。

这里会在End of interrupt章节描述

  • Enable signaling of each interrupt group(使能每个中断组信号)
    当中断组由CPU interface进入PE时,每个中断组信号必须被使能
    • ICC_IGRPEN1_EL1: Group1 interrupts
    • ICC_IGRPEN0_EL1: Group0 interrupts
      ICC_IGRPEN1_EL1在不同的状态下对应不同的寄存器。Security状态下,ICC_IGRPEN1_EL1控制Group1当前Security状态。在EL3状态下,软件可以通过ICC_IGRPEN1_EL3访问Security Group 1中断使能和Non-Secure Group 1中断使能

5.2.3 PE configuration

一些PE的配置也需要允许接收和处理中断。这里只描述在AArch64状态下,ARMv8-A兼容的PE执行的基本步骤

  • Rounting controls(路由控制)

可以采用PE的SCR_EL3和HCR_EL2控制中断路由。路由控制位决定了该中断在哪个Exception Level中处理。这些寄存器的路由位(routing bits)必须采用软件初始化,因为在reset状态下处于Unknown值

  • Interrupt masks

PE在PSTATE也有异常屏蔽位。当设置这些位,中断会被屏蔽。当reset时,这些位会被设置,也就是说不响应外部中断

  • Vector table

通过设置VBAR_ELn寄存器设置PE的Vector table位置。

在reset时,SCR_EL3和HCR_EL2,VBAR_ELn寄存器也是处于UNKNOWN值。需要通过软件设置VBAR_ELn寄存器指向memory中的合适Vector tables
PE要去处理一个中断,首先要开打路由控制,然后打开中断响应,最后准备好Vector table。通过Vector table进行中断处理。

5.3 SPI, PPI and SGI configuration

到目前为止,我们已经研究了配置中断控制器本身。现在,我们将讨论各个中断源的配置。

  • SPI的配置可以通过Distributor(GICD_* registers)。
  • PPI和SGI的配置可以通过各个Redisrtibutors (GICR_* registers)。
    这些不同的配置机制如下图所示:

对于任意一个INTID,软件必须配置以下信息:

  • Priority (GICD_IPRIORITYn SPI, GICR_IPRIORTYn PPI SGI)

每个INTID都有相应的优先级,使用8-bit unsigned value表示。0x00表示最高优先级,0xff表示最低优先级。GICD_IPRIORITYn 和GICR_IPRIORTYn 的设置描述了怎样屏蔽低优先级和控制抢占

实际上,中断控制器不需要将8bit用满。如果GIC支持两种Security状态(GICD_CTLR.DS==0),则最少需要5bit表示优先级。如果GIC支持一种Security状态,则最少需要4bit表示优先级。

  • Group(GICD_IGROUPn, GICD_IGRPMODn, GICR_IGROUP0, GICR_IGRPMOD0)

中断分组:GROUP0,Secure Group1 和 Non-secure Group1

  • Edge-triggered/level-sensitive(GICD_ICFGRn, GICR_ICFGRn)
    如果该中断是物理信号,那么它必须被配置为边沿触发还是电平触发。
    • SGI总是边沿触发,因此,GICR_ICFGR0读为1,写忽略
  • Enable(GICD_ISENABLERn, GICD_ICENABLER, GICR_ISENABLER0, GICR_ICENABLER0)

每个INTID都有这样的使能位。Set-enable寄存器和Clear-enable寄存器。

这里如果只采用一个寄存器就会涉及到read-modify-write,会影响到其他位的中断

  • Non-maskable
    中断可以配置为不可屏蔽,这样被视为比属于同一组的所有其他中断更高的优先级。也就是说,不可屏蔽的Non-secure Group1中断被视为比其他所有Non-secure Group1中断更高的优先级。
    • 不可屏蔽属性在GICv3.3中添加,需要在PE中进行匹配支持。
    • 只有Secure Group1和Non-secure Group1的中断可以被标记为不可屏蔽。

对于bare metal(裸机)环境,在初始配置后通常无需更改设置。但是,如果必须重新配置一个中断,例如要更改Group设置,则应首先disable该中断,然后再更改其配置。

大多数配置寄存器的重置值都由定义实现(IMPLEMENTATION DEFINED)。这意味着中断控制器的设计者决定值是什么,并且值可能在系统之间有所不同。

5.4 Arm GICv3.1 and the extended INTID ranges

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

  • GICR_ISENABLERn:使能原始PPI范围
  • GICR_ISENABLERnE:使能增加PPI范围(GICv3.1)

5.5 Setting the target PE for SPIs

对于SPI,其中断的目标必须通过GICD_IROUTERn寄存器额外配置。每个SPI都有一个GICD_IROUTERn寄存器,并且由Interrupt_Routing_Mode位来控制路由策略。

  • GICD_IROUTERn.Interrupt_Routing_Mode == 0
    • SPI会被传递到A.B.C.D PE,指定的寄存器
  • GICD_IROUTERn.Interrupt_Routing_Mode == 1
    • SPI可以传递给任意连接的PE。由Distributor选择目标PE,并且每次中断触发都可能会选择不同的PE
    • 这种类型的路由被称为 1-of-N

每个PE都有对应的Redistributor。每个Redistributor都有GICR_CTLR寄存器。如果一个PE不想接受1-of-N中断类型,那么可以通过GICR_CTLR中的DPG1S,DPG1NS和DPG0位设置

6 Handing interrupts

本节描述中断发生时发生的事情:中断如何路由到PE,中断如何相互优先排序,以及在中断结束时发生了什么。

6.1 Routing a pending interrupt to a PE

在4.1.3 Interrupt state machine章节描述了在断言中断源时,中断如何从inactive状态过渡到pending状态。当中断处于pending状态时,中断控制器根据以下条件决定将中断发送到已连接的PE之一。

当一个中断变成Pending时,中断控制器决定是否要发送中断到连接的PE上。一个PE是否被中断控制器选中,依赖于以下条件:

  • Group Enables
    INTID被分配到组(Group0, Secure Group1 or Non-Secure Group 1)。对于每个组,在Distributor和CPU Interface里面都有Group bit。如果一个中断的组被关闭,那么它就不能被分配给PE
  • Interrupt Enables
    每个中断都可以打开和关闭,如果关闭了,该中断不会被传递到PE
  • Routing Controls
    中断控制器决定了哪一个PE可以接收中断
    • 对于SPI:由GICD_IROUTRn控制。SPI可以以特定的PE为目标,或者是所有PE中的任意一个
    • 对于LPI:如果ITS实现,那么LPI的路由信息来自ITS
    • 对于PPI:PPI属于特定的PE,它的处理只能由该PE处理(不需要路由)
    • 对于SGI:原始PE定义了目标PE(进一步描述在第七章 Sending and receiving Software Generated Interrupts章节)
  • Interrupt Priority & Priority Mask
    每个PE在它的CPU interface都有Priority Mask寄存器(ICC_PMR_EL1)。该寄存器定义了中断可以被传递到PE的最小优先级。该中断的优先级只有高于该寄存器值才可被PE识别。
  • Runing Priority
    如果PE没有在处理中断,那么该优先级为idle priority(0xFF)。只有当一个中断比现在正在运行中断的优先级更高,才能发生抢占。

6.2 Taking an interrupt

当进入异常处理程序时,软件不知道发生了哪个中断。处理程序必须读取其中一个中断确认寄存器(Interrupt Acknowledge Registers,IARs),以获得中断的INTID。

CPU interface有两个IAR寄存器。读取该IAR寄存器就会返回INTID。GIC会自动推进中断状态机:由Pending变成active。在一个典型的中断处理任务里,第一步就是去读IAR中断状态寄存器中的值。然而,软件可以在任何时候读取寄存器的值。

有时,IAR不能返回一个有效的INTID。例如,软件读取ICC_IAR0_EL1,应答 Group 0中断,但Pending的中断Group于Group 1。在这种情况下,读取返回其中一个reserved INTIDs,如下表所示:

当读取IAR,如果存在上述中断,则不承认存在中断。

6.2.1 Example of interrupt handing

一个手机系统有一个modem(调制解调器)中断信号,该信号希望在Rich OS(linux系统)Non-secure状态下得到处理

Rich OS: Linux

Trusted OS:OPTE

Secure Monitor:TFA(以前叫ATF)

  1. PE正在Secure EL1模式下执行Trusred OS,此时来了一个modem interrupt变成Pending状态。并且modem interrupt被配置成Non-secure Group1。此时会发送一个FIQ给PE。SCR_EL3.FIQ==1,异常被切换到EL3
  2. Secure Monitor在EL3模式下读取IAR0,返回1021。1021表示该中断希望在Non-secure状态下执行。Secure Monitor执行中断上下文必要的切换(Secure--->Non-secure)
  3. PE处于Non-secure状态,中断又被以IRQ的方式发送给PE(SCR_EL3.IRQ = 0)

其实FIQ和IRQ本质上在ARMv8架构下已经没有区别了,只不过是软件上人为的分开当作两种不同的应用场景使用。以前ARMv7架构下,FIQ和IRQ是两种不同的执行模式,FIQ有自己的寄存器,可以直接拿来用,但是如果是在IRQ下,有些寄存器不能用,因为已经被其他的任务再用。如果直接拿来用会把别人的数据覆盖掉。所以在IRQ模式下,需要先把别人的状态先保存,然后再去用,用完再恢复。但是在ARMv8架构下,已经没有FIQ,IRQ的执行模式了,只有异常等级(Exception Level)。FIQ没有自己专有的寄存器,Fast便体现不出来,但是F可以理解为Forward

上图展示了,Non-secure Group1中断直接从EL1(Trusted OS)切换到EL3。但是该行为可能不是想要的。下图展示了中断最初在Secure EL1

不同点:

  1. SCR_EL3.FIQ = 0,EL1不是直接切换到EL3而是先自查,然后通过SMC切换到EL3
  2. SCR_EL3.FIQ = 1,切换到EL3的时候,SCR_EL3.FIQ 自动切换为1,这样是为了防止此时有Secure Group1中断进来,系统无法从Non-Secure切换到Secure状态下

6.3 Runing priority and preemption

running priority: CPU不处理中断时,running priority表示最低优先级的数值0xff,当PE响应某个中断后,该CPU的running priority表示该中断源的优先级。

PMR用于设置一个中断能够传递到PE的最小优先级。当PE响应到一个中断,则该中断会被赋值到running priority这个寄存器。如果PE向EOI寄存器写值,running priority会恢复到写入之前的值。

running priority被存储在CPU interface的running priority寄存器里(ICC_RPR_EL1)

running priority的概念在抢占的时候比较重要。抢占只有在高优先级向正在处理低优先级的PE发送时候会发生。抢占引入了一些软件的额外复杂性,但是它能阻止低优先级的中断阻塞高优先级中断的处理。

PE去读IAR寄存器导致编码状态数据的变化。那PE什么时候会去读IAR寄存器,很显然,只有PE能看到Pending的状态并且能响应外部中断。这样它才能跳到中断处理函数里,然后才来读IAR寄存器。然后如果PE自动去响应中断,然后跳到中断向量表的过程中,处理器自动把中断响应位关闭。也就是说,在中断处理函数里,还要想要对外部中断进行响应,软件上需要打开该位(中断)操作。PSTAT里I那位要软件清零。在linux看来,不会出现该情况,因此不会去清零。

上图展现了一级抢占。然而,抢占有多级,在一些情况下,有些抢占并不是想要的或者不是必须的。GICv3在抢占的时候允许通过Binary Point register(ICC_BRRn_EL1)定义优先级。Binary Point寄存器将优先级分为两个字段,组优先级和子优先级,如下图所示:

  • ICC_BRRn_EL1 = N
    • 7:N Group
    • N-1:0 Subpriority

对于抢占来说,只有Group优先级是被考虑的,Subpriority是被忽略的。例如以下三个中断:

在该场景中,A可以抢占B和C,B不能抢占C,因为B和C有相同的优先级。为了实现该目标,可以设置N=4

抢占要求中断处理支持中断嵌套

6.4 End of interrupt

当中断被处理完,需要软件通知中断控制器任务已被处理完,让状态机切换到下一个状态。

GICv3将该事件分为两个任务

  • Priority Drop
    需要将running priority恢复到中断发生之前的那个值。
  • Deactivation
    更新中断状态机状态。典型地,从Active state到Inactive state。
    在GICv3架构中,Priority Drop和Deactivation可以同时发生,也可以单独发生。由ICC_CTRL_ELn.EOImode设置
    • EOImode = 0
      如果是Group0 interrupts,写ICC_EOIR0_EL1;如果是Group1 interrupts,写ICC_EOIR1_EL1。中断控制器将Priority Drop和Deactivation两件事一起做。该模式通常用在bare metal(裸机)环境下
    • EOImode = 1
      如果是Group0 interrupts,写ICC_EOIR0_EL1;如果是Group1 interrupts,写ICC_EOIR1_EL1。中断控制器处理Priority Drop。然后再写ICC_DIR_EL1寄存器处理Deactivation。该模式通常用在virtualization环境下

大多数软件将使用EOIMode0。EOImode1最常被管理程序(hypervisors)使用。

当写这些寄存器,软件必须写INTID。

6.5 Checking the current state of the system

6.5.1 Highest priority pending interrupt and running priority

正如它们的名称所示,最高优先级等待中断(Highest Priority Pending Interrupt)寄存器ICC_HPPIR0_EL1和ICC_HPPIR1_EL1报告了一个PE的最高优先级等待中断的INTID。

运行优先级在6.3 Runing priority and preemption节已介绍,并由运行优先级寄存器(ICC_RPR_EL1)报告。

6.5.2 State of individual INTIDs

Distributor指示了每个SPI的当前状态的寄存器。同理,Redistributors提供了PPIs和SGIs的状态的寄存器。

这些寄存器提供了将一个中断改变为一个特定的中断。如果在没有一个外设assert一个中断时,测试一个配置是否正确。这会变得非常有用。

这些寄存器可以分为active state和pending state。下表列出了active state。pending state具有相同的格式:

当affinity routing打开时,GICD_ISACTIVER0和GICD_ICACTIVER0的结果将被视作RES0(reserved)。这是因为GICD_ISACTIVER0和GICD_ICACTIVER0表示INTIDs 0到31,属于SGI和PPI
软件执行在Non-secure状态下,是看不到Group0和Secure Group1的中断。除非通过GICD_NASCRn或者GICR_NASCRn

7 Sending and receiving Software Generated Interrupts

软件生成中断(Software Generated Interrupts ,SGIs)是软件通过写入中断控制器中的一个寄存器来触发的中断。

生成SGI:SGI是通过写入CPU接口中的以下SGI寄存器之一而生成的:

您可以在下图中看到SGI寄存器的基本格式:

7.1 Controlling the SGI ID

SGI ID字段控制生成的INTID。如Interrupt types章节中所述,INTIDs 0-15用于SGIs。

7.2 Controlling the target

SGI寄存器中的中断路由模式(Interrupt Routing Mode,IRM)字段控制一个SGI被发送到哪个PE或哪些PEs。有两个选项:

  • IRM == 0
    中断被发送到...<目标列表>,其中<目标列表>被编码为下的每个关联0节点的1位。这意味着中断最多可以发送到16个PE,这可能包括原始PE(原始的意思就是它自己)。
  • IRM == 1
    中断被发送到所有连接的PE,除了原始PE。

7.3 Controlling the Security state and grouping

SGIs的安全状态(Security state)和分组(Group)由:

  • 由原始PE上的SGI寄存器ICC_SGI0R_EL1、ICC_SGI1R_EL1或ICC_ASGIR_EL1。
  • 目标PE或PEs的GICR_IGROUPR0和GICR_IGRPMODR0寄存器。
    在Secure state下执行的软件可以同时发送Secure和Non-secure的SGIs。在Non-secure state下执行的软件是否可以生成Secure SGIs,这将由GICR_NSACR来控制。
    该寄存器只能由处于Secure state的软件访问。下表显示了GIC通过检查来确定是否转发了一个中断:
  • 原始PE的Secure state
  • 中断所针对的PE的中断处理配置
  • SGI寄存器

此表假设为GICD_CTLR.DS0.当GICD_CTLR.DS1,标记为(*)的SGIs也会被转发。

7.4 Comparison of GICv3 and GICv2

在GICv2中,SGI INIIDs由原始PE和目标PE存储。这意味着一个给定的PE可以有相同的SGI INTID,pending最多8次。

在GICv3组中,SGIs只由目标PE存储。这意味着给定的PE只能有一个pending的SGI INTID。

例如,PEs A和B同时将SGI INTID 5发送到PE C,如下图所示:

C将收到多少个中断?

  • 对于GICv2:两个中断
    GIC将同时接收来自A和b的中断。这两个中断的顺序取决于单独的设计和精确的时间。这两个中断可以通过原始PE的ID的前缀为GICC_IAR返回的INTID来区分。
  • 对于GICv3:一个中断
    因为原始PE不存储SGI,所以不能对两个PE进行相同的中断处理。因此,C只接收一个中断,ID为5,没有前缀。
    该示例假设这两个中断是同时或几乎同时发送。如果C能够在第二个SGI到达之前承认第一个SGI,那么C将在GICv3中看到两个中断。

在遗留操作期间,也就是当GICD_CTLR.ARE = 0,SGIs的行为与Arm CoreLink GICv2中相同。

参考文档

  1. Learn the architecture - Arm Generic Interrupt Controller v3 and v4
  2. GICv3 and GICv4 Software Overview
  3. CoreLink™ GIC-400 Generic Interrupt Controller Technical Reference Manual
相关推荐
Stone.Wu4 天前
快速理解ARM Cortex-M流水线:指令执行过程通俗解释
arm
我在人间贩卖青春4 天前
汇编之分支跳转指令
汇编·arm·分支跳转
我在人间贩卖青春4 天前
汇编之加载存储指令
汇编·arm·寄存器加载存储
我在人间贩卖青春4 天前
汇编之状态寄存器访问指令
汇编·arm·状态寄存器
我在人间贩卖青春4 天前
汇编之软中断指令和协处理指令
汇编·arm·软中断·协处理
我在人间贩卖青春4 天前
汇编之数据处理指令
汇编·arm·数据处理指令
fly的fly8 天前
浅析 QT远程部署及debug方案
qt·物联网·arm
切糕师学AI10 天前
ARM标准汇编(armasm)中的标号(Label)
汇编·arm
CHENG-JustDoIt11 天前
嵌入式开发 | ARM Cortex-M 系列中M3、M4、M23 和 M33四款处理器的深度对比分析
arm开发·单片机·嵌入式硬件·arm
toradexsh18 天前
在NXP iMX8QM上使用 Jailhouse
arm·nxp·toradex·imx8mp·jailhouse