ARM多种异常的处理

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
",source_addr);
 15     //将异常处理地址的值放到0x60000004
 16     memcopy(0x60000010,source_addr,0x1000);
 17     
 18     enable_mmu();
 19     //内存映射将0x00000004映射到0x6000000004    
 20     __asm__ __volatile__( 
 21         "mov r0, #1
"
 22         "ldr r1, [r0]
"
 23      );
 24     printf("welcome back! 
"); 
 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
",source_addr);
 15     //将异常处理地址的值放到0x600000010
 16     memcopy(0x60000010,source_addr,0x1000);
 17 
 18     enable_mmu();
 19     //内存映射将0x00000004映射到0x6000000004    
 20     __asm__ __volatile__(
 21         "mov r0, #1
"
 22         "ldr r1, [r0]
"
 23      );
 24     printf("welcome back! 
");
 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
"
 42          : "=r" (source)
 43      );
 44 
 45 
 46     return source;
 47 
 48 }
 49 
 50 __asm__(
 51 
 52 "voliate_start:
"
 53     //跳转要分三部:
 54     //1:将PC保存到新模式下的lr中;
 55     //2:将CPSR保存在SPSR中
 56     //3:初始化SP
 57     //前两步由硬件完成,而第三部需要手动完成
 58      "sub lr, lr, #4
"
 59      "mov sp, #0x66000000
"//初始化SP
 60      "stmfd sp!, {r0-r12, lr}
"//初始化sp,入栈保护寄存器 
 61     //打印一句话 
 62      "ldr r0, =data_string
"
 63      "ldr r2, show
"
 64      "blx r2
"
 65     //跳回来分两部
 66     //1:将CPSR保存在SPSR中
 67     //2:将PC保存到新模式下的lr中;
 68      "mov sp, #0x66000000
"//
 69      "ldmea sp, {r0-r12, pc}^
"// 
 70 
 71     "show:
"
 72      ".word 0xc3e114d8
"
 73 
 74      "data_string:
"
 75      ".asciz "hello DATA_ABORT!\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
"
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

观察处理模块中58行有一句   "sub lr, lr, #4 "特别要注意这句的重要性,是因为当程序执行发生错误的时候,调用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
",source_addr);
 15     //将异常处理地址的值放到0x60000004
 16     memcopy(0x60000000,source_addr,0x1000);
 17 
 18     enable_mmu();
 19     //内存映射将0x00000004映射到0x6000000004    
 20     __asm__ __volatile__(
 21         ".word 0x77777777
"
 22         "mov r0, #1
"
 23         "ldr r1, [r0]
"
 24      );
 25     printf("welcome back! 
");
 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
"
 42          : "=r" (source)
 43      );
 44 
 45 
 46     return source;
 47 
 48 }
 49 
 50 __asm__(
 51 
 52 "voliate_start:
"
 53     //跳转目录
 54     " b reset
"
 55     " b undefined
"
 56     " b swi
"
 57     " b pre_abt
"
 58     " b data_abt
"
 59     " .word 0
"//占位符号,一个位占4个字节
 60     " b irq
"
 61     " b fiq
"
 62 "
"
 63 
 64     //跳转要分三部:
 65     //1:将PC保存到新模式下的lr中;
 66     //2:将CPSR保存在SPSR中
 67     //3:初始化SP
 68     //前两步由硬件完成,而第三部需要手动完成
 69 "reset:
"
 70 
 71 "undefined:
"
 72      "mov sp, #0x66000000
"//初始化SP
 73      "stmfd sp!, {r0-r12, lr}
"//初始化sp,入栈保护寄存器 
 74     //打印一句话 
 75      "ldr r0, =und_string
"
 76      "ldr r2, show
"
 77      "blx r2
"
 78     //跳回来分两部
 79     //1:将CPSR保存在SPSR中
 80     //2:将PC保存到新模式下的lr中;
 81      "mov sp, #0x66000000
"//
 82      "ldmea sp, {r0-r12, pc}^
"// 
 83 
 84 "swi:
"
 85 
 86 "pre_abt:
"
 87 "data_abt:
"
 88      "sub lr, lr, #4
"
 89      "mov sp, #0x66000000
"//初始化SP
 90      "stmfd sp!, {r0-r12, lr}
"//初始化sp,入栈保护寄存器 
 91     //打印一句话 
 92      "ldr r0, =data_string
"
 93      "ldr r2, show
"
84 "swi:
"
 85 
 86 "pre_abt:
"
 87 "data_abt:
"
 88      "sub lr, lr, #4
"
 89      "mov sp, #0x66000000
"//初始化SP
 90      "stmfd sp!, {r0-r12, lr}
"//初始化sp,入栈保护寄存器 
 91     //打印一句话 
 92      "ldr r0, =data_string
"
 93      "ldr r2, show
"
 94      "blx r2
"
 95     //跳回来分两部
 96     //1:将CPSR保存在SPSR中
 97     //2:将PC保存到新模式下的lr中;
 98      "mov sp, #0x66000000
"//
 99      "ldmea sp, {r0-r12, pc}^
"// 
100 
101 "irq:
"
102 "fiq:
"
103     "show:
"
104      ".word 0xc3e114d8
"
105 
106     "und_string:
"
107      ".asciz "hello UND!\n" 
"
108      "data_string:
"
109      ".asciz "hello DATA_ABORT!\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
"
155         "MCR p15, 0, r0, c3, c0, 0
"//manager
156         "MCR p15, 0, %0, c2, c0, 0
"//addr  
157         "MCR p15, 0, %1, c1, c0, 0
"// enable mmu
158         :
159         : "r" (addr), "r" (mmu)
160         : "r0"
161     );
162     printf("MMU is enable!
");
163 }
View Code

测试一下结果:

总结一下:1.如果处理的是执行中发生的异常,PC回一一条指令:

       "sub lr, lr, #4 "

     2.为了解决处理异常地址错乱,构建一张异常向量表:跳转到指定地点解决:

       //跳转目录
            " b reset "
              " b undefined "
             " b swi "
              " b pre_abt "
              " b data_abt "
              " .word 0 "//占位符号,一个位占4个字节
              " b irq "
             " b fiq "

      //前两步由硬件完成,而第三部需要手动完成
 69 "reset: "
 70
 71 "undefined: "
 72      "mov sp, #0x66000000 "//初始化SP
 73      "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保
    护寄存器
 74     //打印一句话
 75      "ldr r0, =und_string "
 76      "ldr r2, show "
 77      "blx r2 "
...........................

    





 

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