U-boot属于两阶段的Bootloader,第一阶段的文件为cpu/arm920t/start.S 和board\samsung\smdk2410/lowlevel_init.S,前者是平台相关的,后者是开发板相关的。
1.U-Boot第一阶段代码分析
(1)硬件设备初始化
依次完成如下设置:将CPU的工作模式设为管理模式(SVC),关闭WATCHDOG,设置FCLK,HCLK,PCLK的比例,关闭MMU,CACHE。代码在cpu/arm920t/start.S中,
(2)为加载Bootloader的第二阶段代码准备RAM空间。
所谓准备RAM空间,就是初始化内存芯片,使它可用,对于S3C24x0,通过在Start.S中调用lowlevel_init函数来设置存储控制器,使得外接
SDRAM可用,lowlevel_init.S,文件是与开发板相关的,这表示如果外接的设备不一样,可以修改lowlevel_init.S文件中的相关的宏。
_TEXT_BASE: .word TEXT_BASE //这里是获得代码段的起始地址,我的是0x33F80000(在board/xxx/config.mk中 //可到找到“TEXT_BASE=0x33F80000” .globl lowlevel_init //这里相当于定义一个全局的lowlevel_init以方便调用
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 //SMDATA表示这 13个寄存器的值存放的开始地址,值为0x33F8xxxx,处于内 //存中,这一句的作用是把其值加载到r0中 ldr r1, _TEXT_BASE // 把代码的起始地址(0x33F80000)加载到r1中
sub r0, r0, r1 //r0减去r1其结果存入r0,也即SMDATA中的起始地址0x33F8xxxx减去 //0x33F80000,其结果就是13个寄存器的值在NOR Flash存放的开始地址
ldr r1, =BWSCON /* Bus Width Status Controller */ //存储控制器的基地址 add r2, r0, #13*4 //在计算出来的存放地址加上#13*4,然后其结果保存在r2中 //13 个寄存器,每个寄存器占4个字节
0: ldr r3, [r0], #4 //内存中r0的值加载到r3中,然后r0加4,即下一个寄存器的
str r3, [r1], #4 //读出寄存器的值保存到r1中,然后r1也偏移4
cmp r2, r0 //比较r0与r2的值,如果不等继续返回0:执行,也即13个寄存器的值 // 是否读完 bne 0b /* everything is fine now */ mov pc, lr //程序跳转,返回到cpu_init_crit中 .ltorg /* the literal pools origin */ SMRDATA: ...................
|
(3)复制Bootloader的第二阶段代码到RAM空间中
这里将整个U-Boot代码都复制到SDRAM中,这在cpu/arm920t/start.s中实现
relocate: /* 将U-Boot复制到RAM中 */ adr r0, _start /* r0:当前代码的开始地址 */ ldr r1, _TEXT_BASE /* r1:代码段的连接地址*/ cmp r0, r1 /* 测试现在是在FLash中,还在是RAM中,如果要从NandFlash启动的话,这里要根据需要修改 */ beq stack_setup /*如果已经在RAM中,则不需要复制*/
ldr r2, _armboot_start /*_armboot_start在前面定义,是第一条指令的运行地址*/ ldr r3, _bss_start /*在连接脚本U-Boot.lds中定义,是代码段的结束地址*/ sub r2, r3, r2 /* r2 <- 代码段长度 */ add r2, r0, r2 /* r2 <-代码段的结束地址 */
copy_loop: ldmia {r3-r10} /* 从地址[r0]处获得数据 */ stmia {r3-r10} /* 复制到地址[r1]处 */ cmp r0, r2 /* 判断是否复制完毕 */ ble copy_loop /*没有复制完,则继续*/ #endif /* CONFIG_SKIP_RELOCATE_UBOOT */
|
上面这段程序,在使用NANDFlash启动时,需要修改。
(4)设置好栈
/*栈的设置灵活性很大,只要让sp寄存器指向一段没有使用的内存即可*/
stack_setup: ldr r0, _TEXT_BASE /* _TEXT_BASE 为代码段的开始地址,值为0x33F80000 */ sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* 代码段下面,留出一段内存以实现malloc */ sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* 再留出一段内存,存一些全局参数 */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* 最后,留出12字节的内存给abort异常 */
clear_bss: ldr r0, _bss_start /* 下面的都是栈 */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000
|
(5)跳转到第二阶段代码的C入口点
在跳转之前,还要清除BSS段(初始值0,无初始值的全局变量,静态变量放在BSS段。
clear_bss: ldr r0, _bss_start /* BSS段的开始地址,它的值在连接脚本中U-Boot.lds中确定 */ ldr r1, _bss_end /* BSS段的结束地址,它的值在连接脚本u-Boot.lds中确定 */ mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* 向BSS段中写入0值 */ add r0, r0, #4 cmp r0, r1 ble clbss_l
|
现在,C函数的运行环境已经完全准备好,通过如下命令直接跳转,这之后在内存中执行,原先在NorFlash中,它将调用lib_arm/boadr.c中的star_armboot函数,这是第二阶段的入口。
ldr pc, _start_armboot
_start_armboot: .word start_armboot
|
2 U-Boot第二阶段代码分析
U-boot在启动内核之前可以让用户决定是否进入下载模式,即进入U-Boot的控制界面。
第二阶段从lib_arm/borad.c中的start_armboot函数开始,程序的流程如下 :
(1)初始化本阶段要使用到的硬件设备
最主要的是设置系统时钟,初始化串口,只要这两个设置好了,就可以从串口看到打印信息。
board_init 函数设置MPLL,改变系统时钟,它是开发板相关函数。board\samsung\smdk2410/smdk2410.c中实现。串口的初始化函数主 要是serial_init,它设置UART控制器,是CPU相关的函数,在cpu/arm920t/s3c24x0/serial.c中实现。
(2)检测系统内存映射
对于特定的开发板,其内存的分布是明确的,所以可以直接设置,board\samsung\smdk2410\smdk2410.c中的dram_init函数指定了本开发板的内存起始地址为0x30000000,大小为0x40000000。
int dram_init (void) { gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
return 0; }
|
(3) 为内核设置启动参数
在start_armboot()函数的最后,调用main_loop()函数,进行一个无限循环,该函数在common/main.c文件中定义。
u-boot-2009.08\lib_arm\bootm.c文件中,定义了引导Linux内核的 do_bootm_linux()函数,U_Boot也是通过标记列表向内核传递参数的,一般而言,设置这以下两个标记就可以了,在配置文件 include/configs/smdk2410.h中,增加如下两个配置项即可:
#define CONFIG_SETUP_MEMORY_TAGS 1 #define CONFIG_CMDLINE_TAG 1
|
对于ARM架构的CPU来说,都是通过u-boot-2009.08\lib_arm\bootm.c中的do_bootm_linux函数来启动内核 的,这个函数中,设置标记列表,最后通过“theKernel (0, machid, bd->bi_boot_params);”调用内核,其中,这里第1、2、3个参数就分别存储在r0、r1、r2中。theKernel指向内核 存放的地址,(对于ARM架构的CPU,通常是0x30008000),bd->bi_boot_params就是在board_init函数设置 的机器类型ID,而bd->bi_boot_params就是标记列表的地址