内核版本:linux-4.19.8
一、ARM架构的CPU中断基础知识
ARM 体系结构中,存在7 种异常处理。当异常发生时,处理器会把PC 设置为一个特定的存储器地址。这一地址放在被称为向量表(vector table)的特定地址范围内。向量表的入口是一些跳转指令,跳转到专门处理某个异常或中断的子程序。
存储器映射地址0x00000000 是为向量表(一组32 位字)保留的。在有些处理器中,向量表可以选择定位在存储空间的高地址(从偏移量0xffff0000 开始)。一些嵌入式操作系统,如Linux 和Windows CE 就要利用这一特性。
当异常发生时,分组寄存器r14 和SPSR 用于保存处理器状态,操作伪指令如下。
R14_<exception_mode> = return link
SPSR_<exception_mode> = CPSR
CPSR[4∶0] = exception mode number
CPSR[5] = 0 /*进入ARM 状态*/
If <exception_mode> = = reset or FIQ then
CPSR[6] = 1 /*屏蔽快速中断FIQ*/
CPSR[7] = 1 /*屏蔽外部中断IRQ*/
PC = exception vector address
异常向量表
异常返回时,SPSR 内容恢复到CPSR,连接寄存器r14 的内容恢复到程序计数器PC。
对于外部中断IRQ,当处理器的外部中断请求引脚有效,而且CPSR 寄存器的I 控制位被清除时,处理器产生外部中断IRQ 异常。系统中各外部设备通常通过该异常中断请求处理器服务。当外部中断IRQ 发生时,处理器执行下列伪操作。
r14_irq = address of next instruction to be executed + 4
SPSR_irq = CPSR
CPSR[4∶0] = 0b10010 /*进入特权模式*/
CPSR[5] = 0 /*处理器进入ARM 状态*/
/*CPSR[6]保持不变*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff0018
Else
PC = 0x00000018
ARM 内核只有两个外部中断输入信号nFIQ 和nIRQ。但对于一个系统来说,中断源可能多达几十个。为
此,在系统集成的时候,一般都会有一个或多个异常控制器来处理异常信号,即为处理器的 interrupt controller。通用中断处理系统逻辑如下:
在多核处理器中,对于中断控制器的级联,存在两种情况:一是每个中断控制器连接一部分CPU,就相当于一组CPU处理一个中断控制器的中断信号,这种拓扑结构削弱了多核处理器在中断控制中的能力;二是利用root中断控制器的共享中断,将其他中断控制器级联,如上图所示,这种拓扑结构又削弱了次级中断控制器的能力,所以实际使用中,是根据具体处理器来确定的。在单核处理器中就没有这种问题,如S3C2440,采用图中所示的拓扑结构,有root中断控制器处理所有的中断信号。
在多核处理器中,对于中断信号的分发,当一个中断控制器接收到一个中断信号,通过中断信号线发送给CPU,是发送给某一个CPU呢还是广播给所有CPU,或是发送给一组CPU,显然无论是广播给所有CPU还是发送给一组CPU都将导致资源浪费,在ARM体系中,通常的做法是中断控制器预留寄存器来设置每个中断源,从而指定上报的CPU。
二、Linux中断处理流程
当外设发生中断时,CPU进入外部中断异常,CPSR 寄存器的I 控制位被清除,处理器产生外部中断IRQ 异常。CPU进入异常向量表,处理IRQ。在arch\arm\kernel\entry-armv.S有异常向量表如下:
.section .vectors, "ax", %progbits
.L__vectors_start:
W(b) vector_rst
W(b) vector_und
W(ldr) pc, .L__vectors_start + 0x1000
W(b) vector_pabt
W(b) vector_dabt
W(b) vector_addrexcptn
W(b) vector_irq
W(b) vector_fiq
vector_irq 是由宏定义的,宏展开就会发现在保存现场,处理中断,中断处理完毕恢复现场。
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name:
...
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
...
由异常向量表调用vector_irq,它会根据SPSR寄存器的值,判断被中断时CPU是处于USR状态还是SVC状态,然后调用下面的__irq_usr或__irq_svc进入irq_hander
__irq_usr:
usr_entry
kuser_cmpxchg_check
irq_handler
.macro irq_handler
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
ldr r1, =handle_arch_irq
mov r0, sp
badr lr, 9997f
ldr pc, [r1]
#else
arch_irq_handler_default
#endif
9997:
.endm
由此调用handle_arch_irq,这是一个C函数,从名字就可以大致知道,进入具体处理器,处理这个中断。那么handle_arch_irq从哪里来?这里以S3C2440为例:drivers/irqchip/irq-s3c24xx.c
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
s3c2410_init_intc_of
set_handle_irq(s3c24xx_handle_irq);
handle_arch_irq = handle_irq;
中断handle在具体的处理器中断处理初始化中设置的,对于上面这一段代码将在后续解释。分析到这里可以得出,处