现在假设有五条指令,三指令正在执行的时候,四指令在译码过程中发生未定义指令异常,跳转到异常处理程序回来后,因为 PC指向五指令,所以继续执行五指令的内容。
但是,如果程序在三指令执行期间发生错误,处理回来后到达五指令,把四指令跳过了。解决方式是在处理的把PC的值减去四,具体的代码会在后面的中断代码中体现。
前文的最后提到如果三个异常同时发生,三个异常处理程序员都要放到指定的地址中处理。这必定会出现重叠。如下图:
如果我把swi处理函数放在0x4地址,data abort处理函数放在0x10地址。data abort的处理异常函数就会把swi处理函数.处理方式就是在0x00000000地址上建立异常向量表,下面是实现的代码片段:
比如现在要模拟发生未定义指令异常,现在整个流程是:第一步开启MMU,第二步将异常向量表放在0地址。第三步模拟发生未定义异常,跳到0x4地址执行。因为0x4地址也是个跳转指令,就到undef处执行。由于编写大部分程序使用C语言比较方便,在实际处理中,还要进行一次跳转。第四步就是在undef中跳转到C函数,执行函数。第五步。执行完成后回来。回到主函数PC处继续执行。
下面是具体的代码:同时发生两个异常:
1 int (*printf)(char *, ...) = 0xc3e114d8; 2 3 void init_ttb(unsigned long *ttb); 4 void enable_mmu(void); 5 void memcpy(unsigned char *dest, unsigned char *source, int len); 6 void do_ex(); 7 8 int main() 9 { 10 enable_mmu(); 11 12 *(unsigned long *)0x67000000 = do_ex; 13 unsigned long source = 0; 14 __asm__ __volatile__( 15 "ldr %0, =vector_start\n" 16 : "=r" (source) 17 ); 18 memcpy(0, source, 0x1000); 19 20 __asm__ __volatile__( 21 "mov r0, #3\n" 22 "ldr r1, [r0]\n" 23 ".word 0x77777777\n" 24 ); 25 printf("welcome back\n"); 26 } 27 28 void do_ex() 29 { 30 unsigned long cpsr = 0; 31 32 __asm__ __volatile__( 33 "mrs %0, cpsr\n" 34 : "=r" (cpsr) 35 ); 36 printf("cpsr is %x\n", cpsr & 0x1f); 37 } 38 39 __asm__( 40 "vector_start:\n" 41 " b reset\n" 42 " b undef\n" 43 " b swi\n" 44 " b pre_abt\n" 45 " b data_abt\n" 46 " .word 0x0\n" 47 " b irq \n" 48 " b fiq\n" 49 "\n" 50 "reset:\n" 51 "undef:\n" 52 " mov sp, #0x66000000\n" 53 " stmfd sp!, {r0-r12, lr}\n" 54 " \n" 55 " mov r0, #0x67000000\n" 56 " ldr r1, [r0]\n" 57 " blx r1\n" 58 " \n" 59 " mov sp, #0x66000000\n" 60 " ldmea sp, {r0-r12, pc}^\n" 61 "swi:\n" 62 " mov sp, #0x66000000\n" 63 " stmfd sp!, {r0-r12, lr}\n" 64 " \n" 65 " mov r0, #0x67000000\n" 66 " ldr r1, [r0]\n" 67 " blx r1\n" 68 " \n" 69 " mov sp, #0x66000000\n" 70 " ldmea sp, {r0-r12, pc}^\n" 71 "\n" 72 "pre_abt:\n" 73 "data_abt:\n" 74 " mov sp, #0x66000000\n" 75 " sub lr, lr, #4\n" 76 " stmfd sp!, {r0-r12, lr}\n" 77 " \n" 78 " mov r0, #0x67000000\n" 79 " ldr r1, [r0]\n" 80 " blx r1\n" 81 " \n" 82 " mov sp, #0x66000000\n" 83 " ldmea sp, {r0-r12, pc}^\n" 84 "irq:\n" 85 " mov sp, #0x66000000\n" 86 " sub lr, lr, #4\n" 87 " stmfd sp!, {r0-r12, lr}\n" 88 " \n" 89 " mov r0, #0x67000000\n" 90 " ldr r1, [r0]\n" 91 " blx r1\n" 92 " \n" 93 " mov sp, #0x66000000\n" 94 " ldmea sp, {r0-r12, pc}^\n" 95 "fiq:\n" 96 97 ); 98 99 void init_ttb(unsigned long *ttb) 100 { 101 unsigned long va = 0; 102 unsigned long pa = 0; 103 104 for(va=0x00000000; va<0x10000000; va+=0x100000){ 105 pa = va + 0x60000000; 106 ttb[va >> 20] = pa | 2; 107 } 108 109 //10000000~14000000 -> 10000000~14000000 110 for(va=0x10000000; va<0x14000000; va+=0x100000){ 111 pa = va; 112 ttb[va >> 20] = pa | 2; 113 } 114 115 //40000000~80000000 -> 40000000~80000000 116 for(va=0x40000000; va<0x80000000; va+=0x100000){ 117 pa = va; 118 ttb[va >> 20] = pa | 2; 119 } 120 121 //30000000~40000000 -> 50000000~60000000 122 for(va=0x30000000; va<0x40000000; va+=0x100000){ 123 pa = va + 0x20000000; 124 ttb[va >> 20] = pa | 2; 125 } 126 } 127 128 void enable_mmu(void) 129 { 130 unsigned long ttb = 0x70000000; 131 init_ttb(ttb); 132 unsigned long mmu = 0; 133 mmu = 1 | (1 << 3) | (1 << 8); 134 __asm__ __volatile__( 135 "mov r0, #3\n" 136 "mcr p15, 0, r0, c3, c0, 0\n" 137 "mcr p15, 0, %0, c2, c0, 0\n" 138 "mcr p15, 0, %1, c1, c0, 0\n" 139 : 140 : "r" (ttb), "r" (mmu) 141 ); 142 } 143 144 void memcpy(unsigned char *dest, unsigned char *source, int len) 145 { 146 int i = 0; 147 for(i=0; i) 148 dest[i] = source[i]; 149 }
运行截图:
这里要对cpsr(CPU状态寄存器)介绍下,直接看arm手册中的描述:
现在异常发生都到去0x67000000地址中执行。而0x6700000中存放的是do_ex()函数。do_ex()函数的功能是读取cpsr中的后4位,并且打印出来。从上图可以知道,后四位表示的是处于的模式。
17用二进制表示为10111 1b用二进制表示为11011 查阅三十六天所写的模式跳转第一张表格,10111对应Abor模式,11011对应Undefine模式。
说明代码正确。
以后编写代码异常处理代码,就直接do_ex()函数中编写。其它的开启MMU,二级跳转。代码都不变!