bootm命令函数do_bootm位于Cmd_bootm.c,其流程为:确定是否要校验uImage和ramdisk里的数据,默认校验,若想不校验:设置了环境变量verify=n。 s = getenv ("verify");
verify = (s && (*s == "n")) ? 0 : 1;判断命令是否制定了 操作系统 的加载地址。如果没有,使用默认地址CFG_LOAD_ADDR,一般CFG_LOAD_ADDR可以在include/configs文件夹自己单板的配置文件里配置。if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
根据image 加载地址是在 flash还是内存,获取image的 头信息。#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){
read_dataflash(addr, sizeof(image_header_t), (char *)&header);
} else
#endif
memmove (&header, (char *)addr, sizeof(image_header_t));
校验头里的MAGIC NUM和CRC是否正确,头部的CRC计算内容是 :头部64B,且头部CRC处按内容0来计算。if (ntohl(hdr->ih_magic) != IH_MAGIC) {
{ .............................................
puts ("Bad Magic Number
");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
); .............................................
if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksum
");
SHOW_BOOT_PROGRESS (-2);
return 1;
} 根据image type进行不同的处理,如果是linux kernel后面再处理: switch (hdr->ih_type) { .............................................
case IH_TYPE_KERNEL:
name = "Kernel Image";
break; .............................................
}
判断压缩类型,做不同拷贝处理。
未压缩:
如果镜像中load地址与第一个参数一致的话,意思是内核已经在loadaddr准备好了,无需处理。如果镜像中load地址与第一个参数不一致的话,就要从传递的地址拷贝到 image指定的loadaddr(hdr->ih_load)了。如果是压缩类型gzip或者bzip2:就调用相应解压缩函数,将镜像解压到image指定的loadaddr(hdr->ih_load)了。默认预留解压后的大小为8M。当然这个空间大小可以使用这个宏定义CFG_BOOTM_LEN来修改 switch (hdr->ih_comp) {
case IH_COMP_NONE:
if(ntohl(hdr->ih_load) == addr) {
printf (" XIP %s ... ", name);
} else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
size_t l = len;
void *to = (void *)ntohl(hdr->ih_load);
void *from = (void *)data; printf (" Loading %s ... ", name); while (l > 0) {
size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
WATCHDOG_RESET();
memmove (to, from, tail);
to += tail;
from += tail;
l -= tail;
}
#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
}
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
puts ("GUNZIP ERROR - must RESET board to recover
");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;
.............................................
}判断如果是内核,暂时不用处理
switch (hdr->ih_type) {
.............................................
case IH_TYPE_KERNEL:
case IH_TYPE_MULTI:
/* handled below */
break; 根据镜像的不同类型,调用不同的操作系统启动函数。
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break; .............................................
如果linux操作系统就调用do_bootm_linux ,其流程为:如果定义了CONFIG_CMDLINE_TAG,就从环境变量取出bootargs,准备传参数给内核。#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif 从镜像文件的头里提取entry point,即内核的入口地址。 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); 确认命令是否传递了initrd地址,如果有进行相应的处理校验ramdisk的magic num和头的CRC
打印头并校验数据CRC/*
* Check if there is an initrd image
*/
if (argc >= 3) { .............................................}根据配置,准备向内核传递相应的参数。setup_end_tag (bd);
#endif关闭cache和中断,启动内核。bd->bi_boot_params:传递给内核参数地址。tag类型
bd->bi_arch_number:CPU类型在各自单板的board_init函数中初始化。 cleanup_before_linux (); theKernel (0, bd->bi_arch_number, bd->bi_boot_params);