通过日志打印或者打印环境变量,我们可以得到:
bootcmd="run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
查看单板头文件include\configs\mx6ullevk.h得到:
"findfdt="\
"if test $fdt_file = undefined; then " \
"if test $board_name = ULZ-EVK && test $board_rev = 14X14; then " \
"setenv fdt_file imx6ulz-14x14-evk.dtb; fi; " \
"if test $board_name = EVK && test $board_rev = 9X9; then " \
"setenv fdt_file imx6ull-9x9-evk.dtb; fi; " \
"if test $board_name = EVK && test $board_rev = 14X14; then " \
"setenv fdt_file imx6ull-14x14-evk.dtb; fi; " \
"if test $fdt_file = undefined; then " \
"echo WARNING: Could not determine dtb to use; " \
"fi; " \
"fi;\0" \
"loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \
loadimage=ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}\0"
loadfdt=ext2load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}\0" \
"mmcboot=echo Booting from mmc ...; " \
"run mmcargs; " \
"if test ${tee} = yes; then " \
"run loadfdt; run loadtee; bootm ${tee_addr} - ${fdt_addr}; " \
"else " \
"if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \
"if run loadfdt; then " \
"bootz ${loadaddr} - ${fdt_addr}; " \
"else " \
"if test ${boot_fdt} = try; then " \
"bootz; " \
"else " \
"echo WARN: Cannot load the DT; " \
"fi; " \
"fi; " \
"else " \
"bootz; " \
"fi; " \
"fi;\0" \
可以看出首先使用的是mmcboot来启动linux。主要分为三步loadimage loadfdt 和bootz ${loadaddr} - ${fdt_addr},即先将zImage和dtb文件拷贝到内存里面,然后使用bootz引导启动,下面我们来分析bootz。
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int ret;
/* Consume 'bootz' */
argc--; argv++;
if (bootz_start(cmdtp, flag, argc, argv, &images)) return 1;
// We are doing the BOOTM_STATE_LOADOS state ourselves, so must disable interrupts ourselves
bootm_disable_interrupts();
images.os.os = IH_OS_LINUX;
ret = do_bootm_states(cmdtp, flag, argc, argv,
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
BOOTM_STATE_RAMDISK |
#endif
BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
BOOTM_STATE_OS_GO,
&images, 1);
return ret;
}
//主要根据bootz 80800000 - 8300000 传入的参数 初始化images变量
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], bootm_headers_t *images)
{
int ret;
ulong zi_start, zi_end;
ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, images, 1);
/* Setup Linux kernel zImage entry point */
images->ep = simple_strtoul(argv[0], NULL, 16);
ret = bootz_setup(images->ep, &zi_start, &zi_end);
if (ret != 0)
return 1;
lmb_reserve(&images->lmb, images->ep, zi_end - zi_start); //内存相关的
//查找设备树(dtb)文件,找到以后就将设备树的起始地址和长度分别写到images 的 ft_addr 和 ft_len 成员变量中
if (bootm_find_images(flag, argc, argv))
return 1;
return 0;
}
int bootz_setup(ulong image, ulong *start, ulong *end)
{
struct arm_z_header *zi = (struct arm_z_header *)image;
//判断存放的zImage magic是否为LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {
puts("Bad Linux ARM zImage magic!\n");
return 1;
}
*start = zi->zi_start;
*end = zi->zi_end;
#ifndef CONFIG_SPL_FRAMEWORK
printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n",image, *start, *end);
#endif
return 0;
}
ulong bootm_disable_interrupts(void)
{
iflag = disable_interrupts();//空函数
usb_stop(); //turn off USB to prevent the host controller from writing to the SDRAM while Linux is booting
}
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
memset((void *)&images, 0, sizeof(images)); images清零
images.verify = env_get_yesno("verify"); //获取环境变量 verify的值判断其是否是l y Y t T中一个
boot_start_lmb(&images); //内存分配相关,暂不分析
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");//空函数
images.state = BOOTM_STATE_START;
return 0;
}
boot_os_fn *bootm_os_get_boot_func(int os)
{
return boot_os[os];
}
精简后:
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
int states, bootm_headers_t *images, int boot_progress)
{
boot_os_fn *boot_fn;
ulong iflag = 0;
int ret = 0, need_boot_fn;
images->state |= states;
if (states & BOOTM_STATE_START)
ret = bootm_start(cmdtp, flag, argc, argv);
boot_fn = bootm_os_get_boot_func(images->os.os);得到的函数为do_bootm_linux
ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
主要执行boot_fn函数
ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn);
return ret;
}
所有boot工作其实都在下面这个函数
int do_bootm_linux(int flag, int argc, char * const argv[],
bootm_headers_t *images)
{
if (flag & BOOTM_STATE_OS_PREP) {
boot_prep_linux(images);
return 0;
}
if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
boot_jump_linux(images, flag);
return 0;
}
return 0;
}
static void boot_prep_linux(bootm_headers_t *images)
{
char *commandline = env_get("bootargs");
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
debug("using: FDT\n");
if (image_setup_linux(images)) {
printf("FDT creation failed! hanging...");
hang();
}
}
}
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;
debug("## Transferring control to Linux (at address %08lx)" "...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
r2 = (unsigned long)images->ft_addr;
kernel_entry(0, machid, r2);//至此就跳转到内核运行了
}
}
总结:流程其实很简单,首先通过bootz 传入的loadaddr和fdt_addr 来初始化bootm_headers_t *images,然后做一些准备工作后就跳转到内核了。
版权声明:本文为u011728490原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。