bootcmd执行过程

通过日志打印或者打印环境变量,我们可以得到:

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版权协议,转载请附上原文出处链接和本声明。