野火uboot使用extboot启动内核流程
查看野火uboot
参数:
=> printenv bootcmd |
可以看到第一个命令是distro_bootcmd
,事实上,野火的extboot
也就是从这里启动的:
=> printenv distro_bootcmd |
关于distro_bootcmd
更详细的内容可以查看:https://blog.505218.xyz/2024/08/23/Rockchip-%E7%B3%BB%E5%88%97%E8%8A%AF%E7%89%87uboot-distro-cmd/
这里直接进入到bootcmd_mmc0
,也就是从emmc
启动,sd
卡大同小异:
=> printenv bootcmd_mmc0 |
可以看到bootcmd_mmc0
就是设置好devnum
变量,然后执行了mmc_boot
,这是为了将sd
卡和emmc
进行抽象,查看mmc_boot
:
=> printenv mmc_boot |
在mmc_boot
阶段,设置了变量devtype
为mmc
,然后执行scan_dev_for_boot_part
:
=> printenv scan_dev_for_boot_part |
scan_dev_for_boot_part
在scan_dev_for_boot_part
阶段就比较复杂了,在uboot中不方便查看,我们转到头文件查看:
// path: ./include/config_distro_bootcmd.h |
诸行分析:
- 列出${devtype} ${devnum}
下所有bootable
的分区并且将其放到devplist
变量中
- 判断devplist
是否存在,也就是上一步是否成功,如果不成功设置为1
- 使用distro_bootpart
变量遍历devplist
,假如devplist
是2
,那么distro_bootpart
会是0,1,2
- 在for
循环中输出${devtype} ${devnum}:${distro_bootpart}
的文件类型给bootfstype
变量
- 运行scan_dev_for_boot
scan_dev_for_boot
查看scan_dev_for_boot
:
// path: ./include/config_distro_bootcmd.h |
这里其实就是打印一个调试信息,然后遍历boot_prefixes
去运行两个命令。
查看boot_prefixes
:
=> printenv boot_prefixes |
就是一个boot
的前缀,一个是/
一个是/boot/
。
scan_dev_for_scripts
查看scan_dev_for_scripts
:
// path: ./include/config_distro_bootcmd.h |
其中涉及到一个变量为boot_scripts
:
=> printenv boot_scripts |
诸行分析scan_dev_for_scripts
:
- 使用script
变量遍历boot_scripts
- 判断${devnum}:${distro_bootpart} ${prefix}${script}
文件是否存在
- 如果存在运行boot_a_script
查看boot_a_script
:
=> printenv boot_a_script |
将刚才的script
文件加载到scriptaddr
,并且运行该脚本,source
命令就是运行脚本。
=> printenv scriptaddr |
查看野火boot分区下脚本文件
root@lubancat:/boot# ls *.scr |
这里就不分析该内容了,该文件内容如下:
if test -e ${devtype} ${devnum}:${distro_bootpart} /uEnv/uEnv.txt; then |
scan_dev_for_extlinux
查看scan_dev_for_extlinux
:
// path: ./include/config_distro_bootcmd.h |
这里就是判断${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf
文件是否存在,如果存在的话,就去运行boot_extlinux
。
boot_extlinux
查看boot_extlinux
:
=> printenv boot_extlinux |
boot_extlinux
中实际调用了sysboot
命令,sysboot
是一个 U-Boot
命令,用于从特定设备和文件系统加载并启动 Syslinux
引导文件。Syslinux
是一个轻量级的引导加载程序,通常用于引导 Linux
内核。
sysboot
命令格式如下:
sysboot [-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename] |
- -p: 可选参数,表示使用 “pxelinux” 格式的配置文件。
: 设备接口类型,例如 mmc、usb、scsi、eth 等。 - <dev[:part]>: 设备号和可选的分区号。例如,0:1 表示设备 0 的第 1 分区。
- <ext2|fat|any>: 文件系统类型,可以是 ext2、fat 或 any。其中 any 会自动检测文件系统类型。
- [filename]: 可选参数,要加载和解析的 Syslinux 配置文件名,通常是 syslinux.cfg 或 extlinux.conf。
所以boot_extlinux
中就是将${devtype} ${devnum}:${distro_bootpart}
分区中的${prefix}extlinux/extlinux.conf
文件加载到{scriptaddr}
去用sysboot
运行,该分区的文件系统类型设置为any
意思就是让uboot
自动检测文件系统类型。
cfg文件
Syslinux 配置文件(通常命名为 syslinux.cfg 或 extlinux.conf)用于指定启动项和相关参数。这个文件的格式比较简单,通常包含启动菜单和内核启动选项。以下是一个典型的 syslinux.cfg 配置文件示例:
DEFAULT linux |
- DEFAULT: 指定默认启动项的标签(如 LABEL 中定义的名称)。
- PROMPT: 设置是否显示引导菜单。0 表示不显示,直接启动默认项;1 表示显示引导菜单。
- TIMEOUT: 设置等待时间(以1/10秒为单位)。例如,50 表示等待5秒,然后启动默认项。
- LABEL: 定义一个启动项,并给它一个名称(如 linux)。
- KERNEL: 指定要启动的内核文件路径。
- APPEND: 向内核传递的启动参数。例如,root=/dev/sda1 ro quiet 表示根文件系统在 /- dev/sda1,只读挂载,并且启动时保持静默模式。
- INITRD: 指定初始 RAM 磁盘映像(initrd)文件的路径,通常用于加载必要的驱动程序和文件系统支持。
还可以使用更高级的选项:
- SAY: 显示消息给用户。
- MENU DEFAULT: 指定一个菜单项为默认选项。
- FALLBACK: 在默认启动项失败时,指定一个后备启动项。
- MENU LABEL: 给每个启动项设置一个显示在菜单中的名称。
- single: 在 APPEND 中添加 single 参数将 Linux 以单用户模式启动,通常用于故障排查。
DEFAULT linux |
这里需要重点说明的是设备树相关的问题,如果想指定设备树,需要以这样的格式进行指定:
FDT /your-device-tree.dtb |
或者不明确指定设备树,但指定设备树所存在的文件夹,可以用devicetreedir
或者fdtdir
去指定。
devicetreedir / |
或者是
fdtdir/ |
如果是使用指定设备树文件夹,不明确指定设备树的方式,uboot
会使用以下策略来查找设备树:
- 自动选择: U-Boot 可能会自动选择与内核文件同名或类似名称的 .dtb 文件。如果内核名是 Image-4.19.232,那么它可能会尝试查找 Image-4.19.232.dtb 或其他类似名称的设备树文件。
- 在 U-Boot 中,可以通过环境变量(如 fdtfile)指定设备树文件的路径。如果没有在 extlinux.conf 中明确指定设备树,U-Boot 可能会使用这些环境变量中定义的路径。
查看野火boot分区下extlinux.conf
root@lubancat:/boot# cat extlinux/extlinux.conf |
可以看到野火的conf
文件并没有明确指定设备树,而是指定了一个设备树文件夹,查看设备树文件:
root@lubancat:/boot# ls *.dtb |
可以看到并没有与内核同名的设备树文件,那么一定是使用了fdtfile
变量来指定设备树。
进入uboot
查看fdtfile
:
=> printenv fdtfile |