u-boot的环境变量设置、保存、汇总与说明【同时对u-boot的环境变量`bootcmd`和网络启动(run netboot)方式进行了详细解释】
前言
在 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
相关文章:
u-boot的环境变量设置、保存、汇总与说明【同时对u-boot的环境变量`bootcmd`和网络启动(run netboot)方式进行了详细解释】
前言 在 U-Boot 中,环境变量用于配置系统的启动参数和行为。是否能正确理解和设置u-boot中的环境变量是启动Linux系统的关键,所以有必要认真学习了解下各环境变量的意思和作用。 最好的学习材料就是实际的例子,所以本篇博文把我遇到过的各个…...
【后端】Docker一本通
长期更新补充,建议关注收藏点赞 目录 Docker概述安装部署Docker基本操作使用docker部署tomcat使用docker部署mysql Docker概述 docker是⼀个应⽤级隔离的虚拟化技术docker三大核心概念 镜像:是具有源的所有特征的⼀个标记⽂件 仓库:存放镜像…...
基于Spring Boot和Vue的餐饮管理系统设计与实现
大家好,今天要和大家聊的是一款基于Spring Boot和Vue的餐饮管理系统的设计与实现。项目源码以及部署相关事宜请联系我,文末附上联系方式。 项目简介 基于Spring Boot和Vue的餐饮管理系统设计与实现的主要使用者分为管理员、员工和用户。没有授权的用户无…...
如何快速的解除oracle dataguard
有些时候,我们为了使oracle dg的standby库另做他用,需要解除oracle dataguard数据同步。我本地因为standby库存储出现故障,导致dg存在问题,故需要解除。今天,我们通过使用部分命令,实现dg的快速解除。 1&a…...
C语言【指针篇】(四)
前言:正文1. 字符指针变量2. 数组指针变量2.1 数组指针变量是什么?2.2 数组指针变量怎么初始化 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 两段有趣的代码4.3.1 typedef关键字 5. 函数指针数组6. 转移表 总结 前言&am…...
Python基于Django的网络课程在线学习平台【附源码】
博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...
vscode集成DeepSeek
vscode 扩展 安装 Cline Meet Cline,一个可以使用你的CLI和编辑器的AI助手。 得益于 Claude 3.5 Sonnet的代理编码功能,Cline 可以逐步处理复杂的软件开发任务。借助让他创建和编辑文件、探索大型项目、使用浏览器和执行终端命令(在您授予权限后)的工具&…...
【TCAD】Sentaurus 中的“陷阱trap”仿真设置
13.1 陷阱类型 13.2 定义陷阱 13.3 陷阱态密度的类型 13.4 陷阱空间分布 13.5 陷阱占据 13.6 陷阱横截面 13.7 陷阱作为掺杂 13.8 陷阱填充控制 13.9 陷阱可视化 目标 演示如何使用 Sentaurus 设备在模拟中使用陷阱。13.1 陷阱类型...
Lucene硬核解析专题系列(三):查询解析与执行
Lucene的索引构建为高效搜索奠定了基础,而查询解析与执行则是将用户意图转化为实际结果的关键环节。本篇将从查询的解析开始,逐步深入到查询类型、评分模型和执行流程,揭示Lucene搜索能力的底层原理。 一、查询语法与QueryParser的工作原理 Lucene的查询过程始于用户输入的…...
Linux操作系统5-进程信号3(信号产生总结与核心转储)
上篇文章:Linux操作系统5-进程信号2(信号的4种产生方式,signal系统调用)-CSDN博客 本篇Gitee仓库:myLerningCode/l25 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com) 本篇重点:核心…...
家用可燃气体探测器——家庭燃气安全的坚实防线
随着社会的发展和变迁,天然气为我们的生活带来了诸多便利,无论是烹饪美食,还是温暖取暖,都离不开它的支持。然而,燃气安全隐患如影随形,一旦发生泄漏,可能引发爆炸、火灾等严重事故,…...
【学习笔记】网络设备(华为交换机)基础知识 9 —— 堆叠配置
提示:学习华为交换机堆叠配置,含堆叠的概念、功能、角色、ID和优先级;堆叠的建立过程以及注意事项;包含堆叠的配置命令,以及堆叠的配置案例 一、前期准备 1.已经可以正常访问交换机的命令行接口 Console口本地访问教…...
【Linux】Linux的进程控制
目录 1. 学习思维导图 2.进程创建(fork) 2.1 fork创建进程失败 3.进程终止 3.1 进程退出情况 3.1.1main函数 3.1.2 退出码 3.2 exit/_exit函数 1. exit() 函数 2. _exit() 函数 4.进程等待 4.1 实现进程等待的方法 wait/waitpid方法 区别&a…...
电子电气架构 --- 汽车行业技术变革
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...
【告别双日期面板!一招实现el-date-picker智能联动日期选择】
告别双日期面板!一招实现el-date-picker智能联动日期选择 1.需求背景2.DateTimePicker 现状图3.日期选择器实现代码4.日期选择器实现效果图5.日期时间选择器实现代码6.日期时间选择器实现效果图 1.需求背景 在用户使用时间查询时,我们经常需要按月份筛选…...
利用 Python 爬虫进行跨境电商数据采集
1 引言2 代理IP的优势3 获取代理IP账号4 爬取实战案例---(某电商网站爬取)4.1 网站分析4.2 编写代码4.3 优化代码 5 总结 1 引言 在数字化时代,数据作为核心资源蕴含重要价值,网络爬虫成为企业洞察市场趋势、学术研究探索未知领域…...
2. 在后端代码中加入日志记录模块
1. 说明 日志模块基本上是每一个软件系统开发中必不可少的,主要用于持久记录一些代码运行中的输出信息,辅助编码人员进行代码调试,以及后期软件上线运行报错分析。在Python中加入日志模块比较简单,只需要借助logging和RotatingFi…...
C++ 17 允许在 for 循环,if 语句,switch 语句中初始化变量
看到 c 有这个特性,python 和 java 似乎都没有,根据 AI 的回答进行了一些整理总结。 文章目录 **1. 在 for 循环中初始化变量****特点****多个变量初始化** **2. 在 if 语句中初始化变量(C17 及以上)****示例****特点** **3. 在 s…...
Pycharm中怎么加快下载三方包速度
Pycharm中怎么加快下载三方包速度 使用命令行下载,-i pip install transformers -i https://mirrors.aliyun.com/pypi/simple/ 在Windows系统的PyCharm中使用Python 3.12环境时,可通过以下几种方式配置不同镜像源来加快下载包的速度。 方式一:在PyCharm界面中直接配置镜…...
Transformer 代码剖析7 - 词元嵌入(TokenEmbedding) (pytorch实现)
一、类定义与继承关系剖析 1.1 代码结构图示 #mermaid-svg-9COHbtmHJhpiroHM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9COHbtmHJhpiroHM .error-icon{fill:#552222;}#mermaid-svg-9COHbtmHJhpiroHM .error-t…...
Unity中动态切换光照贴图的方法
关键代码:LightmapSettings.lightmaps lightmapDatas; LightmapData中操作三张图:lightmapColor,lightmapDir,以及一张ShadowMap 这里只操作前两张: using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public cl…...
【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter27-工作者线程
二十七、工作者线程 工作者线程 前端开发者常说:“JavaScript 是单线程的。”这种说法虽然有些简单,但描述了 JavaScript 在浏览器中的一般行为。因此,作为帮助 Web 开发人员理解 JavaScript 的教学工具,它非常有用。单线程就意味…...
Qt基于等待条件QWaitCondition实现的任务队列模型示例
核心概念 Qt中的QWaitCondition是一个用于多线程同步的类,允许线程在某些条件满足时唤醒其他等待的线程。它通常与QMutex配合使用,协调线程之间的执行顺序,适用于生产者-消费者模型、任务队列调度等场景。 wait():使当前线程进…...
本地大模型编程实战(26)用langgraph实现基于SQL数据构建的问答系统(5)
本文将将扩展上一篇文章完成的 langgraph 链,继续使用基于 langgraph 链 ,对结构化数据库 SQlite 进行查询的方法。该系统建立以后,我们不需要掌握专业的 SQL 技能,可以用自然语言询问有关数据库中数据的问题并返回答案。主要完善…...
【RAG生成】生成模块核心技术解密:从理论到实践的全链路优化
RAG知识系列文章 【RAG实践】手把手Python实现搭建本地知识问答系统【RAG进阶】从基础到模块化:深度解析RAG技术演进如何重塑AI知识边界【RAG检索】RAG技术揭秘:检索≠召回?【RAG增强】解密RAG系统排序优化:从基础原理到生产实践…...
近似最近邻(ANN)算法库实战
引言:从“精确”到“近似”的思维跃迁 在电商推荐系统中,每秒需要为上百万用户找到最相关的商品——传统KNN的暴力搜索甚至无法完成一次查询,而近似最近邻(ANN)算法库如Faiss(Facebook)和Annoy…...
Linux与UDP应用2:简易聊天室
UDP应用2:简易聊天室 本篇介绍 在前面的基本使用过程中已经完成了本地和网络通信,既然一个人和一台服务器可以进行通信,那么多个人连接一台服务器也可以和这台服务器实现通信。在这个基础上,如果服务器可以将某个人发给服务器的…...
张雪峰教育观点及争议分析
李升伟 整理 张雪峰(网络常用名,本名张子彪)是中国知名的考研辅导教师、教育领域自媒体人,因其幽默犀利的语言风格和直击痛点的教育观点走红网络。以下是对他的基本介绍及综合评价: --- ### **一、基本情况** 1. **个…...
从0开始的IMX6ULL学习篇——裸机篇之分析粗略IMX6ULL与架构
目录 简单的说一下Cortex-A7架构 讨论ARMv7a-cortex系列的运行模式 寄存器 后言 让我们到NXP的官网上扫一眼。 i.MX 6ULL应用处理器_Arm Cortex-A7单核,频率为900 MHz | NXP 半导体 我们先看CPU Platform,这个是我们的核心。 这里我们的芯片是基于Ar…...
面向实时性的超轻量级动态感知视觉SLAM系统
一、重构后的技术架构设计(基于ROS1 ORB-SLAM2增强) #mermaid-svg-JEJte8kZd7qlnq3E {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JEJte8kZd7qlnq3E .error-icon{fill:#552222;}#mermaid-svg-JEJte8kZd7qlnq3E .…...
