对于Uboot移植工作,有一些技术细节可能不需要我们搞特别清楚,但是通过分析Uboot源码,可以让我们对uboot整个架构有一个清晰的认识,在进行uboot移植的过程中可以有一个清晰的思路。
u-boot 源码目录
平台相关 |
Arch Board Include |
平台无关 |
Api Common Disk Doc Drivers Dts Examples Fs Lib Net Post Spl Test tools |
u-boot 的启动分为两个阶段:
u-boot第一阶段 :
u-boot 的入口是由链接脚本决定的, uboot 下 armv7 链接脚本默认目录为arch/arm/cpu/u-boot.lds ,从下图可以看出,第一个链接的是 CPUDIR/start.o ,其对应的代码为 arch/arm/cpu/armv7/start.S
ENTRY() 和 ENDPROC() 分别为汇编函数的入口和出口
arch/arm/cpu/armv7/start.S :
设置异常向量表:
//ldr指令是将寄存器装载指令,将内存中的地址的内容装载到PC寄存器中。
看到上电后 u-boot 会进入 reset 函数:
可以看到进入 reset 函数后,会先进入 save_boot_params 函数
注意到 save_boot_params 函数中没有进行任何操作,直接返回了,注释告诉我们栈指针还没有初始化,不能向栈中保存
任何数据。
值得注意的是 .weak 伪操作: .weak save_boot_params
可以理解为如果定义了 save_boot_params 这个函数,那么久调用它,如果没有定义,就定义它,具体可以参考http://blog.chinaunix.net/uid-25358071-id-309285.html
然后 reset 函数执行下面的指令:
reset: bl save_boot_params ldr r0, =0x44e10884 ldr r1, =0x10 str r1,[r0] /* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 |
设置了 CPSR 寄存器为 d3 ,使得 CPU 进入 SVC 模式,并禁止中断, CPSR 寄存器详情查看《 ARM 基础知识》
这个时候可以用汇编代码进行点灯,确定代码确实运行到这一步:
#if 1 ldr r0, =0x11000c40 @GPK2_7 led2 ldr r1, [r0] bic r1, r1, #0xf0000000 orr r1, r1, #0x10000000 str r1, [r0] ldr r0, =0x11000c44 mov r1,#0xff str r1, [r0] #endif |
接着调用cpu_init_crit函数,禁止MMU,禁止caches等工作
bl cpu_init_crit
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 mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB /* * 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 11 (Z---) BTB #ifdef CONFIG_SYS_ICACHE_OFF bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache #else orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache #endif 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 rate and 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 #endif |
lowlevel_init.S (mcu-uboot-wk\arch\arm\cpu\armv7\ti81xx)
这个函数就和具体的硬件有关系了,我所使用的单板是Nor flash
.globl lowlevel_init lowlevel_init: /* The link register is saved in ip by start.S */ mov r6, ip /* check if we are already running from RAM */ ldr r2, _lowlevel_init ldr r3, _TEXT_BASE sub r4, r2, r3 sub r0, pc, r4 /* require dummy instr or subtract pc by 4 instead i'm doing stack init */ ldr sp, SRAM_STACK mark1: ldr r5, _mark1 sub r5, r5, r2 /* bytes between mark1 and lowlevel_init */ sub r0, r0, r5 /* r0 <- _start w.r.t current place of execution */ mov r10, #0x0 /* r10 has in_ddr used by s_init() */
#ifdef CONFIG_NOR_BOOT cmp r0, #0x08000000 /* check for running from NOR */ beq ocmc_init_start /* if == then running from NOR */
#ifdef CONFIG1_SPL_BUILD ands r0, r0, #0xC0000000 /* MSB 2 bits <> 0 then we are in ocmc or DDR */ cmp r0, #0x40000000 /* if running from ocmc */ beq nor_init_start /* if == skip ocmc init and jump to nor init */ 将Nor flash中的代码拷贝到Ram中继续执行
#endif mov r10, #0x01 /* if <> we are running from DDR hence skip ddr init */ /* by setting in_ddr to 1 */
b s_init_start /* and jump to s_init */
|
然后跳转到中执行
/* Set stackpointer in internal RAM to call board_init_f */ call_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r0,=0x00000000 bl board_init_f |
Board.c (mcu-uboot-wk\arch\arm\lib)
函数作用是:对 gd 结构体( include/asm/global_data.h )进行设置,进行各种初始化 gd->mon_len = (ulong)&__bss_end - (ulong)_start; 初始化 mon_len ,代表 uboot code 的大小。 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
这些函数中有很多与硬件有关,比如串口
接着会执行 relocate_code(addr_sp, id, addr); 代码自搬移 stack_setup //设置堆栈 copy_loop // clear_bss //清BSS段 jump_2_ram _board_init_r_ofs .word board_init_r - _start //最终跳转到board_init_r执行后续程序,也就是第二阶段 |
U-boot第二阶段 Board.c (mcu-uboot-wk\arch\arm\lib),这阶段的代码与硬件关系很大,uboot的移植工作主要在此处,包括网卡初始化,串口初始化,中断初始化,中断使能,环境变量的初始化等工作。
void board_init_r(gd_t *id, ulong dest_addr) { enable_caches(); board_init(); /* Setup chipselects */ … flash_size = flash_init(); env_relocate(); /* initialize environment ,初始化环境变量*/ gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr"); /* IP Address */ stdio_init(); /* get the devices list going. */ console_init_r(); /* fully init console as a device */ interrupt_init(); /* set up exceptions */ enable_interrupts()/* enable exceptions */ eth_init(gd->bd); /*初始化网卡*/ /*最终进入死循环,进入uboot的主函数*/ /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop(); } } |
Main.c (mcu-uboot-wk\common)
board_init_r进行完板级初始化后最后进入死循环,打印命令行,等待命令输入和解析。到这里uboot的启动过程就全部结束了!
void main_loop (void) { bootcount = bootcount_load(); /*uboot的倒计时*/ bootcount++; bootcount_store (bootcount); sprintf (bcs_set, "%lu", bootcount); setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
update_tftp (0UL); //tftp if (len == -1) //执行相关命令 puts (" else rc = run_command (lastcommand, flag); if (rc <= 0) { /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } } |