u-boot启动过程:通常有两个阶段。第一阶段使用汇编来实现,它完成一些依赖于CPU体系结构的初始化,并调用第二阶段的代码;第二阶段则通常使用c语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。
第一阶段:
1.1、上电后CPU为SVC模式
.globl _start
_start: b reset
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
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
1.2、调用cpu_init_crit
/*the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
cpu_init_crit:
/*
* Invalidate L1 I/D
*/
mov r0, #0 @set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rateand handle
* wake up conditions.
*/
mov ip, lr @persevere link reg across call
bl lowlevel_init @go setup pll,mux,memory
mov lr, ip @restore link
mov pc, lr @back to my caller
1.3、调用lowlevel_init
这个函数在board\freescale\mx6q_sabresd\lowlevel_init.S文件中。
.globl lowlevel_init
lowlevel_init:
inv_dcache /* invalidate the D-CACHE */
init_l2cc /*Disable L2Cache*/
init_aips
init_clock
movpc, lr
这里并没有memory的初始化,因为在u-boot.lds文件中已经知道最开始执行的是board/freescale/mx6q_sabresd/flash_header.s,这里的代码就是对Memory的初始化。
1.4、代码的搬移
如果当前代码不在链接指定的地址上,则需要把u-boot从当前位置拷贝到RAM指定位置中;
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
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 stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 @r2 <- size of armboot
add r2, r0, r2 @r2 <- source endaddress
copy_loop: @copy 32 bytes at a time
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to targetaddress [r1]
cmp r0, r2 @until source end addreee [r2]
ble copy_loop
#endif /*CONFIG_SKIP_RELOCATE_UBOOT */
1.5、栈空间的设置
stack_setup:
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
sub r0, r0, #CONFIG_SYS_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
and sp, sp, #~7 @8 byte alinged for (ldr/str)d
这段代码是用来分配各个栈空间的。包括分配动态内存区,全局数据区,IRQ和FIQ的栈空间,堆栈是进入C函数前必须初始化。
1.6、BSS段的清零
clear_bss:
ldr r0, _bss_start @ find start of bss segment
ldr r1, _bss_end @stop here
mov r2, #0x00000000 @clear value
clbss_l:
str r2, [r0] @clear BSS location
cmp r0, r1 @are we at the end yet
add r0, r0, #4 @increment clear index pointer
bne clbss_l @ keep clearing till at end
1.7、跳到c代码的入口函数
ldr pc, _start_armboot @ jump to C code
_start_armboot: .word start_armboot
这个C入口的函数,是在lib_arm\board.c文件中。它标志着后续将全面启动C语言程序,同时它也是整个u-boot的主函数。
第二阶段功能:start_armboot(lib_arm\board.c)
初始化本阶段要使用到的硬件设备。
检测系统内存映射(memory map)。
将内核映像和根文件系统映像从Flash上读到RAM空间中。
为内核设置启动参数。
调用内核。
2.1、为gd与bd分配空间
/*Pointer is writable since we allocated a register for it */
gd= (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/*compiler optimization barrier needed for GCC >= 3.4 */
__asm____volatile__("": : :"memory");
memset((void*)gd, 0, sizeof (gd_t));
gd->bd= (bd_t*)((char*)gd - sizeof(bd_t));
memset(gd->bd, 0, sizeof (bd_t));
gd->flags|= GD_FLG_RELOC;
2.2、执行初始化列表函数
for(init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if((*init_fnc_ptr)() != 0) {
hang();
}
}
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
board_init, /* basic board dependent setup*/
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* set up exceptions */
#endif
timer_init, /* initialize timer */
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
#if defined(CONFIG_HARD_I2C) ||defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAMbanks */
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init,
#endif
display_dram_config,
NULL,
};
对于这里的初始化函数列表,我们需要注意的是:
board_init:
/*board id for linux */
gd->bd->bi_arch_number= MACH_TYPE_MX6Q_SABRESD;
/*address of boot parameters */
gd->bd->bi_boot_params= PHYS_SDRAM_1 + 0x100;
这两个都是最终要传给linux内核的
2.3、一部分外设的初始化
在第二阶段的代码中,需要注意的是我们系统镜像存储的外设,是否已经初始化。例如在我自制的板子中,使用的是MMC,对应的宏需要定义
#ifdef CONFIG_GENERIC_MMC
puts("MMC: ");
mmc_initialize(gd->bd);
#endif
2.4、main_loop函数(common/main.c)
for(;;) {
main_loop();
}
整个u-boot的执行就进入等待用户输入命令,解析并执行命令的死循环中