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
相关推荐
シ風箏1 天前
Neo4j【环境部署 02】图形数据库Neo4j在Linux系统ARM架构下的安装使用
linux·数据库·arm·neo4j
徐某人..4 天前
ARM嵌入式学习--第八天(PWM)
arm开发·学习·arm
徐某人..5 天前
ARM嵌入式学习--第七天(GPT)
学习·arm
whik11948 天前
Keil-MDK开发环境编译后axf自动转换bin格式文件
stm32·arm·keil·axi·bin·mdk
编程零零七10 天前
【Python】tensorflow中的argmax()函数
开发语言·python·信息可视化·数据分析·arm·python学习·python数据可视化
嵌入式小能手12 天前
shell编程入门之提取字符并设置rtc时间
linux·单片机·嵌入式硬件·arm
爱喝纯牛奶的柠檬16 天前
基于stm23的智慧宿舍系统 (DAY10)_小程序
stm32·嵌入式硬件·物联网·阿里云·小程序·arm
嵌入式小能手16 天前
自动化点亮LED灯之shell点灯
linux·单片机·嵌入式硬件·arm
长沙红胖子Qt20 天前
关于 Qt编译遇到fatal error C1189: #error: “No Target Architecture的 解决方法
qt·architecture·architeture
白书宇22 天前
28.100ASK_T113-PRO Linux+QT 显示一张照片
linux·开发语言·arm开发·嵌入式硬件·qt·arm·硬件工程