Skip to content

LV205-U-Boot启动内核

一、images 全局变量

不管是 bootz 还是 bootm 命令,在启动 Linux 内核的时候都会用到一个重要的全局变量:images,全局变量 images 会在 bootz 命令的执行中频繁使用到,相当于 Linux 内核启动的“灵魂”。images 变量在文件 cmd/bootm.c 中有如下定义:

c
bootm_headers_t images;		/* pointers to os/initrd/fdt images */

images 是 bootm_headers_t 类型的全局变量, bootm_headers_t 是个 boot 头结构体,在文件 include/image.h 中的定义如下(删除了一些条件编译代码):

c
typedef struct bootm_headers
{
	/*
	* Legacy os image header, if it is a multi component image
	* then boot_get_ramdisk() and get_fdt() will attempt to get
	* data from second and third component accordingly.
	*/
	image_header_t *legacy_hdr_os; /* image header pointer */
	image_header_t legacy_hdr_os_copy; /* header copy */
	ulong legacy_hdr_valid;

// 中间的省略 ...

#ifndef USE_HOSTCC
	image_info_t os; /* OS 镜像信息 */
	ulong ep; /* OS 入口点 */

	ulong rd_start, rd_end; /* ramdisk 开始和结束位置 */

	char *ft_addr; /* 设备树地址 */
	ulong ft_len; /* 设备树长度 */

	ulong initrd_start; /* initrd 开始位置 */
	ulong initrd_end; /* initrd 结束位置 */
	ulong cmdline_start; /* cmdline 开始位置 */
	ulong cmdline_end; /* cmdline 结束位置 */
	bd_t *kbd;
#endif

	int verify; /* getenv("verify")[0] != 'n' */

#define BOOTM_STATE_START (0x00000001)
#define BOOTM_STATE_FINDOS (0x00000002)
#define BOOTM_STATE_FINDOTHER (0x00000004)
#define BOOTM_STATE_LOADOS (0x00000008)
#define BOOTM_STATE_RAMDISK (0x00000010)
#define BOOTM_STATE_FDT (0x00000020)
#define BOOTM_STATE_OS_CMDLINE (0x00000040)
#define BOOTM_STATE_OS_BD_T (0x00000080)
#define BOOTM_STATE_OS_PREP (0x00000100)
#define BOOTM_STATE_OS_FAKE_GO (0x00000200)/*'Almost' run the OS*/
#define BOOTM_STATE_OS_GO (0x00000400)
	int state;

#ifdef CONFIG_LMB
	struct lmb lmb; /* 内存管理相关,不深入研究 */
#endif
} bootm_headers_t;

第 15 行(第 335 行): os 成员变量是 image_info_t 类型的,为系统镜像信息。 结构体 image_info_t,也就是系统镜像信息结构体,此结构体在文件 include/image.h 中的定义如下:

c
typedef struct image_info {
	ulong		start, end;		/* start/end of blob */
	ulong		image_start, image_len; /* start of image within blob, len of image */
	ulong		load;			/* load addr for the image */
	uint8_t		comp, type, os;		/* compression, type of image, os type */
	uint8_t		arch;			/* CPU architecture */
} image_info_t;

第 32 ~ 42 行(第 352 ~ 362 行):这 11 个宏定义表示 BOOT 的不同阶段。

二、do_bootz 函数

前边我们分析 uboot 启动流程的时候有分析过,每一个命令最后执行的都是命令对应的 do_xxx 函数。

1. do_bootz 在哪?

该函数在 cmd/bootm.c文件中定义。

2. 做了什么?

我们以下边的行号进行分析:

c
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,
			      BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
			      BOOTM_STATE_OS_GO,
			      &images, 1);

	return ret;
}

第 8 行(第 629 行):调用 bootz_start 函数, bootz_start 函数执行过程后边会再详细分析。

第 15 行(第 636 行):调用函数 bootm_disable_interrupts 关闭中断。

第 17 行(第 638 行):设置 images.os.os 为 IH_OS_LINUX,也就是设置系统镜像为 Linux,表示我们要启动的是 Linux 系统!后面会用到 images.os.os 来挑选具体的启动函数。

第 18 行(第 639 行):调用函数 do_bootm_states 来执行不同的 BOOT 阶段,这里要执行的 BOOT 阶段有:BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 和 BOOTM_STATE_OS_GO。

三、bootz_start 函数

bootz_start 主要用于初始化 images 的相关成员变量。

1. bootz_start 函数在哪?

bootz_start 函数也定义在文 cmd/bootm.c中,函数内容如下:

2. 做了什么?

我们以下边的行号进行分析:

c
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 */
	if (!argc) {
		images->ep = load_addr;
		debug("*  kernel: default image load address = 0x%08lx\n",
				load_addr);
	} else {
		images->ep = simple_strtoul(argv[0], NULL, 16);
		debug("*  kernel: cmdline image address = 0x%08lx\n",
			images->ep);
	}

	ret = bootz_setup(images->ep, &zi_start, &zi_end);
	if (ret != 0)
		return 1;

	lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);

	/*
	 * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not
	 * have a header that provide this informaiton.
	 */
	if (bootm_find_images(flag, argc, argv))
		return 1;

#ifdef CONFIG_SECURE_BOOT
	extern uint32_t authenticate_image(
			uint32_t ddr_start, uint32_t image_size);
	if (authenticate_image(images->ep, zi_end - zi_start) == 0) {
		printf("Authenticate zImage Fail, Please check\n");
		return 1;
	}
#endif
	return 0;
}

第 7 行(第 584 行):调用函数 do_bootm_states,执行 BOOTM_STATE_START 阶段。

第 16 行(第 593 行):设置 images 的 ep 成员变量,也就是系统镜像的入口点,使用 bootz 命令启动系统的时候就会设置系统在 DRAM 中的存储位置,这个存储位置就是系统镜像的入口点,因此 images-> ep = 0X80800000。

第 21 行(第 598 行)调用 bootz_setup 函数,此函数会判断当前的系统镜像文件是否为 Linux 的镜像文件,并且会打印出镜像相关信息, 这个函数后边会再详细说明。

第 31 行(第 608 行):调用函数 bootm_find_images 查找 ramdisk 和设备树(dtb)文件,但是我们没有用到 ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件,此函数后边会详细说明。

3. bootz_setup 函数

bootz_setup 函数,此函数定义在文件 arch/arm/lib/bootm.c 中,函数内容如下:

c
#define	LINUX_ARM_ZIMAGE_MAGIC	0x016f2818

int bootz_setup(ulong image, ulong *start, ulong *end)
{
	struct zimage_header *zi;

	zi = (struct zimage_header *)map_sysmem(image, 0);
	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;

	printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n", image, *start,
	      *end);

	return 0;
}

第 1 行(第 370 行):宏 LINUX_ARM_ZIMAGE_MAGIC 就是 ARM Linux 系统魔术数。

第 7 行(第 376 行):从传递进来的参数 image(也就是系统镜像首地址)中获取 zimage 头。 zImage 头结构体为 zimage_header。

第 8 ~ 11 行(第 377 ~ 380 行):判断 image 是否为 ARM 的 Linux 系统镜像,如果不是的话就直接返回,并且打印出“Bad Linux ARM zImage magic!”。

第 13、14 行(第 382、 383 行):初始化函数 bootz_setup 的参数 start 和 end。

第 16 行(第 385 行)打印启动信息,如果 Linux 系统镜像正常的话就会输出下图所示的信息:

image-20221016160914087

4. bootm_find_images 函数

bootm_find_images 函数,此函数定义在文件 common/bootm.c 中 ,函数内容如下:

c
/**
 * bootm_find_images - wrapper to find and locate various images
 * @flag: Ignored Argument
 * @argc: command argument count
 * @argv: command argument list
 *
 * boot_find_images() will attempt to load an available ramdisk,
 * flattened device tree, as well as specifically marked
 * "loadable" images (loadables are FIT only)
 *
 * Note: bootm_find_images will skip an image if it is not found
 *
 * @return:
 *     0, if all existing images were loaded correctly
 *     1, if an image is found but corrupted, or invalid
 */
int bootm_find_images(int flag, int argc, char * const argv[])
{
	int ret;

	/* find ramdisk */
	ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,
			       &images.rd_start, &images.rd_end);
	if (ret) {
		puts("Ramdisk image is corrupt or invalid\n");
		return 1;
	}

#if defined(CONFIG_OF_LIBFDT)
	/* find flattened device tree */
	ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,
			   &images.ft_addr, &images.ft_len);
	if (ret) {
		puts("Could not find a valid device tree\n");
		return 1;
	}
	set_working_fdt_addr((ulong)images.ft_addr);
#endif

#if defined(CONFIG_FIT)
	/* find all of the loadables */
	ret = boot_get_loadable(argc, argv, &images, IH_ARCH_DEFAULT,
			       NULL, NULL);
	if (ret) {
		printf("Loadable(s) is corrupt or invalid\n");
		return 1;
	}
#endif

	return 0;
}

第 21 ~ 27 行(第 230~235 行):查找 ramdisk,但是我们没有用到 ramdisk,因此这部分代码就不用关心了。

第 29 ~ 36 行(第 237~244 行):查找设备树(dtb)文件,找到以后就将设备树的起始地址和长度分别写到 images 的 ft_addr 和 ft_len 成员变量中。我们使用 bootz 启动 Linux 的时候已经指明了设备树在 DRAM 中的存储地址,因此 images.ft_addr = 0X83000000,长度根据具体的设备树文件而定,比如我现在使用的设备树文件长度为 0X8C81 的话,images.ft_len 就为 0X8C81。

三、do_bootm_states 函数

do_bootz 最后调用的就是函数 do_bootm_states ,而且在 bootz_start 中也调用了 do_bootm_states 函数。

1. do_bootm_states 函数在哪?

do_bootm_states 函数定义在 common/bootm.c 中。

2. 做了什么?

函数 do_bootm_states 根据不同的 BOOT 状态执行不同的代码段,通过如下代码来判断 BOOT 状态:

c
states & BOOTM_STATE_XXX

在 do_bootz 函数中会用到 BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 和 BOOTM_STATE_OS_GO 这三个 BOOT 状态,bootz_start 函数中会用到 BOOTM_STATE_START 这个 BOOT 状态。为了精简代码,方便分析,因此我们将上边的函数 do_bootm_states 进行精简,只留下下面这 4 个 BOOT 状态对应的处理代码:

c
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;

	/*
	* Work through the states and see how far we get. We stop on
	* any error.
	*/
	if (states & BOOTM_STATE_START)
		ret = bootm_start(cmdtp, flag, argc, argv);
	// 中间部分省略 ......

	/* From now on, we need the OS boot function */
	if (ret)
		return ret;
	boot_fn = bootm_os_get_boot_func(images->os.os);
	need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
	                         BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
	                         BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
	if (boot_fn == NULL && need_boot_fn)
	{
		if (iflag)
			enable_interrupts();
		printf("ERROR: booting os '%s' (%d) is not supported\n",
		       genimg_get_os_name(images->os.os), images->os.os);
		bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
		return 1;
	}

	// 中间部分省略 ......
	if (!ret && (states & BOOTM_STATE_OS_PREP))
		ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

	#ifdef CONFIG_TRACE
	/* Pretend to run the OS, then run a user command */
	if (!ret && (states & BOOTM_STATE_OS_FAKE_GO))
	{
		char *cmd_list = getenv("fakegocmd");

		ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
		                       images, boot_fn);
		if (!ret && cmd_list)
			ret = run_command_list(cmd_list, -1, flag);
	}
	#endif

	/* Check for unsupported subcommand. */
	if (ret)
	{
		puts("subcommand not supported\n");
		return ret;
	}

	/* Now run the OS! We hope this doesn't return */
	if (!ret && (states & BOOTM_STATE_OS_GO))
		ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
		                       images, boot_fn);
	// 中间部分省略 ......
	return ret;
}

第 15、16 行(第 604、 605 行):处理 BOOTM_STATE_START 阶段, bootz_start 会执行这一段代码,这里调用函数 bootm_start。函数定义在文件 common/bootm.c 中,函数内容如下:

c
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,
		       char * const argv[])
{
	memset((void *)&images, 0, sizeof(images));
	images.verify = getenv_yesno("verify");

	boot_start_lmb(&images);

	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");
	images.state = BOOTM_STATE_START;

	return 0;
}

第 22 行(第 658 行):通过函数 bootm_os_get_boot_func 来查找系统启动函数,参数 images-> os.os 就是系统类型,根据这个系统类型来选择对应的启动函数,在 do_bootz 中设置 images.os.os = IH_OS_LINUX。函数返回值就是找到的系统启动函数,这里找到的 Linux 系统启动函数为 do_bootm_linux,关于此函数查找系统启动函数的过程后边会再单独分析(第四节)。因此 boot_fn = do_bootm_linux ,后面执行 boot_fn 函数的地方实际上是执行的 do_bootm_linux 函数。

第 37 行(第 676 行):处理 BOOTM_STATE_OS_PREP 状态,调用函数 do_bootm_linux, do_bootm_linux 也是调用 boot_prep_linux 来完成具体的处理过程。 boot_prep_linux 主要用于处理环境变量 bootargs, bootargs 保存着传递给 Linux kernel 的参数。

第 40 ~ 51 行(第 679 ~ 689 行):是处理 BOOTM_STATE_OS_FAKE_GO 状态的,但是要我们没有使能 TRACE 功能,因此 CONFIG_TRACE 也就没有定义,所以这段程序不会编译。

第 62 行(第 699 行):调用函数 boot_selected_os 启动 Linux 内核,此函数第 4 个参数为 Linux 系统镜像头,第 5 个参数就是 Linux 系统启动函数 do_bootm_linux。boot_selected_os 函数定义在文件 common/bootm_os.c 中,函数内容如下:

c
int boot_selected_os(int argc, char * const argv[], int state,
		     bootm_headers_t *images, boot_os_fn *boot_fn)
{
	arch_preboot_os();
	boot_fn(state, argc, argv, images); // 调用 boot_fn 函数,也就是 do_bootm_linux 函数来启动 Linux 内核。  

	/* Stand-alone may return when 'autostart' is 'no' */
	if (images->os.type == IH_TYPE_STANDALONE ||
	    state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */
		return 0;
	bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED);
#ifdef DEBUG
	puts("\n## Control returned to monitor - resetting...\n");
#endif
	return BOOTM_ERR_RESET;
}

四、bootm_os_get_boot_func 函数

do_bootm_states 会调用 bootm_os_get_boot_func 来查找对应系统的启动函数 。

1. bootm_os_get_boot_func 函数在哪?

bootm_os_get_boot_func 函数定义在文件 common/bootm_os.c 中。

2. 做了什么

我们使用下边的行号进行分析:

c
boot_os_fn *bootm_os_get_boot_func(int os)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	static bool relocated;

	if (!relocated) {
		int i;

		/* relocate boot function table */
		for (i = 0; i < ARRAY_SIZE(boot_os); i++)
			if (boot_os[i] != NULL)
				boot_os[i] += gd->reloc_off;

		relocated = true;
	}
#endif
	return boot_os[os];
}

第 3 ~ 16 行(第 495~508 行):条件编译,在我们使用的这个 uboot 中没有用到,因此这段代码无效。

第 17 行(第 509 行):boot_os 是个数组,这个数组里面存放着不同的系统对应的启动函数。boot_os 定义在文件 common/bootm_os.c 中,如下所示:

c
static boot_os_fn *boot_os[] = {
	[IH_OS_U_BOOT] = do_bootm_standalone,
#ifdef CONFIG_BOOTM_LINUX
	[IH_OS_LINUX] = do_bootm_linux, // 是 Linux 系统对应的启动函数 do_bootm_linux 。  
#endif
	//...
};

五、do_bootm_linux 函数

1. do_bootm_linux 函数在哪?

do_bootm_linux 就是最终启动 Linux 内核的函数,此函数定义在文件 arch/arm/lib/bootm.c,函数内容如下:

2. 做了什么?

我们以下边的行号进行分析:

c
int do_bootm_linux(int flag, int argc, char * const argv[],
		   bootm_headers_t *images)
{
	/* No need for those on ARM */
	if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
		return -1;

	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;
	}

	boot_prep_linux(images);
	boot_jump_linux(images, flag);
	return 0;
}

第 13 行(第 351 行):如果参数 flag 等于 BOOTM_STATE_OS_GO 或者 BOOTM_STATE_OS_FAKE_GO 的话就执行 boot_jump_linux 函数。 boot_selected_os 函数在调用 do_bootm_linux 的时候会将 flag 设置为 BOOTM_STATE_OS_GO。

第 14 行(第 352 行):执行函数 boot_jump_linux,这个函数可以看下一小节。

3. boot_jump_linux

此函数定义在文件 arch/arm/lib/bootm.c 中,函数内容如下:

c
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
	void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
			void *res2);
	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);

	kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
				void *res2))images->ep;

	debug("## Transferring control to Linux (at address %lx)...\n",
		(ulong) kernel_entry);
	bootstage_mark(BOOTSTAGE_ID_RUN_OS);

	announce_and_cleanup(fake);

	if (!fake) {
		do_nonsec_virt_switch();
		kernel_entry(images->ft_addr, NULL, NULL, NULL);
	}
#else
	unsigned long machid = gd->bd->bi_arch_number;
	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;

	s = getenv("machid");
	if (s) {
		if (strict_strtoul(s, 16, &machid) < 0) {
			debug("strict_strtoul failed!\n");
			return;
		}
		printf("Using machid 0x%lx from environment\n", machid);
	}

	debug("## Transferring control to Linux (at address %08lx)" \
		"...\n", (ulong) kernel_entry);
	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
	announce_and_cleanup(fake);

	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
		r2 = (unsigned long)images->ft_addr;
	else
		r2 = gd->bd->bi_boot_params;

	if (!fake) {
#ifdef CONFIG_ARMV7_NONSEC
		if (armv7_boot_nonsec()) {
			armv7_init_nonsec();
			secure_ram_addr(_do_nonsec_entry)(kernel_entry,
							  0, machid, r2);
		} else
#endif
			kernel_entry(0, machid, r2);
	}
#endif
}

第 3 ~ 21 行(第 274~292 行):是 64 位 ARM 芯片对应的代码, Cortex-A7 是 32 位芯片,因此用不到。

第 22 行(第 293 行):变量 machid 保存机器 ID,如果不使用设备树的话这个机器 ID 会被传递给 Linux 内核, Linux 内核会在自己的机器 ID 列表里面查找是否存在与 uboot 传递进来的 machid 匹配的项目,如果存在就说明 Linux 内核支持这个机器,那么 Linux 就会启动!如果使用设备树的话这个 machid 就无效了,设备树存有一个“兼容性”这个属性, Linux 内核会比较“兼容性”属性的值(字符串)来查看是否支持这个机器。】、

第 24 行(第 295 行)函数 kernel_entry,看名字“内核_进入”,说明此函数是进入 Linux 内核的,也就是最终的启动内核函数。此函数有三个参数: zero, arch, params,第一个参数 zero 同样为 0;第二个参数为机器 ID; 第三个参数 ATAGS 或者设备树(DTB)首地址,ATAGS 是传统的方法,用于传递一些命令行信息,如果使用设备树的话就要传递设备树(DTB)。

第 28 行(第 299 行):获取 kernel_entry 函数,函数 kernel_entry 并不是 uboot 定义的,而是 Linux 内核定义的, Linux 内核镜像文件的第一行代码就是函数 kernel_entry,而 images-> ep 保存着 Linux 内核镜像的起始地址,起始地址保存的正是 Linux 内核第一行代码。

第 42 行(第 313 行):调用函数 announce_and_cleanup 来打印一些信息并做一些清理工作。此函数定义在文件 arch/arm/lib/bootm.c 中,函数内容如下:

c
static void announce_and_cleanup(int fake)
{
	printf("\nStarting kernel ...%s\n\n", fake ?
		"(fake run for tracing)" : "");
	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
#ifdef CONFIG_BOOTSTAGE_FDT
	bootstage_fdt_add_report();
#endif
#ifdef CONFIG_BOOTSTAGE_REPORT
	bootstage_report();
#endif

#ifdef CONFIG_USB_DEVICE
	udc_disconnect();
#endif
	cleanup_before_linux(); // 调用 cleanup_before_linux 函数做一些清理工作。  
}

该函数在启动 Linux 之前输出“Starting kernel ...”信息 。

image-20221016170514552

第 44 ~ 47 行(第 315 ~ 318 行):是设置寄存器 r2 的值,Linux 内核一开始是汇编代码,因此函数 kernel_entry 就是个汇编函数。向汇编函数传递参数要使用 r0、 r1 和 r2(参数数量不超过 3 个的时候),所以 r2 寄存器就是函数 kernel_entry 的第三个参数。

第 45 行(第 316 行):如果使用设备树的话, r2 应该是设备树的起始地址,而设备树地址保存在 images 的 ftd_addr 成员变量中。

第 46 行(第 317 行):如果不使用设备树的话, r2 应该是 uboot 传递给 Linux 的参数起始地址,也就是环境变量 bootargs 的值。

第 57 行(第 328 行):调用 kernel_entry 函数进入 Linux 内核, uboot 的使命也就完成了。

六、bootz 命令总结

bootz