UBoot的Start.S分析

 

UBoot的Start.S



2010-07-19 04:12:36|  分类:Linux初探|  标签:|字号 订阅

#include < config.h >  
#include < version.h >  
#include < status_led.h >  
 
/*  
 *************************************************************************  
 *  
 * Jump vector table as in table 3.1 in [1]   A
 *  
 *************************************************************************  
 */ 
 
/*  
 * 指定入口函数   A
 */ 
.globl _start   /*使用.globl来声明全局标志. 声明的该标号_start可以被外部使用*/ 
_start:   
    b       start_code          /*start_code子程序(以前叫reset)用于将CPU重置到Supervisor模式下,并禁止IRQ和FIQ中断请求*/ 
    ldr pc, _undefined_instruction  /*让pc指向_undefined_instruction地址处*/ 
    ldr pc, _software_interrupt     /*让pc指向_software_interrupt地址处*/ 
    ldr pc, _prefetch_abort         /*让pc指向_prefetch_abort地址处*/ 
    ldr pc, _data_abort             /*让pc指向_data_abort地址处*/ 
    ldr pc, _not_used               /*让pc指向_not_used地址处*/ 
    ldr pc, _irq                    /*让pc指向_irq地址处*/ 
    ldr pc, _fiq                    /*让pc指向_fiq地址处*/ 
 
      
_undefined_instruction: .word undefined_instruction /*在当前标号_undefined_instruction所在的地址处放入四字节的数据,这个数据就是undefined_instruction标号的地址.意思就是说在当前_undefined_instruction对应的地址中放的是undefined_instruction的地址*/ 
_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                   /*道理同上*/ 
 
    .balignl 16,0xdeadbeef  /*(如果我没有记错的话,ARM中是32位寻址方式,即地址是每32位为一单元,例如PC当前为0x0,pc下一个就是0x04,8位对应一个字节,也对应一个地址,实际上就是一个地址对应一个字节内容. 只是在寻址时是以32位为单位进行的).本句的目的是保证以下语句所对应的存储空间地址是以16个字节来对齐的.如果当前的地址正好是16的倍数的话就什么也不用管了,如果恰好当前地址还差四字节就正好是16的倍数,那么此时就使用0xdeadbeef死牛(外国人真有意思. 0xdeadbeef正好占四个字节)来一起填充进去,以便补齐地址符合16的倍数的要求.  [这里还需要深入地调试验证一下]*/ 
 
 
/*  
 *************************************************************************  
 *  
 * Startup Code (called from the ARM reset exception vector)  
 *  
 * do important init only if we don't start from memory!  
 * relocate armboot to ram  
 * setup stack  
 * jump to second stage  
 *  
 *************************************************************************  
 */ 
/*将来该标记用来作为代码段的开始处*/ 
_TEXT_BASE:               
    .word   TEXT_BASE   /*在当前标号_TEXT_BASE所在的地址处放入TEXT_BASE标号的地址*/ 
 
/*预处理标号 目的:让_armboot_start指向_start标号所在的地址*/       
.globl _armboot_start   /*全局声明标志. 声明的该标号_armboot_start可以被外部使用*/ 
_armboot_start:  
    .word _start        /*在当前标号_armboot_start所在的地址处放入_start标号的地址*/ 
 
/*  
 * These are defined in the board-specific linker script.  
 */ 
/*预处理标号 目的:让_bss_start指向__bss_start标号所在的地址*/   
.globl _bss_start       /*全局声明标志. 声明的该标号_bss_starts可以被外部使用*/ 
_bss_start:  
    .word __bss_start   /*在当前标号_bss_start所在的地址处放入__bss_start标号的地址*/ 
 
/*预处理标号 目的:让_bss_end指向_end标号所在的地址*/       
.globl _bss_end         /*全局声明标志. 声明的该标号_bss_end可以被外部使用*/ 
_bss_end:  
    .word _end          /*在当前标号_bss_end所在的地址处放入_end标号的地址*/ 
 
#ifdef CONFIG_USE_IRQ   /*如果定义了CONFIG_USE_IRQ*/  
/* IRQ stack memory (calculated at run-time) */ 
.globl IRQ_STACK_START  /*预处理标号 目的:让IRQ_STACK_START指向地址0x0badc0de(这个需要根据硬件更改)*/ 
IRQ_STACK_START:  
    .word   0x0badc0de  
 
/* IRQ stack memory (calculated at run-time) */ 
/*预处理标号 目的:让FIQ_STACK_START指向地址0x0badc0de(这个需要根据硬件更改)*/ 
.globl FIQ_STACK_START  
FIQ_STACK_START:  
    .word 0x0badc0de  
#endif  
 
 
/*  
 * the actual start code  //真正的启动代码  
 */ 
start_code:  
    /*  
     * set the cpu to SVC32 mode    //设置CPU的工作模式为SVC32模式.  
     */ 
    mrs r0,cpsr         /*将cpsr寄存器中的值转到r0寄存器中*/ 
    bic r0,r0,#0x1f /*目的是将r0的bit[4:0]清0,其他位不变,然后再放入r0中. 需要注意的是在的值的反码按位做逻辑与后,再保存到目标寄存器"的说法是不正确的.估计是直译并没有验证而写出来的内容. 简记:bic的功能,想将源寄存器(中间的寄存器)中的数的某位清0,就将最右边的数的某位置1即可,然后再送往目的寄存器(最左边的寄存器)就行了*/ 
    orr r0,r0,#0xd3 /*0xD3=110[1 0011] | CPSR的位定义见2410的手册P48 Figure2-6 |  此处目的:是在r0寄存器上面运行后的数的基础上,将其对应的bit[7,6,4,1,0]位都置1. 看点:bit[4:0]=10011就是让S3C2410工作在Supervisor模式下,bit[7:6]=11是禁止IRQ和FIQ中断请求*/ 
    msr cpsr,r0     /*将上面处理后的r0中的值再转到CPSR寄存器中*/ 
 
    /*bl coloured_LED_init  //目的是完成彩色LED的初始化.我的板没有,也就不用了*/ 
    /*bl red_LED_on         //目的是完成红色LED的显示.我的板没有,也就不用了*/ 
 
//如果定义了CONFIG_AT91RM9200DK,CONFIG_AT91RM9200EK,CONFIG_AT91RM9200DF中的任意一个,就会执行其中的语句.我才不用他们呢 呵呵  
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)  
    /*  
     * relocate exception table  
     */ 
    ldr r0, =_start         /*将标号_start对应的值放入r0寄存器中*/ 
    ldr r1, =0x0            /*将立即数放入r1寄存器中*/ 
    mov r2, #16             /*将立即数0x10放入r2寄存器中*/ 
copyex:  
    subs    r2, r2, #1      /*将r2中的值-1后,再放入r2里面。 S:决定本条指令是否影响CPSR的条件标志位,这里放上s就是说该指令会影响更新CPSR的条件标志位,如没有s就不更新CPSR的条件标志位了*/ 
    ldr r3, [r0], #4        /*将r0中值对应的地址中的数据放入r3,然后r0中值+4再给r0.也就是两步:r3<-[r0], r0<-r0+4*/ 
    str r3, [r1], #4        /*将r3中值存入r1中所对应的地址中. 然后r1中值+4再给r1. 同样两步:r3->[r1], r1<-r1+4  实际上面一句和本句的目的无非就是把r0对应地址中的数据转给r1对应的地址中*/ 
    bne copyex  /*这里实际上是在测试"subs r2,r2,#1",目的是看r2有没有递减16次,没有就返回copyex处继续执行.因为当r2减1到0后,0-1=-1肯定就影响到了标志位,而bne测试的就是条件标志位^_^*/ 
#endif  
 
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)  
/* turn off the watchdog 关闭看门狗*/ 
 
# if defined(CONFIG_S3C2400)        /*如果定义了CONFIG_S3C2400,就执行下面的语句*/  
#  define pWTCON        0x15300000  
#  define INTMSK        0x14400008  /* Interupt-Controller base addresses */  
#  define CLKDIVN   0x14800014  /* clock divisor register */  
#else                               /*如果定义了CONFIG_S3C2410,也就属于其他情况了,就执行下面的语句*/  
#  define pWTCON        0x53000000  /*"WATCHDOG TIMER CONTROL REGISTER看门狗定时器控制寄存器"的地址0x53000000 见S3C2410手册P408*/  
#  define INTMSK        0x4A000008  /*"INTERRUPT MASK REGISTER中断屏蔽寄存器"的地址:0x4A000008 见S3C2410手册P333*/  
#  define INTSUBMSK 0x4A00001C      /*针对INTMAK具体化的一个中断请求屏蔽寄存器,其地址0x4A00001C 见S3C2410手册P340*/  
#  define CLKDIVN   0x4C000014      /*CPU时钟分频控制寄存器,地址0x4C000014 见S3C2410手册P213*/  
# endif  
 
    ldr     r0, =pWTCON             /*将看门狗寄存器的地址转给r0*/ 
    mov     r1, #0x0                /*将0x0放入r1中*/ 
    str     r1, [r0]                /*将r1寄存器中的值存入r0所表示的地址中,此时r0中的值意思是个地址 实际上pWTCON的bit[5]为0,就已经关闭看门狗了^_^*/ 
 
    /*  
     * mask all IRQs by setting all bits in the INTMR - default  
     */ 
    mov r1, #0xffffffff             /*将立即数0xFFFFFFFF放入r1中*/ 
    ldr r0, =INTMSK                 /*将INTMSK所代表的值放入r0中 实际上这句就等于"ldr r0, #0x4A000008"*/ 
    str r1, [r0]                    /*将r1寄存器中的值存入r0所表示的地址中,就是把0xFFFFFFFF存入INTMSK寄存器中关闭所有中断*/ 
# if defined(CONFIG_S3C2410)        /*如果定义的是CONFIG_S3C2410,就执行此if中的句子*/  
    ldr r1, =0x3ff                  /*将立即数0x3FF放入r1中*/ 
    ldr r0, =INTSUBMSK              /*将INTSUBMSK所代表的值放入r0中 实际上这句就等于"ldr r0, =0x4A00001C"或"ldr r0,#4A00001C*/ 
    str r1, [r0]                    /*将r1寄存器中的值存入r0所表示的地址中,就是把0x3FF存入INTSUBMSK寄存器中,以便屏蔽INTSUBMSK的bit[9:0]对应的中断请求. 问题是为什么没有屏蔽bit[10]对应的那个INT_ADC对应的中断请求,估计是认为不可能发生吧 呵呵*/ 
# endif  
 
    /* FCLK:HCLK:PCLK = 1:2:4 */ 
    /* default FCLK is 120 MHz ! 缺省的系统主频是120MHz*/ 
    ldr r0, =CLKDIVN    /*将CLKDIVN所代表的值放入r0中 实际上这句就等于"ldr r0, =0x4C000014" 像这种用法实际上就是为将来r0中的值作为地址来使用做好铺垫*/    
    mov r1, #3          /*将立即数0x03放入r1中*/ 
    str r1, [r0]        /*将r1中的值放入r0所表示的地址中,就是把0x03存入CLKDIVN寄存器中,以便让bit[1:0]=11,最终实现FCLK:HCLK:PCLK = 1:2:4的比例^_^*/ 
#endif  /* CONFIG_S3C2400 || CONFIG_S3C2410 */  
 
    /*  
     * we do sys-critical inits only at reboot,  
     * not when booting from ram!  
     * 我们仅仅在重启时对系统做临界初始化,而不在RAM运行中完成.   
     */ 
#ifndef CONFIG_SKIP_LOWLEVEL_INIT   /*如果定义了CONFIG_SKIP_LOWLEVEL_INIT,就跳到cpu_init_crit去执行其对应的子程序*/  
    bl  cpu_init_crit  
#endif  
 
 
#ifndef CONFIG_AT91RM9200   /*如果没有定义CONFIG_AT91RM9200,就编译下面的内容*/  
#ifndef CONFIG_SKIP_RELOCATE_UBOOT      /*如果没有定义了CONFIG_SKIP_RELOCATE_UBOOT,就编译下面的内容*/   
//调试阶段的代码是直接在RAM中运行的,而最后需要把这些代码固化到Flash 中,因此U-Boot需要
自己从Flash 转移到 
 //RAM 中运行,这也是重定向的目的所在。 
 //通过adr 指令得到当前代码的地址信息:假如U-boot是从RAM开始运行,则从adr,r0,_start 得到
的地址信息为 
 //r0=_start=_TEXT_BASE=TEXT_BASE=0xa3000000; 假如U-boot 从Flash 开始运行,即从处
理器对应的地址运行, 
 //则r0=0x0000,这时将会执行copy_loop标识的那段代码了。 
 // _TEXT_BASE  定义在board/smdk2410/config.mk 中

relocate:               /* relocate U-Boot to RAM   重新部署U-Boot到内存RAM中   */ 
/*注意这里的r0中的值要保留住,后面搬运要用.作为起始地址_start*/    
    adr r0, _start      /* 将_start对应的地址放入r0中,这里没必要使用ldr,因为_start标号就在眼前,呵呵 */ 
    ldr r1, _TEXT_BASE      /*将标号_TEXT_BASE所对应的地址放入r1中*/ 
    cmp     r0, r1                  /*让r0中的值减去r1中的值,从而影响CPSR中对应的条件标志位. 如果r0的值减去r1的值为0,那么就使CPSR中的bit[30]对应的Z位置1,因为Z=1表示运算的结果为零,Z=0表示运算的结果不为零. 见电子书 */ 
    beq     stack_setup /* 判断当前CPSR中的条件标志位,如果Z=1,就跳转到stack_setup标号处去执行. 实际上这里就是说如果r0和r1中的值相等的话,就跳转到stack_setup标号处去执行. */ 
 
    ldr r2, _armboot_start  /* 将_armboot_start标号对应的地址放入r2中 */ 
    ldr r3, _bss_start  /* 将_bss_start标号对应的地址放入r3中 */ 
    sub r2, r3, r2      /* 将r3中的值-r2中的值,然后将差值放入r2中 */ 
    add r2, r0, r2      /* 将r0中的值+r2中的值,然后将和放入r2中. 实际上就是为了得到: [_start标号所在的地址+(_bss_start标号所在的地址-_armboot_start标号所在的地址)]. 这里就是找到代码的结束地址放入r2中了         */ 
 
/*简记:将r0所表示的地址处的代码搬运到r1所表示的地址处. r0中的值就是代码的开始地址,r2中的值就是代码的结束地址,r1就是代码被搬运的目标地址*/ 
copy_loop:  
    ldmia   r0!, {r3-r10}       /* 将r0对应地址中的值先放入r3中,然后将r0中地址递增4字节后放入r0中再将对应地址中的数据给r4,以此类推直到放入r10中.共递减了七次    */ 
    stmia   r1!, {r3-r10}       /* 将r3中的数据存入r1对应的地址中,然后r1对应的地址递增4字节后放入r0中(假设为a)处,再将r4中的数据存入地址a处,依次类推直到r10中的数据也放入相应的地址处.    关于IA的含义事后递增,参见*/ 
    cmp r0, r2      /* until source end addreee [r2] | 这里直接简记成:比较r0中值和r2中的值.    */    
    ble copy_loop   /* 这里直接简记成:如果上句r0中的值<=r2中的值的话,就跳转到copy_loop继续搬运代码. le的条件助记符的含义参见    */ 
#endif  /* CONFIG_SKIP_RELOCATE_UBOOT */  
#endif  
 
 
/* Set up the stack | 设置堆栈    */ 
stack_setup:  
    ldr r0, _TEXT_BASE      /*将_TEXT_BASE标号所在地址放入r0中 */ 
    sub r0, r0, #CFG_MALLOC_LEN /*将r0中值减去CFG_MALLOC_LEN代表的立即数后放入r0中.这个常量的具体定义在文件board/fads.h */ 
    sub r0, r0, #CFG_GBL_DATA_SIZE /*再将r0中值减去CFG_GBL_DATA_SIZE代表的立即数后放入r0中*/ 
#ifdef CONFIG_USE_IRQ/*如果定义了CONFIG_USE_IRQ,就再将r0中值减去CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ代表的立即数后放入r0中*/  
    sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)  
#endif  
    sub sp, r0, #12     /*继续将r0中的值减去12后放入sp里面.即保留3个字(12个字节的长度. 因为这里说的一个字是32位,占4个字节)  */ 

{

 ;获取_TEXT_BASE
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */
 ;获取分配区域起始指针,CFG_MALLOC_LEN=128*1024+CFG_ENV_SIZE=128*1024+0x10000=192K
 sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
 ;另外分配128bytes来存储开发板信息
 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 ;再减去12bytes用于栈起点
}
  




http://www.360doc.com/content/10/1009/14/3038654_59581790.shtml
http://www.360doc.com/content/12/0830/20/8731062_233256410.shtml


clear_bss:/*clear_bss标号和clbss_1标号的功能:完成BSS段的清零*/ 
    ldr r0, _bss_start      /* 将_bss_start标号所对应的位置放入r0中 */ 
    ldr r1, _bss_end        /* 将_bss_end标号所对应的位置放入r1中 */ 
    mov r2, #0x00000000     /* 将立即数0x0放入r2中 */ 
 
clbss_l:  
    str r2, [r0]        /*再将r2中的数转到r0所表示的地址处 */ 
    add r0, r0, #4      /* 将r0中的值+4后,再放入r0. 实际上就是让r0中表示的地址下移四个字节*/ 
    cmp r0, r1          /* 简记:比较r0的值和r1的值 */ 
    ble clbss_l         /* 简记:如果上句r0的值<=r1的值,就继续跳到clbss_1去完成对BSS段清零的功能*/ 
 
    ldr pc, _start_armboot  /* 让pc指向_start_armboot继续执行*/ 
 
_start_armboot: .word start_armboot/*在当前标号_start_armboot所在的地址处放入四字节的数据,这个数据就是start_armboot标号的地址.意思就是说在当前_start_armboot对应的地址中放的是start_armboot的地址. 将汇编中的_start_armboot标号映射到C语言中start_armboot()函数.该函数位于lib_arm目录下的文件board.c中*/ 
 
 
/*  
 *************************************************************************  
 *  
 * CPU_init_critical registers  
 *  
 * setup important registers  
 * setup memory timing  
 *  
 *************************************************************************  
 */ 
 
/*  
 *这里的话,一句两句说不清楚,怕把你和我都搞晕了.还是不说的啦,只要知道下面一句的功能就行了.  
 *功能:设置CP15寄存器 这里完成的功能:失效Icache和Dcache,禁能MMU和cache  
 */ 
#ifndef CONFIG_SKIP_LOWLEVEL_INIT   /*如果没有定义CONFIG_SKIP_LOWLEVEL_INIT,就执行里面的语句. 之所以我还废话,只是可以方便你可以随时随地断章的来阅读该部分,凡事有始有终嘛*/  
cpu_init_crit:  
    /*  
     * flush v4 I/D caches  | 失效指令cache和数据cache  
     */ 
    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 stuff and caches |   禁能MMU和cache  
     */ 
    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  
     * because memory timing is board-dependend, you will  
     * find a lowlevel_init.S in your board directory.  
     */ 
    mov ip, lr  
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)  
 
#else  
    bl  lowlevel_init  
#endif  
    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  /* 将sp指向地址-72,然后再给sp. 也就是就空出72个字节. 72=4*13 */ 
    sub sp, sp, #S_FRAME_SIZE  
    stmia   sp, {r0 - r12}      /* 将r0~r12共13个寄存器的数据都转到sp上句空出的空间里面.具体过程是:先将r0中断数据转到sp指向的地址处,然后将sp调整到sp+4位置处,再将r1中的数据转过去,以此类推... */ 
    ldr r2, _armboot_start      /* 将_arboot_start标号对应的地址放入r2中 */ 
    sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)  /* 将r2中的值-CONFIG_STACKSIZE+CFG_MALLOC_LEN,再放入r2*/ 
    sub r2, r2, #(CFG_GBL_DATA_SIZE+8)   /* 再将r2中的值-CFG_GBL_DATA_SIZE+8后放入r2中 */ 
    ldmia   r2, {r2 - r3}       /* 将r2中的值(表示的地址)对应的开始的2个地址数据转给r2,r3中 */ 
    add r0, sp, #S_FRAME_SIZE       /* 将sp+(S_FRAME_SIZE的值)放入r0中 */ 
 
    add r5, sp, #S_SP           /* 将sp+(S_SP的值)放入r5中 */ 
    mov r1, lr                  /* 将lr所代表的值放入r1中 */ 
    stmia   r5, {r0 - r3}       /* 将r0~r3中的数据放入r5所表示的地址处 */ 
    mov r0, sp                  /* 将sp中的值放入r0中 */ 
    .endm  
 
    .macro  irq_save_user_regs  
    sub sp, sp, #S_FRAME_SIZE   /* 将sp指向地址-72,然后再给sp. 也就是就空出72个字节. 72=4*13 */ 
    stmia   sp, {r0 - r12}      /* 将r0~r12共13个寄存器的数据都转到sp上句空出的空间里面.具体过程是:先将r0中断数据转到sp指向的地址处,然后将sp调整到sp+4位置处,再将r1中的数据转过去,以此类推... */ 
    add     r7, sp, #S_PC       /* 将sp+S_PC的值放入r7中 */ 
    stmdb   r7, {sp, lr}^       /*DB:表示先减1,再传送.结合本句就是:将让r7中的值减1(因为r8所对应的地址将在下一句用来存放lr的数据^_^),再把sp放入此时r7所表示的地址处,然后r7中的值再减1,再把lr的值放入此时r7表示的地址处. 符号^的意思:因为当前寄存器中没有涉及R15,在STM指令中它的意思就是告诉CPU,我们操作的是用户模式<见 ARM体系结构与编程 P25>下的寄存器r7. 简记:将sp和lr中的值保护起来*/ 
    str     lr, [r7, #0]        /* 将lr中的数据存入r7所表示的地址中. 实际上是r7中的值+0,还是r7中的数 */ 
    mrs     r6, spsr            /* 将spsr中数据放入r6中 */ 
    str     r6, [r7, #4]        /* 将r6中的数据存入(r7中的值+4)所表示的地址中 注意:这里可没有改变r7中原有的值哟*/ 
    str     r0, [r7, #8]        /* 将r0中的数据存入(r7中的值+8)所表示的地址中 */ 
    mov r0, sp                  /* 将sp中的值放入r0中 */ 
    .endm  
 
    .macro  irq_restore_user_regs   /* 恢复 */ 
    ldmia   sp, {r0 - lr}^          /* 将sp所对应的堆栈的数据送到r0~r14个寄存器中,因为lr就是r14. IA:表示先传送,再递增地址(4字节递增 32位嘛) ^:这里不涉及R15,所以只是想告诉CPU使用的是用户模式下的寄存器r0~r14*/ 
    mov r0, r0                      /* 这句干嘛?  */ 
    ldr lr, [sp, #S_PC]             /* 将sp的值(就是堆栈里面的地址)+S_PC后的新地址(当然还是堆栈里面)的数据放入lr中*/ 
    add sp, sp, #S_FRAME_SIZE       /* 将sp的值+S_FRAME_SIZE后,再放入sp里面 */ 
    subs    pc, lr, #4              /*将lr中的值-4后,放入pc里面。 S:决定本条指令是否影响CPSR的条件标志位,这里放上s就是说该指令会影响更新CPSR的条件标志位,如没有s就不更新CPSR的条件标志位了*/ 
    .endm  
 
    .macro get_bad_stack              
    ldr r13, _armboot_start     /* 将标号_armboot_start的对应的地址放入r13中 */ 
    sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) /* 将r13中的值-(CONFIG_STACKSIZE+CFG_MALLOC_LEN)后,再放入r13中 */ 
    sub r13, r13, #(CFG_GBL_DATA_SIZE+8) /* 再将r13中的值-(CFG_GBL_DATA_SIZE+8)后,再放入r13中 */ 
 
    str lr, [r13]           /* 将lr中的数据存入此时r13中所表示的地址中 */ 
    mrs lr, spsr            /* 将spsr中的数据放入lr中 */ 
    str     lr, [r13, #4]   /* 再将lr中的数据放入(r13中值+4)所表示的地址中 */ 
 
    mov r13, #MODE_SVC          /* 将立即数MODE_SVC放入r13中 */ 
    @ msr   spsr_c, r13         /* 本行注释掉了 */ 
    msr spsr, r13               /* 将r13中的数据放入spsr中 */ 
    mov lr, pc                  /* 将pc中的值放入lr中 */ 
    movs    pc, lr              /* 将lr中的值放入pc中 s:决定指令的操作将会影响CPSR中条件标志位的值. 不明白这里仅仅是传送没有运算,怎么会影响CPSR中的条件标志位的值呢? */ 
    .endm  
 
    .macro get_irq_stack        /*设置IRQ*/ 
    ldr sp, IRQ_STACK_START     /* 将IRQ_STACK_START标号对应的地址放入sp中 */ 
    .endm  
 
    .macro get_fiq_stack        /*设置FIQ*/ 
    ldr sp, FIQ_STACK_START     /* 将FIQ_STACK_START标号对应的地址放入sp中 */ 
    .endm  
 
/*  
 * exception handlers  
 */ 
    .align  5   /* 下面的一句使用2的5次方=32位对齐,即四字节对齐. 也就是保证每句占用四字节的空间 */ 
undefined_instruction:  /* 未定义指令 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_undefined_instruction    /* 跳转到C语言中的函数do_undefined_instruction() 位于本目录下的interrupts.c*/ 
 
    .align  5  
software_interrupt:     /* 软件中断 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_software_interrupt       /* 跳转到C语言中的函数do_software_interrupt() 位于本目录下的interrupts.c*/ 
 
    .align  5  
prefetch_abort:         /* 预取指令 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_prefetch_abort           /* 跳转到C语言中的函数do_prefetch_abort() 位于本目录下的interrupts.c*/ 
 
    .align  5  
data_abort:             /* 数据 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_data_abort   /* 跳转到C语言中的函数do_data_abort() 位于本目录下的interrupts.c*/ 
 
    .align  5  
not_used:               /* 保留 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_not_used     /* 跳转到C语言中的函数do_not_used(). 位于本目录下的interrupts.c*/ 
 
#ifdef CONFIG_USE_IRQ   /* 如果定义了CONFIG_USE_IRQ 也就是说你想使用IRQ中断 */  
 
    .align  5  
irq:                    /* IRQ */ 
    get_irq_stack       /* 调用宏get_irq_stack */ 
    irq_save_user_regs  /* 调用宏irq_save_user_regs */ 
    bl  do_irq          /* 跳转到C语言中的函数do_irq(). 位于本目录下的interrupts.c*/ 
    irq_restore_user_regs   /* 调用宏irq_restore_user_regs */ 
 
    .align  5  
fiq:                    /* FIQ*/ 
    get_fiq_stack       /* 调用宏get_fiq_stack */ 
    /* someone ought to write a more efficiency fiq_save_user_regs 意思:应该有人写一个更有效的fiq_save_user_regs. 因为目前对于IRQ和FIQ都使用的同一个irq_save_user_regs*/ 
    irq_save_user_regs  /* 调用宏irq_save_user_regs */ 
    bl  do_fiq          /* 跳转到C语言中的函数do_fiq(). 位于本目录下的interrupts.c*/ 
    irq_restore_user_regs   /* 调用宏irq_restore_user_regs */ 
 
#else  
 
    .align  5  
irq:  
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_irq          /* 跳转到C语言中的函数do_irq(). 位于本目录下的interrupts.c*/ 
 
    .align  5  
fiq:  
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_fiq          /* 跳转到C语言中的函数do_fiq(). 位于本目录下的interrupts.c*/ 
 
#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值