ARM中异常模式的跳转

  例如在人的生活中,表情有喜怒哀乐.在ARM工作状态中,也有不同的模式如下表所示:

(详细内容请参考 ARM架构参考手册第二章41页)

        对应的中文详细模式如下:

                 

今天主要对ARM的 undefined模式来认识ARM中处理异常的机制,在ARM中,如果遇到异常情况,首先会找异常向量表,如下图(详细请参考ARM架构手册54页)

例如,有以下代码。在板子运行的时候就会发生异常。对应的,由于.word这是undifined异常,对应异常向量表,就会在00000004这个位子来寻求解决异常的办法.......

 1    #include《stdio.h》
 2 
 3 int main()
 4   {
 5     
 6       __asm__ __volatile__(
 7           ".word 0x77777777
"
 8       );
 9   
10     
11 
12   }
异常
 
 

但是。。。。不巧的是在ARM中地址00000000到10000000地址是ROM,ROM是只可读,不可写的。所以,这就是MMU存在的意义之所在了,来一个“隔山打牛”把内存00000004这个地址映射到其他可以写入的地方,以后操作00000004这个地址就是在操作其他地址了。

 顺便回顾上一次MMU的代码。详细请参考上一篇笔记

  1 
  2 int (*printf)(char *, ...) = 0xc3e114d8;
  3 
  4 void init_ttb(unsigned long *addr);
  5 void enable_mmu(void);
  6 
  7 int main()
  8 {
  9 
 10     printf("hello mmu!
");
 11     //30000000---40000000 ----> 500000000----600000000
 12     unsigned long *pp=0x50001000;
 13     *pp= 0x111111111;
 14     printf("*pp is%x 
",*pp);
 15 
 16     enable_mmu();
 17 
 18     unsigned long *pv=0x30001000;
 19     printf("*pv is %x
",*pv);
 20 }
 21 
 22 void init_ttb(unsigned long *addr)
 23 {
 24     unsigned long va = 0;//定义虚拟地址
 25     unsigned long pa = 0;//定义物理地址
 26 
 27     //40000000-------80000000   ====  40000000------80000000
 28     for(va=0x40000000; va<=0x80000000; va+=0x100000){
 29         pa = va;
 30         addr[va >> 20] = pa | 2;
 31         //|2的目的是将0-2位置为10此时将是小页模式4K
 32     }
 33 
 34     //10000000-------14000000   ====  10000000------14000000
 35     for(va=0x10000000; va<=0x14000000; va+=0x100000){
 36         pa = va;
 37         addr[va >> 20] = pa | 2;
 38     }
 39 
 40     //30000000-------40000000   ====  50000000------60000000
 41     for(va=0x30000000; va<0x40000000; va+=0x100000){
 42         pa = va + 0x20000000;
 43         addr[va >> 20] = pa | 2;
 44     }
 45 }
 46 
 47 void enable_mmu(void)
 48 
 49 {
 50     unsigned long addr = 0x60000000;
 51     init_ttb(addr);
 52     //step:初始化页表
 53 
 54     unsigned long mmu = 1 | (1 << 1) | (1 << 8);
 55     //将MMU的第0,1,8位置1
 56     __asm__ __volatile__(
 57         "mov r0, #3
"
 58         "MCR p15, 0, r0, c3, c0, 0
"//manager
 59         "MCR p15, 0, %0, c2, c0, 0
"//addr  
 60         "MCR p15, 0, %1, c1, c0, 0
"// enable mmu
 61         :
 62         : "r" (addr), "r" (mmu)
 63         : "r0"
 64     );
 65     printf("MMU is enable!
");
 66 }
 67 

 在代码中,首先,在0x 30001000的地址中存入了数据0x11111111.然后,将0x30000000地址映射到了0x50000000的地址中,最后,打印0x50001000地址里的值,就是打印0x30001000的地址里的值。

以下是对于处理异常模式跳转的代码:

 1 
  2 int (*printf)(char *, ...) = 0xc3e114d8;
  3 
  4 void init_ttb(unsigned long *addr);
  5 void enable_mmu(void);
  6 unsigned long volitale_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=volitale_init();
 13     //异常事件处理函数
 14     printf("souce addr is %x
",source_addr);
 15     //将异常处理地址的值放到0x60000004
 16     memcopy(0x60000004,source_addr,0x100);
 17 
 18     enable_mmu();
 19     //内存映射将0x00000004映射到0x6000000004    
 20     __asm__ __volatile__(
 21          ".word 0x77777777
"
 22      );
 23     printf("welcome back! 
");
 24 
 25 
 26 }
 27 
 28 void memcopy(unsigned long* dest,unsigned long* source,int len)
 29 {
 30     int i=0;;
 31     for(i=0;i<len;i++)
 32         dest[i]=source[i];
 33 }
 34 
 35 unsigned long  volitale_init()
 36 {
 37     unsigned long source;
 38     __asm__ __volatile__(
 39          "ldr %0, =vector_start
"
 40          : "=r" (source)
 41      );
 42 
 43 
 44     return source;
 45 
 46 }
 47 //跳转要分三部:
 48 //1:将PC保存到新模式下的lr中;
 49 //2:将CPSR保存在SPSR中
 50 //3:初始化SP
 51 //前两步由硬件完成,而第三部需要手动完成
 52 
 53 __asm__(
 54      "vector_start:
"
 55      "mov sp, #0x66000000
"//初始化SP
 56      "stmfd sp!, {r0-r12, lr}
"//初始化sp,入栈保护寄存器 
 57 
 58     //打印一句话 
 59      "ldr r0, =string
"
 60      "ldr r2, show
"
 61      "blx r2
"
 62 
 63      "mov sp, #0x66000000
"//
 64      "ldmea sp, {r0-r12, pc}^
"// 
 65 
 66 
 67      "loop:
"
 68      "b loop
"
 69 
 70 
 71      "show:
"
 72      ".word 0xc3e114d8
"
 73 
 74      "string:
"
 75      ".asciz "hello undefined\n" 
"
 76      ".align 2
"
 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<=0x14000000; 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
"
121         "MCR p15, 0, r0, c3, c0, 0
"//manager
122         "MCR p15, 0, %0, c2, c0, 0
"//addr  
123         "MCR p15, 0, %1, c1, c0, 0
"// enable mmu
124         :
125         : "r" (addr), "r" (mmu)
126         : "r0"
127     );
128     printf("MMU is enable!
");
129 }
View Code

在程序中,

volitale_init()函数的作用就是获得处理异常函数的地址,存到变量source中并返回.

voctor_start汇编函数主要就是实现了从SVR模式跳入到UND模式,在UND模式中打印了一句话

hello undefined并且跳出来在代码中,跳转模式主要有三步,而跳出模式也有两步,代码中没给出,就是 /1:将CPSR保存在SPSR中 2:将PC保存到新模式下的lr中; 。

memcopy()函数的作用是:将source_addr里的数据拷贝到0x60000004地址,一共拷贝len个地址

实际上就是将处理异常函数的地址存到可以读写的地址。下次到0x00000004地址找处理函数的时候久直接调用到了处理异常函数。

接下来是enable_mmu()函数,用法与上一个一样。但是,此时在制表函数init_ttb()中。新把

从00000000----10000000的地址映射到了60000000-----70000000 地址

对于汇编部分,对于不太了解的没关系,主要了解实现功能是什么。掌握主要流程, 到最后再作

统一总结。

Makefile:

1 
  2 all:
  3     arm-none-linux-gnueabi-gcc -c mmu.c -o mmu.o
  4     arm-none-linux-gnueabi-ld -Ttext=0x41000000 mmu.o  -o mmu 
  5     arm-none-linux-gnueabi-objcopy  -Ielf32-littlearm -Obinary  mmu mmu.bin
  6 
  7 clean:
  8     rm -rf mmu mmu.o  mmu.bin
  9 
 10 
 11 
View Code

在PC终端make

在minicom板子上dnw到41000000地址go41000000

 

  

原文地址:https://www.cnblogs.com/hongzhunzhun/p/4504397.html