一、images变量
无论bootz 还是 bootm 命令,在启动 Linux 内核的时候都会用到一个重要的全局变量:images
images 在文件 cmd/bootm.c 中有如下定义:
bootm_headers_t images; /* pointers to os/initrd/fdt images */
images 是 bootm_headers_t 类型的全局变量, bootm_headers_t 是个 boot 头结构体,在文件
include/image.h 中的定义如下(删除了一些条件编译代码):
304 typedef struct bootm_headers {
305 /*
306 * Legacy os image header, if it is a multi component image
307 * then boot_get_ramdisk() and get_fdt() will attempt to get
308 * data from second and third component accordingly.
309 */
310 image_header_t *legacy_hdr_os; /* image header pointer */
311 image_header_t legacy_hdr_os_copy; /* header copy */
312 ulong legacy_hdr_valid;
313
......
333
334 #ifndef USE_HOSTCC
335 image_info_t os; /* OS 镜像信息 */
336 ulong ep; /* OS 入口点 */
337
338 ulong rd_start, rd_end; /* ramdisk 开始和结束位置 */
339
340 char *ft_addr; /* 设备树地址 */
341 ulong ft_len; /* 设备树长度 */
342
343 ulong initrd_start; /* initrd 开始位置 */
344 ulong initrd_end; /* initrd 结束位置 */
345 ulong cmdline_start; /* cmdline 开始位置 */
346 ulong cmdline_end; /* cmdline 结束位置 */
347 bd_t *kbd;
348 #endif
349
350 int verify; /* getenv("verify")[0] != 'n' */
351
352 #define BOOTM_STATE_START (0x00000001)
353 #define BOOTM_STATE_FINDOS (0x00000002)
354 #define BOOTM_STATE_FINDOTHER (0x00000004)
355 #define BOOTM_STATE_LOADOS (0x00000008)
356 #define BOOTM_STATE_RAMDISK (0x00000010)
357 #define BOOTM_STATE_FDT (0x00000020)
358 #define BOOTM_STATE_OS_CMDLINE (0x00000040)
359 #define BOOTM_STATE_OS_BD_T (0x00000080)
360 #define BOOTM_STATE_OS_PREP (0x00000100)
361 #define BOOTM_STATE_OS_FAKE_GO (0x00000200)/*'Almost' run the OS*/
362 #define BOOTM_STATE_OS_GO (0x00000400)
363 int state;
364
365 #ifdef CONFIG_LMB
366 struct lmb lmb; /* 内存管理相关,不深入研究 */
367 #endif
368 } bootm_headers_t;
335 行,的 os 成员变量是 image_info_t 类型的,为系统镜像信息
352~362 行,这 11 个宏定义表示 BOOT 的不同阶段
接下来看一下335行的结构体 image_info_t,也就是系统镜像信息结构体
此结构体在文件include/image.h 中的定义如下:
292 typedef struct image_info {
293 ulong start, end; /* blob 开始和结束位置*/
294 ulong image_start, image_len; /* 镜像起始地址(包括 blob)和长度 */
295 ulong load; /* 系统镜像加载地址*/
296 uint8_t comp, type, os; /* 镜像压缩、类型, OS 类型 */
297 uint8_t arch; /* CPU 架构 */
298 } image_info_t;
全局变量 images 会在 bootz 命令的执行中频繁使用到,相当于 Linux 内核启动的“灵魂”
二、do_bootz 函数
bootz 命令的执行函数为 do_bootz,在文件 cmd/bootm.c 中有如下定义:
622 int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const
argv[])
623 {
624 int ret;
625
626 /* Consume 'bootz' */
627 argc--; argv++;
628
629 if (bootz_start(cmdtp, flag, argc, argv, &images))
630 return 1;
631
632 /*
633 * We are doing the BOOTM_STATE_LOADOS state ourselves, so must
634 * disable interrupts ourselves
635 */
636 bootm_disable_interrupts();
637
638 images.os.os = IH_OS_LINUX;
639 ret = do_bootm_states(cmdtp, flag, argc, argv,
640 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
641 BOOTM_STATE_OS_GO,
642 &images, 1);
643
644 return ret;
645 }
629 行,调用 bootz_start 函数
636 行,调用函数 bootm_disable_interrupts 关闭中断
638 行,设置 images.os.os 为 IH_OS_LINUX,也就是设置系统镜像为 Linux,表示我们
要启动的是 Linux 系统!后面会用到 images.os.os 来挑选具体的启动函数
639 行,调用函数 do_bootm_states 来执行不同的 BOOT 阶段,这里要执行的 BOOT 阶
段有: BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 和BOOTM_STATE_OS_GO
三、bootz_start 函数
bootz_srart 函数也定义在文件 cmd/bootm.c 中,函数内容如下:
578 static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,
579 char * const argv[], bootm_headers_t *images)
580 {
581 int ret;
582 ulong zi_start, zi_end;
583
584 ret = do_bootm_states(cmdtp, flag, argc, argv,
585 BOOTM_STATE_START, images, 1);
586
587 /* Setup Linux kernel zImage entry point */
588 if (!argc) {
589 images->ep = load_addr;
590 debug("* kernel: default image load address = 0x%08lx\n",
591 load_addr);
592 } else {
593 images->ep = simple_strtoul(argv[0], NULL, 16);
594 debug("* kernel: cmdline image address = 0x%08lx\n",
595 images->ep);
596 }
597
598 ret = bootz_setup(images->ep, &zi_start, &zi_end);
599 if (ret != 0)
600 return 1;
601
602 lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);
603
604 /*
605 * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not
606 * have a header that provide this informaiton.
607 */
608 if (bootm_find_images(flag, argc, argv))
609 return 1;
610
......
619 return 0;
620 }
584 行,调用函数 do_bootm_states,执行 BOOTM_STATE_START 阶段
593 行,设置 images 的 ep 成员变量,也就是系统镜像的入口点,使用 bootz 命令启动系统的时候就会设置系统在 DRAM 中的存储位置,这个存储位置就是系统镜像的入口点
因此 images->ep=0X80800000
598 行,调用 bootz_setup 函数,此函数会判断当前的系统镜像文件是否为 Linux 的镜
像文件,并且会打印出镜像相关信息, bootz_setup 函数稍后会讲解
608 行,调用函数 bootm_find_images 查找 ramdisk 和设备树(dtb)文件,但是我们没有
用到 ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件,此函数稍后也会讲解
看一下 bootz_setup 函数,此函数定义在文件 arch/arm/lib/bootm.c 中,函数内容如下
370 #define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
371
372 int bootz_setup(ulong image, ulong *start, ulong *end)
373 {
374 struct zimage_header *zi;
375
376 zi = (struct zimage_header *)map_sysmem(image, 0);
377 if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {
378 puts("Bad Linux ARM zImage magic!\n");
379 return 1;
380 }
381
382 *start = zi->zi_start;
383 *end = zi->zi_end;
384
385 printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n", image, *start, *end);
386
387 return 0;
388 }
370 行,宏 LINUX_ARM_ZIMAGE_MAGIC 就是 ARM Linux 系统魔术数
376 行,从传递进来的参数 image(也就是系统镜像首地址)中获取 zimage 头
zImage 头结构体为 zimage_header
377~380 行,判断 image 是否为 ARM 的 Linux 系统镜像,如果不是的话就直接返回,
并且打印出“Bad Linux ARM zImage magic!”
382、 383 行,初始化函数 bootz_setup 的参数 start 和 end
385 行,打印启动信息,如果 Linux 系统镜像正常的话就会输出
看一下函数 bootm_find_images,此函数定义在文件 common/bootm.c 中,函数内容如下:
225 int bootm_find_images(int flag, int argc, char * const argv[])
226 {
227 int ret;
228
229 /* find ramdisk */
230 ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,
231 &images.rd_start, &images.rd_end);
232 if (ret) {
233 puts("Ramdisk image is corrupt or invalid\n");
234 return 1;
235 }
236
237 #if defined(CONFIG_OF_LIBFDT)
238 /* find flattened device tree */
239 ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,
240 &images.ft_addr, &images.ft_len);
241 if (ret) {
242 puts("Could not find a valid device tree\n");
243 return 1;
244 }
245 set_working_fdt_addr((ulong)images.ft_addr);
246 #endif
......
258 return 0;
259 }
230~235 行,是跟查找 ramdisk,但是这没有用到 ramdisk,因此这部分代码不用管
237~244 行,是查找设备树(dtb)文件,找到以后就将设备树的起始地址和长度分别写到images 的 ft_addr 和 ft_len 成员变量中。使用 bootz 启动 Linux 的时候已经指明了设备树在DRAM 中的存储地址,因此 images.ft_addr=0X83000000,长度根据具体的设备树文件而定,比如现在使用的设备树文件长度为 0X8C81,因此 images.ft_len=0X8C81。bootz_start 函数就了解到这
四、do_bootm_states 函数
do_bootz 最 后 调 用 的 就 是 函 数 do_bootm_states , 而 且 在 bootz_start 中 也 调 用 了
此函 数 定 义 在 文件common/bootm.c 中,函数代码如下:
591 int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[],
592 int states, bootm_headers_t *images, int boot_progress)
593 {
594 boot_os_fn *boot_fn;
595 ulong iflag = 0;
596 int ret = 0, need_boot_fn;
597
598 images->state |= states;
599
600 /*
601 * Work through the states and see how far we get. We stop on
602 * any error.
603 */
604 if (states & BOOTM_STATE_START)
605 ret = bootm_start(cmdtp, flag, argc, argv);
606
607 if (!ret && (states & BOOTM_STATE_FINDOS))
608 ret = bootm_find_os(cmdtp, flag, argc, argv);
609
610 if (!ret && (states & BOOTM_STATE_FINDOTHER)) {
611 ret = bootm_find_other(cmdtp, flag, argc, argv);
612 argc = 0; /* consume the args */
613 }
614
615 /* Load the OS */
616 if (!ret && (states & BOOTM_STATE_LOADOS)) {
617 ulong load_end;
618
619 iflag = bootm_disable_interrupts();
620 ret = bootm_load_os(images, &load_end, 0);
621 if (ret == 0)
622 lmb_reserve(&images->lmb, images->os.load,
623 (load_end - images->os.load));
624 else if (ret && ret != BOOTM_ERR_OVERLAP)
625 goto err;
626 else if (ret == BOOTM_ERR_OVERLAP)
627 ret = 0;
628 #if defined(CONFIG_SILENT_CONSOLE)
&& !defined(CONFIG_SILENT_U_BOOT_ONLY)
629 if (images->os.os == IH_OS_LINUX)
630 fixup_silent_linux();
631 #endif
632 }
633
634 /* Relocate the ramdisk */
635 #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
636 if (!ret && (states & BOOTM_STATE_RAMDISK)) {
637 ulong rd_len = images->rd_end - images->rd_start;
638
639 ret = boot_ramdisk_high(&images->lmb, images->rd_start,
640 rd_len, &images->initrd_start, &images->initrd_end);
641 if (!ret) {
642 setenv_hex("initrd_start", images->initrd_start);
643 setenv_hex("initrd_end", images->initrd_end);
644 }
645 }
646 #endif
647 #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB)
648 if (!ret && (states & BOOTM_STATE_FDT)) {
649 boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
650 ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,
651 &images->ft_len);
652 }
653 #endif
654
655 /* From now on, we need the OS boot function */
656 if (ret)
657 return ret;
658 boot_fn = bootm_os_get_boot_func(images->os.os);
659 need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
660 BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
661 BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
662 if (boot_fn == NULL && need_boot_fn) {
663 if (iflag)
664 enable_interrupts();
665 printf("ERROR: booting os '%s' (%d) is not supported\n",
666 genimg_get_os_name(images->os.os), images->os.os);
667 bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
668 return 1;
669 }
670
671 /* Call various other states that are not generally used */
672 if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
673 ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
674 if (!ret && (states & BOOTM_STATE_OS_BD_T))
675 ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
676 if (!ret && (states & BOOTM_STATE_OS_PREP))
677 ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
678
679 #ifdef CONFIG_TRACE
680 /* Pretend to run the OS, then run a user command */
681 if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
682 char *cmd_list = getenv("fakegocmd");
683
684 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
685 images, boot_fn);
686 if (!ret && cmd_list)
687 ret = run_command_list(cmd_list, -1, flag);
688 }
689 #endif
690
691 /* Check for unsupported subcommand. */
692 if (ret) {
693 puts("subcommand not supported\n");
694 return ret;
695 }
696
697 /* Now run the OS! We hope this doesn't return */
698 if (!ret && (states & BOOTM_STATE_OS_GO))
699 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,657 return ret;
658 boot_fn = bootm_os_get_boot_func(images->os.os);
659 need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
660 BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
661 BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
662 if (boot_fn == NULL && need_boot_fn) {
663 if (iflag)
664 enable_interrupts();
665 printf("ERROR: booting os '%s' (%d) is not supported\n",
666 genimg_get_os_name(images->os.os), images->os.os);
667 bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
668 return 1;
669 }
670
671 /* Call various other states that are not generally used */
672 if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
673 ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
674 if (!ret && (states & BOOTM_STATE_OS_BD_T))
675 ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
676 if (!ret && (states & BOOTM_STATE_OS_PREP))
677 ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
678
679 #ifdef CONFIG_TRACE
680 /* Pretend to run the OS, then run a user command */
681 if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
682 char *cmd_list = getenv("fakegocmd");
683
684 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
685 images, boot_fn);
686 if (!ret && cmd_list)
687 ret = run_command_list(cmd_list, -1, flag);
688 }
689 #endif
690
691 /* Check for unsupported subcommand. */
692 if (ret) {
693 puts("subcommand not supported\n");
694 return ret;
695 }
696
697 /* Now run the OS! We hope this doesn't return */
698 if (!ret && (states & BOOTM_STATE_OS_GO))
699 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,657 return ret;
658 boot_fn = bootm_os_get_boot_func(images->os.os);
659 need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
660 BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
661 BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
662 if (boot_fn == NULL && need_boot_fn) {
663 if (iflag)
664 enable_interrupts();
665 printf("ERROR: booting os '%s' (%d) is not supported\n",
666 genimg_get_os_name(images->os.os), images->os.os);
667 bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
668 return 1;
669 }
670
671 /* Call various other states that are not generally used */
672 if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
673 ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
674 if (!ret && (states & BOOTM_STATE_OS_BD_T))
675 ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
676 if (!ret && (states & BOOTM_STATE_OS_PREP))
677 ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
678
679 #ifdef CONFIG_TRACE
680 /* Pretend to run the OS, then run a user command */
681 if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
682 char *cmd_list = getenv("fakegocmd");
683
684 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
685 images, boot_fn);
686 if (!ret && cmd_list)
687 ret = run_command_list(cmd_list, -1, flag);
688 }
689 #endif
690
691 /* Check for unsupported subcommand. */
692 if (ret) {
693 puts("subcommand not supported\n");
694 return ret;
695 }
696
697 /* Now run the OS! We hope this doesn't return */
698 if (!ret && (states & BOOTM_STATE_OS_GO))
699 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
657 return ret;
658 boot_fn = bootm_os_get_boot_func(images->os.os);
659 need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
660 BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
661 BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
662 if (boot_fn == NULL && need_boot_fn) {
663 if (iflag)
664 enable_interrupts();
665 printf("ERROR: booting os '%s' (%d) is not supported\n",
666 genimg_get_os_name(images->os.os), images->os.os);
667 bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
668 return 1;
669 }
670
671 /* Call various other states that are not generally used */
672 if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
673 ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
674 if (!ret && (states & BOOTM_STATE_OS_BD_T))
675 ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
676 if (!ret && (states & BOOTM_STATE_OS_PREP))
677 ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
678
679 #ifdef CONFIG_TRACE
680 /* Pretend to run the OS, then run a user command */
681 if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
682 char *cmd_list = getenv("fakegocmd");
683
684 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
685 images, boot_fn);
686 if (!ret && cmd_list)
687 ret = run_command_list(cmd_list, -1, flag);
688 }
689 #endif
690
691 /* Check for unsupported subcommand. */
692 if (ret) {
693 puts("subcommand not supported\n");
694 return ret;
695 }
696
697 /* Now run the OS! We hope this doesn't return */
698 if (!ret && (states & BOOTM_STATE_OS_GO))
699 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn);
..........
712 return ret;
713 }
函数 do_bootm_states根据不同 BOOT 状态执行不同的代码段,通过如下代码判断BOOT 状态:
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 状态
604、 605 行,处理 BOOTM_STATE_START 阶段, bootz_start 会执行这一段代码,这里
调用函数 bootm_start,此函数定义在文件 common/bootm.c 中,函数内容如下:
69 static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,
70 char * const argv[])
71 {
72 memset((void *)&images, 0, sizeof(images)); /* 清空 images */
73 images.verify = getenv_yesno("verify");/* 初始化 verfify 成员 */
74
75 boot_start_lmb(&images);
76
77 bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");
78 images.state = BOOTM_STATE_START;/* 设置状态为 BOOTM_STATE_START */
79
80 return 0;
81 }
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 函数
676 行,处理 BOOTM_STATE_OS_PREP 状态,调用函数 do_bootm_linux, do_bootm_linux
也是调用 boot_prep_linux 来完成具体的处理过程。 boot_prep_linux 主要用于处理环境变量
bootargs, bootargs 保存着传递给 Linux kernel 的参数
679~689 行,是处理 BOOTM_STATE_OS_FAKE_GO 状态的,但是要我们没用使能 TRACE
功能,因此宏 CONFIG_TRACE 也就没有定义,所以这段程序不会编译。
699 行,调用函数 boot_selected_os 启动 Linux 内核,此函数第 4 个参数为 Linux 系统镜
像头,第 5 个参数就是 Linux 系统启动函数 do_bootm_linux。 boot_selected_os 函数定义在文件
common/bootm_os.c 中,函数内容如下:
476 int boot_selected_os(int argc, char * const argv[], int state,
477 bootm_headers_t *images, boot_os_fn *boot_fn)
478 {
479 arch_preboot_os();
480 boot_fn(state, argc, argv, images);
......
490 return BOOTM_ERR_RESET;
491 }
480 行调用 boot_fn 函数,也就是 do_bootm_linux 函数来启动 Linux 内核
五、bootm_os_get_boot_func 函数
do_bootm_states 会调用 bootm_os_get_boot_func 来查找对应系统的启动函数,此函数定义
在文件 common/bootm_os.c 中,函数内容如下:
493 boot_os_fn *bootm_os_get_boot_func(int os)
494 {
495 #ifdef CONFIG_NEEDS_MANUAL_RELOC
496 static bool relocated;
497
498 if (!relocated) {
499 int i;
500
501 /* relocate boot function table */
502 for (i = 0; i < ARRAY_SIZE(boot_os); i++)
503 if (boot_os[i] != NULL)
504 boot_os[i] += gd->reloc_off;
505
506 relocated = true;
507 }
508 #endif
509 return boot_os[os];
510 }
495~508 行是条件编译,在本 uboot 中没有用到,因此这段代码无效,只有 509 行有效
509 行中 boot_os 是个数组,这个数组里面存放着不同的系统对应的启动函数。 boot_os 也定
义在文件 common/bootm_os.c 中,如下所示
435 static boot_os_fn *boot_os[] = {
436 [IH_OS_U_BOOT] = do_bootm_standalone,
437 #ifdef CONFIG_BOOTM_LINUX
438 [IH_OS_LINUX] = do_bootm_linux,
439 #endif
......
465 #ifdef CONFIG_BOOTM_OPENRTOS
466 [IH_OS_OPENRTOS] = do_bootm_openrtos,
467 #endif
468 };
438 行就是 Linux 系统对应的启动函数: do_bootm_linux
六、do_bootm_linux 函数
经过前面的分析知道了 do_bootm_linux 就是最终启动 Linux 内核的函数,此函数定义在文件 arch/arm/lib/bootm.c,函数内容如下:
339 int do_bootm_linux(int flag, int argc, char * const argv[],
340 bootm_headers_t *images)
341 {
342 /* No need for those on ARM */
343 if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
344 return -1;
345
346 if (flag & BOOTM_STATE_OS_PREP) {
347 boot_prep_linux(images);
348 return 0;
349 }
350
351 if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
352 boot_jump_linux(images, flag);
353 return 0;
354 }
355
356 boot_prep_linux(images);
357 boot_jump_linux(images, flag);
358 return 0;
359 }
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
352 行,执行函数 boot_jump_linux,此函数定义在文件 arch/arm/lib/bootm.c 中,函数内容如下:
272 static void boot_jump_linux(bootm_headers_t *images, int flag)
273 {
274 #ifdef CONFIG_ARM64
......
292 #else
293 unsigned long machid = gd->bd->bi_arch_number;
294 char *s;
295 void (*kernel_entry)(int zero, int arch, uint params);
296 unsigned long r2;
297 int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
298
299 kernel_entry = (void (*)(int, int, uint))images->ep;
300
301 s = getenv("machid");
302 if (s) {
303 if (strict_strtoul(s, 16, &machid) < 0) {
304 debug("strict_strtoul failed!\n");
305 return;
306 }
307 printf("Using machid 0x%lx from environment\n", machid);
308 }
309
310 debug("## Transferring control to Linux (at address %08lx)" \
311 "...\n", (ulong) kernel_entry);
312 bootstage_mark(BOOTSTAGE_ID_RUN_OS);
313 announce_and_cleanup(fake);
314
315 if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
316 r2 = (unsigned long)images->ft_addr;
317 else
318 r2 = gd->bd->bi_boot_params;
319
......
328 kernel_entry(0, machid, r2);
329 }
330 #endif
331 }
274~292 行,是 64 位 ARM 芯片对应的代码, Cortex-A7 是 32 位芯片,因此用不到
293 行,变量 machid 保存机器 ID,如果不使用设备树的话这个机器 ID 会被传递给 Linux内核, Linux 内核会在自己的机器 ID 列表里面查找是否存在与 uboot 传递进来的 machid 匹配的项目,如果存在就说明 Linux 内核支持这个机器,那么 Linux 就会启动!如果使用设备树的话这个 machid 就无效了,设备树存有一个“兼容性”这个属性, Linux 内核会比较“兼容性”属性的值(字符串)来查看是否支持这个机器。
295 行,函数 kernel_entry,看名字“内核_进入”,说明此函数是进入 Linux 内核的,此函数有三个参数: zero, arch, params,第一个参数 zero 同样为 0;第二个参数为机器 ID; 第三个参数 ATAGS 或者设备树(DTB)首地址, ATAGS 是传统的方法,用于传递一些命令行信息之类的,如果使用设备树的话就要传递设备树(DTB)。
299 行,获取 kernel_entry 函数,函数 kernel_entry 并不是 uboot 定义的,而是 Linux 内核定义的, Linux 内核镜像文件的第一行代码就是函数 kernel_entry,而 images->ep 保存着 Linux内核镜像的起始地址,起始地址保存的正是 Linux 内核第一行代码
313 行,调用函数 announce_and_cleanup 来打印一些信息并做一些清理工作,此函数定义在文件 arch/arm/lib/bootm.c 中,函数内容如下:
72 static void announce_and_cleanup(int fake)
73 {
74 printf("\nStarting kernel ...%s\n\n", fake ?
75 "(fake run for tracing)" : "");
76 bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
......
87 cleanup_before_linux();
88 }
74 行,在启动 Linux 之前输出“Starting kernel ...”信息
87 行,调用 cleanup_before_linux 函数做一些清理工作
回到函数 boot_jump_linux定义, 315~318 行是设置寄存器 r2 的值,那为什么要设置 r2 的呢? Linux 内核一开始是汇编代码,因此函数 kernel_entry 就是个汇编函数。向汇编函数传递参数要使用 r0、r1 和 r2(参数数量不超过 3 个的时候),所以 r2 寄存器就是函数 kernel_entry 的第三个参数
316 行,如果使用设备树的话, r2 应该是设备树的起始地址,而设备树地址保存在 images的 ftd_addr 成员变量中
317 行,如果不使用设备树的话, r2 应该是 uboot 传递给 Linux 的参数起始地址,也就是环境变量 bootargs 的值
328 行,调用 kernel_entry 函数进入 Linux 内核,此行将一去不复返, uboot 的使命也就完成了!
总结一下 bootz 命令的执行过程,如图所示:

bootz_start 主要用于初始化 images 的相关成员变量,为启动做准备工作