做网站需要多少钱,外链网址,抖音seo排名源码,同城招聘网站自助建站在上一节中#xff0c;我们分析了U-BOOT初始化的流程#xff0c;最后就是进入U-Boot的命令行中执行了#xff0c;如果用户没有任何操作#xff0c;则经过固定延时后将执行默认的bootcmd环境变量里的指令#xff0c;那这里面肯定就是启动内核了。在U-BOOT简介及命令行指令详…在上一节中我们分析了U-BOOT初始化的流程最后就是进入U-Boot的命令行中执行了如果用户没有任何操作则经过固定延时后将执行默认的bootcmd环境变量里的指令那这里面肯定就是启动内核了。在U-BOOT简介及命令行指令详解中我们知道最后执行的应该是bootz指令那本节就来看一下这个指令如何启动内核的。 文章目录 1 bootz的执行时机1.1 do_bootz函数 2 bootz_start函数2.1 bootm_headers_t结构体2.2 do_bootm_states:BOOTM_STATE_START2.2.1 bootm_start 2.3 bootz_setup2.4 bootm_find_images 3 do_bootm_states启动内核3.1 bootm_os_get_boot_func3.2 boot_prep_linux3.3 boot_selected_os3.3.1 boot_jump_linux3.3.1.1 announce_and_cleanup3.3.1.2 kernel_entry 1 bootz的执行时机
首先我们来看一下在U-Boot命令行中输入bootz后会执行到什么函数中。在cmd/bootz.c中我们发现了下面的声明 这个指令定义也会注册到cli_loop中在U-Boot输入bootz命令时就会执行do_bootz函数。
1.1 do_bootz函数
下面就来看一下do_bootz函数 在这个函数中首先我们执行了bootz_start函数看样子应该是根据我们提供的参数做一些初始化处理。
接着执行bootm_disable_interrupts关闭中断。在执行 bootz 命令启动加载操作系统之前U-Boot处于加载操作系统的状态在这个阶段需要手动关闭中断确保中断不会干扰到关键的操作。但实际上在ARM中这个函数的实现为空。
最后调用do_bootm_states这里一方面是告诉U-Boot我们启动的是Linux另一方面第五个参数为启动需要执行的状态包括BOOTM_STATE_OS_PREP、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO。
其中BOOTM_STATE_RAMDISK用于实现RAM的文件系统这里没有执行
所以接下来我们就来分析一下bootz_start和do_bootm_states函数。
2 bootz_start函数
完整函数如下
2.1 bootm_headers_t结构体
在分析这个函数之前我们发现有一个bootm_headers_t的images变量在这里经常用到使用到的bootm_headers_t变量在bootm.c中有声明
bootm_headers_t images;完整结构体声明如下
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;/** The fit_ members are only used with FIT, but it involves a lot of* #ifdefs to avoid compiling that code. Since FIT is the standard* format, even for SPL, this extra data size seems worth it.*/const char *fit_uname_cfg; /* configuration node unit name */void *fit_hdr_os; /* os FIT image header */const char *fit_uname_os; /* os subimage node unit name */int fit_noffset_os; /* os subimage node offset */void *fit_hdr_rd; /* init ramdisk FIT image header */const char *fit_uname_rd; /* init ramdisk subimage node unit name */int fit_noffset_rd; /* init ramdisk subimage node offset */void *fit_hdr_fdt; /* FDT blob FIT image header */const char *fit_uname_fdt; /* FDT blob subimage node unit name */int fit_noffset_fdt;/* FDT blob subimage node offset */void *fit_hdr_setup; /* x86 setup FIT image header */const char *fit_uname_setup; /* x86 setup subimage node name */int fit_noffset_setup;/* x86 setup subimage node offset */#ifndef USE_HOSTCCimage_info_t os; /* os image info */ulong ep; /* entry point of OS */ulong rd_start, rd_end;/* ramdisk start/end */char *ft_addr; /* flat dev tree address */ulong ft_len; /* length of flat device tree */ulong initrd_start;ulong initrd_end;ulong cmdline_start;ulong cmdline_end;struct bd_info *kbd;
#endifint verify; /* env_get(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;#if defined(CONFIG_LMB) !defined(USE_HOSTCC)struct lmb lmb; /* for memory mgmt */
#endif
} bootm_headers_t;Legacy Image Header legacy_hdr_os指向传统OS镜像头部的指针。legacy_hdr_os_copy用于存储OS镜像头部的备份。legacy_hdr_valid标志变量指示legacy_hdr_os中的头部是否有效。 FIT Format Members fit_uname_cfg指向配置节点的名称适用于FIT格式。fit_hdr_os、fit_hdr_rd、fit_hdr_fdt、fit_hdr_setup指向FIT格式中OS、initramdisk、FDTblob和x86setup镜像头部的指针。fit_uname_os、fit_uname_rd、fit_uname_fdt、fit_uname_setup分别是FIT格式中OS、initramdisk、FDTblob和x86setup镜像节点的名称。fit_noffset_os、fit_noffset_rd、fit_noffset_fdt、fit_noffset_setup分别是FIT格式中OS、initramdisk、FDTblob和x86setup镜像节点的偏移。 Image Information and Execution Pointers os存储OS镜像信息的结构体。ep存储OS的入口点。rd_start、rd_endRAMdisk的起始和结束地址。ft_addr、ft_lenFlatDeviceTree平面设备树的地址和长度。initrd_start、initrd_endinitramdisk的起始和结束地址。cmdline_start、cmdline_end命令行的起始和结束地址。kbd键盘设备的信息。 Verification and Boot State Variables verify标志变量指示是否进行引导镜像的验证。state用于跟踪引导过程的状态通过定义的宏表示不同的引导状态。 Logical Memory Block(LMB) lmb用于内存管理的结构体。仅在配置启用LMB且非主机编译环境下可用。
该结构体主要用于存储引导过程中各个阶段的头部信息包括传统的OS镜像头部和FIT格式的头部信息。这些信息在引导过程中被不同的部分使用以确保正确加载和执行操作系统。
2.2 do_bootm_states:BOOTM_STATE_START
do_bootm_states这里states参数为BOOTM_STATE_START执行流程大概如下
do_bootm_statesret bootm_start(cmdtp, flag, argc, argv);boot_fn bootm_os_get_boot_func(images-os.os);...实际上只执行了bootm_start函数后面的boot_fn执行后续引导函数但在BOOTM_STATE_START状态下没有执行。
2.2.1 bootm_start 这个函数就是重置images变量然后调用一些函数来填充images里的一些参数最后设置当前images的状态为BOOTM_STATE_START。
其中boot_start_lmb函数lmb的意思是Logical Memory Block这个函数的作用是通过环境变量获取引导过程中可用内存的起始地址和大小然后赋值给images中的相关变量。这里执行了boot_start_lmb函数在2 bootz_start中函数就不会执行下面这一行了
lmb_reserve(images-lmb, images-ep, zi_end - zi_start);所以等会就不分析这个函数了。
2.3 bootz_setup
函数如下 这个函数比较简单了就是zImage镜像有一个固定的头固定头有一些固定的字段
#define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
#define BAREBOX_IMAGE_MAGIC 0x00786f62判断这两个字段其一是否匹配若匹配则表示这段内存中保存了zImage我们就把zImage所在内存的头尾赋值start和end变量。
我们可以看一下zImage的二进制 我们可以发现头信息中有LINUX_ARM_ZIMAGE_MAGIC。
2.4 bootm_find_images
由于我们没有使用ram disk再去掉没有执行的宏定义该函数如下 实际上就是获取设备树文件的相关信息然后做一些内存检测。
3 do_bootm_states启动内核
现在回到do_bootz函数最后的do_bootm_states函数中由前面的2.2 do_bootm_states:BOOTM_STATE_START可知在bootz_start中执行了这个函数但是参数是BOOTM_STATE_START。
而在这里states参数为BOOTM_STATE_OS_PREP|BOOTM_STATE_OS_FAKE_GO|BOOTM_STATE_OS_GO。
为了方便分析这里我把与这三个states无关的代码删掉了函数如下
3.1 bootm_os_get_boot_func
首先来看一下bootm_os_get_boot_func函数这个函数很简单 其中boot_os的定义如下 在do_bootz中我们已经设置images-os.os为IH_OS_LINUX了。所以bootm_os_get_boot_func返回的就是do_bootm_linux函数。
3.2 boot_prep_linux
我们继续往下分析代码 这里就是执行boot_fn函数实际上就是do_bootm_linux函数。看一下这个函数 由于我们的参数是BOOTM_STATE_OS_PREP所以这里仅执行了boot_prep_linux(images)。由函数名可以猜测出这是为了准备启动Linux而初始化一些参数。看一下这个函数
static void boot_prep_linux(bootm_headers_t *images)
{char *commandline env_get(bootargs);if (CONFIG_IS_ENABLED(OF_LIBFDT) images-ft_len) {
#ifdef CONFIG_OF_LIBFDTdebug(using: FDT\n);if (image_setup_linux(images)) {panic(FDT creation failed!);}
#endif} else if (BOOTM_ENABLE_TAGS) {debug(using: ATAGS\n);setup_start_tag(gd-bd);if (BOOTM_ENABLE_SERIAL_TAG)setup_serial_tag(params);if (BOOTM_ENABLE_CMDLINE_TAG)setup_commandline_tag(gd-bd, commandline);if (BOOTM_ENABLE_REVISION_TAG)setup_revision_tag(params);if (BOOTM_ENABLE_MEMORY_TAGS)setup_memory_tags(gd-bd);if (BOOTM_ENABLE_INITRD_TAG) {/** In boot_ramdisk_high(), it may relocate ramdisk to* a specified location. And set images-initrd_start * images-initrd_end to relocated ramdisks start/end* addresses. So use them instead of images-rd_start * images-rd_end when possible.*/if (images-initrd_start images-initrd_end) {setup_initrd_tag(gd-bd, images-initrd_start,images-initrd_end);} else if (images-rd_start images-rd_end) {setup_initrd_tag(gd-bd, images-rd_start,images-rd_end);}}setup_board_tags(params);setup_end_tag(gd-bd);} else {panic(FDT and ATAGS support not compiled in\n);}board_prep_linux(images);
}如果使能了OF_LIBFDT或BOOTM_ENABLE_TAGS就执行对应的if分支大概都是根据GD或命令行参数填充全局变量params它定义如下
static struct tag *params;struct tag {struct tag_header hdr;union {struct tag_core core;struct tag_mem32 mem;struct tag_videotext videotext;struct tag_ramdisk ramdisk;struct tag_initrd initrd;struct tag_serialnr serialnr;struct tag_revision revision;struct tag_videolfb videolfb;struct tag_cmdline cmdline;/** Acorn specific*/struct tag_acorn acorn;/** DC21285 specific*/struct tag_memclk memclk;} u;
};3.3 boot_selected_os
最后do_bootm_states就执行boot_selected_os了从注释可以看出来这里就会启动Linux内核了如果正常启动这个函数就不会返回了。 该函数实际上也是调用boot_fn(do_bootm_linux)函数如果成功的话函数就不会再返回了 在来看一下do_bootm_linux函数 这次就是执行第三个if分支的boot_jump_linux(images, flag)函数了从函数名可以看出就是最终我们跳转运行Linux内核的函数。
3.3.1 boot_jump_linux
最后就来看一下这个函数为了代码简洁省略了不会执行的部分 首先我们从GD中获取machine id赋值给machid然后从环境变量中获取machid对比一下是否一致。接下来的bootstage_mark是输出boot进度的函数。
3.3.1.1 announce_and_cleanup 这里实际上就是做一些清除函数比如在U-Boot中我们初始化了USB我们就要关闭。和所有的BootLoader一样上电时系统是什么状态在运行APP前就要恢复什么状态。否则可能会影响后续Linux内核程序的运行。具体可以参考我的这篇文章单片机中BootLoader的严谨实现详解。
3.3.1.2 kernel_entry
最后我们就执行
kernel_entry(0, machid, r2);前面我们设置了
kernel_entry (void (*)(int, int, uint))images-ep;这个函数实际上是Linux内核中的第一行代码类似于
static void (*farewellBootloader)(void) 0;
farewellBootloader (void (*)(void))appEntry;
farewellBootloader();这样就跳转到Linux中执行代码了。至于这边传了三个参数分别作为r0r1和r2寄存器的值传递给Linux内核使用。
至于Linux内核如何使用这些参数后续我们分析Linux内核启动源码的时候再来看。