前言
在 U-Boot 中,环境变量用于配置系统的启动参数和行为。是否能正确理解和设置u-boot中的环境变量是启动Linux系统的关键,所以有必要认真学习了解下各环境变量的意思和作用。
最好的学习材料就是实际的例子,所以本篇博文把我遇到过的各个u-boot的环境变量作个汇总,以便将来工作和学习时能进行参考。
如何查看当前u-boot的环境变量
输入下面的命令可查看当前u-boot的所有环境变量:
printenv
如何修改当前u-boot的某个环境变量的值
可以用setenv
命令进行设置,示例如下:
setenv serverip 192.168.5.11
如何对修改后的环境变量进行保存
可以用下面的命令保存下当前的所有环境变量值,下次再运行u-boot时用的就是新的环境变量值。
saveenv
saveenv
命令会将当前所有的环境变量值保存到U-Boot的环境存储中。这个命令会将内存中的环境变量(包括bootargs等设置)写入到U-Boot的存储介质(例如SPI Flash、eMMC、SD卡等)中,以便在设备重新启动时能够保留这些配置。
IMX6ULL_PRO开发板原装的u-boot的环境变量
u-boot启动后按任意键停止u-boot的自动启动进程,然后输入下面的命令查看其所有环境变量:
printenv
运行结果如下:
baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootcmd=run findfdt;run findtee;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
bootcmd_mfg=run mfgtool_args; if test ${tee} = yes; then bootm ${tee_addr} ${initrd_addr} ${fdt_addr}; else bootz ${loadaddr} ${initrd_addr} ${fdt_addr}; fi;
bootdelay=3
bootdir=/boot
bootscript=echo Running bootscript from mmc ...; source
console=ttymxc0
eth1addr=00:01:3f:2d:3e:4d
ethact=ethernet@020b4000
ethaddr=00:01:1f:2d:3e:4d
ethprime=eth1
fdt_addr=0x83000000
fdt_file=100ask_imx6ull-14x14.dtb
fdt_high=0xffffffff
fdtcontroladdr=9ef40478
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 setenv fdt_file imx6ull-14x14-alpha.dtb; fi; fi;
image=zImage
initrd_addr=0x83800000
initrd_high=0xffffffff
ip_dyn=yes
loadaddr=0x80800000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=ext2load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}
loadimage=ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}
loadtee=fatload mmc ${mmcdev}:${mmcpart} ${tee_addr} ${tee_file}
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unused
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${tee} = yes; then run loadfdt; run loadtee; bootm ${tee_addr} - ${fdt_addr}; else 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; fi;
mmcdev=1
mmcpart=2
mmcroot=/dev/mmcblk1p2 rootwait rw
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; run netargs; setenv get_cmd tftp; ${get_cmd} ${image}; ${get_cmd} ${fdt_addr} ${fdt_file}; bootz ${loadaddr} - ${fdt_addr};
panel=TFT43AB
script=boot.scr
tee=no
tee_addr=0x84000000
tee_file=uTee-6ullevkEnvironment size: 2661/8188 bytes
解释如下:
在 U-Boot 中,环境变量用于配置系统的启动参数和行为。以下是上面这些环境变量的解释:
-
baudrate=115200
:串口通信的波特率,U-Boot 将通过串口与主机进行通信,115200 是常见的串口波特率。 -
board_name=EVK
:板卡名称,在这个环境中是 EVK(评估板)。 -
board_rev=14X14
:板卡的版本号,指示当前硬件的版本。 -
boot_fdt=try
:是否尝试加载设备树(FDT,Flattened Device Tree)。设置为try
表示如果设备树存在,尝试加载它。 -
bootcmd=...
:启动命令。它定义了在启动时 U-Boot 执行的一系列操作,包括查找设备树(findfdt
)、加载脚本(loadbootscript
)和启动操作(如从 MMC 或网络启动)。 -
bootcmd_mfg=...
:制造工具的启动命令,通常用于设备制造时的特殊启动过程。 -
bootdelay=3
:启动延时,表示 U-Boot 启动前的等待时间,单位是秒。 -
bootdir=/boot
:启动目录,指定从哪个目录加载启动文件,通常是/boot
。 -
bootscript=echo Running bootscript from mmc ...; source
:定义启动脚本,执行脚本时将输出提示信息并从 MMC 加载脚本。 -
console=ttymxc0
:控制台设备,ttymxc0
表示使用该串口作为控制台。 -
eth1addr=00:01:3f:2d:3e:4d
和ethaddr=00:01:1f:2d:3e:4d
:分别是网卡接口eth1
和eth0
的 MAC 地址。 -
ethact=ethernet@020b4000
:激活的网络接口。表示ethact
对应的硬件地址。 -
ethprime=eth1
:指定首选的网络接口(此处为eth1
)。 -
fdt_addr=0x83000000
:设备树文件的加载地址。 -
fdt_file=100ask_imx6ull-14x14.dtb
:设备树文件的名称。 -
fdt_high=0xffffffff
:设备树的高地址,通常用于 U-Boot 以确保在启动时使用正确的内存区域。 -
fdtcontroladdr=9ef40478
:指向设备树结构的地址。 -
findfdt=...
:查找设备树文件的命令。根据不同的硬件版本,选择合适的设备树文件。 -
image=zImage
:指定内核镜像文件名,这里是zImage
(压缩内核映像)。 -
initrd_addr=0x83800000
和initrd_high=0xffffffff
:初始化 RAM 磁盘(initrd)映像的加载地址和高地址。 -
ip_dyn=yes
:是否动态获取 IP 地址,yes
表示启用 DHCP。 -
loadaddr=0x80800000
:内核镜像加载的地址。 -
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}
:加载启动脚本的命令,fatload
用于从 MMC 卡加载文件。 -
loadfdt=ext2load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}
:从 MMC 加载设备树文件的命令。 -
loadimage=ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}
:从 MMC 加载内核镜像的命令。 -
loadtee=fatload mmc ${mmcdev}:${mmcpart} ${tee_addr} ${tee_file}
:加载可信执行环境(TEE)映像的命令。 -
mfgtool_args=...
:制造工具相关的启动参数,通常与设备的生产相关。 -
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
:定义从 MMC 启动时传递给内核的启动参数。 -
mmcautodetect=yes
:是否自动检测 MMC 设备。 -
mmcboot=...
:从 MMC 启动的命令,包含加载内核和设备树、启动内核等操作。 -
mmcdev=1
:指定 MMC 设备编号,1
通常指代第二个 MMC 设备(设备编号从 0 开始)。 -
mmcpart=2
:指定 MMC 的分区号。 -
mmcroot=/dev/mmcblk1p2 rootwait rw
:指定根文件系统的位置,这里是mmcblk1p2
,并传递给内核启动时的参数。 -
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
:通过网络启动时的命令,包括 NFS 根文件系统的挂载和 DHCP 配置。 -
netboot=...
:通过网络启动的命令,首先从 DHCP 获取 IP 地址,然后通过 TFTP 下载内核镜像和设备树文件,并启动。 -
panel=TFT43AB
:显示面板的型号,TFT43AB 表示 4.3 英寸的 TFT 显示屏。 -
script=boot.scr
:启动脚本文件的名称。 -
tee=no
:是否启用可信执行环境(TEE)。设置为no
表示不启用。 -
tee_addr=0x84000000
:TEE 映像的加载地址。 -
tee_file=uTee-6ullevk
:TEE 映像文件的名称。
总结:
这些环境变量主要配置了启动过程中的设备、文件系统、内核映像、设备树等参数。通过修改这些变量,用户可以定制启动行为,例如选择启动设备、设备树文件、内核镜像等。
从FSL Yocto Project Community BSP
提取出的u-boot的环境变量
具体这个u-boot的源码、修改、编译和运行见我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145662136
u-boot启动后按任意键停止u-boot的自动启动进程,然后输入下面的命令查看其所有环境变量:
printenv
打印的结果如下:
baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootcmd=run findfdt;run findtee;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
bootcmd_mfg=run mfgtool_args;if iminfo ${initrd_addr}; then if test ${tee} = yes; then bootm ${tee_addr} ${initrd_addr} ${fdt_addr}; else bootz ${loadaddr} ${initrd_addr} ${fdt_addr}; fi; else echo "Run fastboot ..."; fastboot 0; fi;
bootdelay=3
bootscript=echo Running bootscript from mmc ...; source
console=ttymxc0
emmc_ack=1
emmc_dev=1
eth1addr=00:01:3f:2d:3e:4d
ethact=ethernet@20b4000
ethprime=eth1
fastboot_dev=mmc1
fdt_addr=0x83000000
fdt_file=undefined
fdt_high=0xffffffff
fdtcontroladdr=9df6d770
findfdt=if test $fdt_file = undefined; then if test $board_name = ULZ-EVK && test $board_rev = 14X14; then setenv fdt_file imx6ulz-14x14-evk.dtb; fi; 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;
findtee=if test $tee_file = undefined; then if test $board_name = ULZ-EVK && test $board_rev = 14X14; then setenv tee_file uTee-6ulzevk; fi; if test $board_name = EVK && test $board_rev = 9X9; then setenv tee_file uTee-6ullevk; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv tee_file uTee-6ullevk; fi; if test $tee_file = undefined; then echo WARNING: Could not determine tee to use; fi; fi;
image=zImage
initrd_addr=0x86800000
initrd_high=0xffffffff
ip_dyn=yes
kboot=bootz
loadaddr=0x80800000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
loadtee=fatload mmc ${mmcdev}:${mmcpart} ${tee_addr} ${tee_file}
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc clk_ignore_unused
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${tee} = yes; then run loadfdt; run loadtee; bootm ${tee_addr} - ${fdt_addr}; else 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; fi;
mmcdev=1
mmcpart=1
mmcroot=/dev/mmcblk1p2 rootwait rw
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${tee} = yes; then ${get_cmd} ${tee_addr} ${tee_file}; ${get_cmd} ${fdt_addr} ${fdt_file}; bootm ${tee_addr} - ${fdt_addr}; else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; 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; fi;
script=boot.scr
sd_dev=1
serial#=2e1181d769237caa
splashimage=0x8c000000
tee=no
tee_addr=0x84000000
tee_file=undefinedEnvironment size: 3388/8188 bytes
这些打印出的环境变量的大概解释
在U-Boot中,printenv
命令列出了环境变量的当前设置。每个环境变量用于配置系统的启动参数、设备设置等。以下是这些环境变量的解释:
-
baudrate=115200
- 设置U-Boot的串口通信波特率为115200。
-
board_name=EVK
- 设定开发板的名称为EVK(Evaluation Kit)。
-
board_rev=14X14
- 设置开发板的硬件版本为14X14。
-
boot_fdt=try
- 设置U-Boot启动时是否尝试加载设备树文件。如果为
try
,表示尝试加载设备树文件,若加载失败则继续启动。
- 设置U-Boot启动时是否尝试加载设备树文件。如果为
-
bootcmd=run findfdt; run findtee; mmc dev ${mmcdev}; mmc dev ${mmcdev}; if mmc rescan; then ...
bootcmd
是启动时执行的命令。此命令定义了启动顺序,包括:- 寻找设备树文件(
findfdt
)。 - 寻找TEE(可信执行环境,
findtee
)。 - 启动SD卡(
mmc dev
)。 - 如果SD卡存在且能被扫描到,加载启动脚本、内核镜像,设备树等。
- 寻找设备树文件(
-
bootcmd_mfg=run mfgtool_args;if iminfo ${initrd_addr}; then ...
- 这是制造商工具模式下的启动命令。它在启动时加载初始化内核镜像和设备树文件,或在失败时进入fastboot模式。
-
bootdelay=3
- 启动延迟为3秒,在启动时等待的时间。
-
bootscript=echo Running bootscript from mmc ...; source
- 启动脚本,它会在启动时执行脚本(通常用于自动化启动流程)。
-
console=ttymxc0
- 设置控制台输出的设备为
ttymxc0
,通常指的是串口0。
- 设置控制台输出的设备为
-
emmc_ack=1
- 启用eMMC设备确认。
emmc_dev=1
- 设置eMMC设备的编号为1。
eth1addr=00:01:3f:2d:3e:4d
- 设置以太网接口1的MAC地址。
ethact=ethernet@20b4000
- 设置当前活动的以太网控制器为
ethernet@20b4000
。
ethprime=eth1
- 设置以太网设备的首选接口为
eth1
。
fastboot_dev=mmc1
- 设置fastboot模式下的设备为
mmc1
,通常是SD卡或eMMC。
fdt_addr=0x83000000
- 设置设备树文件加载的内存地址为
0x83000000
。
fdt_file=undefined
- 设备树文件未设置,通常在找不到设备树文件时会用
undefined
表示。
fdt_high=0xffffffff
- 设置设备树加载的内存上限地址为
0xffffffff
,指示内核可以使用的最大内存地址。
fdtcontroladdr=9df6d770
- 设备树控制地址,通常用于指向设备树的内存地址。
findfdt=if test $fdt_file = undefined; then ...
- 寻找设备树文件的脚本,根据不同的开发板名称和版本选择合适的设备树文件。
findtee=if test $tee_file = undefined; then ...
- 寻找TEE文件的脚本,确保选择合适的可信执行环境文件。
image=zImage
- 设置内核镜像文件名为
zImage
。
initrd_addr=0x86800000
- 设置初始化内存盘(initrd)的加载地址为
0x86800000
。
initrd_high=0xffffffff
- 设置内存盘的高地址为
0xffffffff
,即指示内核加载initrd时使用的内存范围。
ip_dyn=yes
- 启用动态IP分配(通过DHCP)。
kboot=bootz
- 设置内核启动命令为
bootz
,通常用于启动压缩内核(zImage
)。
loadaddr=0x80800000
- 设置内核镜像、设备树、脚本等文件的加载地址为
0x80800000
。
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}
- 加载启动脚本的命令,从MMC卡中加载脚本文件(通常是
boot.scr
)。
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
- 从MMC卡加载设备树文件。
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
- 从MMC卡加载内核镜像。
loadtee=fatload mmc ${mmcdev}:${mmcpart} ${tee_addr} ${tee_file}
- 从MMC卡加载TEE镜像。
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc clk_ignore_unused
- 设置在制造商工具模式下的启动参数。
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
- 设置从MMC启动时的内核启动参数,包括控制台设置和根文件系统位置。
mmcautodetect=yes
- 启用MMC自动检测。
mmcboot=echo Booting from mmc ...; run mmcargs; ...
- 从MMC卡启动的命令,包括加载内核、设备树和TEE等。
mmcdev=1
- 设置MMC设备为1(通常指SD卡或eMMC设备)。
mmcpart=1
- 设置MMC分区为1。
mmcroot=/dev/mmcblk1p2 rootwait rw
- 设置根文件系统的位置为
/dev/mmcblk1p2
,并指定rootwait
(等待根文件系统)和rw
(以读写模式挂载)。
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
- 设置网络启动时的启动参数,包括NFS根文件系统和DHCP。
netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs; ...
- 从网络启动的命令,包括加载内核镜像、设备树等。
script=boot.scr
- 启动脚本文件的名称,通常是
boot.scr
。
sd_dev=1
- 设置SD卡设备为1。
serial#=2e1181d769237caa
- 设置设备的唯一序列号。
splashimage=0x8c000000
- 设置启动时显示的Splash屏幕图像的地址。
tee=no
- 禁用TEE(可信执行环境)。
tee_addr=0x84000000
- 设置TEE镜像的加载地址为
0x84000000
。
tee_file=undefined
- TEE文件未定义。
这些环境变量控制了U-Boot的各种设置和启动行为,确保系统按照预定的方式启动,加载正确的内核、设备树、文件系统等。如果你需要更改某些启动参数,可以通过修改这些变量来调整启动流程。
环境变量bootcmd
的详细介绍
请详细解释参数bootcmd
:
bootcmd=run findfdt;run findtee;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
bootcmd
是 U-Boot 启动时执行的主要命令,它定义了启动的逻辑流程。你提供的 bootcmd
命令非常复杂,包含了多个步骤。下面是对每个部分的详细解释:
bootcmd
参数解释:
bootcmd=run findfdt;run findtee;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
run findfdt; run findtee;
run findfdt
run findtee
findfdt
和findtee
是两个自定义的环境变量,它们分别用于寻找设备树文件(FDT,Flattened Device Tree)和 TEE(可信执行环境)文件。findfdt
会根据板子的名称和版本选择对应的设备树文件,并设置fdt_file
环境变量。findtee
会设置tee_file
环境变量,选择适当的可信执行环境(TEE)镜像文件。- 这些命令帮助 U-Boot 在启动时加载正确的配置文件。
mmc dev ${mmcdev}; mmc dev ${mmcdev};
mmc dev ${mmcdev}
mmc dev ${mmcdev}
mmc dev
是 U-Boot 中用于选择当前活动的 MMC 设备的命令。这里的${mmcdev}
是一个变量,表示 MMC 设备的编号。- 这行命令做了两次
mmc dev ${mmcdev}
,这通常用于确保所选的 MMC 设备被正确初始化并切换为当前设备。 - 一般来说,
mmcdev
会被设置为一个值(如 1),表示设备号,通常指的是 SD 卡或 eMMC 存储设备。
if mmc rescan; then ... fi;
if mmc rescan; then ...
mmc rescan
是一个命令,用于重新扫描 MMC 卡。如果 MMC 卡没有正确初始化或有新的卡插入,可以使用该命令来检测并重新扫描卡。- 这个
if
语句检查 MMC 卡是否成功扫描并连接。如果成功(即mmc rescan
返回true
),则进入then
分支,继续执行后面的命令。
if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi;
if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi;
-
这个部分根据条件判断来决定启动的方式,分为以下几步:
-
run loadbootscript
:- 尝试加载启动脚本(通常是
boot.scr
文件)。loadbootscript
是一个自定义命令,定义了如何从 MMC 卡加载脚本文件。 - 如果加载成功,则执行
run bootscript
,即运行实际的启动脚本,通常包含一些初始化步骤。
- 尝试加载启动脚本(通常是
-
else if run loadimage
:- 如果加载启动脚本失败,则尝试加载内核镜像文件。
loadimage
是自定义命令,用于加载内核镜像(通常是zImage
或uImage
)。 - 如果加载成功,则执行
run mmcboot
,即使用 MMC 启动内核,启动系统。
- 如果加载启动脚本失败,则尝试加载内核镜像文件。
-
else run netboot
:- 如果无法加载启动脚本或内核镜像,则尝试通过网络启动(
netboot
)。netboot
是自定义命令,用于通过网络从 TFTP 或其他协议加载镜像并启动。
- 如果无法加载启动脚本或内核镜像,则尝试通过网络启动(
-
else run netboot; fi
else run netboot; fi
- 如果
mmc rescan
失败(即没有检测到 MMC 卡),则直接跳到run netboot
,通过网络启动系统。
总结:
这个 bootcmd
定义了一个灵活的启动流程,确保在不同情况下都能找到合适的启动方式。具体流程如下:
- 先尝试查找并设置设备树文件和 TEE 文件。
- 然后检查 MMC 设备(SD 卡或 eMMC)是否正确连接并扫描。
- 如果扫描成功,首先尝试加载启动脚本,若失败则尝试加载内核镜像并从 MMC 启动。如果这些都失败,则通过网络启动。
- 如果 MMC 设备没有正确连接或无法扫描,则通过网络启动。
这个流程确保了系统能够在不同的硬件环境下灵活地找到启动方式,保证启动的可用性。
手动方式实现网络启动的分析
在上面对环境变量bootcmd
的分析中,我们知道,当u-boot自动进行Linux系统的启动时,如果u-boot没有能从MMC设备启动内核,那么它会从网络进行启动。
我们在进行内核和设备树开发时,也常常用网络的方式启动内核,因为这样很方便嘛,不用去进行烧写操作,不过此时我们常常用手动的方式实现内核的网络启动。
比如我在下面这篇博文对设备树、内核、根文件系统进行移植时就需要用手动的方式实现内核的网络启动:
https://blog.csdn.net/wenhao_ir/article/details/145822475
在实际研发中,我们通常会按任意键让u-boot的自动启动停下来,如下图所示:
然后我们可以手动执行下面这条命令让u-boot以网络方式启动内核。
run netboot
接下来,我们就根据这些环境变量来分析一下这条命令执行后的运行过程。
从命令printenv
的结果来看,netboot
是一个环境变量,其内容如下:
netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${tee} = yes; then ${get_cmd} ${tee_addr} ${tee_file}; ${get_cmd} ${fdt_addr} ${fdt_file}; bootm ${tee_addr} - ${fdt_addr}; else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; 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; fi;
其解释如下 :
环境变量netboot
的详细分析
netboot
参数定义了如何通过网络启动(即通过 TFTP 或 NFS 等协议)。该命令使用了条件判断、环境变量和一些 U-Boot 提供的命令来实现灵活的网络启动过程。下面是对该命令的详细解析:
netboot
参数解释:
netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${tee} = yes; then ${get_cmd} ${tee_addr} ${tee_file}; ${get_cmd} ${fdt_addr} ${fdt_file}; bootm ${tee_addr} - ${fdt_addr}; else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; 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; fi;
echo Booting from net ...;
echo Booting from net ...
- 在启动过程中,这个命令输出一条信息,提示系统正在通过网络启动。
echo
是一个简单的命令,用来显示文本。
${usb_net_cmd};
${usb_net_cmd}
- 这里执行了一个环境变量
${usb_net_cmd}
。这个变量通常包含与 USB 网络设备(如 USB Ethernet 适配器)相关的命令,确保网络设备正确初始化。如果系统通过 USB 以太网适配器连接网络,它会执行这些命令来配置网络连接。
run netargs;
run netargs
- 这个命令会运行
netargs
环境变量中的命令,通常是设置网络启动所需的启动参数。例如,netargs
可能包含设置bootargs
(启动参数)的命令,如动态分配 IP 地址(DHCP)等。bootargs
这个参数很重要,详细的分析见下文【搜索关键词“事关重要的环境变量”】。
if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi;
if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi;
ip_dyn
是一个环境变量,表示是否启用动态 IP 地址分配(通过 DHCP)。- 如果
ip_dyn
为yes
,则使用 DHCP 获取 IP 地址。在这种情况下,get_cmd
环境变量被设置为dhcp
,表示 U-Boot 使用 DHCP 协议获取网络配置。 - 如果
ip_dyn
为no
,则使用 TFTP 来加载内核镜像、设备树等文件。此时,get_cmd
被设置为tftp
。
- 如果
${get_cmd} ${image};
${get_cmd} ${image}
- 这里使用了
${get_cmd}
,它的值要么是dhcp
,要么是tftp
,取决于之前的判断。 ${image}
是内核镜像的名称(通常是zImage
或uImage
)。- 如果
get_cmd
是dhcp
,系统会首先通过 DHCP 获取 IP 地址。 - 如果
get_cmd
是tftp
,系统会通过 TFTP 协议从网络服务器下载内核镜像。get_cmd
实际上指示了如何获取内核镜像。
if test ${tee} = yes; then ${get_cmd} ${tee_addr} ${tee_file}; ${get_cmd} ${fdt_addr} ${fdt_file}; bootm ${tee_addr} - ${fdt_addr};
if test ${tee} = yes; then ${get_cmd} ${tee_addr} ${tee_file};${get_cmd} ${fdt_addr} ${fdt_file};bootm ${tee_addr} - ${fdt_addr};
tee
是一个环境变量,表示是否启用 TEE(可信执行环境)。如果tee
为yes
,则:get_cmd ${tee_addr} ${tee_file}
会从网络获取 TEE 文件,tee_addr
是加载地址,tee_file
是 TEE 镜像文件的名称。get_cmd ${fdt_addr} ${fdt_file}
会从网络获取设备树文件(DTB)。fdt_addr
是设备树加载的地址,fdt_file
是设备树文件的名称。bootm ${tee_addr} - ${fdt_addr}
:使用bootm
命令来启动内核,bootm
的参数包括 TEE 镜像的地址(tee_addr
)、内核镜像的地址(-
表示不加载内核镜像),以及设备树的地址(fdt_addr
)。这样内核、设备树和 TEE 会一并启动。
else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi;
else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; thenbootz ${loadaddr} - ${fdt_addr};else if test ${boot_fdt} = try; then bootz;else echo WARN: Cannot load the DT;fi;
fi;
- 这个部分处理设备树(FDT)的加载:
- 如果
boot_fdt
为yes
或try
,则尝试从网络获取设备树文件(fdt_file
)。 - 如果成功获取设备树文件,使用
bootz
启动内核,其中loadaddr
是内核镜像的加载地址,fdt_addr
是设备树的加载地址。 - 如果设备树文件加载失败,且
boot_fdt
为try
,则尝试直接启动内核,不加载设备树(即执行bootz
)。 - 如果无法加载设备树并且
boot_fdt
不为try
,则输出警告信息"WARN: Cannot load the DT"
,即无法加载设备树。
- 如果
else bootz; fi; fi;
else bootz; fi; fi;
- 如果
tee
为no
,且没有设备树文件的要求(boot_fdt
也为no
),直接执行bootz
来启动内核。此时系统仅加载内核镜像,不加载设备树和 TEE 文件。
通过环境变量netboot
总结出网络启动方式的流程
通过上面的对 netboot
内容的分析,整个 netboot
命令的流程如下:
- 输出提示信息,表明正在通过网络启动。
- 配置网络(如果嵌入式板子使用的是 USB 网络设备)。
- 设置网络启动的参数(使用 DHCP 还是 TFTP的形式来获取内核的设备树文件和镜像)。
- 尝试从网络获取内核镜像。
- 如果启用 TEE,则获取 TEE 镜像和设备树文件,并通过
bootm
启动系统。 - 如果未启用 TEE,尝试获取设备树文件,如果成功加载设备树,使用
bootz
启动内核。如果设备树无法加载并且boot_fdt
为try
,则直接启动内核。 - 如果所有步骤失败,显示警告或直接启动内核。
该命令定义了灵活的网络启动机制,可以根据系统配置和环境条件选择合适的启动方式。
环境变量netargs
的分析(事关重要的环境变量bootargs
)
环境变量netargs
的定义如下:
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
环境变量netargs
在上面已经分析的环境变量netboot
的具体内容中被用到,相关的内容为:
run netargs
具体的语义环境如下:
netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs;.......
所以,其实环境变量netboot
中的命令run netargs
等效于下面的语句:
run setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
可见,其实就是设置向内核传递参数的环境变量bootargs
,关于这个环境变量bootargs
的详细介绍见博文 https://blog.csdn.net/wenhao_ir/article/details/145901614
所以,如果我们要改变境变量bootargs
的内容,实际上应该是去修改环境变量netargs
,而不是直接去修改环境变量bootargs
,你如果直接去修改环境变量bootargs
,那么在执行命令 run netboot
时,会被命令run netargs
替换为在环境变量netargs
中写好的对环境变量bootargs
的设置值。
我在实际移植内核时该如何设置环境变量
在博文 https://blog.csdn.net/wenhao_ir/article/details/145822475 中,进行了对设备树和Linux内核的移植。
在移植时,我们是通过TFTP的形式来获取内核和设备树文件,所以根据上面的分析,要进行下面这样的设置:
01-设置环境变量ip_dyn
的值为no,如果ip_dyn
的值为yes,则当以netboot
的方式启动内核时,u-boot会使用 DHCP 协议获取网络配置,然后从这个利用DHCP 协议配置好的网络中去获取内核镜像和设备树文件。
setenv ip_dyn no
02-设置设备树文件的名字
setenv fdt_file imx6ull-14x14-evk.dtb
03-设置TFTP的服务器地址和NFS的服务器地址:
setenv serverip 192.168.5.11
我在博文 https://blog.csdn.net/wenhao_ir/article/details/145814363 中已经证实了,serverip
这个环境变量就是u-boot中TFTP协议使用的服务器地址。详情搜索博文 https://blog.csdn.net/wenhao_ir/article/details/145814363 中的关键词“这个地址必须设置”。
根据上面的分析,环境变量serverip不仅是TFTP的服务器地址,还是环境变量netargs
中对NFS设置的服务器地址,当然你也可以把环境变量netargs
中的相关环境名更改一下,比如把:
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
中的nfsroot=${serverip}
改为nfsroot=${serverip_nfs}
,就可以单独设置 NFS的服务器地址了。
关于这其中涉及到的关键的环境变量bootargs
,我在本篇博文的前面已经介绍了,请搜索“事关重要的环境变量”。在是u-boot启动过程中,bootargs
是u-boot传递给Linux内核的一个关键参数,它包含了内核启动时所需要的各种配置选项。U-Boot通过设置环境变量bootargs
来指定这些启动参数。内核获取到这些参数后就可以按照这些配置去启动和设置内核。
04-设置NFS的目录地址
环境变量netargs
中就要用到这个值,设置如下:
setenv nfsroot /home/book/mybuild/nfs_linux_rootfs
05-设置u-boot的IP地址
由于不使用DHCP方式获取网络配置,所以当然要为u-boot指定一个IP地址了,命令如下:
setenv ipaddr 192.168.5.9
06-重新设置环境变量netargs
的值
因为我需要挂载是可读可写的,而不是只读的,所以需要在NFS挂载设置时加上rw
属性,具体的设置如下:
setenv netargs 'setenv bootargs console=${console},${baudrate} root=/dev/nfs rw ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp'
其实这里本质上修改的是环境变量bootargs
的值,环境变量bootargs
的内容就是u-boot向内核传递的参数,详情见 https://blog.csdn.net/wenhao_ir/article/details/145901614
关于这个修改的探索过程和来历详见 https://blog.csdn.net/wenhao_ir/article/details/145883835 【搜索关键词“修改NFS挂载方式为可读可写并再次测试”】
以上这些环境变量设置完毕后,就可以用下面的命令从网络启动Linux系统了:
run netboot
后续的启动过程详见下面两篇博文:
https://blog.csdn.net/wenhao_ir/article/details/145883835
https://blog.csdn.net/wenhao_ir/article/details/145822475