u-boot启动流程
u-boot启动过程分为两个阶段(2440为例):
1. 第一阶段功能:
入口是start.S汇编文件:
- 硬件设备初始化
//跳转到reset函数,设置cpu模式为SVC32管理模式
.globl _start
_start: b reset
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
//关看门狗
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, =INTMSK
str r1, [r0]
- 为加载Bootloader的第二阶段代码准备RAM空间
//配置cpu,cpu_init_crit初始化SDRAM
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
blne cpu_init_crit
cpu_init_crit:
/*
* flush v4 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 stuff 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
* 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
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
sub r0, r0, r1
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
/* the literal pools origin */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0xb1
.word 0x30
.word 0x30
- 复制Bootloader的第二阶段代码到RAM空间中
/* 重定位,即将代码拷贝到RAM空间中*/
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
/* CopyCode2Ram 为判断从nor启动还是nand启动,判断方法:
* nor启动时地址从0开始,nand启动时地址不是从0,所以0地址为nor时需要一
* 定序列才能写成功,所以写0地址时判断是否写成功则可判断是nor或nand启动。
*/
#else
add r2, r0, r2 /* r2 <- source end address */
/* 清bss段,bss段为需要初始化为0的静态变量和全局变量
* _bss_start和_bss_end值可以根据u-boot-1.1.6/board/100ask24x0/u-boot.lds链接文件的
*/
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
- 设置好栈(第二阶段调用C函数需要用栈)
//设置栈
/* Set up the 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 */
- 跳转到第二阶段代码的C入口点
//start_armboot为第二阶段启动函数
_start_armboot: .word start_armboot
2. 第二阶段功能:
第二阶段入口c语言函数start_armboot():
- 初始化第二阶段要用到的硬件设备
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/* init_sequence */
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
/* 其中board_init里面可以知道2440开发板的机器码和内核参数存储地址等数据 */
size = flash_init ();
nand_init(); /* go init the NAND */
/* 初始化norflash和nandflash,之后uboot有能力读写flash*/
main_loop ();//这是检测命令输入事件,即uboot是命令的处理
- 检测系统内存映射
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
- 将内核映像和根文件系统映像从Flash上读取到RAM空间中
- 为内核设置启动参数
- 调用内核
、* 下面是uboot倒数计时的实现 */
s = getenv ("bootcmd");
//bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CFG_HUSH_PARSER
{
printf("Booting Linux ...\n"); //若倒数计时时没有按键输入则调用bootcmd命令
run_command (s, 0);
}
# else
bootcmd的值:
bootcmd=nand read.jffs2 0x30007FC0 kernel bootm 0x30007FC0;
从nandflash kernel分区的0x30007FC0
读取内核,然后通过bootm 0x30007FC0
命令启动内核。
下面分析读内核命令:nand read.jffs2 0x30007FC0 kernel
内核的镜像文件uImage由头部+镜像组成,头部有加载地址
in_load
和入口地址in_ep
,这些信息是在代码(u-boot-1.1.6\include\configs\100ask24x0.h)
里写死的。
2440分区配置信息:
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \
"128k(params)," \
"2m(kernel)," \
"-(root)"
/* 分区位于nanflash上面,其中bootloader占前256k地址,后面以此是params,kernel,root;*/
下面是2440的分区信息:
OpenJTAG> mtd
device nand0 <jz2440-0>, # parts = 4
#: name size offset mask_flags
0: u-boot 0x00040000 0x00000000 0
1: params 0x00020000 0x00040000 0
2: kernel 0x00200000 0x00060000 0
3: rootfs 0x0fda0000 0x00260000 0
active partition: nand0,0 - (u-boot) 0x00040000 @ 0x00000000
defaults:
mtdids : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)
所以从上可以得知kernel分区信息是
offset:0x00060000, size:0x00200000
则nand read.jffs2 0x30007FC0 kernel
解析为nand read.jffs2 0x30007FC0 0x00060000 0x00200000
即从0x00060000
读出0x00200000
大小的的kernel
放到地址0x30007FC0
上。
调用完上面命令就完成了kernel的加载,具体实现可以查看nand的实现函数do_nand
接下来就是执行命令bootm 0x30007FC0
bootm命令功能:
- 先读取uImage的头部信息获得kernel的加载地址和入口地址
- 若kernel的当前地址并不位于加载地址,则将kernel移到加载地址,然后再跳到入口地址执行。
- 调用
do_bootm_linux
启动内核:
3.1 告诉内核一些启动参数(设置启动参数)
setup_start_tag、setup_memory_tags、setup_commandline_tag、setup_end_tag
上面这四个函数是把参数排列到内存,地址可由u-boot-1.1.6\board\100ask24x0.c\
的board_init()
函数gd->bd->bi_boot_params
可知,按格式设置完参数存放地址后,内核启动的时候就会到该地址读取参数。
3.2 跳到入口地址:
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
启动的时候传入两个参数bd->bi_arch_number
和bd->bi_boot_params
,bd->bi_arch_number
是机器ID,用于给内核判断是否支持的机器id,bd->bi_boot_params
是前面说的参数地址,这两个参数值可以查询u-boot-1.1.6\board\100ask24x0.c\
的board_init()
函数。