ARM平台U-boot启动内核命令如:
bootcmd=bootm 0xc4040014
在./common/cmd_bootm.c文件中,bootm命令对应的do_bootm函数,当分析uImage中信息发现OS是Linux时,调用./lib_arm/armlinux.c文件中的do_bootm_linux函数来启动Linux kernel。
do_bootm函数(bootm命令)分析
image_header_t header;
ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
……
if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
//判断运行bootm时是否指定了程序加载地址,若没有则使用默认的加载地址,load_addr在此函数前面是这样定义的: ulong load_addr = CFG_LOAD_ADDR;
……
memmove (&header, (char *)addr, sizeof(image_header_t));
//将image 的 header(u-boot添加的64Byte文件头)复制到header指向的内存。
if (ntohl(hdr->ih_magic) != IH_MAGIC) {
……
{
printf ("Bad Magic Number/n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
//判断文件头中的幻数是否为IH_MAGIC,所以如果不是u-boot镜像格式,会输出提示信息”Bad Magic Number”。
data = (ulong)&header;
len = sizeof(image_header_t);
checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0;
if (crc32 (0, (char *)data, len) != checksum) {
printf ("Bad Header Checksum/n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
//检查image 文件头header的CRC32校验和。
data = addr + sizeof(image_header_t);
len = ntohl(hdr->ih_size);
……
if (verify) {
if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC/n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
}
//检查image 数据部分data的校验和。
len_ptr = (ulong *)data;
#if defined(__PPC__)
if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
if (hdr->ih_arch != IH_CPU_I386)
……
#else
# error Unknown CPU type
#endif
{
printf ("Unsupported Architecture 0x%x/n", hdr->ih_arch);
SHOW_BOOT_PROGRESS (-4);
return 1;
}
//检查image header 中的arch类型是否正确。
switch (hdr->ih_type) {
……
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
……
default: printf ("Wrong Image Type for %s command/n", cmdtp->name);
SHOW_BOOT_PROGRESS (-5);
return 1;
}
//判断image的类型
……
switch (hdr->ih_comp) {
case IH_COMP_NONE:
……
case IH_COMP_GZIP:
……
#ifdef CONFIG_BZIP2
……
default:
……
}
//根据image所采用的压缩类型,将image解压到hdr->ih_load指向的地址,这个ih_load就是在做内核镜像时mkimage中的-a选项指定的地址,-a选项指定的是内核解压后的地址。
……
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
……
#ifdef CONFIG_RAW_FLASH_PART
/* for raw flash partition */
init_bd_kdcpart();
#endif
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
……
}
//内核已经解压完了,接下来启动Linux内核,把控制权传递给了do_bootm_linux函数。
do_bootm_linux函数分析
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
……
ulong initrd_start, initrd_end; // initrd的起始地址和结束地址
ulong data;
void (*theKernel)(int zero, int arch, uint params);
// Linux 内核的入口参数,zero = 0,arch为平台编号,params为传递给内核的参数在内存中的地址。
image_header_t *hdr = &header;
bd_t *bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif
//如果在include/configs/.h定义了CONFIG_CMDLINE_TAG则将bootargs环境变量传递给内核。所以如果发现无法向内核传递参数,应该检查一下CONFIG_CMDLINE_TAG是否定义。
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
// hdr为指向image header的指针,hdr->ih_ep就是我们用mkimage创建image时-e选项的参数:内核的入口地址。把函数地址hdr->ih_ep(Entry Point Address)赋给theKernel。
……
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
……
memcpy((void *)(bd->bi_boot_params + BD_OFFSET), (const void *)bd, sizeof(bd_t));
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
//给内核传参数(含根文件系统地址)。这里bd->bi_arch_number和bd->bi_boot_params在具体开发板的board_init函数里面初始化。
gd->bd->bi_arch_number = 387;
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x00000100;
gd->flags = 0;
}