stm32mp uboot启动内核前的准备工作分析
本文主要分析uboot进入main_loop后所做的工作
uboot的启动流程和源码分析,已经有很多文章写的非常详细,笔者在查阅资料时没有找到uboot启动kernel的详细过程,故而将查看源码的细节记录在此文中。
本例使用buildroot,如果想要找到文中相关文件和代码请下载相关资源。
简要预备知识
uboot启动流程,按文件和函数调用次序
ENTRY(_start)(arch\arm\cpu\u-boot.lds)–>
_start(arch\arm\lib\vectors.S)–>
reset(arch\arm\cpu\armv7\start.S)–>
ENTRY(_main)(arch\arm\lib\crt0.S)–>
board_init_r(board_r.c)–>run_main_loop–>
main_loop(common\mian.c)
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
if (IS_ENABLED(CONFIG_VERSION_VARIABLE))
env_set("ver", version_string); /* set version variable */
cli_init();
if (IS_ENABLED(CONFIG_USE_PREBOOT))
run_preboot_environment_command();
if (IS_ENABLED(CONFIG_UPDATE_TFTP))
update_tftp(0UL, NULL, NULL);
s = bootdelay_process(); ---->此处获得环境变量bootcmd,返回为s
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s); ---->读秒完成,执行s中的命令,bootcmd
cli_loop();
panic("No CLI available");
}
需要用到的环境变量
bootcmd=bootcmd_stm32mp
boot_device=mmc
boot_instance=0
bootfstype=ext4
boot_prefixes=/ /boot/ ---->
boot_syslinux_conf=extlinux/extlinux.conf ---->这两个变量会被boot.scr.cmd脚本修改
boot_scripts=boot.scr.uimg boot.scr
这些环境变量大多是在uboot编译阶段定义的
CONFIG_BOOTCOMMAND="run bootcmd_stm32mp" ---->uboot的config文件
bootcmd_stm32mp的定义在include/configs/stm32mp1.h中
#define STM32MP_BOOTCMD "bootcmd_stm32mp=" \
"echo \"Boot over ${boot_device}${boot_instance}!\";" \
"if test ${boot_device} = serial || test ${boot_device} = usb;" \
"then stm32prog ${boot_device} ${boot_instance}; " \
"else " \
"run env_check;" \
"if test ${boot_device} = mmc;" \
"then env set boot_targets \"mmc${boot_instance}\"; fi;" \
"if test ${boot_device} = nand ||" \
" test ${boot_device} = spi-nand ;" \
"then env set boot_targets ubifs0; fi;" \
"if test ${boot_device} = nor;" \
"then env set boot_targets mmc0; fi;" \
"run distro_bootcmd;" \
"fi;\0"
开始执行命令
下文将对命令的内容进行梳理,说明:
- “=”等号是uboot环境变量赋值语句,其后跟的是环境变量被赋值的内容,通过run命令就可以执行这些内容
- ---->后是作者添加的注释,这里只对重要部分进行说明
- 按照命令的调用顺序进组织
bootcmd=bootcmd_stm32mp=
echo "Boot over ${boot_device}${boot_instance}!";
if test ${boot_device} = serial || test ${boot_device} = usb;then
stm32prog ${boot_device} ${boot_instance}; ---->检测到usb或者serial连接则执行stm32prog(usb下载工具),本例不适用
else
run env_check;
if test ${boot_device} = mmc;then
env set boot_targets "mmc${boot_instance}"; ---->boot_targets=mmc0 用于sdcard启动
fi;
if test ${boot_device} = nand || test ${boot_device} = spi-nand ;then
env set boot_targets ubifs0;
fi;
if test ${boot_device} = nor;then
env set boot_targets mmc0;
fi;
run distro_bootcmd; ----> 执行
fi;
distro_bootcmd=
for target in ${boot_targets}; do
run bootcmd_${target}; ---->bootcmd_mmc0,执行
done
bootcmd_mmc0=devnum=0; run mmc_boot ----> 执行
mmc_boot=
if mmc dev ${devnum}; then ---->mmc dev 命令的作用 mmc dev [dev][part] -show or set current mmc device [partition]
mmc dev 命令打印switch to partitions #0, OK mmc0 is current device
devtype=mmc;
run scan_dev_for_boot_part;
fi
scan_dev_for_boot_part=
part list ${devtype} ${devnum} -bootable devplist; ---->part命令作用 part list <interface> <dev> [flags] <vername>
- print a device's partition table 只有前两项参数时
- set environment variable to the list of partitions
flags can be -bootable (list only bootable partitions)
这里devplist = 4 5
env exists devplist || setenv devplist 1; ---->如果devplist不存在就将其设置为1
for distro_bootpart in ${devplist}; do
if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then ---->如果mmc0:4或5为ext4 则执行以下命令
run scan_dev_for_boot;
fi;
done;
setenv devplist ---->删除环境变量devplist
part list 打印分区列表,如下:
Part Start LBA End LBA Name
Attributes
Type GUID
Partition GUID
1 0x00000022 0x00001021 "fsbl1"
attrs: 0x0000000000000000
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
type: linux
guid: 0f722ba4-afe1-4055-a551-50f88e910456
2 0x00001022 0x00002021 "fsbl2"
attrs: 0x0000000000000000
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
type: linux
guid: 89b47e2f-0ac0-48f1-8d2d-05e46bff3838
3 0x00002022 0x00004021 "ssbl"
attrs: 0x0000000000000000
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
type: linux
guid: 3a111867-7661-487b-a3f2-4f92fb327730
4 0x00004022 0x001f8021 "rootfs1"
attrs: 0x0000000000000004
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
type: linux
guid: c9efdd7d-f92d-4599-b502-84bdeb61eadd
5 0x001f8022 0x004e6021 "rootfs2"
attrs: 0x0000000000000004
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
type: linux
guid: 168fc786-1ae4-4e57-b813-ef402e7587be
通过 -bootable devplist 将有bootable属性的分区号写入环境变量devplist中,此处为4 5,因4 5两个分区可以boot,具体根据分区情况确定
这里说明,uboot是通过查找分区中的文件来确定内核文件的,而不是通过具体的地址(较早前的版本是通过具体地址定位内核文件的,笔者推测没有查阅相关资料)
scan_dev_for_boot=
echo Scanning ${devtype} ${devnum}:${distro_bootpart}...;
for prefix in ${boot_prefixes}; do
run scan_dev_for_extlinux;
run scan_dev_for_scripts;
done;
run scan_dev_for_efi;
注意:
- scan_dev_for_extlinux 是查找extlinux.conf文件并执行的命令,这个conf文件是提供给用户修改启动项使用的
- scan_dev_for_scripts 是查找boot.scr.uimg脚本并执行的命令,这个脚本的作用是初始化一些环境变量,以便可以顺利找到上述conf文件
本例中使用boot.scr.uimg对一些环境变量初始化后才能找到extlinux.conf,具体说就是初始化了conf文件的路径和全名,此脚本的镜像可以通过mkimage工具生成 - boot.scr.uimg并不是一定要使用的,也可以通过initrd脚本进行,具体问题具体分析
下面分析一下boot.scr.cmd脚本
# Generate boot.scr.uimg:
# ./tools/mkimage -C none -A arm -T script -d boot.src.cmd boot.scr.uimg
#
# M4 Firmware load
env set m4fw_name "rproc-m4-fw.elf" ---->m4核的固件名
env set m4fw_addr ${kernel_addr_r} ---->m4核的内存位置,这个是固定的
env set boot_m4fw 'rproc init; rproc load 0 ${m4fw_addr} ${filesize}; rproc load_rsc 0 ${m4fw_addr} ${filesize}; rproc start 0' ---->启动m4核的命令
# boot M4 Firmware when available
env set scan_m4fw 'if test -e ${devtype} ${devnum}:${distro_bootpart} ${m4fw_name};then echo Found M4 FW $m4fw_name; if load ${devtype} ${devnum}:${distro_bootpart} ${m4fw_addr} ${m4fw_name}; then run boot_m4fw; fi; fi;' ---->寻找m4固件并加载和运行
# Update DISTRO command= search in sub-directory and load M4 firmware
env set boot_prefixes "/${boot_device}${boot_instance}_${board_name}_" ---->conf文件的前缀,查找conf文件的关键变量,是事先约定的
env set boot_extlinux "run scan_m4fw;${boot_extlinux}"
if test ${boot_device} = mmc; then
if test ${distro_bootpart} > 4; then
env set boot_prefixes "/mmc${boot_instance}_${board_name}-optee_"
fi
#start the correct exlinux.conf
run scan_dev_for_boot_part ---->运行scan_dev_for_boot_part开始查找conf并运行
elif test ${boot_device} = nand; then
#start the correct exlinux.conf without remount UBI
run scan_dev_for_boot
elif test ${boot_device} = nor; then
#SDCARD boot
run bootcmd_mmc0
#NAND boot
env set boot_prefixes "/nand0_${board_name}_"
run bootcmd_ubifs0
#EMMC boot
env set boot_prefixes "/${boot_device}${boot_instance}-mmc1_${board_name}_"
run bootcmd_mmc1
fi
echo SCRIPT FAILED... ${boot_prefixes}extlinux/extlinux.conf not found !
注意:正常情况下脚本从run scan_dev_for_boot_part退出,开始执行scan_dev_for_boot_part,本例中出现了一个比较奇怪的逻辑:
两次执行scan_dev_for_boot_part,第一次时执行到run scan_dev_for_extlinux; 因为没有脚本进行环境变量初始化,所以没有找到extlinux.conf文件,所以接着执行了run scan_dev_for_scripts; 进行脚本执行…
这里是可以进行优化的,也可以通过不同的方式实现。
下面将scan_dev_for_extlinux和scan_dev_for_scripts展开分析:
scan_dev_for_extlinux= ----> 寻找syslinux menu file,即以extlinux.conf结尾的文件
if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then
echo Found ${prefix}${boot_syslinux_conf};
run boot_extlinux;
echo SCRIPT FAILED: continuing...;
fi
scan_dev_for_scripts=
for script in ${boot_scripts}; do
if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then
echo Found U-Boot script ${prefix}${script}; ---->打印信息 Found U-Boot script /boot/boot.scr.uimg
run boot_a_script;
echo SCRIPT FAILED: continuing...;
fi;
done
boot_a_script=
load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; ----> 载入脚本到内存地址scriptaddr
source ${scriptaddr} ---->执行脚本,此为启动脚本
boot_extlinux=
sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf}
---->sysboot命令作用 加载并分析conf文件
启动conf是提供给用户修改启动项用的,文件中内容模板如下:
menu title Select the boot mode
TIMEOUT 20
LABEL meican_printer
KERNEL /boot/uImage ---->指示内核的位置,与conf文件位于同一个分区
FDT /boot/stm32mp157c.dtb ---->dtb文件位置
APPEND root=/dev/mmcblk1p4 rootwait rw console=ttySTM0,115200
至此内核启动,完结!
本文深入剖析了STM32MPU上的U-Boot如何启动内核,从uboot_main_loop开始,详细解释了环境变量配置、设备检测、分区查找、脚本执行等步骤,特别是如何通过bootcmd_stm32mp命令确定启动设备和执行启动命令。通过对boot.scr.uimg和extlinux.conf等文件的解析,展示了内核启动前的准备工作。

3892

被折叠的 条评论
为什么被折叠?



