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\n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
);
.............................................
if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksum\n");
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\n");
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);