当前位置:   article > 正文

rpi4b引导ubuntu分析------distro_bootcmd_## error: "distro_bootcmd" not defined

## error: "distro_bootcmd" not defined

1. 分析启动命令bootcmd

首先要在在uboot界面终止引导到linux中。

  1. U-Boot> pri bootcmd
  2. bootcmd=run distro_bootcmd

可见,bootcmd实际是执行distro_bootcmd命令,如下:

  1. U-Boot> pri distro_bootcmd
  2. distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done

如上,distro_bootcmd遍历执行boot_targets,如下:

  1. U-Boot> pri boot_targets
  2. boot_targets=mmc0 mmc1 usb0 pxe dhcp

可见,最终是执行bootcmd_{mmc0、mmc1、usb0、pxe、dhcp}中的一个。对于rpi4b,只有mmc0有效,如下:

  1. U-Boot> mmc list
  2. mmcnr@7e300000: 1
  3. emmc2@7e340000: 0 (SD)
  4. U-Boot> pri bootcmd_mmc0
  5. bootcmd_mmc0=devnum=0; run mmc_boot
  6. U-Boot> pri bootcmd_mmc1
  7. bootcmd_mmc1=devnum=1; run mmc_boot

如上,无论bootcmd_mmc0还是bootcmd_mmc1来启动内核,都是调用mmc_boot,如下:

  1. U-Boot> pri mmc_boot
  2. mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi
  3. U-Boot> pri scan_dev_for_boot_part
  4. scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done; setenv devplist

可见,第一步是探测mmc(SD)是否存在if mmc dev ${devnum},第二步则执行scan_dev_for_boot_part命令来获取分区信息,如下:

  1. U-Boot> part list mmc 0
  2. Partition Map for MMC device 0 -- Partition Type: DOS
  3. Part Start Sector Num Sectors UUID Type
  4. 1 2048 524288 87c6153d-01 0c Boot
  5. 2 526336 14997471 87c6153d-02 83

如上,对于可引导分区是有标识的,因此首先获取哪个分区可以引导,并将结果放在devplist变量中。然后执行if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype;来确认是否是可引导分区,如下:

  1. U-Boot> fstype mmc 0:1 bootfstype
  2. U-Boot> echo $?
  3. 0

最后使用scan_dev_for_boot来查找启动目录和文件,如下:

  1. U-Boot> pri scan_dev_for_boot
  2. 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;

首先认为启动文件在/目录或者/boot/,其它目录不考虑,如下:

  1. U-Boot> pri boot_prefixes
  2. boot_prefixes=/ /boot/

然后,使用scan_dev_for_extlinux/scan_dev_for_scripts/scan_dev_for_efi检测启动方式,如下:

  1. U-Boot> pri scan_dev_for_extlinux
  2. scan_dev_for_extlinux=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

查看boot_syslinux_conf文件,如下:

  1. U-Boot> printenv boot_syslinux_conf
  2. boot_syslinux_conf=extlinux/extlinux.conf

由于并没有extlinux.conf这个文件,因此执行到boot_a_script命令,如下:

  1. U-Boot> printenv scan_dev_for_scripts
  2. 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}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done

由于,boot_scripts如下:

  1. U-Boot> printenv boot_scripts
  2. boot_scripts=boot.scr.uimg boot.scr

可见,第一个分区中的boot.scr最终在这里被识别到,并赋值给script变量,然后使用boot_a_script将脚本加载到内存,并执行,如下:

boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}

2. 启动脚本

最后,使用boot.scr来做内核启动引导,如下:

  1. # Ubuntu Classic RPi U-Boot script (for armhf and arm64)
  2. # Expects to be called with the following environment variables set:
  3. #
  4. # devtype e.g. mmc/scsi etc
  5. # devnum The device number of the given type
  6. # distro_bootpart The partition containing the boot files
  7. # (introduced in u-boot mainline 2016.01)
  8. # prefix Prefix within the boot partiion to the boot files
  9. # kernel_addr_r Address to load the kernel to
  10. # fdt_addr_r Address to load the FDT to
  11. # ramdisk_addr_r Address to load the initrd to.
  12. # 设置设备树地址
  13. # Take fdt addr from the prior stage boot loader, if available
  14. if test -n "$fdt_addr"; then
  15. fdt addr ${fdt_addr}
  16. fdt move ${fdt_addr} ${fdt_addr_r} # implicitly sets fdt active
  17. else
  18. fdt addr ${fdt_addr_r}
  19. fi
  20. fdt get value bootargs /chosen bootargs
  21. setenv bootargs " ${bootargs} quiet splash"
  22. if test -z "${fk_image_locations}"; then
  23. setenv fk_image_locations ${prefix}
  24. fi
  25. for pathprefix in ${fk_image_locations}; do
  26. # Store the gzip header (1f 8b) in the kernel area for comparison to the
  27. # header of the image we load. Load "vmlinuz" into the portion of memory for
  28. # the RAM disk (because we want to uncompress to the kernel area if it's
  29. # compressed) and compare the word at the start
  30. mw.w ${kernel_addr_r} 0x8b1f # little endian
  31. # 加载内核
  32. if load ${devtype} ${devnum}:${distro_bootpart} ${ramdisk_addr_r} ${pathprefix}vmlinuz; then
  33. kernel_size=${filesize}
  34. # 判断内核镜像是否被压缩
  35. if cmp.w ${kernel_addr_r} ${ramdisk_addr_r} 1; then
  36. # gzip compressed image (NOTE: *not* a self-extracting gzip compressed
  37. # kernel, just a kernel image that has been gzip'd)
  38. echo "Decompressing kernel..."
  39. unzip ${ramdisk_addr_r} ${kernel_addr_r}
  40. setenv kernel_size ${filesize}
  41. setenv try_boot "booti"
  42. else
  43. # Possibly self-extracting or uncompressed; copy data into the kernel area
  44. # and attempt launch with bootz then booti
  45. echo "Copying kernel..."
  46. cp.b ${ramdisk_addr_r} ${kernel_addr_r} ${kernel_size}
  47. setenv try_boot "bootz booti"
  48. fi
  49. # 加载根文件系统
  50. if load ${devtype} ${devnum}:${distro_bootpart} ${ramdisk_addr_r} ${pathprefix}initrd.img; then
  51. setenv ramdisk_param "${ramdisk_addr_r}:${filesize}"
  52. else
  53. setenv ramdisk_param "-"
  54. fi
  55. # 启动内核
  56. for cmd in ${try_boot}; do
  57. echo "Booting Ubuntu (with ${cmd}) from ${devtype} ${devnum}:${partition}..."
  58. ${cmd} ${kernel_addr_r} ${ramdisk_param} ${fdt_addr_r}
  59. done
  60. fi
  61. done

如上,在boot.scr中,加载内核镜像以及根文件系统镜像,之后使用'booti bootm'等启动内核。

3. 设备树的加载

但有疑问,设备树是何时被加载到内存的呢?

此时,start4.elf文件是有些莫名其妙,由于带有".elf"结尾,因此可猜测是执行文件,如下:

  1. $ file start4.elf
  2. start4.elf: ELF 32-bit LSB executable, Broadcom VideoCore III, version 1 (SYSV), statically linked, stripped

果然是执行文件,同时,在uboot的环境变量中可看到,如下:

fdtfile=broadcom/bcm2711-rpi-4-b.dtb

根据博通官方说明,芯片启动时是会自动执行start4.elf文件的,找到一丝线索如下:

  1. $ strings start4.elf | grep config.txt
  2. config.txt
  3. Found SD card, config.txt = %d, start.elf = %d, recovery.elf = %d, timeout = %d
  4. config.txt

可见,start4.elf的确会读取并分析config.txt中的内容。

同样,查找设备树加载地址device_tree_address,也能找到蛛丝马迹。

  1. $ grep device_tree_address . -nR
  2. Binary file ./start4.elf matches
  3. $ strings start4.elf | grep device_tree_address
  4. device_tree_address
  5. device_tree_address

由此断定,树莓派4B启动后,运行start4.elf程序,由start4.elf来提前加载设备树,执行:

  1. [pi4]
  2. kernel=uboot_rpi_4.bin

kernel标签所指定的程序。此节仅猜测,无相关编译器,无法逆向。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/287233
推荐阅读
相关标签
  

闽ICP备14008679号