ARM通用中断控制器GIC之中断处理简介

在阅读本章之前,可以参考笔者之前关于GIC的一些描述:

ARM通用中断控制器GIC(generic Interrupt Controller)简介

ARM架构Generic Interrupt Controller(GIC)之Distributor和CPU interface功能介绍

ARM架构Generic Interrupt Controller(GIC)详解之术语介绍

文章目录

  • 一,中断处理简述
  • 二,在多处理器系统中处理不同类型的中断
  • 三,如何识别所支持的中断
    • 1,GICD_ISENABLERn, Interrupt Set-Enable Registers
    • 2,Interrupt Clear-Enable Registers, GICD_ICENABLERn
    • 3,Interrupt Controller Type Register, GICD_TYPER
      • GICD_TYPER[10] -- SecurityExtn
      • GICD_TYPER[7:5] -- CPUNumber
      • GICD_TYPER[4:0] -- ITLinesNumber
    • 4,Distributor Control Register, GICD_CTLR
    • 5,中断识别相关寄存器使用总结
      • 如何找到GIC所支持的中断
  • 四,中断处理一般流程
  • 中断处理后动作
    • 1,优先级下降(Priority drop)
    • 2 ,中断消失(Interrupt deactivation)
    • 3, Interrupt Acknowledge Register, GICC_IAR
    • 4, End of Interrupt Register, GICC_EOIR

一,中断处理简述

中断处理过程包括了:

  • 如何识别辨认中断
  • 软件层如何配置GIC,从而控制中断
  • GIC在每个CPU interface上为每个中断维护的状态机
  • 处理器的异常模型如何与GIC交互
    GIC架构支持单处理器和多处理器系统,无论是单处理器还是多处理器系统,GIC架构都可以包含GIC安全扩展:
  • 能够识别一个与之相连的,已经实现了ARM安全扩展的处理器,对GIC寄存器是安全访问还是非安全访问。
  • 实现了判断对GIC寄存器是安全访问还是非安全访问,以至于:
    – 可以复用(bank)一些寄存器,使同一个寄存器能够分为安全和非安全的拷贝。
    – 一些寄存器是安全的(secure),这意味着只能在安全模式下访问。
    – 剩下的寄存器是公共的(common),这意味着它们可以在安全和非安全模式下被访问。
  • 可以使用中断分组(interrupt grouping)特性来处理安全中断和非安全中断,比如:
    • Group 0 中断为安全中断
    • Group 1 中断为非安全中断
  • 在多处理器系统中,可能只在它的一些CPU interface中实现了GIC的安全扩展。
    除了GIC v1版本不支持GIC安全扩展,在其他GIC架构中都支持中断分组(interrupt grouping):
  • 默认情况下,所有中断都是Group 0中断,并且使用 IRQ中断请求被发送给与之相连的处理器。
  • 每个中断可以被配置成 Group 0 或者 Group 1 中断。
  • 一个CPU interface也可以被配置成使用FIQ中断请求,对一个已连接的处理器发送 Group 0 中断信号。

二,在多处理器系统中处理不同类型的中断

GIC支持外设中断( peripheral interrupts)和软件产生的中断(software-generated interrupts):

  • 软件产生的中断(SGI)使用GIC的 N-N 模型。
  • 外设(硬件)中断使用 1-N 模型。

三,如何识别所支持的中断

GIC架构通过中断ID来识别不同类型的中断,为了能够正确地处理中断,软件层必须知道GIC所支持的中断ID。 ARM强烈建议将已实现的中断进行分组,以使用最低的ID号和尽可能小的中断ID范围,因为这减少了必须实现的寄存器数量以及必须检查的中断路由范围。

1,GICD_ISENABLERn, Interrupt Set-Enable Registers

为了正确地处理中断,软件必须知道GIC所支持的中断ID。GICD_ISENABLERn寄存器为每个中断提供了一个Set-enable位,Set-enable位写1,说明该位对应的中断可以从Distributor转发到CPU interface。读取该bit,也可以获知对应中断是否被使能。
使用限制:
1. 如果该寄存器bit对应的是未实现的中断,则表现为RAZ(Read As Zero)和WI(Write Ignore)。
2. 如果实现了安全扩展,非安全状态下访问Group 0(安全中断)中断对应的寄存器bit,结果为RAZ和WI。
GICD_ISENABLER bit assignments
GICD_ISENABLERn里每个bit位对应一个中断ID的中断,比如GICD_ISENABLER0寄存器控制着中断ID为0至31的中断。
GICD_ISENABLER0 可以控制:

  • SGIs, 中断ID为15-0, 对应的寄存器bit为[15:0]
  • PPIs, 中断ID为31-16, 对应的寄存器bit为 [31:16]
    剩余的GICD_ISENABLERn,从GICD_ISENABLER1开始,则是为SPI中断提供对应的控位。
    假设某个中断的ID为M,则GICD_ISENABLERn的n为M ÷ 32的整数部分,它在GICD_ISENABLERn中对应bit位序号为 M mod 32。比如中断ID为66的中断,它对应的寄存器为GICD_ISENABLER2,并且GICD_ISENABLER2中的GICD_ISENABLER2[2]为该中断对应的bit位。
    此外,对于SGI中断,对GICD_ISENABLERn的对应bit的读写是未定义的,对于PPI和SPI是有效的。
    禁用中断仅禁用将中断从Distributor转发到任何CPU interface。它不会阻止中断改变状态,例如如果它已经为active,则不能阻止其变为pending或者active and pending。

2,Interrupt Clear-Enable Registers, GICD_ICENABLERn

中断清除寄存器GICD_ICENABLERn为每个中断提供了一个Clear-enable bit,写1将会禁止Distributor将对应的中断转发给CPU interface,读取对应bit位可以知道该中断是否被使能。
对于SGI,GICD_ICENABLERn对应bit的读写无影响。
对于PPI和SPI:

  • 读到0:说明对应中断被禁止转发
  • 读到1:说明对应中断可以被转发
  • 写入0:无影响
  • 写入1:禁止对应中断被转发,写入1后立即读取该bit位,将会返回0。
    GICD_ICENABLERn其他特性和GICD_ISENABLERn一致。

3,Interrupt Controller Type Register, GICD_TYPER

ARM通用中断控制器GIC之中断处理简介_第1张图片
GICD_TYPER提供GIC的一些配置信息:

  • 是否实现安全扩展
  • GIC最大支持的中断数量
  • 已经实现的CPU interface 数量
  • 如果实现了安全扩展,所实现的最大LSPI(Lockable Shared Peripheral Interrupts)数量

GICD_TYPER[10] – SecurityExtn

GICD_TYPER[10] bit为1说明实现了安全扩展,为0则无。

GICD_TYPER[7:5] – CPUNumber

GICD_TYPER[7:5]为GIC实现的CPU interface数量,如果GICD_TYPER[7:5]为0b11,说明实现了4个CPU interface。

GICD_TYPER[4:0] – ITLinesNumber

GICD_TYPER[4:0]为GIC所支持的最大的中断数量。若GICD_TYPER[4:0]=N,则说明最多支持 32*(N+1)个中断。比如N=3,则最多支持128个中断,中断号为0~127。GICD_TYPER[4:0]=0b11111=1020为所支持的最大的中断ID号,并且1020至1023作为其他用途的中断ID而被保留使用。由于SGI和PPI中断的数量固定,所以ITLinesNumber能说明GIC所能支持的SPI中断的最大数量,ITLinesNumber的值也是以下寄存器的n值:

  • GICD_IGROUPRn
  • GICD_ISENABLERn
  • GICD_ICENABLERn
  • GICD_ISPENDRn
  • GICD_ICPENDRn
  • GICD_ISACTIVERn
  • GICD_IPRIORITYRn
  • GICD_ITARGETSRn
  • GICD_ICFGRn

4,Distributor Control Register, GICD_CTLR

该寄存器将控制Distributor是否能将处于pending状态的中断转发到CPU interfaces中。
ARM通用中断控制器GIC之中断处理简介_第2张图片
如果没有实现安全扩展,比如在GICv1版本中,GICD_CTLR寄存器只有一个enable bit,用于控制Distributor向CPU interface转发处于pending状态的中断。

在GICv2中,如果实现了安全扩展,则有两个enable为,分别为Group0和Group1。
ARM通用中断控制器GIC之中断处理简介_第3张图片
如果enable位置0,说明Distributor不能将中断转发,置1则表明Distributor可以在优先级规则下将中断转发。
需要注意的是,即使将Distributor的全局enable位置0,禁止了Distributor的转发功能,其他GIC寄存器仍可以正常进行读写,这意味着在重新enable Distributor之前,软件层可以改变PPI和SPI中断的中断状态,比如:

  • 通过写入对应的GICD_ISPENDRn寄存器使中断处于pending状态。
  • 通过写入对应GICC_EOIR 或GICC_AEOIR寄存器 ,可以移除中断的active状态。
    当 GICD_CTLR.的{EnableGrp1, EnableGrp0}被设置为不能转发中断,读取 GICC_IAR 或者GICC_AIAR寄存器时将返回一个虚假的中断ID。

5,中断识别相关寄存器使用总结

如何找到GIC所支持的中断

  1. 首先读取GICD_TYPER寄存器, 从GICD_TYPER寄存器的ITLinesNumber字段可以知道所实现的GICD_ISENABLERn寄存器的数量,也就是n的大小,从中也可知当前GIC可支持多少个SPI。
  2. 通过写入GICD_CTLR寄存器,可以禁止Distributor将中断转发到CPU interface。
  3. 对于每个已经实现的GICD_ISENABLERn,从GICD_ISENABLER0开始:
    - 对GICD_ISENABLERn.写入0xFFFFFFFF
    - 然后读取GICD_ISENABLERn.的值,如果读到1,则表明GIC支持对应的中断ID。
    软件可以使用GICD_ICENABLERn寄存器来发现哪些中断被永久enable了。对于每个已经实现的GICD_ICENABLERn,从GICD_ICENABLER0开始:
    - 对GICD_ICENABLERn.写入0xFFFFFFFF,将会禁止所有可以被禁止的中断。
    - 读取GICD_ICENABLERn的值,如果读到1,说明对应中断被永久使能。
    - 然后再对GICD_ISENABLERn中对应需要被重新enable的中断bit位写入1。
    当软件完成这次搜索,通常会写入GICD_CTLR寄存器来重新使能Distributor的转发功能。
    此外,如果软件使用的是非安全访问,则只能搜索并控制被配置为非安全的中断。如果GIC实现了中断分组,软件可以:
    • 写入GICD_IGROUPRn寄存器,将中断配置成Group0还是Group1.
    • 使用GICD_CTLR.EnableGrp0 和GICD_CTLR.EnableGrp1 两个bit位来单独控制Group0和Group1的中断转发。

四,中断处理一般流程

在Distributor中为每个被CPU interface所支持的中断维护了一个状态机,每个中断可以具有一下四种状态:

  • inactive
  • pending
  • active
  • active and pending
    当GIC获知了一个中断请求,该中断的状态会被标记为pending,重新产生一个pending状态的中断也不会影响当前中断的状态。
    中断的一般处理流程如下:
  1. GIC首先确认哪些中断已被使能
  2. 对于每个处于pending状态的中断,GIC将确认该中断的目标处理器,即要被转发到何处。
  3. 对于每个CPU interface,Distributor 将转发处于pending状态,并且优先级最高的中断到对于CPU interface。
  4. 每个CPU interface决定是否给与它相连的处理器发送中断请求信号。
  5. 处理器获知了中断,GIC将返回中断ID,并且更新中断状态。
  6. 在处理完中断后,处理器将会发送EOI(End Of Interrupt)给GIC
    在更多的细节中,还有以下步骤:
    1. GIC可以决定哪个中断被使能,一个没有被使能的中断在GIC中无影响。
    2. 对于每个被使能的处于pending状态的中断,Distributor决定要转发到哪里。
    3. 对于每个处理器,Distributor基于它保存的每个中断的优先级信息,判断出优先级最高的中断,并将它转发到目标CPU interface中。
    4. 如果Distributor正在转发一个中断请求到CPU interface中,CPU interface将会判断该中断是否有足够的优先级,然后再给处理器发送中断信号。
    5. 当处理器发生中断异常时,它会读取其CPU interface的GICC_IAR寄存器以确认该中断,这个读操作将返回一个中断ID。
    6. 当处理器完成对中断的处理操作时,它必须发信号通知GIC已完成处理,总是需要有效地写入 EOIR寄存器,并且可能也需要写入 deactivate interrupt register, GICC_DIR寄存器。

对于每个CPU interface,GIC体系结构要求对EOIR的有效写入的顺序与从GICC_IAR或GICC_AIAR读取的顺序相反,因此每个有效的EOIR写入都将引用最近的一次中断确认。
如果在EOIR写入之后,没有具有足够优先级的pending中断,则CPU interface将向处理器取消中断异常请求。
CPU interface绝不会向与之相连的处理器发送处于active and pending状态的中断信号,它只会发送处理pending状态,并且有足够的优先级的中断:

  • 对于PPI和SGI中断(N-N),特定中断ID的活跃状态在CPU interface之间被复用(banked),这意味着:如果一个特定中断ID的中断在一个CPU interface中处于active或者active and pending状态,那么在CPU interface上就不会发出具有相同ID的中断信号。
  • 对于SPI来讲(1-N),中断的活跃状态对所有CPU interface是共享的,这意味着:如果一个特定中断ID的中断在一个CPU interface中处于active或者active and pending状态,那么在任何 CPU interface上就不会发出具有相同ID的中断信号。

中断处理后动作

当处理器完成对中断的处理后,它必须将通知GIC已完成处理。中断处理完成后,GIC需要一些步骤来改变它的状态:优先级下降(Priority drop)和中断消失(Interrupt deactivation)。

1,优先级下降(Priority drop)

优先级下降(Priority drop)是在有效写入EOIR(即GICC_EOIR或GICC_AEOIR)时发生的运行优先级(Running priority)下降。有效写入是指写入不会不可预测,不会被忽略,并且写入的中断ID值不会大于1019。

  • Running priority(运行时优先级):在CPU interface中,具有最高优先级且处于active状态的中断的group priority,没有被有效地写入EOI寄存器。如果CPU interface中没有active的中断,并且没有对EOI进行有效的写入,当前running priority将会变为idle priority。
  • Idle priority(空闲优先级):Idle priority是指可以被分配给一个中断的最低优先级,在一个实现了8-bit字段用来描述中断优先级大小的实例中,Idle priority的值为0xFF。否则,它要么是0xFF,要么是GICD_IPRIORITYRn寄存器中的最大值,并且该寄存器中断priority字段可以自己赋值。
    在优先级下降时,运行的优先级从EOIR写入所引用的中断的优先级降低到:
  • 没有EOI写入的,处于active状态中优先级最高的优先级
  • 如果当前没有active的中断,则是空闲优先级(优先级最小)。

2 ,中断消失(Interrupt deactivation)

中断消失就是中断状态的改变:

  • 从active and pending 到 pending
  • 从active 到 idle
    在GICv1 和GICv2中,当对GICC_CTLR寄存器的EOImode字段设置为0时,对EOIR寄存器的有效写入也会造成对应中断消失。
    在GICv2中,设置GICC_CTLR寄存器的EOImode字段设置为1时,将中断处理后动作分为优先级下降和中断消失两部分,此时对于中断处理的程序,必须:
  1. 对EOIR寄存进行有效地写入,使得在CPU interface中的中断优先级下降。
  2. 然后写入 GICC_DIR,使对应中断消失。
    GIC架构对EOIR寄存器的写入是有顺序要求的,以至于:
  • 对GICC_EOIR的有效写入,对应的是最近一次被确认的中断。
  • 对GICC_AEOIR的有效写入,对应的是最近一次被确认的Group1中断。
  • 写入GICC_EOIR是否会影响Group0 中断或者Group1中断,这取决于: GICC_CTLR寄存器的AckCtl字段,并且当前GIC实现了安全扩展,这次写入是secure还是non-secure的。

在实现了安全扩展的GICv2中:

  • GICC_AEOIR是GICC_EOIR的Non-secure copy的别名
  • GICC_AIAR是GICC_IAR 的Non-secure copy的别名
  • GICC_AIAR和GICC_AEOIR是安全寄存器,这意味着它们只能通过安全访问来访问。

对于GICC_DIR寄存器的写入则无顺序要求,但是如果GICC_CTLR寄存器的EOImode字段被设为0,或者是GICC_CTLR寄存器的EOImode字段被设为1,但是此时并没有对应写入 GICC_EOIR 或者 GICC_AEOIR寄存器,此时对GICC_DIR寄存器的写入造成的影响是不可预知的。

当虚拟化物理中断时,ARM建议,对于与运行虚拟机的处理器对应的每个CPU接口:

  • GICC_CTLR的EOImode字段设为1
  • 如果GIC实现了GIC安全扩展,则使用GICC_CTLR的EOImodeNS位设置为1

3, Interrupt Acknowledge Register, GICC_IAR

处理器可以读取GICC_IAR寄存器来获取被接收的中断的ID,读该寄存的动作相当于一个获知中断的过程。
GICC_IAR bit assignments

  1. GICC_IAR[12:10], CPUID,对于多处理器系统中的SGI中断,这个字段标识了请求中断的那个处理器。它将返回产生这个中断请求的CPU interface的编号,比如3,则表示是core3写入GICD_SGIR寄存器来产生中断的,对于其他中断来讲,该字段是RAZ(Read As Zero)
  2. GICC_IAR[9:0] ,Interrupt ID,中断ID。
    处理器读取 GICC_IAR寄存将返回对应CPU interface中,处于pending状态且优先级最高的中断的ID,以下情况下,将会读到虚假中断ID:
  • Distributor的转发功能被禁止。

  • CPU interface的发送中断信号功能被禁止。

  • CPU interface中没有足够的优先级的,且处于pending状态的中断可以被发送到对应处理器。
    以下是一个GIC返回中断ID号1023时的示例,同时也说明了读取GICC_IAR是严格时序的( timing critical):

    1. 一个外设断言了一个电平敏感的中断
    2. 该中断有足够的优先级,因此GIC会将它发送给目标处理器
    3. 外设解除断言中断。由于没有其他处于pending状态,且有足够优先级的中断。GIC将取消发送到处理器的中断请求。
    4. 在目标处理器获知中断已被解除断言之前读取GICC_IAR,由于当前没有足够优先级的pending中断发送给目标处理器,GIC将返回一个虚假中断ID-1023。

4, End of Interrupt Register, GICC_EOIR

处理器写入GICC_EOIR寄存器将会通知CPU interface 当前中断已经处理完成,如果是GICv2架构,当GICC_CTLR.EOImode字段被设为1时,CPU interface 还会为当前中断进行中断优先级下降操作(Priority Drop)。
 GICC_EOIR bit assignments

  • GICC_EOIR[12:10],CPUID字段,在多处理器实现中,如果当前写是针对SGI中断,这个字段将包含访问对应GICC_IAR所得到的CPUID,对于其他中断来说,该字段将会被写入0(SBZ,Should Be Zero)。
  • GICC_EOIR[9:0],EOIINTID,中断ID,访问对于的GICC_IAR寄存器所获得的值。
    每一次从GICC_IAR中有效地读取中断ID时,与之相连的处理器将会对应地写入GICC_EOIR寄存器。写入GICC_EOIR寄存器的中断ID必须是从GICC_IAR中读取到的值。
    如果 从GICC_IAR中读到的是虚假中断ID( spurious interrupt ID),软件就没有必要将这个值写入EOIR寄存器,应为写入虚假中断ID将会被GIC忽略。
    此外,ARM强烈建议将GICC_IAR寄存器的值整个保留下来,然后写入对应的EOIR寄存器。

你可能感兴趣的:(ARM,ARM_GIC,1024程序员节)