历史上的今天
今天是:2025年04月26日(星期六)
2019年04月26日 | ARM中断向量表的简单分析
2019-04-26 来源:eefocus
一般编写arm的裸机程序的时候,创建中断向量表就把它放在0x00000000~0x0000001c中,一般都放在这个位置上。但是中断向量表也可以放在0xffff0000~0xffff001c中,知道这是怎么设置的么?开始看到的时候真的有点奇怪,因为在学习arm的时候,根本没去看arm中的协处理器CP15中的c1控制寄存器中的v位来控制,我们一般都使用默认的值0,则必须将中断向量表放在0x00000000~0x0000001c中。
在看Linux内核对arm中的中断的初始化的时候,就一直对0xffff0000的地址有点怀疑,果然在网上发现这个地址不是随便写的,当我看到arm的协处理器进行控制,中断向量表的地址的时候,真的是哭笑不得啊!!
有人肯定会问?v位是什么时候设置的呢?其实仔细的朋友就知道在head.S中,在创建完页表的时候,如add pc,r10,#PROCINFO_INITFUNC
别急,r10保存在前面设置的procinfo的地址,但是很多人就觉得PROCINFO_INITFUNC的宏定义就不知道在哪找了,在include/asm/asm-offset.h中有定义。
这些搞懂了,首先必须将中断向量表拷贝到0xffff0000的地址上去,把中断处理函数也拷贝到0xffff0200的地址上去,那么在中断向量表进行跳转的时候,如b vector_irq+stubs_offset,但是stubs_offset的偏移怎么设置呢?如果用b vector_irq的话,它就会跳转到原先的中断处理函数中去,因为它也拷贝到了0xffff0200的地址上去,所以将__vector_start-_stubs_start+0x200的话就转移到拷贝后的地址上去执行了。
很多人应该会有点疑问吧,vector_irq好像找不到,别急,细心点,就在宏定义.macro vector_stubs,name,mode,correction中对各种处理函数有定义,所以很快就将中断向量表创建好了。
Linux Version : 2.6.29
1. start_kernel-->setup_arch-->early_trap_init
1:
memcpy((void
*)vectors, __vectors_start, __vectors_end - __vectors_start);
2:
memcpy((void
*)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
3:
memcpy((void
*)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
对于第一行:
__vectors_start 和 __vectors_end 定义在 arch/arm/kernel/entry-armv.S , 它们之间保存了中断向量表。
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
10:
b vector_fiq + stubs_offset
11:
12:
.globl __vectors_end
13:
__vectors_end:
vectors 的地址为CONFIG_VECTORS_BASE , 在.config中定义为0xffff0000
所以 第1行就是把中断向量表拷贝到0xffff0000
对于第二行:
vector_stub是一个带参数的宏,第一个是name,第二个是arm excepiton mode,第三个是为了得到返回地址,lr需要减去的偏移
1:
.macro vector_stub, name, mode, correction=0
2:
.align 5
3:
4:
vector_/name:
5:
.if
/correction
6:
sub lr, lr, #/correction @得到正确的返回地址
7:
.endif
8:
9:
@
10:
@ Save r0, lr_
11:
@ (parent CPSR)
12:
@
13:
stmia sp, {r0, lr} @ save r0, lr
14:
mrs lr, spsr
15:
str lr, [sp, #8] @ save spsr
16:
17:
@
18:
@ Prepare for
SVC32 mode. IRQs remain disabled.
19:
@
20:
mrs r0, cpsr
21:
eor r0, r0, #(/mode ^ SVC_MODE) @把cpsr内容与(mode^SVC_mode)异或,即r0里为SVC_MODE
22:
msr spsr_cxsf, r0 @把r0的值写入整个spsr寄存器(cxsf表示要往哪个字节写入)
23:
24:
@
25:
@ the branch table must immediately follow this
code
26:
@
27:
and lr, lr, #0x0f @lr为spsr_
28:
mov r0, sp @
29:
ldr lr, [pc, lr, lsl #2] @lr=pc+mode*4,其中pc为紧接着30的指令,即vector_stub后的第一条指令
30:
movs pc, lr @ movs会把spsr的值赋给cpsr,所以branch to handler in
SVC mode
31:
ENDPROC(vector_/name)
32:
.endm
再来看下vector 跳转表
1:
.long
__irq_usr @ 0 (USR_26 / USR_32)
2:
.long
__irq_invalid @ 1 (FIQ_26 / FIQ_32)
3:
.long
__irq_invalid @ 2 (IRQ_26 / IRQ_32)
4:
.long
__irq_svc @ 3 (SVC_26 / SVC_32)
5:
.long
__irq_invalid @ 4
6:
.long
__irq_invalid @ 5
7:
.long
__irq_invalid @ 6
8:
.long
__irq_invalid @ 7
9:
.long
__irq_invalid @ 8
10:
.long
__irq_invalid @ 9
11:
.long
__irq_invalid @ a
12:
.long
__irq_invalid @ b
13:
.long
__irq_invalid @ c
14:
.long
__irq_invalid @ d
15:
.long
__irq_invalid @ e
16:
.long
__irq_invalid @ f
这里只有usr 和svc 有入口,而其他都是invalid ,是因为linux只会从usr(application) 和svc(kernel)两种mode跳转到exception来
__stubs_start 和 __stubs_end 之间的代码简化后为:
1:
__stubs_start:
2:
vector_irq: @vector_stub irq, IRQ_MODE, 4
3:
vector_dabt: @vector_stub dabt, ABT_MODE, 8
4:
vector_pabt: @vector_stub pabt, ABT_MODE, 4
5:
vector_und: @vector_stub und, UND_MODE
6:
vector_fiq:
7:
vector_addrexcptn:
8:
.LCvswi:
9:
__stubs_end:
由此可以知道 __stubs_start 和 __stubs_end 之间定义了各种异常的入口
我们再来看为什么异常入口是“b vector_und + stubs_offset”, 同时为什么stubs_offset 的定义如下
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
arm 的跳转指令b 是跳转到相对于PC的一个偏移地址( offset ),汇编器在编译时会对label 减去PC 得到offset,同时vector 拷贝后是如下排列的
__vectors_start
B vector_
__vectors_end
+0x200
__stubs_start
vector_
__stubs_end
因此,”b vector_” 的label –PC = offset, 而offset 为 b 指令与vector的offset,即
vector_
= vector_
1
2
所以异常入口为“b vector_und + stubs_offset”, 同时stubs_offset= __vectors_start + 0x200 – __stubs_start
我们可以通过objdump反汇编来验证:
00000060
.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
60 : e24ee004 sub lr, lr, #4 ; 0x4
64: e88d4001 stm sp, {r0, lr}
…
1d4: e1a00000 .word 0xe1a00000
1d8: e1a00000 .word 0xe1a00000
1dc: e1a00000 .word 0xe1a00000
000001e0
/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
…
__vectors_start:
swi SYS_ERROR0
284: ef9f0000 svc 0x009f0000
b vector_und + stubs_offset
288: ea0000dd b 604
ldr pc, .LCvswi + stubs_offset
28c: e59ff410 ldr pc, [pc, #1040] ; 6a4
b vector_pabt + stubs_offset
290: ea0000bb b 584
b vector_dabt + stubs_offset
294: ea00009a b 504
b vector_addrexcptn + stubs_offset
298: ea0000fa b 688
b vector_irq + stubs_offset
29c: ea000078 b 484
b vector_fiq + stubs_offset
2a0: ea0000f7 b 684
上一篇:2440裸板程序之时钟初始化
史海拾趣
|
我现在学习windows ce6.0 用的C#平台,希望大家能够介绍一下是否有关于windows ce6.0应用程序开发,希望是用C#进行开发的参考书籍,或者是介绍在ce6.0中对API的一些介绍,最好也是用C#进行举例说明的。谢谢大家了!… 查看全部问答> |
|
我2台机子EVC都装了EVC的WINCE5.0模拟器SDK ,一台机子上EVC呈现编译能在WINCE5模拟器跑起来,而将此程序复制到另外一台机子的WINCE5模拟器却跑不起来,提示Cannot open(or one of its components).Make sure the path and filen~~不 ...… 查看全部问答> |
|
typedef struct { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; unsigned char bit3 : 1; unsigned char bit4 : 1; unsigned char bit5 : 1; unsigned char bit6 : 1; unsigned char bit7 : 1; } tpBIT; # ...… 查看全部问答> |
|
不知道为什么,我的程序也是在没有按键的时候就直接进入中断程序。下面是我的一个很简单的程序,大家帮忙找找原因,多谢! #include <msp430x44x.h> void main(void) { int i; WDTCTL=WDTPW+WDTHOLD; //停止看门狗 L ...… 查看全部问答> |
|
前一阵子在写XS128的nrf24l01的收发程序 还是挺难写的 网上的代码都没有XS128的 现在都弄通了 所以把自己的成果传上来 希望大家共同学习 [ 本帖最后由 iwbago 于 2011-8-19 09:29 编辑 ]… 查看全部问答> |




