3.7.4. 环境变量加载

如前面所述,环境变量的处理有两个阶段:

  1. 环境变量初始

  2. 环境变量读取

如下面的调用流程, env_init()board_init_f() 阶段执行,但具体的加载过程 在 board_init_r() 阶段执行。

reset // arch/riscv/cpu/start.S
|-> save_boot_params // arch/riscv/mach-zx/lowlevel_init.S
|-> ...
|-> board_init_f(); // common/board_f.c
    |-> setup_reloc(); // common/board_f.c
    |-> jump_to_copy(); // common/board_f.c
        |-> relocate_code(); // arch/riscv/cpu/start.S
            |-> invalidate_icache_all()
            |-> flush_dcache_all()
            |-> board_init_r();
                |   // 逐个调用 init_sequence_r 里面的函数,其中包括 initr_env
                |-> initr_env(); // common/board_r.c
                    |-> should_load_env(void);// common/board_r.c
                    |   // 由于没有在 DTS 中进行配置,总是返回 1
                    |   // fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1);
                    |-> env_relocate(); // env/common.c
                        |-> env_load(); // env/env.c

U-Boot 支持在多种存储介质中保存环境变量,并且提供了一个插拔式的机制,用于实现从 不同的存储介质中加载环境变量内容。

在添加新的环境变量加载器时,只需要实现对应存储介质的 loadsave 函数, 然后通过宏 U_BOOT_ENV_LOCATION 将对应的信息添加到 .u_boot_list_2 链接段中。 比如下面的定义,将生成 .u_boot_list_2_env_driver_2_spinand 信息。

U_BOOT_ENV_LOCATION(spinand) = {
    .location   = ENVL_SPINAND,
    ENV_NAME("SPINAND")
    .load       = env_spinand_load,
#if defined(CMD_SAVEENV)
    .save       = env_save_ptr(env_spinand_save),
#endif
};

env_driver_lookup() 函数首先通过检查当前的启动设备,然后从 .u_boot_list_2 段中查找对应启动设备的 ENV 加载驱动,最后调用对应的驱动读取并导入环境变量内容。

env_load(); // env/env.c
|-> env_driver_lookup(ENVOP_LOAD, prio);
|   |-> loc = env_get_location(); // board/zx/m4/env_location.c
|   |   |-> bd = aic_get_boot_device(); // 获取当前的启动设备
|   |
|   |-> _env_driver_lookup(loc);
|       |-> drv = ll_entry_start();
|       // 查找指定存储介质的 env 加载驱动
|
|-> drv->load();
        // 此处调用个存储介质的对应函数
        env_ram_load(); // board/zx/m4/env_location.c
        env_spinand_load(); // env/spinand.c
        env_sf_load(); // env/sf.c
        env_mmc_load(); // env/mmc.c

具体各种存储介质中,环境变量保存的位置可参考 存储介质上的保存