imx6ull-系统移植篇12——bootcmd环境变量

前言

在之前两篇博客:uboot移植(上),我们先使用NXP 官方开发板 uboot 进行编译测试,在正点原子开发板上正常跑起来;然后在uboot移植(下),我们学习如何在 uboot 中添加我们的开发板或者开发平台。

可谓是万事俱备,只欠东风,终于可以启动 Linux 内核了。让我们来看看移植的uboot能不能将linux系统跑起来吧~

等等,在启动 Linux 内核之前我们先来学习两个重要的环境变量 bootcmd bootargs。本讲实验先来讲解bootcmd 环境变量。

bootcmd 环境变量

bootcmd 和 bootagrs 是采用类似 shell 脚本语言编写的,里面有很多的变量引用,这些变量其实都是环境变量 , 有很多是NXP 自己定义的 。

文 件 mx6ull_alientek_emmc.h 中 的 宏CONFIG_EXTRA_ENV_SETTINGS 保存着这些环境变量的默认值。

bootcmd源码

bootcmd 保存着 uboot 默认命令, uboot 倒计时结束以后就会执行 bootcmd 中的命令。

这些命令一般都是用来启动 Linux 内核的,比如读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 Linux 内核。

板子第一次运行 uboot 的时候会使用默认值来设置 bootcmd 环境变量,我们可以在 uboot 启动以后进入命令行设置 bootcmd 环境变量的值。如果 EMMC 或者 NAND 中没有保存 bootcmd 的值
,就都会使用默认值。

打开文件 include/env_default.h,代码如下:

/*
 * (C) Copyright 2000-2010
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Andreas Heppel <aheppel@sysgo.de>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

/* 环境变量初始化文件
 * 该文件定义了U-Boot的默认环境变量
 * 根据不同的配置宏,环境变量会被编译到不同的存储段中
 */

#include <env_callback.h>  // 环境变量回调函数相关头文件

/* 环境变量存储结构选择:
 * 根据不同的配置宏选择环境变量的存储方式
 */
#ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
/* 嵌入式环境变量结构体
 * 适用于环境变量存储在特定存储区域的情况
 */
env_t environment __PPCENV__ = {
	ENV_CRC,	/* CRC校验值,用于验证环境变量完整性 */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
	1,		/* 标志位:1表示有效(用于冗余环境变量) */
#endif
	{
#elif defined(DEFAULT_ENV_INSTANCE_STATIC)
/* 静态环境变量数组
 * 适用于环境变量作为静态数据存储的情况
 */
static char default_environment[] = {
#else
/* 默认环境变量数组
 * 最常见的情况,环境变量作为常量数据存储
 */
const uchar default_environment[] = {
#endif

/* 环境变量回调列表(可选) */
#ifdef	CONFIG_ENV_CALLBACK_LIST_DEFAULT
	ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif

/* 环境变量标志列表(可选) */
#ifdef	CONFIG_ENV_FLAGS_LIST_DEFAULT
	ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
#endif

/* 以下是各种环境变量的定义,每个变量都以"var=value\0"的形式存储 */

/* 内核启动参数 */
#ifdef	CONFIG_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			"\0"
#endif

/* 默认启动命令 */
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif

/* RAM启动命令 */
#ifdef	CONFIG_RAMBOOTCOMMAND
	"ramboot="	CONFIG_RAMBOOTCOMMAND		"\0"
#endif

/* 网络启动命令 */
#ifdef	CONFIG_NFSBOOTCOMMAND
	"nfsboot="	CONFIG_NFSBOOTCOMMAND		"\0"
#endif

/* 启动延时(秒) */
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	"bootdelay="	__stringify(CONFIG_BOOTDELAY)	"\0"
#endif

/* 串口波特率 */
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
	"baudrate="	__stringify(CONFIG_BAUDRATE)	"\0"
#endif

/* 加载回显设置 */
#ifdef	CONFIG_LOADS_ECHO
	"loads_echo="	__stringify(CONFIG_LOADS_ECHO)	"\0"
#endif

/* 首选以太网接口 */
#ifdef	CONFIG_ETHPRIME
	"ethprime="	CONFIG_ETHPRIME			"\0"
#endif

/* IP地址配置 */
#ifdef	CONFIG_IPADDR
	"ipaddr="	__stringify(CONFIG_IPADDR)	"\0"
#endif

/* TFTP服务器IP */
#ifdef	CONFIG_SERVERIP
	"serverip="	__stringify(CONFIG_SERVERIP)	"\0"
#endif

/* 自动加载设置 */
#ifdef	CONFIG_SYS_AUTOLOAD
	"autoload="	CONFIG_SYS_AUTOLOAD		"\0"
#endif

/* 预启动命令 */
#ifdef	CONFIG_PREBOOT
	"preboot="	CONFIG_PREBOOT			"\0"
#endif

/* 根文件系统路径 */
#ifdef	CONFIG_ROOTPATH
	"rootpath="	CONFIG_ROOTPATH			"\0"
#endif

/* 网关IP */
#ifdef	CONFIG_GATEWAYIP
	"gatewayip="	__stringify(CONFIG_GATEWAYIP)	"\0"
#endif

/* 子网掩码 */
#ifdef	CONFIG_NETMASK
	"netmask="	__stringify(CONFIG_NETMASK)	"\0"
#endif

/* 主机名 */
#ifdef	CONFIG_HOSTNAME
	"hostname="	__stringify(CONFIG_HOSTNAME)	"\0"
#endif

/* 启动文件名 */
#ifdef	CONFIG_BOOTFILE
	"bootfile="	CONFIG_BOOTFILE			"\0"
#endif

/* 加载地址 */
#ifdef	CONFIG_LOADADDR
	"loadaddr="	__stringify(CONFIG_LOADADDR)	"\0"
#endif

/* 时钟频率显示单位 */
#ifdef	CONFIG_CLOCKS_IN_MHZ
	"clocks_in_mhz=1\0"
#endif

/* PCI总线延迟 */
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
	"pcidelay="	__stringify(CONFIG_PCI_BOOTDELAY)"\0"
#endif

/* 硬件平台信息 */
#ifdef	CONFIG_ENV_VARS_UBOOT_CONFIG
	"arch="		CONFIG_SYS_ARCH			"\0"  // CPU架构
	"cpu="		CONFIG_SYS_CPU			"\0"  // CPU型号
	"board="	CONFIG_SYS_BOARD		"\0"  // 板级名称
	"board_name="	CONFIG_SYS_BOARD		"\0"  // 板级名称(别名)
#ifdef CONFIG_SYS_VENDOR
	"vendor="	CONFIG_SYS_VENDOR		"\0"  // 厂商名称
#endif
#ifdef CONFIG_SYS_SOC
	"soc="		CONFIG_SYS_SOC			"\0"  // SoC型号
#endif
#endif

/* 额外环境变量设置 */
#ifdef	CONFIG_EXTRA_ENV_SETTINGS
	CONFIG_EXTRA_ENV_SETTINGS  // 包含板级特定的额外环境变量
#endif

	"\0"  // 环境变量结束标记

#ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
	}  // 嵌入式环境变量结构体结束
#endif
};  // 环境变量定义结束

可以看见:

  • uchar default_environment[ ] 数组保存环境变量。
  • 代码里指定了很多环境变量的默认值,比如 bootcmd 的默认值就是CONFIG_BOOTCOMMAND, bootargs 的默认值就是 CONFIG_BOOTARGS。

我们可以在mx6ull_alientek_emmc.h 文件中通过设置宏 CONFIG_BOOTCOMMAND 来设置bootcmd 的默认值, NXP 官方设置的 CONFIG_BOOTCOMMAND 值如下:

#define CONFIG_BOOTCOMMAND \
    "run findfdt;" \                   // 1. 确定设备树文件
    "mmc dev ${mmcdev};" \             // 2. 切换MMC设备(EMMC)
    "if mmc rescan; then " \           // 3. 检测设备存在性
        "if run loadbootscript; then " \  // 4. 尝试加载boot.scr
            "run bootscript; " \          //   → 存在则执行脚本
        "else " \                         //   → 不存在则:
            "if run loadimage; then " \    // 5. 加载内核镜像
                "run mmcboot; " \          //   → 成功则启动内核
            "else run netboot; " \         //   → 失败则网络启动
            "fi; " \
        "fi; " \
    "else run netboot; fi"              // 6. 设备不存在时网络启动

启动流程示意图​​:

关键命令说明​​:

命令

功能

findfdt

根据硬件环境自动选择设备树文件

mmc dev ${mmcdev}

选择MMC设备(${mmcdev}通常为0-SD卡,1-eMMC)

mmc rescan

重新初始化MMC设备

loadbootscript

从存储设备加载boot.scr脚本

bootscript

执行boot.scr中的命令

loadimage

加载内核镜像(如zImage)

mmcboot

执行完整的MMC启动流程

netboot

通过TFTP/NFS网络启动

代码分析

run findfdt:

使用的是 uboot 的 run 命令来运行 findfdt, findfdt 是 NXP 自行添加的环境变量。 findfdt 是用来查找开发板对应的设备树文件(.dtb)。

MX6ULL EVK 的设备树文件为 imx6ull-14x14-evk.dtb, findfdt 内容如下:

findfdt="
if test $fdt_file = undefined; then
    if test $board_name = EVK && test $board_rev = 9X9; then
        setenv fdt_file imx6ull-9x9-evk.dtb; 
    fi;
    if test $board_name = EVK && test $board_rev = 14X14; then
        setenv fdt_file imx6ull-14x14-evk.dtb;
    fi;
    if test $fdt_file = undefined; then
        echo WARNING: Could not determine dtb to use; 
    fi;
fi;"

findfdt 里面用到的变量有 fdt_file, board_name, board_rev,这三个变量内容如下:

  • fdt_file=undefined
  • board_name=EVK
  • board_rev=14X14

代码流程如下:

IMX6ULL EVK 板子的设备树文件就是 imx6ull-14x14-evk.dtb,因此 run findfdt 的结果就是设置 fdt_file 为 imx6ull-14x14-evk.dtb

mmc dev ${mmcdev}

用于切换 mmc 设备, mmcdev 为 1,因此这行代码就是: mmc dev 1,也就是切换到 EMMC 上。

mmc rescan 扫描

使用命令 mmc rescan 扫描看有没有 SD 卡或者 EMMC 存在,

  • 如果 mmc 设备存在的话就从mmc 设备启动;
  • 如果没有的话,就执行 run netboot, netboot也是一个自定义的环境变量,这个变量是从网络启动 Linux 的。
loadbootscript变量

运行 loadbootscript 环境变量,内容如下:

loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};

代码展开以后就是

loadbootscript=fatload mmc 1:1 0x80800000 boot.scr;

loadbootscript 变量的作用:从 mmc1 的分区 1 中读取文件 boot.src 到 DRAM 的 0X80800000 处。但是 mmc1 的分区 1 中没有 boot.src 这个文件。

bootscript 变量

如果加载 boot.src 文件成功的话就运行 bootscript 环境变量, bootscript 的内容如下:

bootscript=echo Running bootscript from mmc ...;
source

因为 boot.src 文件不存在,所以 bootscript 也就不会运行。

loadimage变量

果 loadbootscript 没有找到 boot.src 的话就运行环境变量 loadimage,环境变量loadimage 内容如下:

loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}

将变量的值代入展开以后如下:

loadimage=fatload mmc 1:1 0x80800000 zImage

loadimage环境变量的作用: 从 mmc1 的分区中读取 zImage 到内存的 0X80800000 处,而 mmc1的分区 1 中存在 zImage。

mmcboot变量

加载 linux 镜像文件 zImage 成功以后就运行环境变量 mmcboot,否则的话运行netboot 环境变量。

mmcboot 环境变量如下:

mmcboot="echo Booting from mmc ...; 
run mmcargs; 
if test ${boot_fdt} = yes || test ${boot_fdt} = try; then 
    if run loadfdt; then 
        bootz ${loadaddr} - ${fdt_addr}; 
    else 
        if test ${boot_fdt} = try; then 
            bootz; 
        else 
            echo WARN: Cannot load the DT; 
        fi; 
    fi; 
else 
    bootz; 
fi;"

这段代码主要逻辑:

1. 输出信息“Booting from mmc ...”。

2. 运行环境变量 mmcargs, mmcargs 用来设置 bootargs。

 3. 判断boot_fdt是否为yes或者try,此处boot_fdt = try。

4. 运行环境变量 loadfdt,环境变量 loadfdt 定义如下:

loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}

展开以后就是:

loadfdt=fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb

因此 loadfdt 的作用:从 mmc1 的分区 1 中读取 imx6ull-14x14-evk.dtb 文件并放到 0x83000000处

5. 如果读取.dtb 文件成功的话那就调用命令 bootz 启动 linux,调用方法如下:

bootz ${loadaddr} - ${fdt_addr};

展开就是:

bootz 0x80800000 - 0x83000000 

至此 Linux 内核启动,如此复杂的设置就是为了从 EMMC 中读取 zImage 镜像文件和设备树文件。

mmc dev 1 //切换到 EMMC
fatload mmc 1:1 0x80800000 zImage //读取 zImage 到 0x80800000 处
fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb //读取设备树到 0x83000000 处
bootz 0x80800000 - 0x83000000 //启动 Linux

bootcmd设置

聪明的朋友都知道这段代码很复杂,那是因为NXP 官方为了兼容不同板子所以写的复杂脚本,我们已经知道我们使用的什么板子,可以自己简化下宏CONFIG_BOOTCOMMAND 的设置。

比 如我们要从EMMC 启动 , 那么宏CONFIG_BOOTCOMMAND 就可简化为:

#define CONFIG_BOOTCOMMAND \
"mmc dev 1;" \
"fatload mmc 1:1 0x80800000 zImage;" \
"fatload mmc 1:1 0x83000000 imx6ull-alientek-emmc.dtb;" \
"bootz 0x80800000 - 0x83000000;"

或者可以直接在 uboot 中设置 bootcmd 的值,这个值就是保存到 EMMC 中的,命令如下:

setenv bootcmd 'mmc dev 1; 
               fatload mmc 1:1 80800000 zImage; 
               fatload mmc 1:1 83000000 imx6ullalientek-emmc.dtb; 
               bootz 80800000 - 83000000;'
saveenv

好了,到此为止吧!下讲内容我们来说一下环境变量 bootargs,和如何用uboot 启动 Linux 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值