微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > linux2.6.26内核中ARM中断实现详解

linux2.6.26内核中ARM中断实现详解

时间:11-09 来源:互联网 点击:

当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量(±32M)写入指令码。从上面的代码可以看到中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。我们把搬移前的中断向量表(__vectors_start 到 __vectors_end之间的区域)中的irq入口地址记irq_PC,它在中断向量表的偏移量就是irq_PC-vectors_start, vector_irq在stubs中的偏移量是vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start在0xffff0处,而stubs_start在0xffff0200处,所以搬移后的vector_irq相对于中断向量中的中断入口地址的偏移量就是,200+vector_irq在stubs中的偏移量-中断入口vector_irq在中断向量表(vectors)中的偏移量,即200+ vector_irq-stubs_start-irq_PC+vectors_start = (vector_irq-irq_PC) + vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的vector_irq,减去irq_PC是由汇编器完成的,而后面的 vectors_start+200-stubs_start就应该是stubs_offset,实际上在entry-armv.S中也是这样定义的。

下面是图解:

图中的标号表示的地址在编译内核是就已经确定了,可以在System.map中进行搜索。

搬移前:

此时,编译器在处理B vector_irq的时候,会计算vector_irq与当前PC指针的偏差,然后将这个偏差加到PC上,就实现了跳转到vector_irq执行。

即: 偏移量就是vector_irq - (irq_PC+8) //ARM指令,3级流水,编译器自动处理

搬移后:

此时,可以看到,中断向量表中B vector_irq+x 的地址变成了0xffff0+(irq_PC - __vectors_start)

中断处理函数vector_irq的地址变成了0xffff0200+(vector_irq-__stubs_start)

为了在执行B vector_irq时可以成功,需要重新计算偏移量:

即: 0xffff0200+(vector_irq-__stubs_start) - [ 0xffff0+(irq_PC - __vectors_start) + 8]

= [vector_irq - (irq_PC + 8)] + (__vectors_start+0x200+__stubs_start)

跟之前的对比可以得出差异就是(__vectors_start+0x200+__stubs_start),即stubs_offset,即 B vectors_irq + stubs_offset。

linux-2.6.26内核中ARM中断实现详解(2)

作者:刘洪涛,华清远见嵌入式学院金牌讲师,ARM公司ATC授权培训讲师。

三、中断处理过程

这一节将以S3C2410为例,描述linux-2.6.26内核中,从中断开始,中断是如何一步一步执行到我们注册函数的。

3.1 中断向量表 arch\arm\kernel\entry-armv.S

__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:

中断发生后,跳转到b vector_irq + stubs_offset的位置执行。注意现在的向量表的初始位置是0xffff0。

3.2 中断跳转的入口位置 arch\arm\kernel\entry-armv.S

.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4 @IRQ_MODE在include\asm\ptrace.h中定义:0x12
.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)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f

上面代码中vector_stub宏的定义为:

.macro vector_stub, name, mode, correction=0
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
@
@ Save r0, lr_ (parent PC) and spsr_
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE)
msr spsr_cxsf, r0 @为后面进入svc模式做准备

@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f @进入中断前的mode的后4位
@#define USR_MODE 0x10
@#define FIQ_MODE 0x11
@#define IRQ_MODE 0x12
@#define SVC_MODE 0x13
@#define ABT_MODE 0x17
@#define UND_MODE 0x1b
@#define SYSTEM_MODE 0x1f
mov r0, sp
ldr lr, [pc, lr, lsl #2] @如果进入中断前是usr,则取出PC+4*0的内容,即__irq_usr @如果进入中断前是svc,则取出PC+4*3的内容,即__irq_svc
movs pc, lr @ 当指令的目标寄存器是PC,且指令以S结束,则它会把@ spsr的值恢复给cpsr branch to handler in SVC mode
.endm
.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
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)

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top