在嵌入式设备中需要一段代码来引导系统的启动,就像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
#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
_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 /*以上是异常向量的处理*/
/*
*************************************************************************
*
* 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
*
*************************************************************************
*/
/*
* the actual reset code
*/
#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
# endif
cmp r0, r2 /*直到拷完 */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/*
*************************************************************************
*
* 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
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
*************************************************************************
*/
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
bad_save_user_regs
bl do_undefined_instruction
.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
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_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
_bss_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 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
.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 */
/*
* 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模式了*/
#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]
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]
* 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
# 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 */
/* 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
* 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 /* 如果相等就不需要移动 */
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 小于源码的结束地址 */
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} /* 拷到目标寄存器 */
ldmia r0!, {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 */
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 */
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
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]
/* try doing this stuff after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMR
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
/* 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
*
*************************************************************************
*/
#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
* 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 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
mov pc, lr
.ltorg
*/
/* CONFIG_SKIP_LOWLEVEL_INIT */
*/
/* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* 异常的处理,就是代码刚开始的那一段,这个不用太在意,都是根据CPU来写的
*
*************************************************************************
*
* 异常的处理,就是代码刚开始的那一段,这个不用太在意,都是根据CPU来写的
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
@ 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_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 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
#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
*/
* 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
sub sp, sp, #S_FRAME_SIZE /*这里相当于sp=sp-72*/
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
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
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
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
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]
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
@ 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
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
* exception handlers
*/
.align 5
undefined_instruction:
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
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
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
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
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
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
.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
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
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif