第三十七天:Tiny4412驱动开发之二级跳转

  ARM五级流水线:取值,译码,执行,访存,回写。未定义指令异常和SWI软中断发生在译码阶段,其它异常发生在执行阶段。

  

  现在假设有五条指令,三指令正在执行的时候,四指令在译码过程中发生未定义指令异常,跳转到异常处理程序回来后,因为 PC指向五指令,所以继续执行五指令的内容。

  但是,如果程序在三指令执行期间发生错误,处理回来后到达五指令,把四指令跳过了。解决方式是在处理的把PC的值减去四,具体的代码会在后面的中断代码中体现。

  前文的最后提到如果三个异常同时发生,三个异常处理程序员都要放到指定的地址中处理。这必定会出现重叠。如下图:

   

  如果我把swi处理函数放在0x4地址,data abort处理函数放在0x10地址。data abort的处理异常函数就会把swi处理函数.处理方式就是在0x00000000地址上建立异常向量表,下面是实现的代码片段:

   

  比如现在要模拟发生未定义指令异常,现在整个流程是:第一步开启MMU,第二步将异常向量表放在0地址。第三步模拟发生未定义异常,跳到0x4地址执行。因为0x4地址也是个跳转指令,就到undef处执行。由于编写大部分程序使用C语言比较方便,在实际处理中,还要进行一次跳转。第四步就是在undef中跳转到C函数,执行函数。第五步。执行完成后回来。回到主函数PC处继续执行。

  下面是具体的代码:同时发生两个异常:

  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 void do_ex();
  7
  8 int main()
  9 {
 10     enable_mmu();
 11
 12     *(unsigned long *)0x67000000 = do_ex;
 13     unsigned long source = 0;
 14     __asm__ __volatile__(
 15         "ldr %0, =vector_start\n"
 16         : "=r" (source)
 17     );
 18     memcpy(0, source, 0x1000);
 19
 20     __asm__ __volatile__(
 21         "mov r0, #3\n"
 22         "ldr r1, [r0]\n"
 23         ".word 0x77777777\n"
 24     );
 25     printf("welcome back\n");
 26 }
 27
 28 void do_ex()
 29 {
 30     unsigned long cpsr = 0;
 31
 32     __asm__ __volatile__(
 33         "mrs %0, cpsr\n"
 34         : "=r" (cpsr)
 35     );
 36     printf("cpsr is %x\n", cpsr & 0x1f);
 37 }
 38
 39 __asm__(
 40 "vector_start:\n"
 41 "    b reset\n"
 42 "    b undef\n"
 43 "    b swi\n"
 44 "    b pre_abt\n"
 45 "    b data_abt\n"
 46 "    .word 0x0\n"
 47 "    b irq \n"
 48 "    b fiq\n"
 49 "\n"
 50 "reset:\n"
 51 "undef:\n"
 52 "    mov sp, #0x66000000\n"
 53 "    stmfd sp!, {r0-r12, lr}\n"
 54 "    \n"
 55 "    mov r0, #0x67000000\n"
 56 "    ldr r1, [r0]\n"
 57 "    blx r1\n"
 58 "    \n"
 59 "    mov sp, #0x66000000\n"
 60 "    ldmea sp, {r0-r12, pc}^\n"
 61 "swi:\n"
 62 "    mov sp, #0x66000000\n"
 63 "    stmfd sp!, {r0-r12, lr}\n"
 64 "    \n"
 65 "    mov r0, #0x67000000\n"
 66 "    ldr r1, [r0]\n"
 67 "    blx r1\n"
 68 "    \n"
 69 "    mov sp, #0x66000000\n"
 70 "    ldmea sp, {r0-r12, pc}^\n"
 71 "\n"
 72 "pre_abt:\n"
 73 "data_abt:\n"
 74 "    mov sp, #0x66000000\n"
 75 "    sub lr, lr, #4\n"
 76 "    stmfd sp!, {r0-r12, lr}\n"
 77 "    \n"
 78 "    mov r0, #0x67000000\n"
 79 "    ldr r1, [r0]\n"
 80 "    blx r1\n"
 81 "    \n"
 82 "    mov sp, #0x66000000\n"
 83 "    ldmea sp, {r0-r12, pc}^\n"
 84 "irq:\n"
 85 "    mov sp, #0x66000000\n"
 86 "    sub lr, lr, #4\n"
 87 "    stmfd sp!, {r0-r12, lr}\n"
 88 "    \n"
 89 "    mov r0, #0x67000000\n"
 90 "    ldr r1, [r0]\n"
 91 "    blx r1\n"
 92 "    \n"
 93 "    mov sp, #0x66000000\n"
 94 "    ldmea sp, {r0-r12, pc}^\n"
 95 "fiq:\n"
 96
 97 );
 98
 99 void init_ttb(unsigned long *ttb)
100 {
101     unsigned long va = 0;
102     unsigned long pa = 0;
103
104     for(va=0x00000000; va<0x10000000; va+=0x100000){
105         pa = va + 0x60000000;
106         ttb[va >> 20] = pa | 2;
107     }
108
109     //10000000~14000000  -> 10000000~14000000
110     for(va=0x10000000; va<0x14000000; va+=0x100000){
111         pa = va;
112         ttb[va >> 20] = pa | 2;
113     }
114
115     //40000000~80000000  -> 40000000~80000000
116     for(va=0x40000000; va<0x80000000; va+=0x100000){
117         pa = va;
118         ttb[va >> 20] = pa | 2;
119     }
120
121     //30000000~40000000  -> 50000000~60000000
122     for(va=0x30000000; va<0x40000000; va+=0x100000){
123         pa = va + 0x20000000;
124         ttb[va >> 20] = pa | 2;
125     }
126 }
127
128 void enable_mmu(void)
129 {
130     unsigned long ttb = 0x70000000;
131     init_ttb(ttb);
132     unsigned long mmu = 0;
133     mmu = 1 | (1 << 3) | (1 << 8);
134     __asm__ __volatile__(
135         "mov r0, #3\n"
136         "mcr p15, 0, r0, c3, c0, 0\n"
137         "mcr p15, 0, %0, c2, c0, 0\n"
138         "mcr p15, 0, %1, c1, c0, 0\n"
139         :
140         : "r" (ttb), "r" (mmu)
141     );
142 }
143
144 void memcpy(unsigned char *dest, unsigned char *source, int len)
145 {
146     int i = 0;
147     for(i=0; i<len; i++)
148         dest[i] = source[i];
149 }

    运行截图:

    

这里要对cpsr(CPU状态寄存器)介绍下,直接看arm手册中的描述:

 

现在异常发生都到去0x67000000地址中执行。而0x6700000中存放的是do_ex()函数。do_ex()函数的功能是读取cpsr中的后4位,并且打印出来。从上图可以知道,后四位表示的是处于的模式。

  17用二进制表示为10111  1b用二进制表示为11011  查阅三十六天所写的模式跳转第一张表格,10111对应Abor模式,11011对应Undefine模式。

说明代码正确。

  以后编写代码异常处理代码,就直接do_ex()函数中编写。其它的开启MMU,二级跳转。代码都不变!

 

            

时间: 11-24

第三十七天:Tiny4412驱动开发之二级跳转的相关文章

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

ARM支持七种模式,如下图所示: 1. User Mode:用户模式.    2.FIQ Mode:快速中断模式. 3. IRQ Mode:中断模式.中断(不包括软中断)处理函数在这种模式下执行.        4. Supervisor Mode:监视模式.软中断(SWI)处理函数在这种模式下执行.   5. Abort Mode:所有同内存保护相关的异常均在这种模式下执行.   6. Undefined Mode:处理无效指令的异常处理函数在这种模式下执行. 7. System Mode:特

第三十五天:Tiny4412驱动开发之配置MMU

MMU表示内存管理单元,负责虚拟内存映射到物理内存. 虚拟地址映射到物理地址的关键是构建映射表.MMU就是利用映射表格将虚拟地址转换成物理地址.虚拟地址在32系统中为4G,地址占4字节,如果映射表格中虚拟地址和物理地址是一一对应的关系,一条记录就占8字节,那么映射表就要32G.这明显是不合理的. 于是通过二级映射解决这个问题.地址共32位,把前12位作为基地址,后20位作为偏移量,将虚拟地址和物理地址作为的前12位一一对应,12位就是2的12次方为4k. 一条记录8字节,那么映射表格就只有32k

Android实战简易教程-第三十七枪(ListView中点击button跳转到拨号界面实例)

最近讨论了一个项目需求,在ListView的Item中放置了一个类似电话的图标,点击图标可以将号码调到拨号界面.实现起来很是容易,原理也易懂,较为实用,项目中有需要的可以直接引入. 我模拟了一个简单的demo.代码如下: 1.ListAdapter.java: package com.example.listviewphone; import java.util.List; import android.content.Context; import android.content.Intent

QT开发(四十七)——数据库驱动层

QT开发(四十七)--数据库驱动层 驱动层为具体的数据库和SQL接口层之间提供了底层的桥梁,主要类包括Qt SQL模块中的QSqlDriver.QSqlDriverCreator.QSqlDriverCreatorBase.QSqlDriverPlugin和QSqlResult. 一.QSqlDriver QSqlDriver是访问具体SQL数据库的抽象基类,不能直接使用.如果要创建自定义的数据库驱动,可以根据需要重写QSqlDriver类的纯虚函数和虚函数. 自定义数据库驱动 QSqlData

第三十三天:Tiny4412驱动开发之LED驱动和按键驱动编写

从今天开始进入驱动开发的课程的学习,共完成四件事情.一:u-boot的简单移植,二:uboot中编写helloword程序 三:开发板中led灯的驱动编写,包括led点亮,闪烁,跑马,流水.四:开发板中按键的驱动编写,按下按键后在屏幕中显示字符. 一:u-boot的简单移植 1.进入开发板提供的源码文件包,解压uboot源码包. cd /home/bunfly/source_code/ tar xf uboot_tiny4412-20130729.tgz 2.进入uboot文件夹,更改uboot

Android深度探索HAL与驱动开发 第三章

Android深度探索HAL与驱动开发 第三章 Git使用入门 读书笔记 Git是对源代码进行管理的软件. 一.安装Git # apt-get install git # apt-get install git-doc git-emall git-gui gitk 用以下命令控制Git: # apt-get install git-core # apt-get install git-doc git-svn git-email git-gui gitk 二.查看Git文档 查看git-check

Android深度探索(卷1)HAL与驱动开发 第三章 Git使用入门 读书笔记

Android深度探索(卷1)HAL与驱动开发 第三章 Git使用入门 读书笔记     本章是对Git的一个概述. Linux是一个开源的系统.事实上,在Linux上许多软件都和底层程序以及内核驱动有关,然而Linux内核的版本非常多,如果每个版本上的软件都采用安装包的形式,则匹配这么多版本的安装包将十分庞大.所以有很多软件不是以二进制安装包的形式来安装和使用的.而是将源代码下载下来,并在每个用户自己的Linux中编译并安装,即使用make 和make install 命令.而Linus作为L

Android 深度探索(卷1)HAL 与驱动开发 第三章 GIT 使用入门 心得体会

Android 深度探索(卷1)HAL 与驱动开发 第三章 GIT 使用入门 心得体会 本章主要介绍GIT的学习,以及介绍GIT用于获取诸多开源项目的源代码. 在使用GIT之前我们首先对其安装,其安装命令: #apt-get  install git #apt-get  install git-doc git-svn git-gui gitk 在Linux 下可以直接使用man命令查看指定命令的帮助文档.这对我们初学者提供了很大的帮助. Git的功能很复杂,为此这章节举例为我们演示如何创建版本库

Linux驱动开发之 三 (那些必须要了解的硬件知识 之 存储器篇)

Linux驱动开发之 三 (那些必须要了解的硬件知识 之 存储器篇) 本文重点学习存储器相关的基本知识,网络上对RAM,ROM,FLASH等有非常详细的介绍,老谢将这些知识点摘抄整理并加以注释如下.这个整理的过程也是加深记忆的过程. 1.什么是内存 在计算机的组成结构中,有一个很重要的部分,就是存储器.存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作.存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存),辅助存储器又称