前面已经学习了中断的注册过程,下面由一张流程图来看一下当中断发生时的处理流程:
中断发生之后处理流程
a -- 具体的CPU architecture相关模块进行现场保护,然后调用machine driver执行对应的中断处理handler;
b -- machine driver对应中断处理handler会根据硬件的信息获取HW interrupt id,然后通过irq domain模块翻译成irq number;
c -- 调用该IRQ number对应 hign level irq event handler,在这个hign level的handler中,会通过和中断控制器交互,进行中断处理的flow 控制(中断的嵌套、抢占),最终会遍历我们注册的IRQ action list,调用对应处理函数;
d -- CPU architeccture相关模块进行现场的恢复;
具体分析之前先看几个基础概念:
一、 中断硬件框架
中断的主动通知特性需要硬件设施支持。在数字逻辑电路层面,外部设备和处理器之间有一条专门的中断信号线(Interrupt Line),用于连接外设与CPU的中断引脚(Interrupt Pin)。当外部设备发生状态改变时,可以通过这条信号线向处理器发出一个中断请求(Interrupt Request,IRQ),其中外部设备通常被称作中断源(Interrupt Source)。
处理器一般只有两根左右的中断引脚(Cortex A9 中的 IRQ、FIQ),而管理的外设却很多。为了解决这个问题,现代设备的中断信号线并不是与处理器直接相连,而是与一个称为中断控制器的设备相连接,后者才跟处理器的中断引脚连接。中断控制器一般可以通过处理器进行编程配置,在Cortex A9 中称为通用中断控制器GIC(Gerneric Interrupt Controller)。下图是一个典型的中断硬件连接的系统框架图:
这里的中断控制器是可编程中断控制器PIC(Programmable Interrupt Controller)。上图中,PIC的输出中断信号线连接到处理器的INT引脚上,这是处理器专门用来接收中断信号的pin脚。外部设备的中断线连接到PIC的pin引脚上,这是PIC用来接收外设中断的pin脚。比如第一个设备的中断线通过P0连到PIC上。在实际的硬件平台上,PIC有的在CPU外部,比如x86平台的8259中断控制器;有的被封装到CPU的内部,这广泛见于嵌入式领域。一颗SoC芯片内部集成了处理器和各种外部设备的控制器,其中包括PIC。
IRQ相关信息管理的关键点是一个全局数组,每个数组项对应一个IRQ编号,软件中断号irq就是这个数组的索引,irq将一对一或多对一(共享)映射到硬件中断源编号。不同的操作系统相关数据结构的实现和映射策略实现可能有差别。
对于Exynos4412 硬件操作过程简单说一下:
a -- 当外设检测到中断之后,就会通过interrupt request line上的电平或者边沿通知到该外设连接到的那个中断控制器,中断控制器就会在多个cpu中选一个,并且把该中断同IRQ分发给cpu
b -- 修改CPSR, 模式
c -- 保存发生中断那一点的CPSR值 pc值
d -- mask IRQ exeption。
e -- 设定pc的值为 IRQ vector,一旦跳到中断向量表,后面就是软件行为了
二、 中断向量表
中断向量表其实是处理器内部的概念,因为处理器除了会被外部设备中断外,其内部也可能产生异常等事件,例如在MIPS中,中断只是异常的一种。当这些事件发生时,CPU必须暂停手头上的工作,转而去处理中断或异常,因此处理器需要知道到哪里去获得这些中断或异常的处理函数的目标地址。中断向量表就是用来解决这个问题,其中每一项都是一个中断或异常处理函数的入口地址,具体来说4个字节的函数指针将指向一段汇编微码(intConnectCode)执行跳转。
外部设备的中断常常对应向量表中的某一项,这是通用框架的外部中断处理函数入口,因此在进入通用的中断处理函数之后,系统必须知道正在处理的中断是哪一个设备产生的,而这正是由软件中断号irq定的决。中断向量表的内容是由操作系统在初始化阶段来填写,对于外部中断,操作系统负责实现一个通用的外部中断处理函数,然后把这个函数的入口地址放到中断向量表中的对应位置。用户注册设备驱动ISR,实际上就是挂接到中断向量表中,覆盖某一项的默认处理实现特化。
Exynos4412 的中断向量表在linux-3.14-fs4412\arch\arm\kernel\entry-armv.S
三、中断流程分析
1、当中断发生时会跳转到中断向量表处,就是上面那张表;
2、汇编处理部分如下:
table16个入口,只有2项有效,对应user mode、svc mode
代码走到这个地方 已经svc模式
这里有两个问题
1、handle_arch_irq 从哪来?
gic_init_bases —> set_handle_irq(gic_handle_irq)—> handle_arch_irq = handle_irq;
handle_arch_irq 实际执行的是gic_handle_irq
gic_handle_irq —> handle_IRQ —> generic_handle_irq()—> generic_handle_irq_desc—>desc —>handle_irq(irq, desc); |
s3c_irq_type —>irq_set_handler(data->irq, handle_level_irq); —> __irq_set_handler —> __irq_set_handler----- desc->handle_irq = handle; handle_level_irq() handle_irq_event —> handle_irq_event_percpu —>res = action->handler(irq, action->dev_id);真正调用到我们注册的中断处理函数 |