XXX
分类: 嵌入式
2014-03-31 10:15:01
原文地址:start.S进一步、更详细的、深入的解释和分析 作者:自语的骆驼
#include
l config.h这个头文件在u-boot-1.1.6/include/linux目录下,执行命令
# make smdk2410_config之前,内容如下:
#ifndef _LINUX_CONFIG_H
#define _LINUX_CONFIG_H
/* #include
#endif
执行make smdk2410_config之之后,又重新生成,内容为:
/* Automatically generated – do not edit */
#incl? “config/smdk2410.h”
其中config/smdk2410.h这个文件是和开发板密切相关的,里面主要是一些系统各硬件的宏定义与设定,以及条件编译指令,对以后做移植工作至关重要!!
#include
l version.h这个头文件在u-boot-1.1.6/include/下,文件内容为:
#ifndef __VERSION_H__
#define __VERSION_H__
#incl? "version_autogenerated.h"
#endif /* __VERSION_H__ */
version_autogenerated.h这个头文件,在编译的时候会自动生成,内容为:
#define U_BOOT_VERSION “U-BOOT 1.1.6”
注:config.h和version_autogenerated.h这两个头文件具体怎么生成的,可以参考顶层 oot的Makefile。
.globl _start
l 这是在定义u-boot的启动定义入口点,汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。
l .global是GNU ARM汇编的一个伪操作,声明一个符号可被其他文档引用,相当于声明了一个全局变量,.globl和.global相同。该部分为处理器的异常处理向量表。地址范围为0x0000 0000 ~ 0x0000 0020,刚好8条指令。
l 为什么是8条指令呢?这里来算一算。首先,一条arm指令为32bit(位),0x0000 0020换算成十进制为2^5=32B(字节),而32(B) = 4 * 8(B) = 4 * 8 * 8( bit),所以刚好8条指令(一个字节Byte包含8个位bit)。
l 为了方便后面的计算,我们可以先熟练换算:
0x0000,0100 256 Byte
0x0000,1000 4KB 256*4*4B
0x0001,0000 64K 4*4*4KB
0x0010,0000 1M 64*4*4KB
0x0100,0000 16M 1*4*4MB
0x1000,0000 256M 16*4*4MB
那么0x8000 就是 8(十进制) X 0x1000 = 32K
0x400000 = 4M
下面是在汇编程序种经常会遇到的异常向量表。Arm处理器一般包括复位、未定义指令、SWI、预取终止、数据终止、IRQ、FIQ等异常,其中U-Boot中关于异常向量的定义如下:
_start: b reset
l _start 标号表明 oot程序从这里开始执行。
l b是不带返回的跳转(bl是带返回的跳转),意思是无条件直接跳转到reset标号出执行程序。b是最简单的分支,一旦遇到一个 b 指令,ARM 处理器将立即跳转到给定的地址,从那里继续执行。注意存储在分支指令中的实际的值是相对当前的 R15 的值的一个偏移量;而不是一个绝对地址。它的值由汇编器来计算,它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位。
ldr pc, _undefined_instr tion //未定义指令
ldr pc, _software_interrupt //软中断SWI
ldr pc, _prefetch_abort //预取终止
ldr pc, _data_abort //数访问终止
ldr pc, _not_used
ldr pc, _irq //中断请求IRQ
ldr pc, _fiq //快速中断FIQ
_undefined_instr tion: .word undefined_instr tion
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
l .word为ARM汇编特有的伪操作符,语法如下:
.word
作用:插入一个32-bit的数据队列。(与armasm中的DCD功能相同)
我们可以使用.word把标识符作为常量使用,例如:
Start:
valOfStart:
.word Start
这样一来,程序的开头Start便被存入了内存变量valOfStart中。即.word伪操分配了一段字内存单元(分配的单元都是字对齐的),并用伪操作中的Start进行初始化(.long和.int作用与之类似)。
.balignl 16,0xdeadbeef
l .align伪操作用于表示对齐方式:通过添加填充字节使当前位置满足一定的对齐方式。
.align {alignment} {,fill} {,max}
其中,alignment用于指定对齐方式,可能的取值为2的次幂,缺省为4。fill是填充内容,缺省用0填充。max是填充字节数最大值,假如填充字节数超过max, 就不进行对齐,例如:
.align 4
指定对齐方式为字对齐,2的4次方为16(bit),两个字节,即一个字。更详细的解释,可以参阅这篇博客:
http://zqwt.012.blog.163.com/blog/static/12044684201031102956976/
/*************************************************************************
* 下面这几句实际上说的是 oot的启动流程
*************************************************************************
* Startup Code (reset vector) //启动代码(复位向量)
* do important init only if we don't start from memory!
* //如果不从ram启,我们就在此做一些重要的初始化
* relocate armboot to ram //搬运代码到ram中去执行
* setup stack //设置堆栈
* jump to second stage //跳到第二阶段去执行board.c
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
l TEXT_BASE在开发板相关的目录中的/u-boot-1.1.6/board/smdk2410/config.mk文档中定义, 他定义了代码在运行时所在的地址, 那么_TEXT_BASE中保存了这个地址
.globl _armboot_start
_armboot_start:
.word _start
l 用.globl声明 _armboot_start 并用_start 来进行初始化。在board/u-boot.lds
中定义(是不是在编译的时候定义的???)。
l 下面这几行代码是在S3C2410开发板的链接脚本board/u-boot.lds中给出定义的。
声明_bss_start并用__bss_start来初始化,其中__bss_start定义在和板相关的u-boot.lds中。_bss_start保存的是__bss_start这个标号所在的地址, 这里涉及到当前代码所在的地址不是编译时的地址的情况,这里直接取得该标号对应的地址,不受编译时地址的影响. _bss_end也是同样的道理。
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ //如果定义了CONFIG_USE_IRQ就执行下面的代码
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/*
* the act l reset code
* 复位代码从这里开始执行
*/
reset:
l 设置CPU的状态类型为SVC特权模式(一共7种权限,这是其中的一种)。
Reset即复位,在系统中经常会用到,该操作是异常处理的第一个操作,其主要目的是设置CPU模式为SVC特权模式。在此,有必要介绍一下ARM处理器的7种工作模式。
l CPSR(当前程序状态寄存器)的低5位用于定义当前操作模式,如图示:
l 除用户模式外的其他6种模式称为特权模式 。
特权模式中除系统模式以外的5种模式又称为异常模式,即 :
FIQ(Fast Interrupt Reqst)
IRQ(Interrupt ReQst)
SVC(Supervisor)
中止(Abort)
未定义(Undefined)
l ARM处理器总共有37个32位寄存器,可以分为以下两类寄存器 :
1)31个通用寄存器(R0~R15)
R0~R15;
R13_svc、R14_svc;
R13_abt、R14_abt;
R13_und、R14_und;
R13_irq、R14_irq;
R8_frq-R14_frq。
2)6个状态寄存器
CPSR;SPSR_svc、SPSR_abt、SPSR_und、SPSR_irq和SPSR_fiq 。
l 但是这32个寄存器不能同时被访问,具体哪些寄存器是可编程访问的,取决于微处理器的工作状态以及具体的运行模式。但在任何时候,通用寄存器R0~R14、程序计数器(R15)PC、一个或两个状态寄存器都是可访问的。通用寄存器包括R0~R15(31个),可以分为3类:
1)未分组寄存器R0~R7
R0~R7是 不分组寄存器。在所有处理器模式下,未分组寄存器都指向同一个物理寄存器,也就是说它们每一个都访问的是同一个物理寄存器,它们未被系统用作特殊的用途。 但必须得注意,未分组寄存器没有被系统用于特别的用途,任何可采用通用寄存器的应用场合都可以使用相同的未分组寄存器,这样可能会造成寄存器中数据的破 坏,所以必须注意对同一寄存器在不同模式下使用时的数据保护。
2)分组寄存器R8~R14
分组寄存器R8-R12:
FIQ模式分组寄存器R8~R12。
FIQ以外的分组寄存器R8~R12。
分组寄存器R13、R14
寄存器R13通常用做堆栈指针SP。
寄存器R14用作子程序链接寄存器(Link Register-LR),也称为LR。
3)程序计数器R15
寄存器R15被用作程序计数器,也称为PC 。
R15值的改变将引起程序执行顺序的变化,这有可能引起程序执行中出现一些不可预料的结果。
ARM处理器采用多级流水线技术,因此保存在R15的程序地址并不是当前指令的地址。一些指令对于R15的用法有一些特殊的要求。在ARM状态下,R15的位[1:0]为0,位[31:2]用于保存PC;在Thumb状态下R15的位[0]为0,位[31:1]用于保存PC。
l 这里有必要了解一Thumb状态下的寄存器组织
Thumb状态下的寄存器集是ARM状态下寄存器集的子集。程序员可以直接访问8个通用的寄存器(R0~R7),程序计数器PC、堆栈指针SP、连接寄存器LR和当前状态寄存器CPSP。其实,每一种特权模式都各有一组SP,LR和SPSR。可以看看下面这张图:
l ARM程序状态寄存器
所有处理器模式下都可以访问当前的程序状态寄存器CPSR。CPSR包含条件码标志、中断禁止位、当前处理器模式以及其它状态和控制信息。
在每种异常模式下都有一个对应的物理寄存器——程序状态保存寄存器SPSR。当异常出现时,SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。
下面这张图是CPSR和SPSR的格式:
mrs r0,cpsr //把当前程序状态寄存器中的数据拷贝给r0
bic r0,r0,#0x1f // r0和0x1f(00011111)的反码进行位与,目的是给r0的后5位清零
orr r0,r0,#0xd3
/* r0和0xd3 (11010011)进行位或,最后得到r0=11010011,
* 目的是设置r0的后5位为10011,让ARM进入SVC特权模式 */
msr cpsr,r0
/* 把当前r0中保存的CPU的状态拷贝给cpsr,让ARM进入SVC特权模式 */
l MRS {} Rd, CPSR|SPSR
这条指令的意思是将CPSR|SPSR传送到Rd。我们可以使用这两条指令将状态寄存器传送到一般寄存器,只修改该寄存器必要的位,再将结果传送回状态寄存器,这样能够最好地完成对CRSP或SPSR的修改。
MSR {} CPSR_|SPSR_,Rm 或是 MSR {} CPSR_f|SPSR_f,#
MRS和MSR配合使用,作为更新PSR的“读取--修改--写回”序列的一部分
bic r0,r1,r2 ;r0:=r1 and not r2
orr ro,r1,r2 ;r0:=r1 or r2这几条指令执行完毕后,进入SVC32模式,该模式主要用来处理软件中断(SWI)
我们可以在S3C2410x芯片手册中研读这一段文字:
THE PROGRAM STATUS REGISTERS
The ARM920T contains a Current Program Status Register(CPSR), plus five Saved Program Status Registers(SPSRs) for use by exception handlers.
ARM920T具有一个当前程序状态寄存器(CPSR),还有5个保存程序状态寄存器(SPSR)供异常处理程序使用。
These register's functions are:
l Hold information about the most recently performed ALU operation
保存有算术逻辑单元最近的操作
l Control the enabling and disabling of interrupts
控制着开启和关闭中断
l Set the processor operating mode
设置处理器的操作模式
图2 Program Status Register Format
那么,现在我们很容易理解:11010011 表示CPU禁止任何中断、仍处于arm状态、切换到特权模式
l 下面这几行代码的作用是定义了一些宏,给寄存器赋值,以后会用到
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTS MSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
l 这里就是所谓的关闭看门狗。
看门狗即watchdog timer,是一个定时器电路,一般有一个输入叫喂狗,一个输出叫MCU(Micro Controller Unit多点控制单元)的RST端(复位端)。MCU正常工作的时候,每隔一段时间输出一个信号到喂狗端,给WDT(watchdog timer abbreviation)清零,如果超过规定的时间不喂狗(一般在程序跑飞时,会发生这种情况,此时WDT规定的时间被超过),RST就给出一个复位信号到MCU,然后MCU复位。很明显,看门狗的作用就是防止程序发生死循环,或者说程序跑飞。寄存器具体怎么设置,可以根据S3C2410的用户手册来实现。
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON //还是把寄存器pWTCON的地址装载到r0中
mov r1, #0x0 //给r1清零
str r1, [r0]
/* 把r1中的数据(全0)装载(存储)到寄存器[r0]所包含的有效地址,即给寄存器WTCON清零,目的是关闭看门狗 */
l 禁止所有中断
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK //是不是把INTMSK的地址0X4A000008存储在r0中
str r1, [r0] //把r1中的数据存储在寄存器[r0]所包含的有效地址,屏蔽所有中断源
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff // 0x3ff =00111111
ldr r0, =INTS MSK
str r1, [r0]
# endif
这是S3C2410参考手册中列出的中断控制器(Interrupt Controller)表格:
l 设置时钟频率
S3C2410用户手册推荐FLCKHCLKPCLK=124,其中FCLK默认是120MHz,通常FCLK用于CPU,HCLK用于AHB总线,PCLK用于APB总线,具体实现如下:
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3 //0011 HDIVN=1;PDIVN=1
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
l /* we do sys-critical inits only at reboot, not when booting from ram! */
我们只在系统reboot的时候做一些至关重要的系统初始化,而不在从sram启动的时候做这件事情。
B 转移指令,跳转到指令中指定的目的地址,BL 带链接的转移指令,像B相同跳转并把转移后面紧接的一条指令地址保存到链接寄存器LR(R14)中,以此来完成子程式的调用。
该语句首先调用cpu_init_crit进行CPU的初始化,并把下一条指令的地址保存在LR中,以使得执行完后能够正常返回。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //跳转
#endif
l 下面这段代码的作用是所谓搬运代码,即重定向
#ifndef CONFIG_SKIP_RELOCATE_ OOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
//_start为当前程序执行的位置(地址)
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
//_TEXT_BASE为从RAM中开始执行的基地址,其实我们最终
// 要把代码拷贝到_TEXT_BASE上去
cmp r0, r1 /* don't reloc during debug */
//如果当前程序执行的地址等于从RAM中开始执行的基地址了(该搬运了吧),那么
//跳转到堆栈设置(搬运之前,首先得设置堆栈)
beq stack_setup //先去设置堆栈,再回来搬运代码(重定向)
ldr r2, _armboot_start
//_armboot_start 为 oot的起始地址(_armboot_start的值就等于_start)
ldr r3, _bss_start
// _bss_start为 oot的结束地址
s r2, r3, r2 /* r2 <- size of armboot */
//让r3和r2做算数减法的目的是计算出 oot镜像的大小r2
add r2, r0, r2 /* r2 <- source end address */
//r2(_bss_start) = r0(_start)+ r2( oot镜像大小)
l 这几行代码是一个循环,做的事儿就是所谓的搬运代码
l 这里有条指令ldmia,解释如下:
多寄存器传输——数据块操作
多寄存器传输——堆栈操作
数据块操作和堆栈操作的比较
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0](_start) */
stmia r1!, {r3-r10} /* copy to target address [r1](_TEXT_BASE) */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop //Branch if Less than or Eq l
#endif /* CONFIG_SKIP_RELOCATE_ OOT */
l 设置堆栈(设置SDRAM的基地址_TEXT_BASE = 0x33F80000)。
这里需要注意的是堆栈在代码段的下方!宏CFG_MALLOC_LEN和CFG_GBL_DATA_SIZE在目标板的头文件中定义./u-boot/incl?/configs/smdk2410.h,可以按下面这张图来理解:
所谓重定向后的u-boot |
分配IRQ和FRQ的栈空间 |
动态内存区长度 |
全局数据的大小 |
代码段的开始地址 |
stack_setup:
ldr r0, _TEXT_BASE
/* upper 128 KiB: relocated oot 这句话意思是,_TEXT_BASE上面是128 KiB重定位的u-boot */
s r0, r0, #CFG_MALLOC_LEN
/* malloc area _TEXT_BASE向下是内存分配空间 */
s r0, r0, #CFG_GBL_DATA_SIZE
/* bdinfo 然后是bdinfo结构体地址空间 */
#ifdef CONFIG_USE_IRQ
s r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
s sp, r0, #12
/* leave 3 words for abort-stack 给堆栈异常留3个字的空间 */
l 这里是所谓BSS段清零
BSS(Block Started by Symbol)段是可执行文件中的一种数据段。通常ARM编译器生成的可执行文件由两部分数据组成,分别是代码段和数据段。代码段又分为可执行代码段(text)和只读数据段(rodata);数据段又分为初始化数据段(data)和未初始化数据段(bss)。
clear_bss:
ldr r0, _bss_start /* 找到bss段的开始地址 */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... 用一个循环,做清零工作 */
add r0, r0, #4
cmp r0, r1
ble clbss_l
#if 0
/* try doing this st? after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END st? after relocation */
#endif
l 跳转到start_armboot函数入口,_start_armboot保存函数入口指针
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/*start_armboot()在lib_arm/board.c中定义,它类似于Linux内核的start_kernel(),它们都是一种系统初始化的接口函数:在start_kernel()中集中完成了内核几乎所有资源的初始化,包括CPU相关的资源和外设接口等;而在start_armboot()中也要完成一些初始化工作。*/
/*
*************************************************************************
*
* CPU_init_critical registers
* CPU_init_critical临界区寄存器
* setup important registers
* setup memory timing
* 此处要设置一些重要的寄存器,并进行内存测试。*/
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
l 设置CP15寄存器
CP15寄存器是系统控制协处理寄存器,用于连接在内存中的也表描述符,此外还用于决定对MMU的操作。设置CP15寄存器的目的是失效Icache(指令cache)和Dcache(数据cache),然后禁止MMU和cache。
cpu_init_crit:
/*
* flush v4 I/D caches
使I/D caches失效
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU st? and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
在搬运代码之前,我们必须设置好sram的定时
* because memory timing is board-dependend, you will
因为sram定时是依赖于具体的开发板的,你将会
* find a lowlevel_init.S in your board directory.
在自己的开发板目录board目录下发现一个
lowlevel_init.S文件。实际上,这个文件所做的工作主要就是内存的初始化。
*/
l 初始化RAM时钟。因为内存时钟是依赖开发板硬件的,所以在board的相应目录下可以找到memsetup.s文件
mov ip, lr //lr为链接寄存器
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* Interrupt handling 中断处理
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
s sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
s r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
s r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
s sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
s s pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
s r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
s r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers 中断处理程序
*/
.align 5
undefined_instr tion:
get_bad_stack
bad_save_user_regs
bl do_undefined_instr tion
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif