stm32mp uboot启动内核前的准备工作分析

本文深入剖析了STM32MPU上的U-Boot如何启动内核,从uboot_main_loop开始,详细解释了环境变量配置、设备检测、分区查找、脚本执行等步骤,特别是如何通过bootcmd_stm32mp命令确定启动设备和执行启动命令。通过对boot.scr.uimg和extlinux.conf等文件的解析,展示了内核启动前的准备工作。

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"

开始执行命令

下文将对命令的内容进行梳理,说明:

  1. “=”等号是uboot环境变量赋值语句,其后跟的是环境变量被赋值的内容,通过run命令就可以执行这些内容
  2. ---->后是作者添加的注释,这里只对重要部分进行说明
  3. 按照命令的调用顺序进组织
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;

注意:

  1. scan_dev_for_extlinux 是查找extlinux.conf文件并执行的命令,这个conf文件是提供给用户修改启动项使用的
  2. scan_dev_for_scripts 是查找boot.scr.uimg脚本并执行的命令,这个脚本的作用是初始化一些环境变量,以便可以顺利找到上述conf文件
    本例中使用boot.scr.uimg对一些环境变量初始化后才能找到extlinux.conf,具体说就是初始化了conf文件的路径和全名,此脚本的镜像可以通过mkimage工具生成
  3. 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	

至此内核启动,完结!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值