ARM体系的CPU有以下7种工作模式:
1、用户模式(Usr):用于正常执行程序;
2、快速中断模式(FIQ):用于高速数据传输;
3、外部中断模式(IRQ):用于通常的中断处理;
4、管理模式(svc):操作系统使用的保护模式;
5、数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储以及存储保护;
6、系统模式(sys):运行具有特权的操作系统任务;
7、未定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件;
如下图:
要进行模式跳转,就需要要有异常事件在触发它进行模式跳转,在ARM中,异常有如下几种:
(1)在异常向量表中,需要注意的是未定义指令(undefined),软中断(swi)是发生在译码阶段,其他五种都发生在执行阶段;ARM中的流水线分为:取值,译码,执行,仿存,回写这五步,当一条指令正在执行时,它的下一条指令正在译码,下下一条在取值,pc指向的是正在取值的那条指令,在模式跳转要回去的时候这点需要认真考虑;
(2)通过上图我们可以看出要是发生undefined,它就会自动跳到0x0000004这个地址去执行,那如果我们在这个地址放一段代码,到时候就可以知道有没有发生异常,是不是跳到0x0000004这个;思想就是这样,但是现在有遇到一个新的问题,因为在这个三星公司将0x00000000—0x00010000为iROM,这个地址是只能读,不能改,因此我们就要需开启mmu,把0x00000004映射到其他空闲的地址上去:
下面是代码:
1 int (*printf)(char *, ...) = 0xc3e114d8; 2 void enable_mmu(); 3 void memcpy(unsigned long *dest, unsigned long *source, int len); 4 void init_table(unsigned long *addr); 5 unsigned long swi_init(); 6 7 int main() 8 { 9 unsigned long source = swi_init(); 10 printf("source is %x\n", source); 11 12 memcpy(0x60000004, source, 0x1000); 13 enable_mmu(); 14 15 __asm__ __volatile__ ( 16 ".word 0xffffffff\n" 17 ); 18 19 printf("welcom back\n"); 20 } 21 22 unsigned long swi_init() 23 { 24 unsigned long addr; 25 __asm__ __volatile__ ( 26 "ldr %0, =start \n" 27 : "=r" (addr) 28 ); 29 30 return addr; 31 } 32 33 void memcpy(unsigned long *dest, unsigned long *source, int len) 34 { 35 int i = 0; 36 for(i = 0; i < len; i++) { 37 dest[i] = source[i]; 38 } 39 } 40 41 void enable_mmu() 42 { 43 /*构建表*/ 44 unsigned long addr = 0x50000000; 45 init_table(addr); 46 /*打开mmu*/ 47 unsigned long mmu = 0; 48 mmu = 1 | (1 << 1) | (1 << 3) | (1 << 8); 49 __asm__ __volatile__ ( 50 "mov r0, #3\n" 51 "MCR p15, 0, r0, c3, c0, 0\n"//设置为管理员 52 "MCR p15, 0, %0, c2, c0, 0\n"//设置表的地址 53 "MCR p15, 0, %1, c1, c0, 0\n"//开启mmu 54 : 55 : "r" (addr), "r" (mmu) 56 : 57 ); 58 59 } 60 61 __asm__ ( 62 63 "start: \n" 64 "mov sp, #0x47000000\n" 65 "stmdb sp!, {r0-r12, lr}\n" 66 67 "ldr r3, data\n" 68 "ldr r0, =str_und\n" 69 "blx r3\n" 70 71 /*跳回去代码*/ 72 "mov sp, #0x47000000\n" 73 "ldmdb sp, {r0-r12, pc}^\n"// 74 75 "data:\n" 76 ".word 0xc3e114d8\n" 77 78 "str_und:\n" 79 ".asciz \"this is undefined\\n\"\n" 80 ".align 2\n" 81 ); 82 83 void init_table(unsigned long *addr) 84 { 85 unsigned long va = 0; 86 unsigned long phys = 0; 87 88 //0x40000000-0x80000000 -> 0x40000000-0x80000000 89 for(va = 0x40000000; va < 0x80000000; va += 0x100000) { 90 phys = va; 91 addr[va >> 20] = phys | 2; 92 } 93 94 //0x10000000-0x14000000 -> 0x10000000-0x140000000 95 for(va = 0x10000000; va < 0x14000000; va += 0x100000) { 96 phys = va; 97 addr[va >> 20] = phys | 2; 98 } 99 //0x10000000-0x14000000 -> 0x10000000-0x140000000 100 for(va = 0x0; va < 0x10000000; va += 0x100000) { 101 phys = va + 0x60000000; 102 addr[va >> 20] = phys | 2; 103 } 104 105 }
在开发板运行结果如下:
总结一下:当一个模式发生跳转是,需要三步:(1)将pc存到lr中,pc->lr;(2)将cpsr存到spsr,cpsr-spsr;(3)初始化sp;第一二步由系统硬件自动完成,第三部需要我们手动完成;
模式跳回去的时候逆过过来就ok,初始化sp,将lr还给pc,spsr还给cpsr,注意需要同时一起还,73行
上面代码还有一问题就是,要是几个异常一起发生怎么办,因为我们不能控制它每次执法生一个吧,cpu留给我们处理每个异常的只有4个字节,那么我们就需要进行二级跳转了,下面贴出二级跳转代码:
1 int (*printf)(char *, ...) = 0xc3e114d8; 2 void enable_mmu(); 3 void init_table(unsigned long *addr); 4 void memcpy(unsigned char *dest, unsigned char *src, int len); 5 extern unsigned long vector_start; 6 7 int main() 8 { 9 memcpy(0x70000000, vector_start, 0x1000); 10 enable_mmu(); 11 12 __asm__ __volatile__( 13 ".word 0x77777777\n" 14 "mov r3, #3\n" 15 "ldr r0, [r3]\n" 16 ); 17 18 printf("welcom back\n"); 19 20 return 0; 21 } 22 23 void memcpy(unsigned char *dest, unsigned char *src, int len) 24 { 25 int i = 0; 26 for(i = 0; i < len; i++) { 27 dest[i] = src[i]; 28 } 29 } 30 31 void enable_mmu() 32 { 33 //step 1: creat ttb 34 unsigned long addr = 0x50000000; 35 init_table(addr); 36 //step 2: enable mmu 37 unsigned long mmu = 0; 38 mmu = 1 | (1 << 1) | (1 << 3) | (1 << 8); 39 __asm__ __volatile__ ( 40 "mov r0, #3\n" 41 "MCR p15, 0, r0, c3, c0, 0\n"//set manage 42 "MCR p15, 0, %0, c2, c0, 0\n"//set ttb 43 "MCR p15, 0, %1, c1, c0, 0\n"//enable mmu 44 : 45 : "r" (addr), "r" (mmu) 46 : 47 ); 48 49 } 50 51 __asm__( 52 53 "vector: \n" 54 " b reset\n" 55 " b und\n" 56 " b swi\n" 57 " b pre_abt\n" 58 " b data_abt\n" 59 " .word 0x0\n" 60 " b irq\n" 61 " b fiq\n" 62 63 "reset:\n" 64 65 "und:\n" 66 /*模式跳转进来分三步: 67 *(1)将pc存到lr,pc->lr 68 *(2)将cpsr存到spsr 69 *(3)初始化sp 70 *前两部系统硬件帮我们完成,第三步需要我们手动配置*/ 71 " mov sp, #0x47000000\n" 72 " stmdb sp!, {r0-r12, lr}\n" 73 74 " ldr r3, show\n" 75 " ldr r0, =str_und\n" 76 " blx r3\n" 77 78 /*回去的时候逆回去就ok*,^表示同时还回去*/ 79 " mov sp, #0x47000000\n" 80 " ldmdb sp, {r0-r12, pc}^ \n" 81 82 "swi:\n" 83 84 " mov sp, #0x47000000\n" 85 " stmdb sp!, {r0-r12, lr}\n" 86 87 " ldr r3, show\n" 88 " ldr r0, =str_swi\n" 89 " blx r3\n" 90 91 " mov sp, #0x47000000\n" 92 " ldmdb sp, {r0-r12, pc}^ \n" 93 "pre_abt:\n" 94 95 "data_abt:\n" 96 97 " mov sp, #0x47000000\n" 98 " sub lr, lr, #4\n"//这句需要好好理解 99 " stmdb sp!, {r0-r12, lr}\n" 100 101 " ldr r3, show\n" 102 " ldr r0, =str_data\n" 103 " blx r3\n" 104 105 " mov sp, #0x47000000\n" 106 " ldmdb sp, {r0-r12, pc}^ \n" 107 108 "irq:\n" 109 110 "fiq:\n" 111 112 "show:\n" 113 " .word 0xc3e114d8\n" 114 115 "str_data:\n" 116 " .asciz \"this is data abort\\n\"\n" 117 " .align 2\n" 118 119 "str_swi:\n" 120 " .asciz \"this is swi \\n\"\n" 121 " .align 2\n" 122 123 "str_und:\n" 124 " .asciz \"this is undefined \\n\"\n" 125 " .align 2\n" 126 127 ".global vector_start\n" 128 "vector_start: \n" 129 ".word vector \n " 130 131 ); 132 133 void init_table(unsigned long *addr) 134 { 135 unsigned long va = 0; 136 unsigned long phys = 0; 137 138 //0x40000000-0x80000000 -> 0x40000000-0x80000000 139 for(va = 0x40000000; va < 0x80000000; va += 0x100000) { 140 phys = va; 141 addr[va >> 20] = phys | 2; 142 } 143 144 //0x10000000-0x14000000 -> 0x10000000-0x140000000 145 for(va = 0x10000000; va < 0x14000000; va += 0x100000) { 146 phys = va; 147 addr[va >> 20] = phys | 2; 148 } 149 //0x10000000-0x14000000 -> 0x10000000-0x140000000 150 for(va = 0x0; va < 0x10000000; va += 0x100000) { 151 phys = va + 0x70000000; 152 addr[va >> 20] = phys | 2; 153 } 154 155 }
下面是运行结果: