ARM中的流水线分为:取值,译码,执行,仿存,回写这五步,SWI(软中断)和UND中断都出现在译码阶段,而其他5种中断都发生在执行阶段。SWI和UND异常两种处理方法步骤都差不多,但是如果是异常出现在执行阶段要怎么样处理呢?
int main()
10 {
11 //发生异常时会进入异常模式跳转到0000 0004地址处理异常事件
12 unsigned long source_addr=data_abort_init();
13 //异常事件处理函数
14 printf("swi_souce addr is %x\n",source_addr);
15 //将异常处理地址的值放到0x60000004
16 memcopy(0x60000010,source_addr,0x1000);
17
18 enable_mmu();
19 //内存映射将0x00000004映射到0x6000000004
20 __asm__ __volatile__(
21 "mov r0, #1\n"
22 "ldr r1, [r0]\n"
23 );
24 printf("welcome back! \n");
25
26
27 }
如果代码中的汇编语言执行的时候将会发生DATA Abrot异常,将会到0x0000010地址区执行处理代码其中,处理执行的时候发生异常和处理译码时候的异常都大同小异。但是需要注意的是:
1
2 int (*printf)(char *, ...) = 0xc3e114d8;
3
4 void init_ttb(unsigned long *addr);
5 void enable_mmu(void);
6 unsigned long data_abort_init();
7 void memcopy(unsigned long* dest,unsigned long* source,int len);
8
9 int main()
10 {
11 //发生OBORT异常时会进入异常模式跳转到0000 0010地址处理异常事件
12 unsigned long source_addr=data_abort_init();
13 //异常事件处理函数
14 printf("swi_souce addr is %x\n",source_addr);
15 //将异常处理地址的值放到0x600000010
16 memcopy(0x60000010,source_addr,0x1000);
17
18 enable_mmu();
19 //内存映射将0x00000004映射到0x6000000004
20 __asm__ __volatile__(
21 "mov r0, #1\n"
22 "ldr r1, [r0]\n"
23 );
24 printf("welcome back! \n");
25
26
27 }
28
29 void memcopy(unsigned long* dest,unsigned long* source,int len)
30 {
31 int i=0;;
32 for(i=0;i<len;i++)
33 dest[i]=source[i];
34 }
35
36 unsigned long data_abort_init()
37 {
38 unsigned long source;
39 __asm__ __volatile__(
40
41 "ldr %0, =voliate_start\n"
42 : "=r" (source)
43 );
44
45
46 return source;
47
48 }
49
50 __asm__(
51
52 "voliate_start:\n"
53 //跳转要分三部:
54 //1:将PC保存到新模式下的lr中;
55 //2:将CPSR保存在SPSR中
56 //3:初始化SP
57 //前两步由硬件完成,而第三部需要手动完成
58 "sub lr, lr, #4\n"
59 "mov sp, #0x66000000\n"//初始化SP
60 "stmfd sp!, {r0-r12, lr}\n"//初始化sp,入栈保护寄存器
61 //打印一句话
62 "ldr r0, =data_string\n"
63 "ldr r2, show\n"
64 "blx r2\n"
65 //跳回来分两部
66 //1:将CPSR保存在SPSR中
67 //2:将PC保存到新模式下的lr中;
68 "mov sp, #0x66000000\n"//
69 "ldmea sp, {r0-r12, pc}^\n"//
70
71 "show:\n"
72 ".word 0xc3e114d8\n"
73
74 "data_string:\n"
75 ".asciz \"hello DATA_ABORT!\\n\" \n"
76
77 );
78
79 void init_ttb(unsigned long *addr)
80 {
81 unsigned long va = 0;//定义虚拟地址
82 unsigned long pa = 0;//定义物理地址
83
84 //40000000-------80000000 ==== 40000000------80000000
85 for(va=0x40000000; va<=0x80000000; va+=0x100000){
86 pa = va;
87 addr[va >> 20] = pa | 2;
88 //|2的目的是将0-2位置为10此时将是小页模式4K
89 }
90
91 //00000000-------10000000 ==== 60000000------70000000
92 for(va=0x00000000; va<=0x10000000; va+=0x100000){
93 pa = va+0x60000000;
94 addr[va >> 20] = pa | 2;
95 }
96
97 //10000000-------14000000 ==== 10000000------14000000
98 for(va=0x10000000; va<=0x30000000; va+=0x100000){
99 pa = va;
100 addr[va >> 20] = pa | 2;
101 }
102
103 //30000000-------40000000 ==== 50000000------60000000
104 for(va=0x30000000; va<0x40000000; va+=0x100000){
105 pa = va + 0x20000000;
106 addr[va >> 20] = pa | 2;
107 }
108 }
109
110 void enable_mmu(void)
111
112 {
113 unsigned long addr = 0x70000000;
114 init_ttb(addr);
115 //step:初始化页表
116
117 unsigned long mmu = 1 | (1 << 1) | (1 << 8);
118 //将MMU的第0,1,8位置1
119 __asm__ __volatile__(
120 "mov r0, #3\n"
121 "MCR p15, 0, r0, c3, c0, 0\n"//manager
122 "MCR p15, 0, %0, c2, c0, 0\n"//addr
123 "MCR p15, 0, %1, c1, c0, 0\n"// enable mmu
124 :
125 : "r" (addr), "r" (mmu)
126 : "r0"
127 );
128 printf("MMU is enable!\n");
129 }
View Code
观察处理模块中58行有一句 "sub lr, lr, #4\n"特别要注意这句的重要性,是因为当程序执行发生错误的时候,调用PC-->LR CPSR-->SPSR LR--->PC SPSR--->CPSR。而回来的时候PC却跑到了下一条指令去了。
如果很难理解的话,不妨先记得除了UND和SWI模式,其他的异常模式都需要在处理前将PC的指令前移动一位好了。
接下来是处理多种异常了,因为在实际中,只出现一种异常的情况很少,一般都是几种异常同时出现,假如是出现的是UND和DATAABORT异常,寄存器先会到04地址去处理UND,但是问题来了,假如处理处理UND异常的指令很长(因为一般不止6个字节),这时候又遇到了ABORT异常当到10地址取处理ABORT异常的时候,取到的却不是想要的值。
为了解决这个问题,在处理的时候我们对应构建一张异常向量表,当到对应地址去寻找解决异常的时候,B到对应的解决方法里去!实现一个二级的跳转,这样,多种异常同时来我也不怕不怕啦!
1
2 int (*printf)(char *, ...) = 0xc3e114d8;
3
4 void init_ttb(unsigned long *addr);
5 void enable_mmu(void);
6 unsigned long data_abort_init();
7 void memcopy(unsigned long* dest,unsigned long* source,int len);
8
9 int main()
10 {
11 //发生异常时会进入异常模式跳转到0000 0004地址处理异常事件
12 unsigned long source_addr=data_abort_init();
13 //异常事件处理函数
14 printf("swi_souce addr is %x\n",source_addr);
15 //将异常处理地址的值放到0x60000004
16 memcopy(0x60000000,source_addr,0x1000);
17
18 enable_mmu();
19 //内存映射将0x00000004映射到0x6000000004
20 __asm__ __volatile__(
21 ".word 0x77777777\n"
22 "mov r0, #1\n"
23 "ldr r1, [r0]\n"
24 );
25 printf("welcome back! \n");
26
27
28 }
29
30 void memcopy(unsigned long* dest,unsigned long* source,int len)
31 {
32 int i=0;;
33 for(i=0;i<len;i++)
34 dest[i]=source[i];
35 }
36
37 unsigned long data_abort_init()
38 {
39 unsigned long source;
40 __asm__ __volatile__(
41 "ldr %0, =voliate_start\n"
42 : "=r" (source)
43 );
44
45
46 return source;
47
48 }
49
50 __asm__(
51
52 "voliate_start:\n"
53 //跳转目录
54 " b reset\n"
55 " b undefined\n"
56 " b swi\n"
57 " b pre_abt\n"
58 " b data_abt\n"
59 " .word 0\n"//占位符号,一个位占4个字节
60 " b irq\n"
61 " b fiq\n"
62 "\n"
63
64 //跳转要分三部:
65 //1:将PC保存到新模式下的lr中;
66 //2:将CPSR保存在SPSR中
67 //3:初始化SP
68 //前两步由硬件完成,而第三部需要手动完成
69 "reset:\n"
70
71 "undefined:\n"
72 "mov sp, #0x66000000\n"//初始化SP
73 "stmfd sp!, {r0-r12, lr}\n"//初始化sp,入栈保护寄存器
74 //打印一句话
75 "ldr r0, =und_string\n"
76 "ldr r2, show\n"
77 "blx r2\n"
78 //跳回来分两部
79 //1:将CPSR保存在SPSR中
80 //2:将PC保存到新模式下的lr中;
81 "mov sp, #0x66000000\n"//
82 "ldmea sp, {r0-r12, pc}^\n"//
83
84 "swi:\n"
85
86 "pre_abt:\n"
87 "data_abt:\n"
88 "sub lr, lr, #4\n"
89 "mov sp, #0x66000000\n"//初始化SP
90 "stmfd sp!, {r0-r12, lr}\n"//初始化sp,入栈保护寄存器
91 //打印一句话
92 "ldr r0, =data_string\n"
93 "ldr r2, show\n"
84 "swi:\n"
85
86 "pre_abt:\n"
87 "data_abt:\n"
88 "sub lr, lr, #4\n"
89 "mov sp, #0x66000000\n"//初始化SP
90 "stmfd sp!, {r0-r12, lr}\n"//初始化sp,入栈保护寄存器
91 //打印一句话
92 "ldr r0, =data_string\n"
93 "ldr r2, show\n"
94 "blx r2\n"
95 //跳回来分两部
96 //1:将CPSR保存在SPSR中
97 //2:将PC保存到新模式下的lr中;
98 "mov sp, #0x66000000\n"//
99 "ldmea sp, {r0-r12, pc}^\n"//
100
101 "irq:\n"
102 "fiq:\n"
103 "show:\n"
104 ".word 0xc3e114d8\n"
105
106 "und_string:\n"
107 ".asciz \"hello UND!\\n\" \n"
108 "data_string:\n"
109 ".asciz \"hello DATA_ABORT!\\n\" \n"
110
111 );
112
113 void init_ttb(unsigned long *addr)
114 {
115 unsigned long va = 0;//定义虚拟地址
116 unsigned long pa = 0;//定义物理地址
117
118 //40000000-------80000000 ==== 40000000------80000000
119 for(va=0x40000000; va<=0x80000000; va+=0x100000){
120 pa = va;
121 addr[va >> 20] = pa | 2;
122 //|2的目的是将0-2位置为10此时将是小页模式4K
123 }
124
125 //00000000-------10000000 ==== 60000000------70000000
126 for(va=0x00000000; va<=0x10000000; va+=0x100000){
127 pa = va+0x60000000;
128 addr[va >> 20] = pa | 2;
129 }
130
131 //10000000-------14000000 ==== 10000000------14000000
132 for(va=0x10000000; va<=0x14000000; va+=0x100000){
133 pa = va;
134 addr[va >> 20] = pa | 2;
135 }
136
137 //30000000-------40000000 ==== 50000000------60000000
138 for(va=0x30000000; va<0x40000000; va+=0x100000){
139 pa = va + 0x20000000;
140 addr[va >> 20] = pa | 2;
141 }
142 }
143
144 void enable_mmu(void)
145
146 {
147 unsigned long addr = 0x70000000;
148 init_ttb(addr);
149 //step:初始化页表
150
151 unsigned long mmu = 1 | (1 << 1) | (1 << 8);
152 //将MMU的第0,1,8位置1
153 __asm__ __volatile__(
154 "mov r0, #3\n"
155 "MCR p15, 0, r0, c3, c0, 0\n"//manager
156 "MCR p15, 0, %0, c2, c0, 0\n"//addr
157 "MCR p15, 0, %1, c1, c0, 0\n"// enable mmu
158 :
159 : "r" (addr), "r" (mmu)
160 : "r0"
161 );
162 printf("MMU is enable!\n");
163 }
View Code
测试一下结果:
总结一下:1.如果处理的是执行中发生的异常,PC回一一条指令:
"sub lr, lr, #4\n"
2.为了解决处理异常地址错乱,构建一张异常向量表:跳转到指定地点解决:
//跳转目录
" b reset\n"
" b undefined\n"
" b swi\n"
" b pre_abt\n"
" b data_abt\n"
" .word 0\n"//占位符号,一个位占4个字节
" b irq\n"
" b fiq\n"
//前两步由硬件完成,而第三部需要手动完成
69 "reset:\n"
70
71 "undefined:\n"
72 "mov sp, #0x66000000\n"//初始化SP
73 "stmfd sp!, {r0-r12, lr}\n"//初始化sp,入栈保
护寄存器
74 //打印一句话
75 "ldr r0, =und_string\n"
76 "ldr r2, show\n"
77 "blx r2\n"
...........................