PART 1:什么是中断

前言

不同的外部设备、不同的体系结构、不同的OS其中断实现机制都有差别。
中断存在的意义是CPU控制外部设备的同时,外部设备也可以高效的“控制CPU”。这种设计思想扩展到了外部硬件控制CPU、软件控制CPU、CPU运行管理等三个方面。因此,使用events事件表示,更加贴切实际,但在平时描述中,基本上仍然采用了中断这个词。
events的分类如下图所示,主要包括中断和异常,外部硬件控制CPU对应hardware interupt,软件控制CPU对应software interrupt,CPU运行管理则对应exception。(所以脑子里还是要丰富这个认识)
(你整得戳得代码就容易产生异常,你如果对这个没有映像,说明友友你是个软件小能手。)
在这里插入图片描述

1、什么是中断

我想用我的语言去描述,写了删除了,写的是个狗屁,于是我去查, 一个微信公众号写的不错:
中断是为了解决外部设备完成某些工作后通知CPU的一种机制(譬如硬盘完成读写操作后通过中断告知CPU已经完成)。早期没有中断机制的计算机就不得不通过轮询来查询外部设备的状态,由于轮询是试探查询的(也就是说设备不一定是就绪状态),所以往往要做很多无用的查询,从而导致效率非常低下。由于中断是由外部设备主动通知CPU的,所以不需要CPU进行轮询去查询,效率大大提升。(中断的意义)
从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器(如 8259A)的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就可以对这个中断进行适当的处理。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些值通常被称为中断请求线。

2、软件中断与硬件中断

现在我们来继续看看这些名词,这是因为其实因为操作的都是物联网设备,那么基本上都是基于ARM体系的硬件。但是刚刚查的资料都是继续x86的,虽然都是linux操作系统。于是这里先继续来丰富点自己的理论知识。

1. 硬件中断:

硬件中断是由某些硬件设备引起的,例如启动 I/O 的请求、硬件故障或类似的事情。引入硬件中断是为了避免在轮询循环中浪费处理器宝贵的时间,等待外部事件。
例如,当 I/O 操作完成时,例如从磁带驱动器将一些数据读入计算机。

2. 软件中断:

软件中断是通过使用 INT 指令调用的。此事件立即停止程序的执行并将执行传递给 INT 处理程序。INT 处理程序通常是操作系统的一部分,并确定要采取的操作。它发生在应用程序终止或从操作系统请求某些服务时。例如,输出到屏幕,执行文件等。

3. 区别

在这里插入图片描述
(我一直以为软件中断的优先级比硬件中断的优先级低的。)
  • • 硬件中断是由外设引发的, 软中断是执行中断指令产生的.
  • • 硬件中断的中断号是由中断控制器提供的, 软中断的中断号由指令直接指出, 无需使用中断控制器.
  • • 硬件中断是可屏蔽的, 软中断不可屏蔽.
  • • 硬件中断处理程序要确保它能快速地完成任务, 这样程序执行时才不会等待较长时间, 称为上半部.
  • • 软中断处理硬中断未完成的工作, 是一种推后执行的机制, 属于下半部.

3、软中断与硬中断

  • • 硬中断是由外部事件引起的因此具有随机性和突发性;硬中断是否可以嵌套的,是否有优先级(由硬件设计体系决定)。
  • • 软中断是执行中断指令产生的,无面外部施加中断请求信号,因此中断的发生不是随机的而是由程序安排好的。
由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包
的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。
为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间
就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(sofTIrq)来完成。
这里解释了我们之前在GIC中断系列中对于中断的两个stage的文字。原来这个中断两个阶段是这个意思。
  • • 软中断是执行中断指令产生的,而硬中断是由外设引发的。
  • • 硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
  • • 硬中断是可屏蔽的,软中断不可屏蔽。
  • • 硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
  • • 软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。
(和软件中断硬件中断对应起来了!!!)

4、中断嵌套、软中断指令、

1、中断嵌套

Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但同种中断除外。软中断不能嵌套,但相同类型的软中断可以在不同CPU上并行执行

2、软中断指令

int是软中断指令。
中断向量表是中断号和中断处理函数地址的对应表。
int n - 触发软中断n。相应的中断处理函数的地址为:中断向量表地址 + 4 * n。
恰好最近看到了一个前辈的公众号,讲关于中断的结构,正好我们称热打铁,一起学习一下ARM的GIC中断架构。

PART 2:ARM的GIC中断架构

篇一:中断及ARM体系中对中断的处理

今天来看一下中断及ARM体系中对中断的处理,直接进入正题。
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
中断的几个主要目的。
  • • 第一,通过中断可以提高CPU效率。假设一种场景,CPU通知其它设备完成某项工作,当设备完成任务后,CPU如何知道呢?一种方式是设备标记状态寄存器,等待CPU来查询,这种做法弊端是程序员在编写程序时不能确切知道设备完成任务所需的时间,也就是说需要定时去查询设备的状态。我们知道,现代CPU一般频率很高,如果去频繁查询外部设备,尤其是低速设备,必将浪费很多的CPU资源;如果查询的时间间隔长,那么外设的利用率就不高。有一种解决办法,那就是外设处理完任务,主动告诉CPU,从而省去了CPU的查询开销。
  • • 中断的第二个用途是,可以维护系统正常运行。现代的操作系统都是多任务系统,表面看起来多个程序在并行,实际上真正的CPU是分时复用的(假设最简单的单核单线程),并不是真的并行。如果这时有一个恶意程序,一直霸占CPU资源不释放,是不是整个系统就会挂死呢?答案是并不会,因为CPU的分配是由操作系统负责完成的,原理就是通过中断把控制权从程序手中交还给操作系统。另外,CPU访问内存或其它设备时,也可能发生一些错误。对于不同错误有不同的处理方式,也可以通过中断来完成。
对于一些特定的应用场景,设备系统需要满足实时性。所谓实时性,就是要求系统在规定的或者可预期的时间内作出反应。此类需求也可以通过中断来满足。
还记得前面讲ARM异常时的概念吗?中断算一种异步异常,《ARM系列 -- 异常和特权》。A系列处理器提供四个管脚来实现中断,分别是:
  • • nIRQ:物理普通中断
  • • nFIQ:物理快速中断
  • • nVIRQ:虚拟普通中断
  • • nVFIQ:虚拟快速中断 如下图所示:
在这里插入图片描述
其中虚拟中断是为了实现虚拟化而准备的,这部分留待以后讲虚拟化时再说。我们只看物理中断。
在开始介绍中断概念时,我们说中断可能会有很多种,每个设备都可能要发中断,而ARM处理器只有两个管脚,那怎么办?这时,就需要有一个模块来收集所有的中断,然后转发给处理器在ARM的体系中,这个模块就是通用中断处理器(generic interrupt controller,以下简称GIC)。
类似ARM的处理器,GIC有架构和实现之分。GIC的架构经历了GICv1,GICv2,GICv3和GICv4。每个架构下有有对应的设计实现,** **
在介绍GIC-600之前,有必要先讲一下中断的一些基本概念。
  • • 首先,中断是分优先级的。很多个设备同时发送中断,处理器必须遵守一定的规则来给所有中断排序,从而决定先响应哪个。这个规则就是中断优先级,操作系统需要维护中断处理器中的优先级寄存器。
  • • 既然有了优先级,就有了中断嵌套。当处理器处理某个低优先级的中断时,来了一个高优先级的中断,CPU可以保留低优先级的中断处理现场,转而处理高优先级中断,待高优先级处理完再继续处理低优先级中断。如果CPU在某段时间内,选择不响应某设备,其中断也可以被屏蔽。
在ARM体系中,对于每个中断,有四个状态:
  • • inactive:中断处于无效状态
  • • pending:中断处于有效状态,但是CPU没有响应该中断
  • • active:中断处于有效状态,CPU在响应该中断
  • • active and pending:CPU在响应该中断,但是该中断源又发送中断过来
在ARM体系中,中断分为以下几个类型(不讨论虚拟中断):
  • • PPI:(Private Peripheral Interrupt,私有外设中断),该中断来源于core,但是该中断只对指定的core有效,所谓的私有即指对core私有;
  • • SPI:(Shared Peripheral Interrupt,共享外设中断),该中断来源于外部设备,该中断可以对所有的core有效;
  • • SGI:(Software Generated Interrupt,软件中断),用于给其它的core发送中断信号;
  • • LPI:(Locality-specific Peripheral Interrupt,特定局部外设中断),是一种基于消息的边沿中断。
ARM又为每种中断分配了中断号,用以区分,其中1020-1023是特殊的中断号。
在这里插入图片描述
图1 GICv3中断号分配对于每个中断,从产生到处理再到结束,是有一个完整的生命周期的。
在这里插入图片描述
图2 物理中断生命周期
  • • generate:外设或软件发起一个中断
  • • distribute:中断经过分组,优先级仲裁等,发送给对应的CPU interface
  • • deliver:CPU interface将中断发送给core
  • • activate:当CPU core开始响应中断,GIC将最高激活优先级的中断设置为激活
  • • priority drop: core发信号给GIC,通知最高优先级中断,GIC可以重置优先级
  • • deactivation:清除中断
这里要解释一下CPU interface。前面也提到了,core提供给中断的物理管脚只有两个,中断会有成百上千个,GIC怎么把这些中断发送给core是一个问题。这时就需要CPU interface了。CPU interface将GIC发送的中断信息,通过IRQ,FIQ管脚,发送给连接到core。CPU interface提供了以下的功能:
  • • 将中断请求发送给core
  • • 中断进行认可
  • • 中断完成识别
  • • 设置中断优先级屏蔽
  • • 定义中断抢占策略
  • • 决定当前处于pending状态最高优先级中断
在GICv3架构中,CPU interface被抽离出来,实现在core内部的。也就是说,在GIC-600中,是不包含CPU interface的。这样做的好处是,可以减少中断响应的时间,并且减少系统总线的占用。对于众核SoC设计来说,其物理设计非常大,CPU interface实现在core内部,也就意味着某些中断寄存器可以放在其内部,这样core就可以很快的访问到这些寄存器了。对于那些常用的寄存器,core不用跋山涉水的通过系统总线或片上网络去频繁访问GIC了。CPU interface与GIC之间,是通过专用的AXI-stream总线来传输信息的。
今天就到这里吧,下面就开始详细GIC-600的内部结构。

篇二:GIC-600的内部结构

闲话少说,今天开始扒GIC-600。
为了适应大规模的SoC设计,GIC-600被设计成分布式IP。
所谓分布式,GIC-600由几种组件构成,每个组件可以跟其它相关模块在物理设计上摆放在一起,并与其拥有共同的电源域;组件之间通过片上网络(network on chip,简称NoC)通信,从而达到更好的时序。
GIC-600的重要组件包括以下几种:
  • • Distributor:GIC-600的核心部件,负责与其它组件通信,主要处理SPI和LPI
  • • Redistributor:与cluster或core交互,主要处理PPI和SGI
  • • ITS(Interrupt Translation Service):处理基于消息的中断,用于解析LPI
  • • SPI collator:顾名思义,收集整理SPI
  • • Wake request:产生wake_request给core
在具体介绍每个组件之前,我们有必要先来研究一下这些组件之间是如何连接的。主要有两种方式,

如何连接

第一种是利用片上网络,比如CMN-600这样的IP

在这里插入图片描述
图1 利用NoC连接GIC-600的组件
第一种方法,适合大规模的物理设计,尤其是全局时钟方案复杂的SoC。比如有很多的cluster,一般而言,一个redistributor对应一个cluster,这样redistributor可以和cluster一起做布局布线,redistributor和cluster之间的通信可以保持在一个比较高的频率,且timing容易实现。
同样,对于有很多PCIe控制器的设计,ITS可以和PCIe控制器放在一起,实现LPI。对于SPI来说,一般多是给低速的外设准备的,所以SPI collator可以和distributor放在一起。
Wake request组件的主要作用就是可以产生唤醒信号给core,如果SoC设计中没有做关于core功耗方面的设计,这个组件可以不需要;如果有功耗控制的需求,这些唤醒信号要给SCP(system control processor)或者相关的模块,再做进一步的设计,具体取决于设计需要了。

第二种是私有的桥接。

在这里插入图片描述
图2 利用私有桥接连接GIC-600的组件
第二种方法,适合规模较小的,全局时钟方案简单的SoC,GIC-600的组件不依赖于片上网络(因为没有可用接口,比如NIC等)。这种方法的弊端是,组件之间的走线可能会非常长,并且可能需要在别的IP中穿行。好处是,不占用任何NoC的带宽。

单个组件

接下来看看每个组件。
  • • 首先是distributor,一个SoC只有一个。其
  • • 它的组件只能与distributor通信。所以,可以看出,distributor是GIC-600中的核心,其作用是收集所有的中断,并转发给相应的目标core。
    在这里插入图片描述
    图3 GIC-600 distributor和SPI collator
对于core来说,可以通过上图中的ACE-Lite slave接口访问GIC-600中除去GITS_TRANSLATER的所有寄存器。由于为了实现LPI,GIC-600的distributor要维护几张表,且这些表格保存在内存中,所以需要一个master接口去访存。在上图中,对应的是ACE-Lite master总线接口。Distributor还包含三组AXI4-Stream接口,分别用于redistributor,ITS和其它distributor(比如多路服务器)的通信。至于Q-channel,不知道大家还记得多少,《SoC设计之功耗 -- Q channel》,在这里主要是为了做时钟管理。
SPI collator对外的接口比较简单,输入SPIs,是来自外设的中断线。在GIC-600中,SPI数目是32的整数倍,目前最多支持960。输出SPI_r,是经过时钟同步的SPI信号,如果需要,可以送给发出SPI的外设。
Wake request模块是配合SoC功耗管理用的,如果需要实现core的idle状态管理,或者powergating,在distributor把中断发给目标core之前必须唤醒该core。所以唤醒信号是每个core一个。功耗管理取决于具体的设计,可以参考前面的文章,《ARM系列 -- PCSA(二)》。
简单介绍完distributor,需要再讲讲AXI4-Stream总线。
在这里插入图片描述
图4 AMBA演进
在AXI4-Stream中,去掉了地址项,允许无限制的数据突发传输规模。正如spec中所说,这个总线是用于master和slave之间交换数据用的。接口信号比较简单,如下图:
在这里插入图片描述
图5 AXI4-Stream接口信号列表
大部分信号和AXI类似,ACLK和ARESETn信号,不必多说。TREADY和TVALID是mater和salve的握手信号,slave可以通过TREADY反压master的数据传送。
需要注意的是TDEST和TID,TDEST提供数据流的路由信息,也就是说一个master可以接几个slave设备。TID提供数据流的标识,意味着master可以给slave交叉发送不同数据流。
如果两个设备要实现双向传输,就需要互为master和slave。拿GIC-600来说,一个SoC中只有一个distributor,同时可以有若干redistributor,但是redistributor只与distributor通信,且需要双向传输。也就是说distributor能看到多个redistributor,而一个redistributor只能看到distributor。
Distributor可以通过TDEST把数据路由给相对应的redistributor,并且通过redistributor发过来的TID以区分不同的redistributor。
TDATA数据流包含三种类型:data、position、null。data是数据;position作为占位符使用,可以用来表征data的相对位置,null不包含任何有用的信息。
数据流的结构可以有很多种,比如可以只传数据;也可以将数据和null或position混合传输。
在这里插入图片描述
图6 连续且对齐的数据流
在这里插入图片描述
图7 连续不对齐的数据流
在这里插入图片描述
图8 byte数据流
ACE-Lite总线就复杂多了,不是一两篇短文能说清楚的,留在以后吧。

篇三:redistributor

在这里插入图片描述
跟distributor连接的部分就不说了。Cpu_active是指示cluster或core的状态,可以用于idle管理。ppi_id用于多核设计时,区分每个redistributor。PPIs就是PPI中断线,在GIC-600是这么描述PPI的:
在这里插入图片描述
图2 PPI描述
从上面可以看出来,所谓的“私有”是说这些中断信号是core专有的。对于PPI,ARMv8定义了三种规格,8,12和16。所以对于不同的core来说,可能PPI数量不一样。
在这里插入图片描述
上面是ARMv8-A的架构spec里,关于timer的图。我们可以看到,core的timer会发PPI,而中断控制器返回FIQ或者IRQ给core。细心的同学可能会问了,在redistributor上面没有FIQ和IRQ的信号啊?其实在第一篇文章里就说了,在现有的GICv3架构下,关于中断FIQ和IRQ,以及系统寄存器等放在了cluster/core端,对外留出了一组接口,叫cpu interface(在GICv2中实现在中断控制器这一侧),也就是图1中最下面的接口。其实这是一组简化的AXI4-Stream。
在这里插入图片描述
图4 GIC stream协议接口由于是双向,所以是两组信号
在这里插入图片描述
图5 redistributor到CPU的信号
在这里插入图片描述
图6 CPU到redistributor的信号
既然是简化的总线协议,为了更便于CPU和redistributor通信,ARM又规定了具体的数据包格式。下图是CPU端发出的命令格式,具体的解释请查阅GICv3的文档,此处就不过多的贴图了。
在这里插入图片描述
图7 CPU interface命令至此,就剩下最复杂的ITS了,这一部分是为了实现基于消息的中断。前面介绍过,LPI是一种基于消息的中断。也就是中断信息不在通过中断线进行传递。ITS就是要将接收到的LPI中断进行解析。
在这里插入图片描述
图8 GIC-600的ITS组件
既然是信息中断,就一定要有区分这些中断的方法,这样才能找到合适的中断处理对策。所以这里有两个概念。
  • • EventID,用来表示外设发送中断的事件类型
  • • DeviceID,用来表示哪一个外设发起LPI
而ITS需要将外设发送的DeviceID,eventID,通过一系列查表,得到LPI的中断号,再使用LPI中断号查表得到该中断的目标CPU。为此,ITS需要维护几张表,分别是device table,interrupt translation tableh和collection。
在这里插入图片描述
当外设写GITS_TRANSLATER寄存器,产生了LPI。这时ITS首先要拿着DeviceID,从device table中选择索引为DeviceID的表项。从该表项中,得到interrupt translation table的位置;
然后再根据EventID,从interrupt translation table中选择索引为EventID的表项。
得到中断号,以及中断所属的collection号;
最后,使用collection号,从collection table中,选择索引为collection号的表项。得到redistributor的映射信息,最后根据collection表项的映射信息,将中断信息路由发送给对应的redistributor。
最后,提一句,GICv3中开始支持亲和性路由(affinity routing)。请原谅我偷点懒,直接把文档中的部分贴出来:
在这里插入图片描述
里要解释一下什么是亲和性,我最初接触这个概念的时候简直是一头雾水,直到有一天看操作系统相关的书才明白是咋回事。
CPU亲和性是一种调度属性(scheduler property),Linux调度器会根据affinity的设置让指定的进程运行在"绑定"的CPU上,而不会在别的CPU上运行。其中有一个好处是,可以提高cache的命中率。当一个进程在某个CPU上运行时,会在该CPU的缓存中维护许多状态。
下次该进程在相同的CPU上运行时,由于缓存中的数据而执行的更快。因此,多处理器的调度器应该考虑这种亲和性,尽可能的进程保持在同一个CPU上。同理,对于并发程序也是有好处的。感慨一下,做CPU设计,到了最后肯定要与操作系统相爱相杀,哈哈哈~~~
这部分是看的前辈《老秦谈芯》公众号的内容,下面我们来学习一下GICv3是怎么用起来的。

PART 3:Bl31中断处理流程概述

一、Bl31中断处理流程概述

  中断处理需要软件和硬件配合完成,GICv3根据中断分组情况以及系统当前运行的异常等级确定中断是以IRQ还是FIQ触发。
CPU通过设置SCR_EL3.IRQ和SCR_EL3.FIQ确定IRQ和FIQ中断分别是被路由到当前异常等级还是被路由到EL3
  若中断被路由到EL3,根据异常发生时系统所处的异常等级,使用的栈指针是SP_EL0还是SP_ELx(x> 0),以及使用的aarch32还是aarch64架构,在每个异常等级下都包含了四张异常等级表。以上具体的中断的路由规则可参考博文:【armv8中断路由机制】[1]
bl31的异常向量表定义在runtime_exceptions.S中,其与下图的定义一致。
但在ATF中只实现了后面两种情形下的中断处理函数,即若当前系统运行在EL3下,则不允许异常发生。为简化讨论,我们只关注aarch64的情形,则实际上bl31只实现了下图中的第三种异常发生时中断的处理。
在这里插入图片描述
这是因为除了系统启动时以外,其它情况下系统运行在bl31则表示其本身是由低异常等级以smc指令进入的,此时系统本身就运行在异常上下文
而系统返回所需要的elr_el3和spsr_el3都被保存在了sp_el3栈中。在bl31中sp_el3只用于保存寄存器等系统状态信息,且所有的参数的存储位置都是预定义好的。此时,若发生了irq或fiq中断则中断处理函数也会将它的elr_el3和spsr_el3保存到sp_el3中,从而导致smc调用的返回信息被覆盖掉,从而使其无法返回。
因此bl31中的异常处理函数是非重入的,bl31运行时当前PE处于关中断状态当然,对于smp系统,由于每个PE的sp_el3是独立的,因此其它PE还是可以响应中断的。
当系统运行在低异常等级时产生group 0中断,则可以通过以上第三张异常向量表跳转到bl31的aarch64异常处理函数,下面以FIQ为例说明其中断处理流程。

二、FIQ为例说明其中断处理流程

1-保存环境

FIQ中断触发时,PE将异常发生时的PSTATE保存到SPSR_EL3将返回地址保存到ELR_EL3(以上是由硬件完成的),( pstate可能指CPU忙时的电源管理,也可能是其它的,但是肯定关于此时CPU状态的。)然后跳转到异常向量表入口处执行中断处理流程

2-执行中断处理流程

fiq_aarch64函数主要由handle_interrupt_exception宏实现,该宏的定义如下:
该函数主要实现了
  • • 1、异常切换时的上下文保存
  • • 2、运行时栈的切换
  • • 3、中断处理函数查询
  • • 4、参数设置和跳转功能。
其详细流程如下:
.macro handle_interrupt_exception label

    bl save_gp_pmcr_pauth_regs                                         (1)

(1)将通用寄存器(x0 – x29以及sp_el0),pmcr以及pauth寄存器保存到sp_el3指定的el3栈中


#if ENABLE_PAUTH

    /* Load and program APIAKey firmware key */

    bl pauth_load_bl31_apiakey

#endif


    mrs x0, spsr_el3

    mrs x1, elr_el3

    stp x0, x1, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]                (2)

    (2)将spsr_el3和elr_el3保存到sp_el3指定的el3栈中


    /* Switch to the runtime stack i.e. SP_EL0 */

    ldr x2, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]             (3)

(3)从el3栈中读取sp_el0栈指针


    mov x20, sp                                                       (4)

(4)将sp_el3栈指针暂存到x20寄存器中


    msr spsel, #MODE_SP_EL0                                          (5)

(5)将当前的runtime栈切换为sp_el0


    mov sp, x2                                                        (6)

(6)恢复sp_el0栈的值


    bl plat_ic_get_pending_interrupt_type                              (7)

(7)读取当前中断的中断号,并根据中断号获取中断类型



    cmp x0, #INTR_TYPE_INVAL

    b.eq interrupt_exit_\label                                          (8)

(8)若中断类型为非法,则直接退出中断处理



    bl get_interrupt_type_handler                                     (9)

(9)获取该中断类型对应的处理函数


    cbz x0, interrupt_exit_\label                                       (10)

(10)若获取处理函数失败,则直接退出中断处理


    mov x21, x0                                                      (11)

(11)将中断类型处理函数指针暂存到x21寄存器中



    mov x0, #INTR_ID_UNAVAILABLE


    /* Set the current security state in the 'flags' parameter */

    mrs x2, scr_el3

    ubfx x1, x2, #0, #1                                                (12)

(12)设置中断处理函数的输入参数0和参数1



    /* Restore the reference to the 'handle' i.e. SP_EL3 */

    mov x2, x20                                                      (13)

(13)将sp_el3指针设置中断处理函数的输入参数2



    /* x3 will point to a cookie (not used now) */

    mov x3, xzr                                                     (14)

(14)设置中断处理函数的输入参数3,该参数始终当前为0



    /* Call the interrupt type handler */

    blr x21                                                         (15)

(15)跳转到中断处理函数并执行实际的中断处理



interrupt_exit_\label:                                              

    /* Return from exception, possibly in a different security state */

    b el3_exit


    .endm
(1)将通用寄存器(x0 – x29以及sp_el0),pmcr以及pauth寄存器保存到sp_el3指定的el3栈中
(2)将spsr_el3和elr_el3保存到sp_el3指定的el3栈中
(3)从el3栈中读取sp_el0栈指针
(4)将sp_el3栈指针暂存到x20寄存器中
(5)将当前的runtime栈切换为sp_el0
(6)恢复sp_el0栈的值
(7)读取当前中断的中断号,并根据中断号获取中断类型
(8)若中断类型为非法,则直接退出中断处理
(9)获取该中断类型对应的处理函数
(10)若获取处理函数失败,则直接退出中断处理
(11)将中断类型处理函数指针暂存到x21寄存器中
(12)设置中断处理函数的输入参数0和参数1
(13)将sp_el3指针设置中断处理函数的输入参数2
(14)设置中断处理函数的输入参数3,该参数始终当前为0
(15)跳转到中断处理函数并执行实际的中断处理
在bl31中实际的中断处理函数有两类,group 0中断和secure group 1中断。group 0中断由exception handler framework(ehf)管理,该框架实现了对bl31中group 0中断的注册和管理,当前sdei框架使用了这种中断类型。
而secure EL1中断一般是bl31为bl32接收并转发给bl32的,如optee在bl31中注册了一个secure el1中断处理函数opteed_sel1_interrupt_handler,该函数比较简单,只是执行了异常等级上下文切换,跳转到bl32的fiq异常处理入口,将中断处理转交给bl32。

3-ehf流程

ehf流程主要包括以下三部分:
  • • (1)向系统注册group 0中断处理函数,它是group 0中断处理的总入口函数。当中断发生时,该中断处理函数会被上面的bl31异常入口函数查询并调用。(大多数使用的)
  • • (2)向系统提供一个与不同优先级绑定的特定中断处理函数注册接口。不同的驱动可以把自己的中断处理函数注册到ehf框架中。
  • • (3)ehf中断处理函数执行中断应答,以及查询并处理特定优先级中断处理函数功能。其基本处理流程如下图:
在这里插入图片描述
感动,这里竟然和我之前看的EHF框架联系起来了呜呜呜

PART 4:gicv2与gicv3的区别

一、gicv2与gicv3

但是在gicv3之前一直都是使用的giv2,这两者有什么区别?
最近在做实验室的版本兼容,很我一直以为这个gicv2和gicv3版本更新的年代不是很久。最后看了一下gicv2的文章发现在2016年都有了,为什么现在还有gicv2,吐槽一下。不过活还是得干,之前做过关于gicv3的东西,但是现在还得整个gicv2,在基于这个gicv3上进行修改,那么必须对比一下这两个之间的差别。
循序渐进,先整概念,再看源码。

二、概念

1、gic概念

gic : general interrupt controller

(1)、模型

在这里插入图片描述

(2)、分类

GIC中断控制器的分类:gicv1(已弃用),gicv2,gicv3,gicv4
  • • ◾gic400,支持gicv2架构版本。
  • • ◾gic500,支持gicv3架构版本。
  • • ◾gic600,支持gicv3架构版本

(3)、核心功能

gic的核心功能,就是对soc中外设的中断源的管理,并且提供给软件,配置以及控制这些中断源。
  • • ◾当对应的中断源有效时,gic根据该中断源的配置,决定是否将该中断信号,发送给CPU。如果有多个中断源有效,那么gic还会进行仲裁,选择最高优先级中断,发送给CPU。
  • • ◾当CPU接受到gic发送的中断,通过读取gic的寄存器,就可以知道,中断的来源来自于哪里,从而可以做相应的处理。
  • • ◾当CPU处理完中断之后,会告诉gic,其实就是访问gic的寄存器,该中断处理完毕。gic接受到该信息后,就将该中断源取消,避免又重新发送该中断给cpu以及允许中断抢占

(4)、gicv3较gicv2的特点

GICv3架构是GICv2架构的升级版,增加了很多东西。变化在于以下:
  • • ◾使用属性层次(affinity hierarchies),来对core进行标识,使gic支持更多的core
  • • ◾将cpu interface独立出来,用户可以将其设计在core内部
  • • ◾增加redistributor组件,用来连接distributor和cpu interface
  • • ◾增加了LPI,使用ITS来解析
  • • ◾对于cpu interface的寄存器,增加系统寄存器访问方式

2、中断的一些概念

(1)、中断的分类、分组

1-中断触发方式

  • • ◾edge-triggered: 边沿触发,当中断源产生一个边沿,中断有效
  • • ◾level-sensitive:电平触发,当中断源为指定电平,中断有效

2-中断类型(红色是gicv2特有)

  • • ◾PPI:(private peripheral interrupt),私有外设中断,该中断来源于外设,但是该中断只对指定的core有效。
  • • ◾SPI:(shared peripheral interrupt),共享外设中断,该中断来源于外设,但是该中断可以对所有的core有效。
  • • ◾SGI:(software-generated interrupt),软中断,软件产生的中断,用于给其他的core发送中断信号
  • • ◾LPI :(Locality-specific Peripheral Interrupt)

3-中断号(红色是gicv2特有)

  • • ◾ID0-ID15,分配给SGI (一般会将0-7给REE,8-15给TEE)
  • • ◾ID16-ID31,分配给PPI
  • • ◾ID32-ID1019,分配给SPI
  • • ◾ID1020-ID1023,特殊中断号
  • • ◾ID1024-ID8191,reserved
  • • ◾8192及其以上,LPI
在这里插入图片描述

4-Group分组

Group分组(GICD_IGROUPRn) – gicv2 ◾group0:安全中断,由nFIQ驱动 ◾group1:非安全中断,由nIRQ驱动
Group分组(GICD_IGROUPRn)– gicv3 ◾group0:安全中断 ◾non-secure group1:非安全中断 ◾secure group1:安全中断
gicv3的irq、fiq的标记
在这里插入图片描述

(2)、中断的状态

在这里插入图片描述

3、gic和ARM Core的连接

(1)、gicv2和ARM Core的连接

ARM cortex-A系列处理器,提供了4个管脚给soc,实现外界中断的传递
  • • ◾nIRQ:物理普通中断
  • • ◾nFIQ: 物理快速中断
  • • ◾nVIRQ: 虚拟普通中断
  • • ◾nVFIQ: 虚拟快速中断
在这里插入图片描述

(2)、gicv3和ARM Core的连接

在这里插入图片描述
gicv3 AXI-stream协议
  • • ◾gic stream协议,是基于AXI-stream协议。用于gic的IRI组件,和cpu interface之间,传输信息。
  • • ◾distributor,redistributor和ITS,统称为IRI组件(interrupt routing infrastructure)。
  • • ◾gic stream协议,包含以下2个接口:
    • • (1)下行AXI-stream接口:用于IRI向cpu interface传递信息,连接
    • • (2)上行AXI-stream接口:用于cpu interface向IRI传递信息
在这里插入图片描述

4、gic的组件

(1)、gicv2的组件

在GICv2中,最大支持8个core
  • • ◾distributor:实现中断分发,对于PPI,SGI是各个core独有的中断,不参与目的core的仲裁,SPI是所有core共享的,根据配置决定中断发往的core。最后选择最高优先级中断发送给cpu interface。寄存器使用 GICD_ 作为前缀。一个gic中,只有一个GICD。
  • • ◾cpu interface:将GICD发送的中断信息,通过IRQ,FIQ管脚,传输给core。每一个core,有一个cpu interface。
  • • ◾virtual cpu interface:将GICD发送的虚拟中断信息,通过VIRQ,VFIQ管脚,传输给core。每一个core,有一个virtual cpu interface。
在这里插入图片描述

(2)、gicv3的组件

  • • ◾distributor:SPI中断的管理,将中断发送给redistributor
  • • ◾redistributor:PPI,SGI,LPI中断的管理,将中断发送给cpu interface
  • • ◾cpu interface:传输中断给core. (实现在core内部的)
  • • ◾ITS (Interrupt Translation Service components ):用来解析LPI中断.
在这里插入图片描述

5、gic的寄存器

(1)、gicv2的寄存器

gicv2寄存器,都是使用memory-mapped的方式去访问的
  • • ◾GICD_: distributor的寄存器
  • • ◾GICH_: 虚拟interface的控制寄存器
  • • ◾GICV_:虚拟interface的控制寄存器
  • • ◾GICC_: 虚拟cpu interface的寄存器

1-Distributor register

在这里插入图片描述
0x100-0x17C — > GICD_ISENABLERn
这个地址范围内一共可以表示32个32bit的寄存器(n=0x7c/4+1)
所以n的值为0-31,共可以表示32*32=1024个中断
在这里插入图片描述

2-cpu interface register

在这里插入图片描述

(2)、gicv3

提供了2种访问方式:
  • • 一种是memory-mapped的访问
  • • 一种是系统寄存器访问
在这里插入图片描述

6、Banking registers

1-什么是banking register?

Register banking refers to providing multiple copies of a register at the same address. The properties of a register access determine which copy of the register is addressed 一个寄存器一个地址,有多份拷贝. 根据寄存器的权限属性来决定访问这个地址的哪个拷贝

2-gic的banked寄存器有两类:

For Security Extensions, some registers are banked to provide separate Secure and Non-secure copies of the registers. For multiprocessor system, some registers are banked.
在这里插入图片描述

7、gicv3的LPI中断

  • • ◾在gicv3中,引入了一种新的中断类型。message based interrupts,消息中断 外设,不在通过专用中断线,向gic发送中断,而是写gic的寄存器,来发送中断。这样的一个好处是,可以减少中断线的个数,为了支持消息中断,gicv3,增加了LPI,来支持消息中断。并且为他分配了特别多的中断号,从8192开始,移植到16777216
  • • LPI的中断的配置,以及中断的状态,是保存在memory的表中,而不是保存在gic的寄存器中的。
    • • ◾GICR_PROPBASER:(64bit)保存LPI中断配置表的基地址
    • • ◾GICR_PENDBASER: (64bit)保存LPI中断状态表的基地址
  • • 当外部发送LPI中断给redistributor,redistributor首先要查该表,也就是要访问memory来获取LPI中断的配置。为了加速这过程,redistributor中可以配置cache,用来缓存LPI中断的配置信息。
  • • 因为有了cache,所以LPI中断的配置信息,就有了2份拷贝,一份在memory中,一份在redistributor的cache中。如果软件修改了memory中的LPI中断的配置信息,需要将redistributor中的cache信息给无效掉。
  • • LPI实现的两种方法:
    • • ◾使用ITS,将外设发送到eventID,转换成LPI 中断号
    • • ◾forwarding方式,直接访问redistributor的寄存器GICR_SERLPIR,直接发送LPI中断

8、gicv2的bypass功能

GICv2支持中断旁路模式,也就是gic外部的FIQ,IRQ直接接到core的FIQ,IRQ上,相当于gic是不使能的。也就是CFGSDISABLE是有效的,将GIC给无效掉。
gicv2支持bypass功能,这样gic就不起作用了,core的中断管脚,直接由soc的其他部门信号驱动。

PART 5:参考资料

一直是一个学习者的身份,以上是我将自己学习gic的过程中,遇到的优秀博客进行了整理,大多数是来自下面资料,感谢前辈们的优秀博客,如果有所遗漏,请联系小生。
gic的配置其实有部分是在atf中进行的,没想到竟然会在学习gic的时候和之前atf中的中断进行相遇,哈哈,可太妙了。
  • • https://blog.csdn.net/dela_/article/details/52751388
  • • https://www.elecfans.com/emb/20190402899054.html
  • • https://zhuanlan.zhihu.com/p/477913808?utm_medium=social&utm_oi=1006489928070488064
  • • https://mp.weixin.qq.com/s?__biz=MzU0MTE1OTU0NA==&mid=2247483954&idx=1&sn=4a28914aa77d7cb38d699591203406a5&chksm=fb2f7b25cc58f2335c890846a55ad94806e2a518d0f8edb3515983e63809f081d812ac898690&scene=178&cur_album_id=2458988255267225602#rd
  • • https://zhuanlan.zhihu.com/p/520161285
  • • https://www.likecs.com/show-203672325.html

引用链接

[1] 【armv8中断路由机制】: https://blog.csdn.net/weixin_45264425/article/details/129478472?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129478472%22%2C%22source%22%3A%22weixin_45264425%22%7D
继续阅读
阅读原文