uboot在进行系统启动和内核加载的时候被分为俩个阶段,
第一阶段主要是写汇编代码,我没有仔细研究,只是看了看移植好的针对mini2440的tekkman的uboot代码。
第二个阶段为由C写成,易于研究和学习。掌握了基本的流程。
(1)第一阶段的功能
? 硬件设备初始化
? 加载U-Boot第二阶段代码到RAM空间
? 设置好栈
? 跳转到第二阶段代码入口
(2)第二阶段的功能
? 初始化本阶段使用的硬件设备
? 检测系统内存映射
? 将内核从Flash读取到RAM中
? 为内核设置启动参数
? 调用内核
第二阶段的C出口函数为:
由于在cpu/arm920t/start.S的最后阶段有如下代码:
ldr pc, _start_armboot
_start_armboot: .word start_armboot 此时pc指针将会跳转到lib_arm/board.c中定义的函数start_armboot().因此start_armboot().函数是c代码的入口代码,分析就从这里起航。
而在函数start_armboot()中有个死循环,
/* main_loop() can return to retry autoboot, if so just run it again. */for (;;)
{main_loop ();接受用户在uboot命令行输入的命令。}
在uboot启动的时候,如果在#define CONFIG_BOOTDELAY 1超时之前用户没有输入,uboot就会自动加载linux内核,
其加载时将使用变量“bootcmd”和 “bootargs”在uboot代码所定义的变量值进行启动代码。
变量“bootcmd”和 “bootargs”的值可以在在加载linux内核前,uboot的命令行中进行修改。
我这俩个参数的值如下:
bootcmd=nfs 0x30008000 192.168.1.149:/opt/FriendlyARM/uImage;bootm —— 需要注意的是再bootcmd变量的最后添加了bootm命令。bootargs=noinitrd root=/dev/nfs proto=tcp,nolock,nfsvers=3, rw nfsroot=192.168.1.149:/mini2440/rootfs ip=192.168.1.144:192.168.1.149::255.255.255.0 console=ttySAC0,115200 init=/linuxrc mem=64M
首先看看启动内核是俩种比较简单明白的方式,boot,bootd命令的实现。
现实代码如下:
/*******************************************************************//* bootd – boot default image *//*******************************************************************/#if defined(CONFIG_CMD_BOOTD)int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){int rcode = 0;
#ifndef CONFIG_SYS_HUSH_PARSERif (run_command (getenv ("bootcmd"), flag) < 0)rcode = 1;#elseif (parse_string_outer (getenv ("bootcmd"),FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)rcode = 1;#endifreturn rcode;}
U_BOOT_CMD(boot,1,1,do_bootd,"boot default, i.e., run ‘bootcmd’","");
/* keep old command name "bootd" for backward compatibility */U_BOOT_CMD(bootd, 1,1,do_bootd,"boot default, i.e., run ‘bootcmd’","");
#endif
从上面的对命令boot,bootd命令实现可以知道,其命令的最终执行的是“bootcmd”命令行参数所定义的值
即"nfs 0x30008000 192.168.1.149:/opt/FriendlyARM/uImage;bootm ":
在上面的函数main_loop ()中又如下代码片段
s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\&;%s\&;\n", s ? s : "<UNDEFINED>");
if (bootdelay >= 0 && s && !abortboot (bootdelay)){# ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1);/* disable Control C checking */# endif
# ifndef CONFIG_SYS_HUSH_PARSERrun_command (s, 0);# elseparse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);# endif
# ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev);/* restore Control C checking */# endif}
# ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) { s = getenv("menucmd"); if (s) {# ifndef CONFIG_SYS_HUSH_PARSERrun_command (s, 0);# elseparse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);# endif }}#endif /* CONFIG_MENUKEY */#endif/* CONFIG_BOOTDELAY */
上的代码中在函数abortboot (bootdelay))
执行的过程中已经超时,则自动的执行"bootcmd" 所定义的命令。
现在看看命令"bootm"命令式如何实现的?
"boot application image from memory",这句话很重要,表明bootm执行是必须确保uImage已经在内从中,
在"bootm addr"中当addr省略的时候bootm加载宏#define CONFIG_SYS_LOAD_ADDR 0x30008000 /* default load address */定义处的内核image。
"bootm"命令的实现函数为do_bootm(),在do_bootm()中完成对linux内核的加载启动。
其加载函数为:do_bootm_linux()函数中获取了"bootargs"环境变量的值。最终将此值传递给linux内核,用来加载文件系统时候使用。
对do_bootm()和do_bootm_linux()函数的分析可以找到看其他朋友的分析。
总结
在uboot中每一个命令都对应一个其实现的函数,在启动linux内核过程中,主要是执行环境变量bootcmd和bootargs所定义的命令。因此,对这俩个变量的定义很重要,如果定义不对就不能正确的加载内核和文件系统。尤其是必须确保文件系统所在分区。
我就想是一只草原中被牧童遗忘的羊,