buildroot 下 raspberrypi 4B 用 uboot 引导内核

想用 uboot 来加载内核,就需要将 Kernel image 和 dtb 传给 uboot,这里还是用他官方 git 上的 kernel 代码。这里用 boot.src 来将这些信息传递给 uboot。而 boot.src 可以用 mkimage 工具生成。mkimage 工具在 uboot-tools 中,可以通过 manifest 来选也可以像我一样加在默认配置文件中。因为要在 host 上使用生成,所有要注意要选择 host 端的。

BR2_PACKAGE_HOST_UBOOT_TOOLS=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE="board/raspberrypi4-64-magic/boot.cmd"

mkimage 工具生成 boot.src 需要 uboot 启动时的 kernel、dtb 以及 kernel cmdline ,启动命令等信息,这里写一个 boot.cmd 的 文件来作为生成 boot.src 的源。在上面配置中指定了文件的路径。

setenv bootargs coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 bcm2708_fb.fbwidth=1024 bcm2708_fb.fbheight=768 bcm2708_fb.fbswap=1 smsc95xx.macaddr=DC:A6:32:6D:CC:7C vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  root=/dev/mmcblk0p2 rootwait console=tty1 console=ttyAMA0,115200

fdt addr ${fdt_addr}
fdt move ${fdt_addr} ${fdt_addr_r}

fatload mmc 0 $kernel_addr_r Image

booti $kernel_addr_r - $fdt_addr_r

第一行是传给 kernel 的命令行,我先复制了原先 buildroot 中打印出来的命令行参数。里面前面的一些都是什么意思还没有研究。

用 fdt 命令将 GPU 处理过放在内存中的 dtb 文件读取。然后移动到内核启动时的指定 dtb 的地址处。

后面用 fatload 命令将内核文件读取到内核启动的地址中。

用 booti 命令指定内核和 dtb 所在的内存地址启动。

关于上面的内核和 dtb 的地址,在 uboot include/config/rpi.h 的代码中有描述。

/*
 * Memory layout for where various images get loaded by boot scripts:
 *
 * I suspect address 0 is used as the SMP pen on the RPi2, so avoid this.
 *
 * Older versions of the boot firmware place the firmware-loaded DTB at 0x100,
 * newer versions place it in high memory. So prevent U-Boot from doing its own
 * DTB + initrd relocation so that we won't accidentally relocate the initrd
 * over the firmware-loaded DTB and generally try to lay out things starting
 * from the bottom of RAM.
 *
 * kernel_addr_r has different constraints on ARM and Aarch64.  For 32-bit ARM,
 * it must be within the first 128M of RAM in order for the kernel's
 * CONFIG_AUTO_ZRELADDR option to work. The kernel itself will be decompressed
 * to 0x8000 but the decompressor clobbers 0x4000-0x8000 as well. The
 * decompressor also likes to relocate itself to right past the end of the
 * decompressed kernel, so in total the sum of the compressed and and
 * decompressed kernel needs to be reserved.
 *
 *   For Aarch64, the kernel image is uncompressed and must be loaded at
 *   text_offset bytes (specified in the header of the Image) into a 2MB
 *   boundary. The 'booti' command relocates the image if necessary. Linux uses
 *   a default text_offset of 0x80000.  In summary, loading at 0x80000
 *   satisfies all these constraints and reserving memory up to 0x02400000
 *   permits fairly large (roughly 36M) kernels.
 *
 * scriptaddr and pxefile_addr_r can be pretty much anywhere that doesn't
 * conflict with something else. Reserving 1M for each of them at
 * 0x02400000-0x02500000 and 0x02500000-0x02600000 should be plenty.
 *
 * On ARM, both the DTB and any possible initrd must be loaded such that they
 * fit inside the lowmem mapping in Linux. In practice, this usually means not
 * more than ~700M away from the start of the kernel image but this number can
 * be larger OR smaller depending on e.g. the 'vmalloc=xxxM' command line
 * parameter given to the kernel. So reserving memory from low to high
 * satisfies this constraint again. Reserving 1M at 0x02600000-0x02700000 for
 * the DTB leaves rest of the free RAM to the initrd starting at 0x02700000.
 * Even with the smallest possible CPU-GPU memory split of the CPU getting
 * only 64M, the remaining 25M starting at 0x02700000 should allow quite
 * large initrds before they start colliding with U-Boot.
 */
#define ENV_MEM_LAYOUT_SETTINGS \
	"fdt_high=" FDT_HIGH "\0" \
	"initrd_high=" INITRD_HIGH "\0" \
	"kernel_addr_r=0x00080000\0" \
	"scriptaddr=0x02400000\0" \
	"pxefile_addr_r=0x02500000\0" \
	"fdt_addr_r=0x02600000\0" \
	"ramdisk_addr_r=0x02700000\0"

接下来就是把 boot.scr 打包进 boot 分区。在 genimage-raspberrypi4-64-magic.cfg 中 boot.vfat 分区文件中添加即可 。

image boot.vfat {
  vfat {
    files = {
      "bcm2711-rpi-4-b.dtb",
      "rpi-firmware-magic/cmdline.txt",
      "rpi-firmware-magic/config.txt",
      "rpi-firmware-magic/fixup.dat",
      "rpi-firmware-magic/start.elf",
      "rpi-firmware-magic/overlays",
      "Image",
      "boot.scr",
      "u-boot.bin"
    }
  }
  size = 100M
}

这里我替换了 rpi-firware 换成了 git 上最新的版本。同时还将 boot 分区大小扩大到了 100M。

烧录后正常启动到内核。

串口无法输入问题排查

之前并没有注意到 GPU 加载了 overlays/miniuart-bt.dtbo 还是用 fatload 命令直接加载的 dtb 文件到 fdt_addr_r 地址启动,所有由于 bt 功能的影响,串口一直无法输入。这个问题查了好久,一路从用户态查到内核,才想到启动时 overlays 中 miniuart-bt.dtbo 被 GPU 应用,而我的 uboot 并没做这样的事情。

在 uboot 下用命令读取 miniuart-bt.dtbo 文件后用 fdt apply 命令应用该 dtbo 结果出错。而 uboot 中的 libfdt 没有任何 debug 开关,但在 内核下可以正常动态加载。后面才想到也许可以用 GPU 现成的 dtb。