天天看点

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构

Linux中断子系统(一)中断控制器GIC架构

备注:

  1. Kernel版本:5.4

  2. 使用工具:Source Insight 4.0

  3. 参考博客:

Linux中断子系统(一)中断控制器及驱动分析

吐血整理|肝翻Linux中断所有知识点

linux kernel的中断子系统之(七):GIC代码分析

中断控制器GIC

  GIC,Generic Interrupt Controller。是ARM公司提供的一个通用的中断控制器。主要作用为:接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。

  当前GIC 有四个版本,GIC v1~v4, V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64系统结构,本文主要介绍GIC v2控制器。

gicv2架构

  GIC-400通过AMBA(Advanced Microcontroller Bus Architecture)片上总线连接到一个或者多个ARM处理器上。

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构

  GIC 是联系外设中断和 CPU 的桥梁,也是各 CPU 之间中断互联的通道(也带有管理功能),它负责检测、管理、分发中断,可以做到:

  • 1、使能或禁止中断;
  • 2、把中断分组到Group0还是Group1(Group0作为安全模式使用连接FIQ ,Group1 作为非安全模式使用,连接IRQ );
  • 3、多核系统中将中断分配到不同处理器上;
  • 4、设置电平触发还是边沿触发方式(不等于外设的触发方式);
  • 5、虚拟化扩展。

  ARM CPU 对外的连接只有2 个中断: IRQ和FIQ ,相对应的处理模式分别是一般中断(IRQ )处理模式和快速中断(FIQ )处理模式。所以GIC 最后要把中断汇集成2 条线,与CPU 对接。

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构

  在gicv2中,gic由两个大模块distributor和interface组成:

  • distributor:主要负责中断源的管理、优先级、中断使能、中断屏蔽等,如下:

      中断分发,对于PPI,SGI是各个core独有的中断,不参与目的core的仲裁,SPI,是所有core共享的,根据配置决定中断发往的core。

      中断优先级的处理,将最高优先级中断发送给cpu interface。

      寄存器使用 GICD_ 作为前缀。一个gic中,只有一个GICD。

  

  • cpu interface:要用于连接处理器,与处理器进行交互。将GICD发送的中断信息,通过IRQ,FIQ管脚,传输给core。

      寄存器使用 GICC_ 作为前缀。每一个core,有一个cpu interface。

  

  • virtual cpu interface:将GICD发送的虚拟中断信息,通过VIRQ,VFIQ管脚,传输给core。每一个core,有一个virtual cpu interface。而在这virtual cpu interface中,又包含以下两个组件:

      virtual interface control:寄存器使用 GICH_ 作为前缀

      virtual cpu interface:寄存器使用 GICV_ 作为前缀

图中的virtual interface,是用于支持虚拟中断,本系列不讨论虚拟中断。

寄存器,分为以下:

  • GICD_*: distributor的寄存器
  • GICH_*: 虚拟interface的控制寄存器
  • GICV_*:虚拟interface的控制寄存器
  • GICC_*: 虚拟cpu interface的寄存器

gic中断分发器(Distributor)

  分发器的主要的作用是检测各个中断源的状态,控制各个中断源的行为,分发各个中断源产生的中断事件到指定的一个或者多个CPU接口上。虽然分发器可以管理多个中断源,但是它总是把优先级最高的那个中断请求送往CPU接口。分发器对中断的控制包括:

  • 打开或关闭每个中断。Distributor对中断的控制分成两个级别。一个是全局中断的控制(GIC_DIST_CTRL)。一旦关闭了全局的中断,那么任何的中断源产生的中断事件都不会被传递到 CPU interface。另外一个级别是对针对各个中断源进行控制(GIC_DIST_ENABLE_CLEAR),关闭某一个中断源会导致该中断事件不会分发到 CPU interface,但不影响其他中断源产生中断事件的分发。
  • 控制将当前优先级最高的中断事件分发到一个或者一组 CPU interface。当一个中断事件分发到多个 CPU interface 的时候,GIC 的内部逻辑应该保证只 assert 一个CPU。
  • 优先级控制。
  • interrupt属性设定。设置每个外设中断的触发方式:电平触发、边缘触发。
  • interrupt group的设定。设置每个中断的 Group,其中 Group0 用于安全中断,支持 FIQ 和 IRQ,Group1 用于非安全中断,只支持 IRQ。
  • 将SGI中断分发到目标CPU上。
  • 每个中断的状态可见。
  • 提供软件机制来设置和清除外设中断的pending状态。

gic中断接口(cpu interface)

CPU接口主要用于和CPU进行接口。主要功能包括:

  • 打开或关闭 CPU interface 向连接的 CPU assert 中断事件。对于 ARM,CPU interface 和 CPU 之间的中断信号线是 nIRQCPU 和 nFIQCPU。如果关闭了中断,即便是 Distributor 分发了一个中断事件到 CPU interface,也不会 assert 指定的 nIRQ 或者 nFIQ 通知 Core。
  • 中断的确认。Core 会向 CPU interface 应答中断(应答当前优先级最高的那个中断),中断一旦被应答,Distributor 就会把该中断的状态从 pending 修改成 active 或者 pending and active(这是和该中断源的信号有关,例如如果是电平中断并且保持了该 asserted 电平,那么就是 pending and active)。ack 了中断之后,CPU interface 就会 deassert nIRQCPU 和 nFIQCPU 信号线。
  • 中断处理完毕的通知。当 interrupt handler 处理完了一个中断的时候,会向写 CPU interface 的寄存器通知 GIC CPU 已经处理完该中断。做这个动作一方面是通知 Distributor 将中断状态修改为 deactive,另外一方面,CPU interface 会 priority drop,从而允许其他的 pending 的中断向 CPU 提交。
  • 为 CPU 设置中断优先级掩码。通过 priority mask,可以 mask 掉一些优先级比较低的中断,这些中断不会通知到 CPU。
  • 设置 CPU 的中断抢占(preemption)策略。
  • 在多个中断事件同时到来的时候,选择一个优先级最高的通知 CPU。

gic中断类别

  givc2,将中断,分成了group0和group1。使用寄存器GICD_IGROUPRn来对每个中断,设置组。

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构
  • group0:安全中断,由nFIQ驱动
  • group1:非安全中断,由nIRQ驱动

  GIC-V2支持三种类型的中断:

  • SGI (Software Generated Interrupt):软件触发的中断。软件可以通过写 GICD_SGIR 寄存器来触发一个中断事件,一般用于核间通信,内核中的 IPI:inter-processor interrupts 就是基于 SGI。
  • PPI (Private Peripheral Interrupt):私有外设中断。这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟。
  • SPI (Shared Peripheral Interrupt):公用的外部设备中断,也定义为共享中断。中断产生后,可以分发到某一个CPU上。中断号ID32 - ID1019用于SPI,ID1020 - ID1023保留用于特殊用途;

gicv2,支持最大1020个中断。其中断号分配如下:

中断类型 硬件中断号 分配 中断来源 寄存器
SGI 0~7 非安全中断 软件 GICD_SGIR
SGI 8~15 安全中断 软件 GICD_SGIR
PPI 16~31 私有中断 外设 no
SPI 32~1019 共享中断 外设 no
reserved 1020~1023 共享中断 no no

gicv2中断寄存器

register map

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构

Distributor register summary

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构
Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构
#define GIC_DIST_CTRL			0x000
#define GIC_DIST_CTR			0x004
#define GIC_DIST_IIDR			0x008
#define GIC_DIST_IGROUP			0x080
#define GIC_DIST_ENABLE_SET		0x100
#define GIC_DIST_ENABLE_CLEAR		0x180
#define GIC_DIST_PENDING_SET		0x200
#define GIC_DIST_PENDING_CLEAR		0x280
#define GIC_DIST_ACTIVE_SET		0x300
#define GIC_DIST_ACTIVE_CLEAR		0x380
#define GIC_DIST_PRI			0x400
#define GIC_DIST_TARGET			0x800
#define GIC_DIST_CONFIG			0xc00
#define GIC_DIST_SOFTINT		0xf00
#define GIC_DIST_SGI_PENDING_CLEAR	0xf10
#define GIC_DIST_SGI_PENDING_SET	0xf20
           

CPU interface register summary

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构

gic CPU interface寄存器定义:

#define GIC_CPU_CTRL			0x00
#define GIC_CPU_PRIMASK			0x04
#define GIC_CPU_BINPOINT		0x08
#define GIC_CPU_INTACK			0x0c
#define GIC_CPU_EOI			0x10
#define GIC_CPU_RUNNINGPRI		0x14
#define GIC_CPU_HIGHPRI			0x18
#define GIC_CPU_ALIAS_BINPOINT		0x1c
#define GIC_CPU_ACTIVEPRIO		0xd0
#define GIC_CPU_IDENT			0xfc
#define GIC_CPU_DEACTIVATE		0x1000
           

gic中断处理流程

中断处理流程,包含了以下几步:

  1. GIC决定每个中断的使能状态,不使能的中断,是不能发送中断的
  2. 如果某个中断的中断源有效,GIC将该中断的状态设置为pending状态,然后判断该中断的目标core
  3. 对于每一个core,GIC将当前处于pending状态的优先级最高的中断,发送给该core的cpu interface
  4. cpu interface接收GIC发送的中断请求,判断优先级是否满足要求,如果满足,就将中断通过nFIQ或nIRQ管脚,发送给core。
  5. core响应该中断,通过读取 GICC_IAR 寄存器,来认可该中断。读取该寄存器,如果是软中断,返回源处理器ID,否则返回中断号。
  6. 当core认可该中断后,GIC将该中断的状态,修改为active状态
  7. 当core完成该中断后,通过写 EOIR (end of interrupt register)来实现优先级重置,写 GICC_DIR 寄存器,来无效该中断。

gic中断优先级

  gicv2,支持最小16个,最大256个中断优先级,如下图所示:

Linux中断子系统(一)中断控制器GIC架构Linux中断子系统(一)中断控制器GIC架构

  如果实现的中断优先级小于256个,那么最低的几个bit,是为0的。

  通过设置GICD_IPRIORITYRn寄存器,来设置中断的优先级。这个寄存器是字节有效的,也就是一个字节,对应一个中断的优先级。优先级数值越小,那么这个中断的优先级越高。

  高优先级的中断,是可以抢占低优先级的中断。

继续阅读