Distro Bootcmd 是U-Boot中设计的一种启动机制,用来自适应各种不同的启动媒介,并从中找到可用的启动镜像然后启动,具体实现逻辑如下:
-
板级自定义启动设备
#define BOOT_TARGET_DEVICES(func) \ func(MMC, mmc, 0) \ func(MMC, mmc, 1) \ func(USB, usb, 0) \ func(PXE, pxe, na) \ func(DHCP, dhcp, na) #include <config_distro_bootcmd.h>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-
BOOT TARGETS
-
定义启动环境变量
从3.H可以看到,distro_bootcmd是一个循环,针对每一个$(boot_targets)运行对应的bootcmd_$(target)。这其间只要有任何一个命令运行成功,就会启动系统,后面的命令不会再运行,否则继续运行后面的命令。
-
$(boot_targets)是什么
3.B中,宏BOOTENV_BOOT_TARGETS。
该宏定义的原型在2.E中:
#define BOOTENV_DEV_NAME(devtypeu, devtypel, instance) \ BOOTENV_DEV_NAME_##devtypeu(devtypeu, devtypel, instance) #define BOOTENV_BOOT_TARGETS \ "boot_targets=" BOOT_TARGET_DEVICES(BOOTENV_DEV_NAME) "\0"
- 1
- 2
- 3
- 4
宏BOOT_TARGET_DEVICES在1中的板级自定义启动设备定义,将其带入,则BOOTENV_BOOT_TARGETS展开为:
boot_targets = BOOTENV_DEV_NAME(MMC, mmc, 0) \ BOOTENV_DEV_NAME(MMC, mmc, 1) \ BOOTENV_DEV_NAME(USB, usb, 0) \ BOOTENV_DEV_NAME(PXE, pxe, na) \ BOOTENV_DEV_NAME(DHCP, dhcp, na)
- 1
- 2
- 3
- 4
- 5
根据:
#define BOOTENV_DEV_NAME(devtypeu, devtypel, instance) \ BOOTENV_DEV_NAME_##devtypeu(devtypeu, devtypel, instance)
- 1
- 2
这个宏里面有个看着比较奇怪的命名,devtypeu、devtypel,结合宏定义的形式,我猜测这里的u代表upper(大写),l代表lower(小写)的意思。
所以上面的boot_targets可以进一步展开:
boot_targets = BOOTENV_DEV_NAME_MMC(MMC, mmc, 0) \ BOOTENV_DEV_NAME_MMC(MMC, mmc, 1) \ BOOTENV_DEV_NAME_USB(USB, usb, 0) \ BOOTENV_DEV_NAME_PXE(PXE, pxe, na) \ BOOTENV_DEV_NAME_DHCP(DHCP, dhcp, na)
- 1
- 2
- 3
- 4
- 5
后续的继续展开分析我们只以mmc 0为例,其他的mmc 1,usb,pxe,dhcp的展开原理相同。
根据2.D:
#define BOOTENV_DEV_NAME_MMC BOOTENV_DEV_NAME_BLKDEV
- 1
boot_targets可以继续展开:
boot_targets = BOOTENV_DEV_NAME_BLKDEV(MMC, mmc, 0) \ BOOTENV_DEV_NAME_BLKDEV(MMC, mmc, 1) \ BOOTENV_DEV_NAME_BLKDEV(USB, usb, 0) \ BOOTENV_DEV_NAME_PXE(PXE, pxe, na) \ BOOTENV_DEV_NAME_DHCP(DHCP, dhcp, na)
- 1
- 2
- 3
- 4
- 5
根据2.C:
#define BOOTENV_DEV_NAME_BLKDEV(devtypeu, devtypel, instance) \ #devtypel #instance " "
- 1
- 2
boot_targets最终展开为:
boot_targets = mmc0 mmc 1 usb0 pxe dhcp
- 1
所以对于这些boot_targets最终的启动命令bootcmd_$(target)分别为:bootcmd_mmc0, bootcmd_mmc1,
bootcmd_usb0, bootcmd_pxe, bootcmd_dhcp。
下面进一步分析bootcmd_mmc0来自哪里:
3.G中有宏BOOT_TARGET_DEVICES(BOOTENV_DEV) 结合BOOT_TARGET_DEVICES在1中的定义展开,如下:
BOOTENV_DEV(MMC, mmc, 0) \ BOOTENV_DEV(MMC, mmc, 1) \ BOOTENV_DEV(USB, usb, 0) \ BOOTENV_DEV(PXE, pxe, na) \ BOOTENV_DEV(DHCP, dhcp, na)\
- 1
- 2
- 3
- 4
- 5
2.F中BOOTENV_DEV的宏定义如下:
#define BOOTENV_DEV(devtypeu, devtypel, instance) \ BOOTENV_DEV_##devtypeu(devtypeu, devtypel, instance)
- 1
- 2
所以可以继续展开:
BOOTENV_DEV_MMC(MMC, mmc, 0) \ BOOTENV_DEV_MMC(MMC, mmc, 1) \ BOOTENV_DEV_USB(USB, usb, 0) \ BOOTENV_DEV_USB(PXE, pxe, na) \ BOOTENV_DEV_DHCP(DHCP, dhcp, na)\
- 1
- 2
- 3
- 4
- 5
根据2.D:
#define BOOTENV_DEV_MMC BOOTENV_DEV_BLKDEV
- 1
进一步展开:
BOOTENV_DEV_BLKDEV(MMC, mmc, 0) \ BOOTENV_DEV_BLKDEV(MMC, mmc, 1) \ BOOTENV_DEV_BLKDEV(USB, usb, 0) \ BOOTENV_DEV_DHCP(PXE, pxe, na) \ BOOTENV_DEV_DHCP(DHCP, dhcp, na)\
- 1
- 2
- 3
- 4
- 5
根据2.B:
#define BOOTENV_DEV_BLKDEV(devtypeu, devtypel, instance) \ "bootcmd_" #devtypel #instance "=" \ "setenv devnum " #instance "; " \ "run " #devtypel "_boot\0"
- 1
- 2
- 3
- 4
这里以mmc0为例,带入,则得到bootcmd_mmc0:
bootcmd_mmc0 = "setenv devnum 0"; " \ "run mmc_boot\0"
- 1
- 2
这里又引入了mmc_boot这命令,它来自何方:
3.A中定义了宏BOOTENV_SHARED_MMC ,根据2.D:
#define BOOTENV_SHARED_MMC BOOTENV_SHARED_BLKDEV(mmc)
- 1
带入2.A:
#define BOOTENV_SHARED_BLKDEV_BODY(devtypel) \ "if " #devtypel " dev ${devnum}; then " \ "setenv devtype " #devtypel "; " \ "run scan_dev_for_boot_part; " \ "fi\0"
#define BOOTENV_SHARED_BLKDEV(devtypel)
#devtypel “_boot=”
BOOTENV_SHARED_BLKDEV_BODY(devtypel)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
得到mm_boot命令如下:
mmc_boot ="if mmc dev ${devnum}; then " \
"setenv devtype mmc "; " \
"run scan_dev_for_boot_part; " \
"fi\0"
- 1
- 2
- 3
- 4
scan_dev_for_boot_part来自3.F, 后面的分析就很容易了。
把上面分析理清楚后,在u-boot启动的时候可以中断u-boot启动流程(一般是按键盘上的任意按键,或者空格键,也有的需要按Ctrl + C)进入u-boot命令行,输入printenv命令,就能看到上面分析的各个变量或者命令的内容。
原创博文,转载请著名出处,欢迎关注博主公众号:HackforFun