U-BOOT源码中start.S分析

来源:互联网 发布:淘宝瑞士军刀旗舰店 编辑:程序博客网 时间:2024/05/10 15:06
在嵌入式设备中需要一段代码来引导系统的启动,就像windows系统是通过BIOS来引导启动的,嵌入式设备中的引导代码叫做Bootloader,Bootloader有很多中,其中U-BOOT是最常用的一种。通过顶层的Makefile分析,U-BOOT是从start.o开始执行的,所以有必要分析start.S的代码,start.S的代码不是很长,主要完成了CPU的模式设置、关看门狗、关闭中断,设置堆栈、清楚bss段、异常中断的处理、将flash中剩余的代码复制到SDRAM中以及跳到第二部分C代码的运行。其源代码如下:
   

#include <config.h>
#include <version.h>

.globl _start
_start: b       reset                                                         /*跳到复位,主要是设置CPU的模式*/
 ldr pc, _undefined_instruction
 ldr pc, _software_interrupt
 ldr pc, _prefetch_abort
 ldr pc, _data_abort
 ldr pc, _not_used
 ldr pc, _irq
 ldr pc, _fiq
_undefined_instruction: .word 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

/*
 *************************************************************************
 *
 * Startup Code (reset 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
.globl _armboot_start
_armboot_start:
 .word _start
/*
 * These are defined in the board-specific linker script.
 */
.globl _bss_start
_bss_start:
 .word __bss_start
.globl _bss_end
_bss_end:
 .word _end
#ifdef 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 actual reset code
 */
reset:               /*复位代码*/
 /*
  * set the cpu to SVC32 mode                                                   /*设置CPU为管理模式,即svc模*/
  */
 mrs r0,cpsr                                                                                  /*MRS 的英文缩写是  Move to Register from State register*/
 bic r0,r0,#0x1f                                                                             /*根据CPSR的各个位的功能设置*/
 orr r0,r0,#0xd3                                                                                   /*bic用于清除操作数1的某些位,0x1f的二进制是00011111*/
 msr cpsr,r0                                                                                       /*则清除r0的1、2、3、4、5为的1*/
                                                      /*指令orr是逻辑或的功能0xd3为11010011,到这里后将r0中的数据给cpsr则已经设置为svc模式了*/
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)                                                              /*对于s3c2400的CPU,因为有这些设置所以u-boot通过配置能够支持比较多的架构的CPU*/
# define pWTCON  0x15300000                                                           /*这里主要是一些宏定义,如看门狗寄存器的控制器*/
# define INTMSK  0x14400008                                                            /* 中断寄存器的基址*/
# define CLKDIVN 0x14800014                                                           /* 时钟的基址*/
#elif defined(CONFIG_S3C2410)                                                          /* 这里是对于S3C2410的一些宏定义*/
# define pWTCON  0x53000000
# define INTMSK  0x4A000008 
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
 ldr     r0, =pWTCON                                                                             /*关看门狗,直接写入0即可*/
 mov     r1, #0x0
 str     r1, [r0]
 /*
  * mask all IRQs by setting all bits in the INTMR - default
  */
 mov r1, #0xffffffff                                                                                       /*关中断,当置为1时相应位被屏蔽*/
 ldr r0, =INTMSK
 str r1, [r0]
# if defined(CONFIG_S3C2410)                                                                 /*如果前面没有宏定义了CONFIG_S3C2410则会忽略这段代码*/
 ldr r1, =0x3ff
 ldr r0, =INTSUBMSK
 str r1, [r0]
# endif
 /* FCLK:HCLK:PCLK = 1:2:4 */
 /* default FCLK is 120 MHz ! */
 ldr r0, =CLKDIVN                                                                                              /*这里是s3c2410的设置,如果是s3c2440则需要另外设置*/
 mov r1, #3
 str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
 /*
  * we do sys-critical inits only at reboot,
  * not when booting from ram!
  */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit     
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:                                                                                           /*  一切都准备好了,这里开始复制代码了    */
 adr r0, _start  /* r0 <- current position of code   */
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
 cmp     r0, r1                
 beq     stack_setup                                                                                  /* 如果相等就不需要移动        */
 ldr r2, _armboot_start
 ldr r3, _bss_start
 sub r2, r3, r2                                                                                        /* r2小于armboot的大小            */
 add r2, r0, r2                                                                                        /* r2 小于源码的结束地址        */
copy_loop:
 ldmia r0!, {r3-r10}                                                                                  /* 开始拷贝]    */
 stmia r1!, {r3-r10}                                                                                  /* 拷到目标寄存器    */
 cmp r0, r2                                                                                                /*直到拷完   */
 ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
                                                                                                                    /* 下面是一些栈的设置        */
stack_setup:
 ldr r0, _TEXT_BASE                                                                                   /* upper 128 KiB: relocated uboot   */
 sub r0, r0, #CFG_MALLOC_LEN                                                                  /* malloc area                      */
 sub r0, r0, #CFG_GBL_DATA_SIZE                                                               /* bdinfo                        */
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 sub sp, r0, #12                                                                                                   /* leave 3 words for abort-stack    */
clear_bss:
 ldr r0, _bss_start                                                                                                 /* find start of bss segment        */
 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      /* 这里的#if 0是用来作注释作用的,如果注释的代码要比较长则可以使用这种方法来注释    */
 /* try doing this stuff 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 stuff after relocation */
#endif
 ldr pc, _start_armboot
_start_armboot: .word start_armboot                                               /*一切都准备好了, 完成后从这里跳到_start_armboot继续执行        */

/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
* setup memory timing
 *
 *************************************************************************
 */

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
 /*
  * flush v4 I/D caches
  */
 mov r0, #0
 mcr p15, 0, r0, c7, c7, 0                                                                                /* 向c7写入0将使ICache与DCache无效*/
 mcr p15, 0, r0, c8, c7, 0                                                                                 /* 向c8写入0将使TLB失效 */
                                                        
                                                                                                                            / * 关闭MMU  */

 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     这里开始初始化RAM
  * because memory timing is board-dependend, you will
  * find a lowlevel_init.S in your board directory.
  */
 mov ip, lr
 bl lowlevel_init                              /*跳到初始化的程序,因为初始化的程序在另外的文件里,这里单独给出*/     
mov lr, ip
 mov pc, lr
#endif            
/*
lowlevel_init:
 /* memory control configuration */
 /* make r0 relative the current location so that it */
 /* reads SMRDATA out of FLASH rather than memory ! */
 ldr     r0, =SMRDATA
 ldr r1, _TEXT_BASE                                             /*将TEXT_BASE的地址传给r1,TEXT_BASE的值就是u-boot开始存放代码的地方*/
 sub r0, r0, r1                                                          /*将ro减去r1,最后将结果送到r0*/
 ldr r1, =BWSCON                                                    /* Bus Width Status Controller */
 add     r2, r0, #13*4
0:
 ldr     r3, [r0], #4
 str     r3, [r1], #4
 cmp     r2, r0
 bne     0b
 /* everything is fine now */
 mov pc, lr
 .ltorg
 */               
 /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
 *************************************************************************
 *
 * 异常的处理,就是代码刚开始的那一段,这个不用太在意,都是根据CPU来写的
*
 *************************************************************************
 */
@
@ 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
 sub sp, sp, #S_FRAME_SIZE        /*这里相当于sp=sp-72*/
 stmia sp, {r0 - r12}   @ Calling r0-r12
 ldr r2, _armboot_start
 sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub 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
 sub 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
 subs pc, lr, #4   @ return & move spsr_svc into cpsr
 .endm
 .macro get_bad_stack
 ldr r13, _armboot_start  @ setup our mode stack
 sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub 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_instruction:
get_bad_stack
 bad_save_user_regs
 bl  do_undefined_instruction
 .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