main_loop函数,将一些没用用到的条件编译去掉后,大致如下。
- void main_loop (void)
-
{
-
#ifndef CONFIG_SYS_HUSH_PARSER
-
static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };//定义命令字符串等变量
-
int len;
-
int rc = 1;
-
int flag;
-
#endif
-
-
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
-
char *s;
-
int bootdelay;
-
#endif
-
#ifdef CONFIG_SYS_HUSH_PARSER
-
u_boot_hush_start (); //初始化hush功能,?
-
#endif
-
-
#ifdef CONFIG_AUTO_COMPLETE
-
install_auto_complete(); //安装自动补全的函数。?
-
#endif
-
-
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
-
s = getenv ("bootdelay"); //从环境变量中获取bootdelay延迟时间
-
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
-
//若环境变量中有值则使用,否则使用配置的默认值
-
-
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
-
s = getenv ("bootcmd"); //获取引导命令
-
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "");
-
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
-
//延时大于0,abortboot函数将延时bootdelay的时间并返回是否有按键按下。
-
-
# ifndef CONFIG_SYS_HUSH_PARSER
-
run_command (s, 0); //执行环境变量中保存的开机后定义的要执行的命令
-
# else
-
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
-
FLAG_EXIT_FROM_LOOP);
-
# endif
-
}
-
#endif /* CONFIG_BOOTDELAY */
-
-
/*
-
* Main Loop for Monitor Command Processing
-
*/
-
#ifdef CONFIG_SYS_HUSH_PARSER
-
parse_file_outer(); //执行命令,该函数一般不返回 就等待执行命令
-
/* This point is never reached */
-
for (;;);
-
#else
-
for (;;) {
-
len = readline (CONFIG_SYS_PROMPT);
-
-
flag = 0; /* assume no special flags for now */
-
if (len > 0)
-
strcpy (lastcommand, console_buffer);
-
else if (len == 0)
-
flag |= CMD_FLAG_REPEAT;
-
-
if (len == -1)
-
puts ("\n");
-
else
-
rc = run_command (lastcommand, flag);
-
-
if (rc <= 0) {
-
/* invalid command or not repeatable, forget it */
-
lastcommand[0] = 0;
-
}
-
}
-
#endif /*CONFIG_SYS_HUSH_PARSER*/
-
}
这个函数其实必须完成的任务并不多 主要是初始化语法功能,安装一些补全命令函数,从环境变量中获取启动延时值,延时中检测是否有按键按下,然后如果没有按键按下就执行bootcmd中的命令,否则进入等待执行命令状态。?
下面简要分析一下引导内核的启动
uboot启动后不按键会执行bootcmd环境变量中定义的命令,默认值在configs/mini2440.h头文件中定义。bootcmd中一般会包含bootm命令。此命令引导内核。执行bootm命令的函数是do_bootm.,然后调用bootm_start获取内核必要信息,下面贴出此函数并简要注释一下。
- static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
-
{
-
ulong mem_start;
-
phys_size_t mem_size;
-
void *os_hdr;
-
int ret;
-
-
memset ((void *)&images, 0, sizeof (images)); //将保存内核镜像信息的结构体清空。images是一个bootm_headers_t 类型的全局变量,这个结构体中又包含了image_header_t。
-
images.verify = getenv_yesno ("verify"); //是否需要校验镜像 从环境变量中获取是否
-
-
lmb_init(&images.lmb); //初始化lmb
-
-
mem_start = getenv_bootm_low(); //获取内存起始地址
-
mem_size = getenv_bootm_size(); //获取内存大小
-
-
lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size); //添加信息到lmb中
-
-
arch_lmb_reserve(&images.lmb); //
-
board_lmb_reserve(&images.lmb); //
-
-
/* get kernel image header, start address and length */
-
os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
-
&images, &images.os.image_start, &images.os.image_len);
-
//此函数会从指定地址尝试获得内核镜像,校验镜像头部信息,填充images结构体,此函数返回映像起始地址,稍后详解
-
if (images.os.image_len == 0) {
-
puts ("ERROR: can't get kernel image!\n");
-
return 1;
-
}
-
-
/*此后的部分也都是一些内核信息的获取设置,在这仅贴出源码中重要注释 */
-
/* get image parameters */ //这一部分将填充images.os中的数据.
-
-
/* find kernel entry point */ //设置images.ep
-
-
images.os.start = (ulong)os_hdr;
-
images.state = BOOTM_STATE_START;
-
return 0;
-
}
boot_start函数分析完毕
boot_get_kernel分析,只将函数中涉及到重要的部分保留
- static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
-
bootm_headers_t *images, ulong *os_data, ulong *os_len)
-
{
-
if (argc < 2) {
-
img_addr = load_addr;
-
//获得映像加载地址,load_addr定义:ulong load_addr = CONFIG_SYS_LOAD_ADDR
-
debug ("* kernel: default image load address = 0x%08lx\n",
-
load_addr);
-
}
-
/* copy from dataflash if needed */
-
img_addr = genimg_get_image (img_addr);
-
//此函数检测img_addr地址是在dataflash上,如果是就移动到RAM中,如果在RAM则直接返回原值.
-
-
//检测映像的格式,基本思想是根据加载地址赋值给结构体,利用结构体中的位置结构到相应的地址处检查
-
switch (genimg_get_format ((void *)img_addr)) {
-
case IMAGE_FORMAT_LEGACY:
-
printf ("## Booting kernel from Legacy Image at %08lx ...\n",
-
img_addr);
-
hdr = image_get_kernel (img_addr, images->verify);//此函数根据加载地址,检测镜像正确性。再以iamge_header_t的形式返回映像头部地址。
-
if (!hdr)
-
return NULL;
-
show_boot_progress (5);
-
-
/* get os_data and os_len */
-
switch (image_get_type (hdr)) {
-
case IH_TYPE_KERNEL:
-
*os_data = image_get_data (hdr); //得到内核起始地址,os_data是上层函数传过来的image结构成员的指针,所以会写到image结构中.
-
*os_len = image_get_data_size (hdr); //得到内核大小
-
break;
-
-
}
-
/*
-
* copy image header to allow for image overwrites during kernel
-
* decompression.
-
*/
-
memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t)); //拷贝映像头信息
-
/* save pointer to image header */
-
images->legacy_hdr_os = hdr;
-
-
images->legacy_hdr_valid = 1;
-
}
-
return (void *)img_addr; //返回映像加载地址
-
-
}
boot_get_kernel分析完毕
再回到do_bootm函数,调用bootm_load_os函数。此函数检测内核镜像是否压缩,如果没有压缩,则查看当下的内核起始地址os.image_start(映像加载地址+映像头长度得到) 是否和映像头中指定的地址相同,如果不同则移动到指定的地址。如果有压缩则根据压缩类型解压缩到映像头中指定的内核起始地址。bootm_load_os函数任务完毕。
do_bootm函数后期关键一句boot_fn = boot_os[images.os.os];
boot_os是一个boot_os_fn类型函数指针的数组。linux对应的函数为do_bootm_linux。此函数在lib_arm/bootm.c中,分析此函数。
此函数平台系统相关,是为启动linux作最后设置,获取machid,和将参数信息写入bd->bi_boot_params所指的地址。最终使用 theKernel
(0, machid, bd->bi_boot_params);跳到内核。函数如下(删除了不编译的部分)
- int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
-
{
-
bd_t *bd = gd->bd;
-
char *s;
-
int machid = bd->bi_arch_number;
-
void (*theKernel)(int zero, int arch, uint params);
-
-
#ifdef CONFIG_CMDLINE_TAG
-
char *commandline = getenv ("bootargs"); //从环境变量中获得bootargs
-
#endif
-
-
if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
-
return 1;
-
-
theKernel = (void (*)(int, int, uint))images->ep; //将内核入口点转成theKernel函数,用于启动内核
-
-
s = getenv ("machid"); //如果环境变量中有机器ID则使用环境变量中的
-
if (s) {
-
machid = simple_strtoul (s, NULL, 16);
-
printf ("Using machid 0x%x from environment\n", machid);
-
}
-
-
show_boot_progress (15);
-
-
debug ("## Transferring control to Linux (at address %08lx) ...\n",
-
(ulong) theKernel);
-
-
#if defined (CONFIG_SETUP_MEMORY_AGS) || \
-
defined (CONFIG_CMDLINE_TAG) || \
-
defined (CONFIG_INITRD_TAG) || \
-
defined (CONFIG_SERIAL_TAG) || \
-
defined (CONFIG_REVISION_TAG) || \
-
defined (CONFIG_LCD) || \
-
defined (CONFIG_VFD)
-
//将开始节点信息写到bd->bi_boot_params地址,此地址在board_init函数中设置为0x30000100,此后的节点信息都依次写入,linux内核将从此地址处读取需要的参数
-
setup_start_tag (bd);
-
#ifdef CONFIG_SETUP_MEMORY_TAGS
-
setup_memory_tags (bd); //写内存地址和大小信息的节点
-
#endif
-
#ifdef CONFIG_CMDLINE_TAG
-
setup_commandline_tag (bd, commandline); //bootargs写入节点
-
#endif
-
#ifdef CONFIG_INITRD_TAG
-
if (images->rd_start && images->rd_end)
-
setup_initrd_tag (bd, images->rd_start, images->rd_end);
-
#endif
-
setup_end_tag (bd); //写结束节点
-
#endif
-
-
/* we assume that the kernel is in place */
-
printf ("\nStarting kernel ...\n\n");
-
-
#ifdef CONFIG_USB_DEVICE
-
{
-
extern void udc_disconnect (void);
-
udc_disconnect ();
-
}
-
#endif
-
-
cleanup_before_linux (); //关中断,cache等
-
-
theKernel (0, machid, bd->bi_boot_params); //跳转到内核.
-
/* does not return */
-
-
return 1;
-
}
uboot就此结束。
阅读(1220) | 评论(0) | 转发(0) |