u-boot 的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下:
(1)定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
(2)设置异常向量(ExceptionVector)。
(3)设置CPU 的速度、时钟频率及终端控制寄存器。
(4)初始化内存控制器。
(5)将ROM 中的程序复制到RAM 中。
(6)初始化堆栈。
(7)转到RAM 中执行,该工作可使用指令ldr pc 来完成。
第一阶段对应的文件是cpu/arm920t/start.S,U-Boot启动第一阶段流程如下:
cpu/arm920t/start.S
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
;定义变量_start,然后跳转到处理器复位代码
.globl _start //u-boot 启动入口
_start: b reset
;产生中断则利用pc 来跳转到对应的中断处理程序中
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 //快速中断向量
;利用.word 来在当前位置放置一个值,这个值实际上就用对应的中断处理函数的地址
;.word 的意义为在当前地址处放入一个16bits 值
_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
*/
mrs r0,cpsr
;bic 清除指定为1的位
bic r0,r0,#0x1f
;orr 逻辑或操作
orr r0,r0,#0xd3
;经过以上两步r0值控制位位11010011,第0~4位标识处理器当前所处模式为10011(32位
管理模式),第6、7位
;为1标识禁止IRQ 和FIQ 中断,第5位为0标识程序运行为arm 状态,若其为1则运行在thumb
状态
;设置处理器为32位管理模式,并运行与arm 状态
msr cpsr,r0
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller baseaddresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
;看门狗寄存器地址
# define pWTCON 0x53000000
;中断掩码寄存器,决定那个中断源被屏蔽,某位为1则屏蔽中断源,初始值为0xFFFFFFFF,
屏蔽所有中断
# define INTMSK 0x4A000008 /* Interupt-Controller baseaddresses */
;中断子掩码寄存器,该寄存器只能屏蔽11个中断源,因此其仅低11位有效,初始值为0x7FF
# define INTSUBMSK 0x4A00001C
;时钟分频控制寄存器
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
;将看门狗寄存器清空,其各位含义为,第0位为1则当看门狗定时器溢出时重启,为0则不重启,初值为1
;第2位为中断使能位,初值为0
;第3、4位为时钟分频因子,初值为00,
;第5位为看门狗的使能位初值为1
;第8~15位为比例因子,初值为0x80
ldr r0, =pWTCON
mov r1, #0x0
;将看门狗寄存器所有位置零,关闭看门狗,其实只要将第5位置0即可
str r1, [r0]
;屏蔽所有中断,实际上中断屏蔽掩码寄存器初值即为0xFFFFFFF
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
;设置子中断掩码寄存器
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
;设置时钟寄存器,CLKDIVN 第0位为PDIVN,为0则PCLK=HCLK,为1则PCLK=HCLK/2
;第1位为HDIVN,为0则HCLK=FCLK,为1则HCLK=FCLK/2
;这里两位均为1,则FCLK:HCLK:PCLK= 4:2:1
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
;对临界寄存器的初始化,如果从ram 中启动则不执行,如果重启则执行
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
;重定向代码,也就是从flash 中复制到ram 中
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
;当前代码地址,adr 获取当前代码的地址信息,若从ram 运行则_start=TEXT_BASE,否则
_start=0x00000000
adr r0, _start /* r0 <- current position of code */
;获取_TEXT_BASE
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
;两者相等,表示从ram 运行则跳转到堆栈设置
beq stack_setup
;不相等则表示从flash 中运行,重定向代码
ldr r2, _armboot_start
;获取未初始化数据段地址
ldr r3, _bss_start
;计算代码段大小
sub r2, r3, r2 /* r2 <- size of armboot */
;计算代码段终止地址
add r2, r0, r2 /* r2 <- source end address */
;复制代码,r0为代码的起始地址,r1为ram 中地址,r2为代码的终止地址
;每次copy 后将r0值递增同r2比较来判断是否复制完成
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack */
stack_setup:
;获取_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 用于栈起点
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
;关闭看门狗
/* 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
; 完成复制后跳转到start_armboot, 到这里就进入函数lib_arm/board.c 的这里将进入stage2
start_armboot 函数中
ldr pc, _start_armboot
_start_armboot: .word start_armboot