您的位置:首页 > 其它

arm体系结构学习笔记 part3 -- 异常处理机制

2010-12-07 22:15 387 查看

先来看看异常向量表:



这个表格给出了当异常发生时候的程序将跳转到的地址 以及跳转到该地址的同时arm核所处的异常模式
以未定义异常为例当arm核执行时遇到未定义的指令时 程序将自动跳转到异常向量表中的0x00000004地址处执行 并将处理器切换为未定义模式 与此同时其实arm核还完成了cpsr到spsr_und的备份以及设置异常处理完毕的返回地址保存到lr_und

而复位异常的跳转地址为0x0 这是每次系统重新加电的时候arm核总是从0x0地址开始取指令指令 而系统上电时系统所处的模式总是svc模式

貌似有点忘了说了 异常向量表总共占用32个字节 总共有7个异常向量 0x00000014是保留的并未使用 所以是(4*7+4)BYTE

文字的说明貌似有点虚 看一下uboot中的第一阶段代码 也是uboot执行的入口:

[leftover-crazy@leftover-crazy ~]$ vi Downloads/u-boot201006/arch/arm/cpu/arm920t/start.S


.globl _start
_start:	b	start_code
ldr	pc, _undefined_instruction
ldr	pc, _software_interrupt
ldr	pc, _prefetch_abort
ldr	pc, _data_abort
ldr	pc, _not_used
ldr	pc, _irq
ldr	pc, _fiq
_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq
.balignl 16,0xdeadbeef


看看这里的b start_code其实是处理复位异常因为异常向量表只有32字节大小,所以采用了跳转的方式来转到相应的异常处理函数。复位异常处理函数的代码如下采用B跳转 B指令跳转的空间为前后32MB范围内代码处于同一文件中几乎是紧挨在中断向量表之后 所以b指令跳转是无任何问题的:

start_code:
/*
* set the cpu to SVC32 mode
*/
mrs	r0, cpsr
bic	r0, r0, #0x1f
orr	r0, r0, #0xd3
msr	cpsr, r0
bl	coloured_LED_init
bl	red_LED_on
#if	defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr	r0, =_start
ldr	r1, =0x0
mov	r2, #16
copyex:
subs	r2, r2, #1
ldr	r3, [r0], #4
str	r3, [r1], #4
bne	copyex
#endif


其它的异常采用ldr pc,label lable分别是

_undefined_instruction: .word undefined_instruction

_software_interrupt: .word software_interrupt

_prefetch_abort: .word prefetch_abort

_data_abort: .word data_abort

_not_used: .word not_used

_irq: .word irq

_fiq: .word fiq

以_irq label为例 ldr pc, _irq 执行的是将irq的值赋给pc irq的值为irq异常处理函数的地址

下面我们来实例解析一下未定义指令异常的处理机制先上代码:

第一步测试,主要做的是观察一下uboot下未定义指令执行时的情况:

makefile:

test.bin: test
arm-2440-linux-gnueabi-objcopy -I elf32-littlearm test -O binary test.bin
cp test.bin ../../
test : test.o
arm-2440-linux-gnueabi-ld -Ttext=0x30000000 test.o  -o test
test.o :test.S
arm-2440-linux-gnueabi-as  test.S -o test.o
clean:
rm -f test.o test test.bin  ../../test.bin
d:
arm-2440-linux-gnueabi-objdump -d test.o


test.S:

.section .text
.global main
main:
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4

.word 0x77777777
sub sp, fp, #12
ldmfd sp, {fp, sp, pc}


这里0x77777777是一条未定义的指令 这个是别人测试的结果

下载并测试的结果如下:

Fantasy >tftp 30000000 test.bin
ERROR: resetting DM9000 -> not responding
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.1.10; our IP address is 192.168.1.11
Filename 'test.bin'.
Load address: 0x30000000
Loading: #
done
Bytes transferred = 24 (18 hex)
Fantasy >go 30000000
## Starting application at 0x30000000 ...
undefined instruction
pc : [<30000010>]          lr : [<33f8a408>]
sp : 33f3fba8  ip : 33f3fbb8     fp : 33f3fbb4
r10: 33f3fcef  r9 : 33faadac     r8 : 33f3ffe0
r7 : 00000000  r6 : 00000002     r5 : 33f3fee8  r4 : 30000000
r3 : 30000000  r2 : 33f3fee8     r1 : 33f3fee8  r0 : 00000001
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

resetting ...


当arm执行未定义指令的时候 板子重启了 这个是uboot设定的对异常的处理 不做多余的展开了 看看下面改进的测试代码二 应该就能清楚了。

下面是第二份的测试代码:

宿舍停电了 代码没法测试了 先贴上来 明天补全测试

makefile:

test.bin vector.bin: test vector
arm-2440-linux-gnueabi-objcopy -I elf32-littlearm test -O binary test.bin
arm-2440-linux-gnueabi-objcopy -I elf32-littlearm vector -O binary vector.bin
cp test.bin vector.bin ../../
test : test.o
arm-2440-linux-gnueabi-ld -Ttext=0x30000000 test.o  -o test
test.o :test.S
arm-2440-linux-gnueabi-as  test.S -o test.o
vector : vector.o
arm-2440-linux-gnueabi-ld -Ttext=0x0 vector.o -o vector
vector.o : vector.S
arm-2440-linux-gnueabi-as  vector.S -o vector.o
clean:
rm -f test.o test  test.bin vector vector.o vector.bin  ../../test.bin ../../vector


vector.S:

.section .text
.global _start
_start:
b reset
b und
b swi
b abt_pre
b abt_data
b .
b irq
b fiq
reset:
und:
mov sp, #0x32000000
adr r0,str
ldr r3,show
mov lr,pc
mov pc,r3
loop:
b loop

swi:
abt_pre:
abt_data:
irq:
fiq:

show:
.word 0x33f9303c
str:
.asciz "undefined instr./n"


mov sp, #0x32000000@这里要设置堆栈寄存器 因为切换到未定义指令异常前,arm处于svc模式,执行到未定义指令时,arm切换到了未定义模式,svc模式下的堆栈由uboot已经设置好,但是未定义模式下的堆栈并未设置,printf函数又需要用到堆栈,如果少了这条语句的话调用printf函数将失败。

loop:

b loop

这里让开发板执行prinrf函数后停止 做个死循环

还有一点就是这段代码是与位置无关的代码 连接时随意指定text段的运行地址都可以。下载到开发板的时候只要下载到0x0处都可以执行。

test.S

.section .text
.global main
main:
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4

.word 0x77777777
sub sp, fp, #12
ldmfd sp, {fp, sp, pc}


测试的时候只要讲test.bin下载到0x30000000 vector.bin下载到0x0出 go 30000000 将会打印出 undefined instr.

附上下载测试结果:

Fantasy >tftp 30000000 test.bin
ERROR: resetting DM9000 -> not responding
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.1.10; our IP address is 192.168.1.11
Filename 'test.bin'.
Load address: 0x30000000
Loading: #
done
Bytes transferred = 24 (18 hex)
Fantasy >tftp 0 vector.bin
ERROR: resetting DM9000 -> not responding
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.1.10; our IP address is 192.168.1.11
Filename 'vector.bin'.
Load address: 0x0
Loading: T #
done
Bytes transferred = 80 (50 hex)
Fantasy >go 30000000
## Starting application at 0x30000000 ...
undefined instr.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: