ARM Linux中断源码分析(2)——中断处理流程
ARM支持7类异常中断,所以中断向量表设8个条目,每个条目4字节,共32字节。 异常名称 复位 未定义的指令 软件中断 指令预取中止 数据访问中止 保留 外部中断请求IRQ 快速中断请求FIQ 中断向量 0x0 0x4 0x8 0x0c 0x10 0x14 0x18 0x1c 异常中断模式 特权模式 特权模式 中止模式 中止模式 IRQ模式 FIQ模式 优先级 1 6 5 2 4 3 未定义指令中止模式 6 回顾第一节所讲的内容,当一个异常或中断发生时,处理器会将PC设置为特定地址,从而跳转到已经初始化好的异常向量表。因此,要理清中断处理流程,先从异常向量表开始。对于ARM Linux而言,异常向量表和异常处理程序都存在arch/arm/kernel/entry_armv.S汇编文件中。 vector异常向量表
点击(此处)折叠或打开
1. .globl __vectors_start
2. __vectors_start: 3. swi SYS_ERROR0
4. b vector_und + stubs_offset 5. ldr pc, .LCvswi + stubs_offset 6. b vector_pabt + stubs_offset 7. b vector_dabt + stubs_offset 8. b vector_addrexcptn + stubs_offset
9. b vector_irq + stubs_offset @中断入口,vector_irq 10. b vector_fiq + stubs_offset 11.
12. .globl __vectors_end 13. __vectors_end:
vector_irq+stubs_offset为中断的入口点,此处之所以要加上
stubs_offset,是为了实现位置无关编程。首先分析一下stubs_offset(宏)是如何计算的:
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
在第3节中已经提到,内核启动时会将异常向量表拷贝到 0xFFFF_0000,将异常向量处理程序的 stub 拷贝到 0xFFFF_0200。图5-1描述了异常向量表和异常处理程序搬移前后的内存布局。
图5-1 异常向量表和异常处理程序搬移前后对比
当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量(±32M)写入指令码。由于内核启动时中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。设搬移后的偏移量为offset,如图5-1所示,
offset = L1+L2
= [0x200 - (irq_PC_X - __vectors_start_X)] + (vector_irq_X - __stubs_start_X)
= [0x200 - (irq_PC - __vectors_start)] + (vector_irq - __stubs_start)
= 0x200 - irq_PC + __vectors_start + vector_irq - __stubs_start
= vector_irq + (__vectors_start + 0x200 - __stubs_start) - irq_PC
令stubs_offset = __vectors_start + 0x200 - __stubs_start 则offset = vector_irq + stubs_offset - irq_PC,所以中断入口点为“b vector_irq + stubs_offset”,其中减去irq_PC是由汇编器在编译时完成的。
vector_irq处理函数
在分析vector_irq处理函数之前,先了解一下当一个异常或中断导致处理器模式改变时,ARM处理器内核的处理流程如下图所示:
中断刚发生时,处理器处于irq模式。在__stubs_start和__stubs_end之间找到vector_irq处理函数的定义vector_stubirq, IRQ_MODE, 4,其中vector_stub是一个宏(在arch/arm/kernel/entry_armv.S中定义),为了分析更直观,我们将vector_stub宏展开如下:
1. /*
2. * Interrupt dispatcher 3. */
4. vector_irq: 5. .if 4
6. sub lr, lr, #4 @在中断发生时,lr指向最后执行的指令地址加上8。只有在当前指令执行
完毕后,才进入中断处理,所以返回地址应指向下一条指令,即(lr-4)处。 7. .endif