第三十六天:Tiny4412驱动开发之模式跳转

    ARM支持七种模式,如下图所示:

  第三十六天:Tiny4412驱动开发之模式跳转_第1张图片

  1、 User Mode:用户模式。

     2、FIQ Mode:快速中断模式。 

       3、 IRQ Mode:中断模式。中断(不包括软中断)处理函数在这种模式下执行。
       4、 Supervisor Mode:监视模式。软中断(SWI)处理函数在这种模式下执行。 

    5、 Abort Mode:所有同内存保护相关的异常均在这种模式下执行。

    6、 Undefined Mode:处理无效指令的异常处理函数在这种模式下执行。

  7、 System Mode:特权模式。
     今天的任务是实现3,5和6模式的跳转。要实现跳转,就要制造异常,下面是异常向量表:

第三十六天:Tiny4412驱动开发之模式跳转_第2张图片

  由上图可知,当发生软中断时,程序会到0x00000008地址执行,并处于监视模式,当发生取数据异常时,程序会到0x00000010处执行,并处于Abort模式,当发生未定义指令异常的时候,程序会到0x00000004处执行,并处于Undefined模式。

  当发生异常的时候,就会跳到特定的地址去执行异常处理 程序,地址是从0x00000000d到0x00000001C。那么问题来了。我们可以根据下表可知,0x00000000~0x00010000地址是iROM,是不能写入处理的。这时候就需要利用到上文讲解的MMU,将0x000000000的地址映射到其它可以访问的地方。

   
   我们先来模拟未定义指令异常,当程序发生未定义指令异常的时候,会跳转到0x000000004处运行,因为C语言不能指定指令存放的地址。所以需要使用内嵌汇编实现。先使用汇编实现处理指令,将指令放到0x60000004地址中,再开启MMU,将0x60000004地址映射到0x00000004地址中。

下面是具体代码的实现: 

 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 
 7 int main()
 8 {
 9     unsigned long source;
10     __asm__ __volatile__(
11         "ldr %0, =vector_start\n"
12         : "=r" (source)
13     );
14     memcpy(0x60000004, source, 0x100);
15     
16     enable_mmu();
17     
18     __asm__ __volatile__(
19         ".word 0x77777777\n"
20     );
21 }
22 
23 
24 void init_ttb(unsigned long *ttb)
25 {
26     unsigned long va = 0;
27     unsigned long pa = 0;
28     
29     for(va=0x00000000; va<0x10000000; va+=0x100000){
30         pa = va + 0x60000000;
31         ttb[va >> 20] = pa | 2;
32     }
33     
34     //10000000~14000000  -> 10000000~14000000
35     for(va=0x10000000; va<0x14000000; va+=0x100000){
36         pa = va;
37         ttb[va >> 20] = pa | 2;
38     }
39 
40     //40000000~80000000  -> 40000000~80000000
41     for(va=0x40000000; va<0x80000000; va+=0x100000){
42         pa = va;
43         ttb[va >> 20] = pa | 2;
44     }
45 
46     //30000000~40000000  -> 50000000~60000000
47     for(va=0x30000000; va<0x40000000; va+=0x100000){
48         pa = va + 0x20000000;
49         ttb[va >> 20] = pa | 2;
50     }
51 }
52 
53 void enable_mmu(void)
54 {
55     unsigned long ttb = 0x70000000;
56     init_ttb(ttb);
57     unsigned long mmu = 0;
58     mmu = 1 | (1 << 3) | (1 << 8);
59     __asm__ __volatile__(
60         "mov r0, #3\n"
61         "mcr p15, 0, r0, c3, c0, 0\n" 
62         "mcr p15, 0, %0, c2, c0, 0\n" 
63         "mcr p15, 0, %1, c1, c0, 0\n" 
64         :
65         : "r" (ttb), "r" (mmu)
66     );
67 }
68 
69 __asm__(
70     "vector_start:\n"
71     "mov sp, #0x66000000\n"
72     "stmfd sp!, {r0-r12, lr}\n"
73     
74     
75     "ldr r0, =string\n"
76     "ldr r2, show\n"
77     "blx r2\n"
78     
79     "loop:\n"
80     "b loop\n"
81     
82     
83     "show:\n"
84     ".word 0xc3e114d8\n"
85     
86     "string:\n"
87     ".asciz \"hello undefined\\n\" \n"
88     ".align 2\n"
89 );
90 
91 void memcpy(unsigned char *dest, unsigned char *source, int len)
92 {
93     int i = 0;
94     for(i=0; i)
95         dest[i] = source[i];
96 }

 

  输出结果为:

  第三十六天:Tiny4412驱动开发之模式跳转_第3张图片

 

   开启MMU代码和建映射表的内容在前文章介绍过了,这里就解释内嵌汇编中异常处理函数。要解释前,还要贴一张表格:

  第三十六天:Tiny4412驱动开发之模式跳转_第4张图片
    在跳转中cpu要完成三件事情,一、将PC保存到新模式下的lr中,二、将CPSR保存在SPSR中,三,初始化SP。

  前两步由硬件完成,第三步要我们完成,56行就是初始化SP代码,因为不同模式下的r1~r12是相同的,所以57行是入栈保护寄存器。后面的代码就是输出一段字符。
   将代码拷到开发板中运行,输出mode undefined 说明模式跳转成功。

      跳转过去了,这么回来呢?将这三步逆着执行就可以了。下面的代码是为了更好的了解将上面的代码分解成三个代码,完成软中断异常的跳转 ,最后还会跳回来。 

    分成三个代码思想更加简单,第一个是开启MMU,第二个是将异常处理代码放到0x00000008处,第三个是软中断触发程序。

 1 nt (*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 
 7 int main()
 8 {
 9     enable_mmu();
10     
11 }
12 
13 
14 void init_ttb(unsigned long *ttb)
15 {
16     unsigned long va = 0;
17     unsigned long pa = 0;
18     
19     for(va=0x00000000; va<0x10000000; va+=0x100000){
20         pa = va + 0x60000000;
21         ttb[va >> 20] = pa | 2;
22     }
23     
24     //10000000~14000000  -> 10000000~14000000
25     for(va=0x10000000; va<0x14000000; va+=0x100000){
26         pa = va;
27         ttb[va >> 20] = pa | 2;
28     }
29 
30     //40000000~80000000  -> 40000000~80000000
31     for(va=0x40000000; va<0x80000000; va+=0x100000){
32         pa = va;
33         ttb[va >> 20] = pa | 2;
34     }
35 
36     //30000000~40000000  -> 50000000~60000000
37     for(va=0x30000000; va<0x40000000; va+=0x100000){
38         pa = va + 0x20000000;
39         ttb[va >> 20] = pa | 2;
40     }
41 }
42 
43 void enable_mmu(void)
44 {
45     unsigned long ttb = 0x70000000;
46     init_ttb(ttb);
47     unsigned long mmu = 0;
48     mmu = 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" 
54         :
55         : "r" (ttb), "r" (mmu)
56     );
57 }
 1 vector_start:
 2            mov sp, #0x66000000
 3     stmfd sp!, {r0-r12, lr}
 4     sub r3,lr,#4
 5     ldr r2,[r3]
 6     
 7     ldr r0, =string
 8     bic r1,r2,#0xff000000
 9     ldr r2, show
10     blx r2
11 
12     mov sp,#0x66000000
13     ldmea sp,{r0-r12,pc}^
14 show:
15     .word 0xc3e114d8
16 string:
17     .asciz "hello swi %d\n"
18     .align 2
1 int (*printf)(char *, ...) = 0xc3e114d8;
2 int main()
3 {
4     __asm__ __volatile__(
5         "swi #88\n"
6     );
7     printf("welcome back\n");
8 }

  运行代码后输出

  第三十六天:Tiny4412驱动开发之模式跳转_第5张图片

  因为处理的汇编函数直接放到0x000000008地址处,就不需要内存拷贝函数了。输出的88 表示软中断号为88.异常处理结束后成功回来。输出welcome back.

        取数据异常的模式跳转和上面的程序类似,就是处理异常存放的地址改为0x00000010.这里就不贴代码了。

   最后老刘留了个问题:如果这三个异常同时发生会怎么样?(提示:二级跳转)

 

 

 

 

 

 

 

 

 

 


 

你可能感兴趣的:(Linux)