[ARM Linux系统移植] U-Boot 启动 Linux 测试
在 u-boot 移植完毕后,就可以测试 u-boot 能否正常启动 linux 系统。再次之前我们需要了解两个重要的环境变量:bootcmd 和 bootargs。
bootcmd 变量里附带了 u-boot “倒计时” 结束之后执行的指令;bootargs 变量里附带了 u-boot 需要传递给 linux 的命令行变量。
1. bootcmd 变量
先说 bootcmd 变量,变量里面附带的指令核心就是读取 zImage 和设备树文件,然后执行 bootz 命令,以此引导 linux。下面我们来梳理一下 i.Mx 官网定义的 bootcmd 变量。
在文件 include/env_default.h 中,我们可以看到,如果没有自行设置相关的环境变量,就会使用一组默认的环境变量。第 32 行定义了默认的 bootargs 变量;第 35 行定义了默认的 bootcmd 变量。
- #ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
- env_t environment __PPCENV__ = {
- ENV_CRC, /* CRC Sum */
- #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
- 1, /* Flags: valid */
- #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
- #ifdef CONFIG_BOOTARGS
- "bootargs=" CONFIG_BOOTARGS "\0"
- #endif
- #ifdef CONFIG_BOOTCOMMAND
- "bootcmd=" CONFIG_BOOTCOMMAND "\0"
- #endif
默认 bootcmd 变量的内容宏 CONFIG_BOOTCOMMAND 在文件 mx6ullevk.h 中定义。
- #define CONFIG_BOOTCOMMAND \
- "run findfdt;" \
- "mmc dev ${mmcdev};" \
- "mmc dev ${mmcdev}; if mmc rescan; then " \
- "if run loadbootscript; then " \
- "run bootscript; " \
- "else " \
- "if run loadimage; then " \
- "run mmcboot; " \
- "else run netboot; " \
- "fi; " \
- "fi; " \
- "else run netboot; fi"
- #endif
bootcmd 中,首先会执行 findfdt 函数(第 214 行),来确认设备树文件的名称。findfdt 函数内容如下所示,逻辑比较简单:依据 board_name 和 board_rev 的变量值,来设置 fdt_file 的变量值。像目前自己手头烧写的 u-boot 中,board_name=EVK、board_rev=14X14,因此就会选择到 imx6ull-14x14-evk.dtb。
- "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;\0" \
接着第 215 至 216 行,bootcmd 中会切换 emmc 设备(自测环境中 mmcdev=0),再进行扫描检测 emmc 设备是否存在。自己手头环境中是存在 emmc 设备的,所以流程走到第 217 至 224 行的分支,其中首先执行 loadbootscript 函数。
loadbootscript 函数的内容如下所示,只有一条 fatload 指令。当前环境中,mmcdev=0、mmcpart=1、loadaddr=0x80800000、script=boot.scr,即从 emmc0 的第 1 个分区中加载 boot.scr 文件到地址 0x80800000 处。
- "loadbootscript=" \
- "fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \
自测环境中没有 boot.scr 这个文件,因此之后会执行到第 220 至 223 行这个分支。首先执行的是第 220 行中的 loadimage 函数,它的内容如下。作用为从 emmc0 的第 1 个分区中加载 zImage 文件(image=zImage)到地址 0x80800000 处。
- "loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \
因为 emmc 中 zImage 文件存在,所以最后会调用第 221 行的 mmcboot 函数。其内容如下所示,第 164 行首先调用 mmcargs 函数,它的作用是设置 bootargs 变量内容,这将在第 2 章中再进行说明。因为 boot_fdt=try,所以会执行 loadfdt 函数。loadfdt 函数从 emmc0 的第 1 个分区中加载设备树文件到地址 0x83000000 处(fdt_addr=0x83000000)。最后执行 bootz 命令。
- "loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}\0"
- "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;\0" \
至此,bootcmd 内容的分析就结束了。因为为了兼容性考虑,官方加载过程会显得比较繁琐,“浓缩”关键的内容就如下 4 行指令:
- mmc dev 0
- fatload mmc 0:1 0x80800000 zImage
- fatload mmc 0:1 0x83000000 imx6ull-14x14-evk.dtb
- bootz 0x80800000 - 0x83000000
第一句切换到 emmc 设备,第二句加载 zImage 文件,第三句加载设备树文件,第四句执行 bootz 命令。
2. bootargs 变量
在第 1 章中已经了解到,bootargs 变量是通过 mmcargs 函数设置的。其中 console=ttymxc0,即设置控制台对应串口设备;baudrate=115200,即指定串口的波特率;CONFIG_BOOTARGS_CMA_SIZE 和 CONFIG_MFG_NAND_PARTITION 宏都为空;root=/dev/mmcblk0p2 rootwait rw,即设置根文件系统位于 emmc0 的第二个分区,后面的 rootwait 表示等待 emmc 设备初始化完成以后再挂载,rw 表示系统是可读可写的。
- "mmcargs=setenv bootargs console=${console},${baudrate} " \
- CONFIG_BOOTARGS_CMA_SIZE \
- CONFIG_MFG_NAND_PARTITION \
- "root=${mmcroot}\0" \
3. 启动 linux 测试
bootcmd 和 bootargs 两个变量说明完毕之后,就可以开始测试移植的 u-boot 能否正常启动 linux。下面我们测试从 emmc 中以及从网络启动 linux。
3.1 从 emmc 中启动
由于目前还不知道如何将需要的文件烧写到 emmc 中,所以我们先直接使用之前体验开发板功能时烧写到 emmc 中的现成内容。我们先在 u-boot 命令行下查看当前 emmc1 中的文件:
- > fatls mmc 1:1
- 38823 imx6ull-14x14-emmc-4.3-480x272-c.dtb
- 38823 imx6ull-14x14-emmc-4.3-800x480-c.dtb
- 38823 imx6ull-14x14-emmc-7-1024x600-c.dtb
- 38823 imx6ull-14x14-emmc-7-800x480-c.dtb
- 39655 imx6ull-14x14-emmc-hdmi.dtb
- 39563 imx6ull-14x14-emmc-vga.dtb
- 6786272 zimage
其中已经有了 zImage 文件和设备树文件,因为当前自己的显示屏是 4.3 寸 800x480 的,所以选择以上第二份设备树文件。因此在 u-boot 命令行中执行以下命令,再重启:
- > setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
- > setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-emmc-4.3-800x480-c.dtb; bootz 80800000 - 83000000;'
- > saveenv
因为是之前体验烧写的版本,所以只要开发板正常显示桌面界面就证明 linux 系统启动成功了。
3.2 从网络启动
从网络启动 linux 系统主要是为了便于调试,这边通过 tftp 服务传输 zImage 文件和设备树文件。tftp 服务的配置在文章 [ARM Linux系统移植] U-Boot 网络命令 中有介绍。在 u-boot 命令行中执行以下命令,即可设置从网络启动:
- > setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
- > setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-4.3-800x480-c.dtb; bootz 80800000 - 83000000'
- > saveenv