此篇主要是分析裸机的,
后面会有一篇分析 linux下面的中断原理
总所周知,2440 启动后,都是从 0 地址开始,我们分析 nand flash 启动流程:
1、nand 启动,会拷贝 nand flash 的前面4k 到片内 ram 运行。
2、初始化 ram 后,从nand 中读取数据到 ram,再跳转到 ram 中运行
此时我们就有个疑问了?
如果后面我们的程序都是在 ram 空间(一般地址为 0x30000000)
那么我们的中断处理函数也应该是在 ( 0x30000000 + 0x18)
但是,我们中断、复位等发生,系统会自动跑到 0 地址中去执行,那岂不是找不到真正的中断函数了?
其实不然,我们来看下:
先说结论:
1、中断(或者复位)产生时,PC指针直接跳转到 0x18(或者0x0)位置运行
2、如果是中断,跳转到 0x18 后,会再次跳转到 相应的内存位置中运行
我们看下 head.S文件吧:
========================================
b Reset
@ 0x04: 未定义指令中止模式的向量地址
b HandleUndef
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
b HandleSWI
@ 0x0c: 指令预取终止导致的异常的向量地址
b HandlePrefetchAbort
@ 0x10: 数据访问终止导致的异常的向量地址
b HandleDataAbort
@ 0x14: 保留
b HandleNotUsed
@ 0x18: 中断模式的向量地址
b HandleIRQ
@ 0x1c: 快中断模式的向量地址、
b HandleFIQ
=========================================
这里我们可以看到,我们汇编出来之后的文件
============================================
30000000 <_start>:
30000000: ea000006 b 30000020 <Reset>
30000004 <HandleUndef>:
30000004: eafffffe b 30000004 <HandleUndef>
30000008 <HandleSWI>:
30000008: eafffffe b 30000008 <HandleSWI>
3000000c <HandlePrefetchAbort>:
3000000c: eafffffe b 3000000c <HandlePrefetchAbort>
30000010 <HandleDataAbort>:
30000010: eafffffe b 30000010 <HandleDataAbort>
30000014 <HandleNotUsed>:
30000014: eafffffe b 30000014 <HandleNotUsed>
30000018: ea000015 b 30000074 <HandleIRQ>
============================================
这里我们可以看到 b 指令跳转,可以看到 HandleIRQ
在 30000074 地址上,所以说,我们我们会跳转到 这个地址?
其实是错的。中断,还是复位之类的,都只会跳转到 0 地址等偏移
特别是最上面的 _start 指令为 b 30000020 ,实际上我们知道机器刚开机的时候,外部 ram 还没准备好,此时直接跳转到 30000020 会失败
事实上,b 是一个地址无关跳转码,我们可以看到 他的指令码实际是 ea000006
B跳转指令是一个32位的指令码:其中[31:28]位是条件码;[27:24]位为“1010”(0xeaffffff)时,表示B跳转指令,为“1011”时,表示BL跳转指令;[23:0]表示偏移地址。
机器码是eb000006 ,二进制为1110 1011000000000000000000000110。
其中1110表示无条件执行,接下的1011就是BL指令,如L==0则就表示B指令,剩下的Offset就是链接位。
BL指令的跳转地址计算:
1.如上图所示,先将24位Offset补码左移两位,得到000000000000000000000110 00 =0X18
2.由于ARM流水线,当前PC永远等于PC+8,所以PC=PC+0X28+8=0X30000000+0X18+8=0X30000020。
我们可以看到下面的 reset ,确实是在 0X30000020 的地方
==============================
30000020 <Reset>:
30000020: e3a0da01 mov sp, #4096 ; 0x1000
30000024: eb00001e bl 300000a4 <disable_watch_dog>
30000028: eb000021 bl 300000b4 <clock_init>
3000002c: eb00003a bl 3000011c <memsetup>
30000030: eb0000b5 bl 3000030c <nand_init>
30000034: e3a00203 mov r0, #805306368 ; 0x30000000
30000038: e3a01000 mov r1, #0 ; 0x0
==============================
自此,我们就明白了,实际上中断发生的时候,命令是
b 30000074
跳转到 0x74 位置运行(注意,不是 30000074 而是 0x74)
当时我们代码实际会被拷贝到 片内 ram 上,片内ram 的基地址是 0 ,而中断便宜是 0x74
那我们来看下 0x74 的代码内容:前面是汇编、后面是编译后dis文件
=====================================================
HandleIRQ:
sub lr, lr, #4 @ 计算返回地址
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
@ 注意,此时的sp是中断模式的sp
@ 初始值是上面设置的4096
ldr lr, =int_return @ 设置调用IRQ_Handle函数后的返回地址
ldr pc, =IRQ_Handle @ 调用中断分发函数,在interrupt.c中
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
==========================================
30000074 <HandleIRQ>:
30000074: e24ee004 sub lr, lr, #4 ; 0x4
30000078: e92d5fff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, lr}
3000007c: e59fe018 ldr lr, [pc, #24] ; 3000009c <.text+0x9c>
30000080: e59ff018 ldr pc, [pc, #24] ; 300000a0 <.text+0xa0>
30000084 <int_return>:
30000084: e8fd9fff ldmia sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}^
30000088: 30002bc0 andcc r2, r0, r0, asr #23
3000008c: 30000064 andcc r0, r0, r4, rrx
30000090: 300005d8 ldrccd r0, [r0], -r8
30000094: 30000070 andcc r0, r0, r0, ror r0
30000098: 300009f4 strccd r0, [r0], -r4
3000009c: 30000084 andcc r0, r0, r4, lsl #1
300000a0: 30000620 andcc r0, r0, r0, lsr #12
====================================================
我们可以看到 这里采用的是 ldr pc =IRQ_Handle
这里采用的是绝对地址了,而不是 b 指令的偏移地址,可以看到实际上它最终是跳转 30000620
地方,此时已经是内存所在的地方了。
我们来看下30000620
可以看到,这里正是我们的中断处理函数,自此,中断如何从 0x18 地址跳转到 0x30000018的流程分析到此结束
==============================================
30000620 <IRQ_Handle>:
30000620: e52de004 str lr, [sp, #-4]!
30000624: e3a0044a mov r0, #1241513984 ; 0x4a000000
30000628: e3a03001 mov r3, #1 ; 0x1
3000062c: e5901014 ldr r1, [r0, #20]
30000630: e3510004 cmp r1, #4 ; 0x4
30000634: e1a0c113 mov ip, r3, lsl r1
30000638: 03a02080 moveq r2, #128 ; 0x80
3000063c: 03a03456 moveq r3, #1442840576 ; 0x56000000
30000640: 058320a8 streq r2, [r3, #168]
30000644: e59f2014 ldr r2, [pc, #20] ; 30000660 <.text+0x660>
30000648: e580c000 str ip, [r0]
3000064c: e5903010 ldr r3, [r0, #16]
30000650: e5803010 str r3, [r0, #16]
30000654: e1a0e00f mov lr, pc
30000658: e792f101 ldr pc, [r2, r1, lsl #2]
3000065c: e49df004 ldr pc, [sp], #4
30000660: 300033f0 strccd r3, [r0], -r0
==============================================