imx6ull-系统移植篇9——bootz启动 Linux 内核

目录

前言

[images 全局变量](#images 全局变量)

images定义

bootm_headers_t定义

image_info_t定义

[do_bootz 函数](#do_bootz 函数)

[bootz_start 函数](#bootz_start 函数)

[bootz_srart 源码](#bootz_srart 源码)

[bootz_setup 函数](#bootz_setup 函数)

bootm_find_images函数

[do_bootm_states 函数](#do_bootm_states 函数)

函数源码

函数分析

[函数 bootm_start](#函数 bootm_start)

[函数 bootm_os_get_boot_func](#函数 bootm_os_get_boot_func)

[BOOTM_STATE_OS_PREP 状态](#BOOTM_STATE_OS_PREP 状态)

[函数 boot_selected_os](#函数 boot_selected_os)

do_bootm_linux函数


前言

本讲内容是:**bootz 启动 Linux 内核过程,**而bootz命令写起来虽然短小,里面干的事还真不少,先来一张 bootz 命令的执行过程,如图:

话不多说,我们开始吧~

当晕头转向的时候,记得回来再看看这张图~

images 全局变量

images定义

不管是 bootz 还是 bootm 命令,在启动 Linux 内核的时候都会用到一个重要的全局变量: images.

images是 bootm_headers_t类型的全局变量,作为 ​​U-Boot 启动 Linux 内核过程中的中央数据枢纽​​,其核心作用是:

  • ​​统一管理所有启动组件​​(内核、ramdisk、设备树等)的元数据和状态
  • ​​控制启动流程的阶段性推进​​(通过状态机机制)
  • ​​确保各组件间的内存隔离与安全校验​
职责维度​ ​具体表现​
​数据整合​ 集中存储内核镜像、ramdisk、设备树的加载地址、大小、校验信息等
​流程控制​ 通过 state位掩码(如 BOOTM_STATE_LOADOS)标记当前启动阶段
​内存安全​ 记录各组件物理地址范围(如 rd_start/end),防止内存重叠
​兼容性处理​ 同时支持 Legacy uImage 和 FIT 格式(通过 legacy_hdr_*os字段)
​调试支持​ 提供 verify标志控制校验强度,便于问题排查

images 在文件 cmd/bootm.c 中有如下定义:

cpp 复制代码
bootm_headers_t images; /* pointers to os/initrd/fdt images */

images 是 bootm_headers_t 类型的全局变量, 而bootm_headers_t 是个 boot 头结构体。

bootm_headers_t定义

bootm_headers_t是 U-Boot 启动流程的 ​​中央控制枢纽​​,负责:

  • ​​多镜像管理​​:协调内核/ramdisk/设备树的加载与验证
  • ​​状态机控制​​:通过 state位域跟踪启动进度
  • ​​内存布局记录​​:保存各组件物理地址范围
  • ​​兼容性处理​​:支持新旧镜像格式(Legacy/uImage/FIT)

bootm_headers_t 在文件include/image.h 中的定义如下:

cpp 复制代码
/**
 * bootm_headers_t - U-Boot 多阶段启动控制核心数据结构
 * 管理内核/ramdisk/设备树的加载、验证与状态跟踪
 */
typedef struct bootm_headers {
    /*-------------------------------------------
     * Legacy镜像支持(旧版uImage格式)
     *-----------------------------------------*/
    image_header_t *legacy_hdr_os;    /* 原始Legacy镜像头指针(需在重定位前访问) */
    image_header_t legacy_hdr_os_copy;/* 镜像头安全拷贝(防止运行时被覆盖) */
    ulong legacy_hdr_valid;           /* 头校验标记:1=有效,0=无效 */

    /*-------------------------------------------
     * 操作系统镜像信息(非主机工具编译时生效)
     *-----------------------------------------*/
#ifndef USE_HOSTCC
    image_info_t os;                  /* OS镜像元数据:
                                       * - type:  镜像类型(IH_TYPE_KERNEL等)
                                       * - comp:  压缩格式(IH_COMP_GZIP等)
                                       * - os:    操作系统类型(IH_OS_LINUX等)
                                       */
    ulong ep;                         /* 内核入口地址(最终跳转执行的PC值) */

    /*-------------------------------------------
     * 内存组件物理地址范围
     *-----------------------------------------*/
    ulong rd_start, rd_end;           /* Ramdisk内存范围[start,end) */
    char *ft_addr;                    /* 扁平设备树(FDT)内存指针 */
    ulong ft_len;                     /* 设备树长度(字节) */
    ulong initrd_start, initrd_end;   /* Initrd范围(通常等同ramdisk) */
    ulong cmdline_start, cmdline_end; /* 内核命令行参数存储区域 */
    bd_t *kbd;                        /* 板级信息指针(传递内存布局等) */
#endif

    /*-------------------------------------------
     * 安全验证控制
     *-----------------------------------------*/
    int verify;                       /* 镜像校验开关:
                                       * 0=关闭校验(环境变量verify=n)
                                       * 1=启用校验(默认)
                                       */

    /*-------------------------------------------
     * 多阶段启动状态机(位掩码组合)
     *-----------------------------------------*/
#define BOOTM_STATE_START      (0x00000001)  /* 启动流程开始 */
#define BOOTM_STATE_FINDOS     (0x00000002)  /* 定位OS镜像阶段 */
#define BOOTM_STATE_FINDOTHER   (0x00000004) /* 查找ramdisk/设备树 */
#define BOOTM_STATE_LOADOS     (0x00000008)  /* 加载OS到内存 */
#define BOOTM_STATE_RAMDISK    (0x00000010)  /* Ramdisk处理中 */
#define BOOTM_STATE_FDT        (0x00000020)  /* 设备树处理中 */
#define BOOTM_STATE_OS_CMDLINE (0x00000040)  /* 内核命令行设置 */
#define BOOTM_STATE_OS_BD_T    (0x00000080)  /* 板级信息传递 */
#define BOOTM_STATE_OS_PREP    (0x00000100)  /* OS启动前准备 */
#define BOOTM_STATE_OS_FAKE_GO (0x00000200)  /* 模拟执行(测试用) */
#define BOOTM_STATE_OS_GO      (0x00000400)  /* 准备跳转到OS */
    int state;                        /* 当前状态(多个状态可叠加) */

    /*-------------------------------------------
     * 内存管理扩展(LMB逻辑内存块)
     *-----------------------------------------*/
#ifdef CONFIG_LMB
    struct lmb lmb;                   /* 逻辑内存块管理器:
                                       * - 记录保留内存区域
                                       * - 防止OS覆盖U-Boot关键数据
                                       */
#endif
} bootm_headers_t;

在结构体里,定义了:

cpp 复制代码
 image_info_t os;

结构体 image_info_t,系统镜像信息结构体。

image_info_t定义

image_info_t是 U-Boot 中用于描述 ​​可执行镜像​​(如内核、ramdisk、设备树等)的核心数据结构,其主要作用包括:

  • 记录镜像的物理内存布局​​
  • ​​标识镜像类型和属性​​
  • ​​控制镜像加载和校验行为

结构体 image_info_t 在文件include/image.h 中的定义如下:

cpp 复制代码
typedef struct image_info {
    /*-------------------------------------------
     * 内存范围描述
     *-----------------------------------------*/
    ulong start, end;          /* Blob的物理内存范围[start, end) 
                                * - 包含头部信息(如uImage头)
                                * - 单位:字节
                                * 示例:0x82000000-0x82012345
                                */

    ulong image_start, image_len; /* 纯镜像数据的起始地址和长度
                                   * - 不含头部(如跳过uImage头)
                                   * - 用于实际执行/解压
                                   * 示例:0x82000040, 0x120000
                                   */

    ulong load;                /* 系统建议的加载地址
                                * - 可能≠start(如重定位场景)
                                * - 架构相关(ARM通常为0x8000)
                                */

    /*-------------------------------------------
     * 镜像属性标识
     *-----------------------------------------*/
    uint8_t comp;              /* 压缩算法类型:
                                * 0=未压缩, 1=gzip, 2=bzip2...
                                * 详见 include/image.h
                                */

    uint8_t type;              /* 镜像类型:
                                * IH_TYPE_KERNEL=内核
                                * IH_TYPE_RAMDISK=ramdisk
                                * IH_TYPE_FIRMWARE=固件
                                */

    uint8_t os;                /* 目标操作系统:
                                * IH_OS_LINUX=Linux
                                * IH_OS_U_BOOT=U-Boot本身
                                */

    int8_t arch;               /* CPU架构:
                                * IH_ARCH_ARM=ARMv7
                                * IH_ARCH_ARM64=AArch64
                                * IH_ARCH_PPC=PowerPC
                                */
} image_info_t;

全局变量 images 会在 bootz 命令的执行中频繁使用到,相当于 Linux 内核启动的"灵魂"。

do_bootz 函数

bootz 命令的执行函数为 do_bootz,do_bootz是 U-Boot 中用于 ​​启动 zImage 格式 Linux 内核​​ 的核心函数,主要完成以下工作:

  • ​​参数预处理​​:跳过命令名 bootz
  • ​​镜像加载​​:通过 bootz_start初始化 images结构
  • ​​状态机控制​​:分阶段执行 OS 准备和跳转
  • ​​中断管理​​:确保内核启动时中断处于关闭状态​

在文件 cmd/bootm.c 中有如下定义:

cpp 复制代码
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    int ret;

    /*-------------------------------------------
     * [1] 参数预处理:跳过命令名 'bootz'
     *-----------------------------------------*/
    argc--; argv++;  // 移除argv[0]的"bootz",保留实际参数

    /*-------------------------------------------
     * [2] 加载zImage镜像并初始化images结构
     *-----------------------------------------*/
    if (bootz_start(cmdtp, flag, argc, argv, &images))
        return 1;  // 失败时返回非零

    /*-------------------------------------------
     * [3] 准备执行内核前的关键操作
     *-----------------------------------------*/
    bootm_disable_interrupts();  // 禁用中断(避免状态不一致)
    images.os.os = IH_OS_LINUX; // 强制标记为Linux内核

    /*-------------------------------------------
     * [4] 分阶段执行内核启动
     * 关键状态:
     * - OS_PREP:  设置启动参数
     * - OS_FAKE_GO: 模拟执行(测试用)
     * - OS_GO:    实际跳转到内核
     *-----------------------------------------*/
    ret = do_bootm_states(cmdtp, flag, argc, argv,
            BOOTM_STATE_OS_PREP |   // 准备阶段
            BOOTM_STATE_OS_FAKE_GO | // 测试执行
            BOOTM_STATE_OS_GO,      // 正式跳转
            &images, 1);           // 传入images结构

    return ret;  // 返回最终状态
}

调用bootz_start 函数,加载zImage镜像并初始化images结构。

调用函数 bootm_disable_interrupts关闭中断。

设置 images.os.os 为 IH_OS_LINUX,也就是设置系统镜像为 Linux,表示我们要启动的是 Linux 系统。

调用函数 do_bootm_states 来执行不同的 BOOT 阶段。

典型调用流程如下:

bootz_start 函数

do_bootz函数调用了bootz_srart 函数,bootz_start是 do_bootz的底层支持函数,专门用于 ​​初始化 zImage 格式的 Linux 内核启动环境​​,核心职责包括:

  • ​​确定内核入口地址​​(images->ep)
  • ​​验证 zImage 格式有效性​​
  • ​​保留内核内存区域​​
  • ​​加载附加镜像​​(ramdisk/设备树)

bootz_srart 源码

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

cpp 复制代码
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;  // zImage实际数据范围(去头部后)

    /*-------------------------------------------
     * [1] 初始化启动状态机(START阶段)
     *-----------------------------------------*/
    ret = do_bootm_states(cmdtp, flag, argc, argv,
                         BOOTM_STATE_START, images, 1);
    if (ret)
        return ret;  // 异常直接退出

    /*-------------------------------------------
     * [2] 确定内核入口地址(ep)
     * - 无参数时使用默认load_addr
     * - 有参数时解析argv[0]作为地址
     *-----------------------------------------*/
    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);  // 16进制解析
        debug("* kernel: cmdline image address = 0x%08lx\n", images->ep);
    }

    /*-------------------------------------------
     * [3] 验证zImage格式并获取数据范围
     * - 检查魔数、压缩标志等
     * - zi_start/end跳过头部得到纯数据区
     *-----------------------------------------*/
    ret = bootz_setup(images->ep, &zi_start, &zi_end);
    if (ret != 0)
        return 1;  // 无效镜像

    /*-------------------------------------------
     * [4] 保留内核内存区域(防其他组件覆盖)
     * - 使用LMB(Logical Memory Block)管理器
     * - 保护范围:[ep, zi_end)
     *-----------------------------------------*/
    lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);

    /*-------------------------------------------
     * [5] 加载附加镜像(ramdisk/设备树)
     * - 解析argv[1]/argv[2]作为initrd和FDT地址
     * - 若未指定则尝试自动检测
     *-----------------------------------------*/
    if (bootm_find_images(flag, argc, argv))
        return 1;

    return 0;  // 成功返回
}

调用函数 do_bootm_states,执行 BOOTM_STATE_START 阶段。

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

调用 bootz_setup 函数,此函数会判断当前的系统镜像文件是否为 Linux 的镜像文件,并且会打印出镜像相关信息。

调用函数 bootm_find_images查找 ramdisk 和设备树(dtb)文件,但是我们没有用到 ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件。

bootz_setup 函数

bootz_srart 函数里会调用bootz_setup 函数,bootz_setup是 zImage 启动流程中的 ​​关键验证函数​​,专门用于:

  • ​​校验 ARM zImage 的合法性​​(通过魔数检查)
  • ​​提取内核的实际数据范围​​(zi_start到 zi_end)
  • ​​输出内核内存布局信息​​(调试用)

bootz_setup函数定义在文件 arch/arm/lib/bootm.c 中。

cpp 复制代码
/* ARM zImage 头部魔数(小端模式) */
#define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818

/**
 * bootz_setup - 验证zImage格式并获取数据范围
 * @image:  zImage在内存中的起始地址
 * @start:  输出参数-内核数据起始地址
 * @end:    输出参数-内核数据结束地址
 * 
 * 返回值: 0=成功, 1=魔数校验失败
 */
int bootz_setup(ulong image, ulong *start, ulong *end)
{
    /* [1] 映射内存到zImage头部结构 */
    struct zimage_header *zi = (struct zimage_header *)map_sysmem(image, 0);

    /* [2] 魔数校验(确认是有效的ARM zImage) */
    if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {
        puts("Bad Linux ARM zImage magic!\n");
        return 1;  // 失败返回
    }

    /* [3] 提取内核数据范围 */
    *start = zi->zi_start;  // 内核数据实际起始地址
    *end = zi->zi_end;      // 内核数据结束地址

    /* [4] 打印调试信息(地址布局) */
    printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n", 
           image, *start, *end);

    return 0;  // 成功返回
}

如果 Linux 系统镜像正常的话就会输出图:

bootm_find_images函数

bootz_srart 函数里会调用 bootm_find_images函数,bootm_find_images是 U-Boot 启动流程中用于 ​​定位和验证附加启动组件​​ 的核心函数,主要负责:

  • ​​加载并验证 ramdisk/initrd 镜像​​
  • ​​查找和处理设备树(FDT)​​
  • ​​管理内存中的组件布局

bootm_find_images函数定义在文件 common/bootm.c 中:

cpp 复制代码
/**
 * bootm_find_images - 定位ramdisk和设备树镜像
 * @flag:   启动标志位(如CMD_FLAG_BOOTD)
 * @argc:   参数个数
 * @argv:   参数数组(如[addr, size, ...])
 * 
 * 返回值: 0=成功, 1=组件加载失败
 */
int bootm_find_images(int flag, int argc, char * const argv[])
{
    int ret;

    /*-------------------------------------------
     * [1] Ramdisk/Initrd 加载
     * - 从argv[1]获取地址或自动检测
     * - 验证格式并设置images.rd_start/end
     *-----------------------------------------*/
    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)
    /*-------------------------------------------
     * [2] 设备树(FDT)加载
     * - 从argv[2]获取地址或自动检测
     * - 设置images.ft_addr/ft_len
     *-----------------------------------------*/
    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);  // 激活当前FDT
#endif

    return 0;  // 成功返回
}

调用boot_get_ramdisk函数,查找 ramdisk,但是我们没有用到 ramdisk,因此这部分代码不用管。

调用boot_get_fdt函数,查找设备树(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 函数。

do_bootm_states是 U-Boot 启动流程的 ​​中央调度器​​,通过状态机机制控制:

  • ​​多阶段启动流程​​(镜像加载、内存管理、OS准备等)
  • ​​错误处理与资源管理​​
  • ​​最终操作系统跳转

函数源码

do_bootm_states函数定义在文件common/bootm.c 中:

cpp 复制代码
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;      // OS启动函数指针(如do_bootm_linux)
    ulong iflag = 0;         // 中断状态保存
    int ret = 0, need_boot_fn;

    /*-------------------------------------------
     * [1] 更新当前状态
     *-----------------------------------------*/
    images->state |= states;

    /*-------------------------------------------
     * [2] 按位处理各启动阶段
     * 注意:任一阶段失败立即终止流程
     *-----------------------------------------*/

    /* [2.1] START阶段:基础初始化 */
    if (states & BOOTM_STATE_START)
        ret = bootm_start(cmdtp, flag, argc, argv);

    /* [2.2] FINDOS阶段:定位OS镜像 */
    if (!ret && (states & BOOTM_STATE_FINDOS))
        ret = bootm_find_os(cmdtp, flag, argc, argv);

    /* [2.3] FINDOTHER阶段:加载附加组件 */
    if (!ret && (states & BOOTM_STATE_FINDOTHER)) {
        ret = bootm_find_other(cmdtp, flag, argc, argv);
        argc = 0;  // 参数已消费
    }

    /* [2.4] LOADOS阶段:加载OS到内存 */
    if (!ret && (states & BOOTM_STATE_LOADOS)) {
        ulong load_end;
        iflag = bootm_disable_interrupts();  // 禁用中断
        ret = bootm_load_os(images, &load_end, 0);
        if (ret == 0) {
            // 保留OS内存区域
            lmb_reserve(&images->lmb, images->os.load, 
                      (load_end - images->os.load));
        } else if (ret != BOOTM_ERR_OVERLAP) {
            goto err;  // 非内存重叠错误直接退出
        }
#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)
        // Linux内核静默启动处理
        if (images->os.os == IH_OS_LINUX)
            fixup_silent_linux();
#endif
    }

    /* [2.5] RAMDISK阶段:重定位initrd */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
    if (!ret && (states & BOOTM_STATE_RAMDISK)) {
        ulong rd_len = images->rd_end - images->rd_start;
        ret = boot_ramdisk_high(&images->lmb, images->rd_start,
                               rd_len, &images->initrd_start, 
                               &images->initrd_end);
        if (!ret) {
            // 更新环境变量
            setenv_hex("initrd_start", images->initrd_start);
            setenv_hex("initrd_end", images->initrd_end);
        }
    }
#endif

    /* [2.6] FDT阶段:设备树处理 */
#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB)
    if (!ret && (states & BOOTM_STATE_FDT)) {
        boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
        ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,
                              &images->ft_len);
    }
#endif

    /*-------------------------------------------
     * [3] 获取OS启动函数
     *-----------------------------------------*/
    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);

    /* [3.1] 检查OS支持性 */
    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;
    }

    /*-------------------------------------------
     * [4] 执行OS相关阶段
     *-----------------------------------------*/
    if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
        ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
    if (!ret && (states & BOOTM_STATE_OS_BD_T))
        ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
    if (!ret && (states & BOOTM_STATE_OS_PREP))
        ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

    /* [4.1] 模拟执行(调试用) */
#ifdef CONFIG_TRACE
    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

    /* [4.2] 最终跳转到OS */
    if (!ret && (states & BOOTM_STATE_OS_GO))
        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
                             images, boot_fn);

err:
    /*-------------------------------------------
     * [5] 错误恢复
     *-----------------------------------------*/
    if (iflag)
        enable_interrupts();  // 恢复中断状态
    return ret;
}

函数分析

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

cpp 复制代码
states & BOOTM_STATE_XXX

do_bootz 函数中会用到

  • BOOTM_STATE_OS_PREP 、
  • BOOTM_STATE_OS_FAKE_GO
  • BOOTM_STATE_OS_GO

bootz_start 函数中会用到:

  • BOOTM_STATE_START这个 BOOT 状态

函数 bootm_start

在处理 BOOTM_STATE_START 阶段,调用函数 bootm_start:

cpp 复制代码
    /* [2.1] START阶段:基础初始化 */
    if (states & BOOTM_STATE_START)
        ret = bootm_start(cmdtp, flag, argc, argv);

bootm_start是 U-Boot 启动流程的 ​​初始化入口函数​​,负责:

  • 清零 images全局结构体,确保启动环境干净
  • 初始化内存管理器(LMB),为后续内存保留做准备
  • 设置启动校验标志(verify环境变量控制)
  • 标记启动阶段(BOOTSTAGE_ID_BOOTM_START)

bootm_start函数定义在文件 common/bootm.c

cpp 复制代码
/**
 * bootm_start - 启动流程初始化函数
 * @cmdtp:   命令表指针(未使用)
 * @flag:    标志位(未使用)
 * @argc:    参数个数(未使用)
 * @argv:    参数数组(未使用)
 * 
 * 返回值: 始终返回0(无错误检查)
 */
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    /*-------------------------------------------
     * [1] 清零全局images结构体
     * - 防止残留数据影响本次启动
     *-----------------------------------------*/
    memset((void *)&images, 0, sizeof(images));

    /*-------------------------------------------
     * [2] 初始化镜像校验标志
     * - 从环境变量verify读取值(默认启用)
     * - verify=n 时关闭校验
     *-----------------------------------------*/
    images.verify = getenv_yesno("verify");  // 返回1=启用,0=禁用

    /*-------------------------------------------
     * [3] 初始化LMB(Logical Memory Block)内存管理器
     * - 记录已占用内存区域(如U-Boot自身)
     * - 为后续内核/ramdisk保留内存做准备
     *-----------------------------------------*/
    boot_start_lmb(&images);

    /*-------------------------------------------
     * [4] 记录启动阶段标记(调试用)
     * - 用于bootstage工具分析启动耗时
     *-----------------------------------------*/
    bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");

    /*-------------------------------------------
     * [5] 设置初始状态
     * - BOOTM_STATE_START是状态机起点
     *-----------------------------------------*/
    images.state = BOOTM_STATE_START;

    return 0;  // 无错误检查,始终返回成功
}

函数 bootm_os_get_boot_func

函 数 do_bootm_states里调用函数 bootm_os_get_boot_func:

cpp 复制代码
 /*-------------------------------------------
     * [3] 获取OS启动函数
     *-----------------------------------------*/
    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);

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

cpp 复制代码
/**
 * bootm_os_get_boot_func - 获取操作系统启动函数(支持重定位)
 * @os: 操作系统类型(IH_OS_xxx枚举值)
 * 
 * 返回值: 成功=函数指针, 失败=NULL
 */
boot_os_fn *bootm_os_get_boot_func(int os)
{
    /*-------------------------------------------
     * [1] 手动重定位处理(某些架构需要)
     *-----------------------------------------*/
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    static bool relocated;  // 重定位标记(仅执行一次)

    if (!relocated) {
        int i;
        /* 遍历函数表,应用重定位偏移 */
        for (i = 0; i < ARRAY_SIZE(boot_os); i++)
            if (boot_os[i] != NULL)
                boot_os[i] += gd->reloc_off;  // 函数地址 += 偏移量

        relocated = true;  // 标记已完成
    }
#endif

    /*-------------------------------------------
     * [2] 返回请求的启动函数
     *-----------------------------------------*/
    return boot_os[os];  // 直接索引全局数组
}

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

cpp 复制代码
static boot_os_fn *boot_os[] = {
    [IH_OS_U_BOOT] = do_bootm_standalone,
#ifdef CONFIG_BOOTM_LINUX
     [IH_OS_LINUX] = do_bootm_linux,
 #endif
......
#ifdef CONFIG_BOOTM_OPENRTOS
    [IH_OS_OPENRTOS] = do_bootm_openrtos,
#endif
};

可以看出来:Linux 系统对应的启动函数: do_bootm_linux

通过函数 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 函数。

BOOTM_STATE_OS_PREP 状态

函 数 do_bootm_states中,处理 BOOTM_STATE_OS_PREP 状态,调用boot_fn.

cpp 复制代码
  /*-------------------------------------------
     * [4] 执行OS相关阶段
     *-----------------------------------------*/
    if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
        ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
    if (!ret && (states & BOOTM_STATE_OS_BD_T))
        ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
    if (!ret && (states & BOOTM_STATE_OS_PREP))
        ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

我们已经解释过, 执行 boot_fn函数的地方实际上是执行的 do_bootm_linux 函数,do_bootm_linux也是调用 boot_prep_linux 来完成具体的处理过程。

boot_prep_linux是 Linux 内核启动前的 ​​最终准备函数​​,负责:

  • ​​设置内核启动参数​​(ATAGS 或设备树)
  • ​​初始化内核命令行​​
  • ​​处理 initrd/ramdisk 内存布局​​
  • ​​验证硬件兼容性

boot_prep_linux 主要用于处理环境变量bootargs, bootargs 保存着传递给 Linux kernel 的参数。

cpp 复制代码
/**
 * boot_prep_linux - 为Linux内核启动做最终准备
 * @images: 包含所有启动信息的全局结构体
 * 
 * 返回值: 0=成功, 非0=错误码
 */
int boot_prep_linux(bootm_headers_t *images)
{
    int ret;

    /*-------------------------------------------
     * [1] 选择参数传递方式(ATAGS或设备树)
     *-----------------------------------------*/
#ifdef CONFIG_OF_LIBFDT
    if (images->ft_len) {
        /* 使用设备树传递参数 */
        ret = image_setup_linux(images);
        if (ret)
            return ret;
    } else
#endif
    {
        /* 使用传统ATAGS方式 */
        setup_start_tag(images->kbd);
        if (images->initrd_start) {
            setup_initrd_tag(images->kbd, 
                           images->initrd_start, 
                           images->initrd_end);
        }
        setup_commandline_tag(images->kbd, 
                            images->cmdline_start);
        setup_end_tag(images->kbd);
    }

    /*-------------------------------------------
     * [2] 处理静默启动(Silent Boot)
     *-----------------------------------------*/
#if defined(CONFIG_SILENT_CONSOLE)
    if (getenv_yesno("silent") == 1) {
        fixup_silent_linux();
        /* 更新命令行参数 */
        if (images->cmdline_start)
            strcat((char *)images->cmdline_start, " quiet");
    }
#endif

    /*-------------------------------------------
     * [3] 内存保留区同步
     *-----------------------------------------*/
#ifdef CONFIG_LMB
    /* 确保内核不会覆盖关键区域 */
    boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
#endif

    /*-------------------------------------------
     * [4] 硬件兼容性检查
     *-----------------------------------------*/
    if (images->os.arch != IH_ARCH_DEFAULT) {
        printf("Unsupported arch: %d\n", images->os.arch);
        return CMD_RET_FAILURE;
    }

    return 0;
}

函数 boot_selected_os

do_bootm_states函数,调用函数 boot_selected_os 启动 Linux 内核:

cpp 复制代码
/* [4.2] 最终跳转到OS */
    if (!ret && (states & BOOTM_STATE_OS_GO))
        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
                             images, boot_fn);

boot_selected_os函数第 4 个参数为 Linux 系统镜像头,

第 5 个参数就是 Linux 系统启动函数:do_bootm_linux

boot_selected_os 函数定义在文件common/bootm_os.c 中:

cpp 复制代码
int boot_selected_os(int argc, char * const argv[], int state,
bootm_headers_t *images, boot_os_fn *boot_fn)
{
    arch_preboot_os();
    480 boot_fn(state, argc, argv, images);
......
    return BOOTM_ERR_RESET;
}

调用 boot_fn 函数,也就是 do_bootm_linux 函数来启动 Linux 内核。

do_bootm_linux函数

经过前面的分析,我们知道了 do_bootm_linux 就是最终启动 Linux 内核的函数,负责:

  • ​​准备内核启动环境​​(参数传递、内存布局)
  • ​​执行架构相关的跳转操作​​
  • ​​处理不同启动阶段的状态标志

do_bootm_linux函数定义在文件 arch/arm/lib/bootm.c

cpp 复制代码
/**
 * do_bootm_linux - Linux内核启动主函数
 * @flag:   状态标志位(BOOTM_STATE_xxx的组合)
 * @argc:   参数个数(通常已处理)
 * @argv:   参数数组(通常已处理)
 * @images: 全局启动信息结构体
 * 
 * 返回值: 0=成功, -1=不支持的操作
 */
int do_bootm_linux(int flag, int argc, char * const argv[],
                 bootm_headers_t *images)
{
    /*-------------------------------------------
     * [1] 过滤ARM架构不需要的状态
     * - BD_T(板级信息表)和CMDLINE已在PREP阶段处理
     *-----------------------------------------*/
    if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
        return -1;

    /*-------------------------------------------
     * [2] 准备阶段(BOOTM_STATE_OS_PREP)
     * - 设置设备树/ATAGS参数
     * - 初始化内核命令行
     *-----------------------------------------*/
    if (flag & BOOTM_STATE_OS_PREP) {
        boot_prep_linux(images);
        return 0;
    }

    /*-------------------------------------------
     * [3] 跳转阶段(BOOTM_STATE_OS_GO/FAKE_GO)
     * - FAKE_GO用于测试(不实际跳转)
     * - GO执行最终跳转
     *-----------------------------------------*/
    if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
        boot_jump_linux(images, flag);
        return 0;
    }

    /*-------------------------------------------
     * [4] 默认处理(未明确指定状态时)
     * - 执行完整启动流程:准备+跳转
     *-----------------------------------------*/
    boot_prep_linux(images);
    boot_jump_linux(images, flag);
    return 0;
}

在跳转阶段(BOOTM_STATE_OS_GO/FAKE_GO),调用函数 boot_jump_linux:

boot_jump_linux是 U-Boot 启动 Linux 内核的 最终跳转函数​​,负责:

  • ​​设置内核启动参数​​(寄存器传参)
  • ​​处理环境变量覆盖​​(如 machid)
  • ​​执行架构相关的跳转操作​​
  • ​​支持模拟启动(测试模式)
cpp 复制代码
/**
 * boot_jump_linux - 跳转到Linux内核的最终函数
 * @images: 包含内核地址、设备树等信息的结构体
 * @flag:   启动标志(含BOOTM_STATE_OS_FAKE_GO等)
 */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
    /*-------------------------------------------
     * [1] 获取板级ID(machid)
     * - 默认从gd->bd读取
     * - 允许环境变量"machid"覆盖
     *-----------------------------------------*/
    unsigned long machid = gd->bd->bi_arch_number; // 默认板级ID
    char *s = getenv("machid");
    if (s) {
        if (strict_strtoul(s, 16, &machid) < 0) {
            debug("Invalid machid format!\n");
            return;
        }
        printf("Using machid 0x%lx from env\n", machid);
    }

    /*-------------------------------------------
     * [2] 准备跳转参数
     * - r0: 固定为0
     * - r1: machid(板级标识)
     * - r2: 设备树地址或ATAGS参数块地址
     *-----------------------------------------*/
    void (*kernel_entry)(int zero, int arch, uint params) = 
        (void (*)(int, int, uint))images->ep;  // 强转函数指针

    unsigned long r2;
    if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
        r2 = (unsigned long)images->ft_addr;  // 使用设备树
    else
        r2 = gd->bd->bi_boot_params;        // 使用传统ATAGS

    /*-------------------------------------------
     * [3] 模拟启动模式(测试用)
     * - 打印信息但不实际跳转
     *-----------------------------------------*/
    int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
    debug("## Ready to jump: ep=0x%08lx, machid=0x%lx, r2=0x%lx\n",
          (ulong)kernel_entry, machid, r2);
    bootstage_mark(BOOTSTAGE_ID_RUN_OS);

    /*-------------------------------------------
     * [4] 清理环境并跳转
     * - 关闭中断/缓存(arch-specific)
     * - 打印最终启动信息
     *-----------------------------------------*/
    announce_and_cleanup(fake);  // 显示"Starting kernel..."消息

    if (!fake) {
        /* 实际跳转(永不返回) */
        kernel_entry(0, machid, r2);
    }
}

看见最后一行的函数 kernel_entryll了吗?调用 kernel_entry 函数进入 Linux 内核,此行将一去不复返, uboot 的使命也就完成了,它可以安息了!

我也安息了。。。玛卡巴卡,晚安~

相关推荐
深度学习04072 小时前
【Linux服务器】-MySQL数据库参数调优
linux·服务器·数据库
老马啸西风4 小时前
windows docker-02-docker 最常用的命令汇总
linux·运维·ubuntu·docker·容器·eureka·maven
sztomarch5 小时前
Tshark-Tcpdump
linux·运维·网络·测试工具·tcpdump
手眼通天王水水5 小时前
【Linux】3. Shell语言
linux·运维·服务器·开发语言
程序员JerrySUN6 小时前
Valgrind Memcheck 全解析教程:6个程序说明基础内存错误
android·java·linux·运维·开发语言·学习
大母猴啃编程6 小时前
再谈文件-ext2文件系统
linux·运维·服务器·网络
NEXU56 小时前
Linux:线程控制
linux·运维·服务器
古井无波 20247 小时前
ARM64高速缓存Cache类型
linux
菜萝卜子9 小时前
【Linux】AKHQ实现kafka可视化
linux·运维·kafka