zoukankan      html  css  js  c++  java
  • 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

     

      

  • 相关阅读:
    Eclipse EE 3.6 failed to create the java virtual machine 解决
    [收藏]家用三线插座(220V单相)正确接线方法
    Java设计模式 Design Pattern:包装模式 Decorator Pattern
    MIME 类型列表
    面试题参考
    循环队列的运用求K阶斐波那契序列
    加密技术仿射密码
    面试题集锦_1
    栈的运用(5)转换逆波兰式
    栈的运用(6)逆波兰式求值
  • 原文地址:https://www.cnblogs.com/hongzhunzhun/p/4504397.html
Copyright © 2011-2022 走看看