liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit c373152673aedc0621ba5d5ee8b5304d98559bdd
parent 7e7171556f92b2aa297c0aff5d9137024f36efaa
Author: Daniel Barlow <dan@telent.net>
Date:   Thu, 15 Feb 2024 20:12:03 +0000

make tftpboot work on devices with old u-boot

Some devices have a U-boot variant that does not accept a third
parameter on the "bootm" command, meaning we can't override the dtb
in the bootloader so have to smush it back into the kernel image

This doesn't work in QEMU but I think the problem is with the
U-Boot configuration for QEMU. It does work on at least one
hardware device so I'm pushing it anyway

Based on
https://gti.telent.net/raboof/liminix/src/branch/tftp-old-uboot

Co-authored-by:  Arnout Engelen <arnout@bzzt.net>

Diffstat:
Mmodules/outputs/tftpboot.nix | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mtests/tftpboot/test.nix | 9++++++++-
2 files changed, 61 insertions(+), 20 deletions(-)

diff --git a/modules/outputs/tftpboot.nix b/modules/outputs/tftpboot.nix @@ -7,6 +7,8 @@ let inherit (lib) mkOption types concatStringsSep; cfg = config.boot.tftp; + hw = config.hardware; + arch = pkgs.stdenv.hostPlatform.linuxArch; in { imports = [ ../ramdisk.nix ]; options.boot.tftp = { @@ -22,6 +24,10 @@ in { type = types.bool; default = false; }; + appendDTB = mkOption { + type = types.bool; + default = false; + }; }; options.system.outputs = { tftpboot = mkOption { @@ -62,24 +68,46 @@ in { uimage = "bootm"; zimage = "bootz"; }; in choices.${cfg.kernelFormat}; + cmdline = concatStringsSep " " config.boot.commandLine; + objcopy = "${pkgs.stdenv.cc.bintools.targetPrefix}objcopy"; + stripAndZip = '' + ${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin + rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin + ''; in - pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc ]; } '' + pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc pkgs.stdenv.cc ubootTools ]; } '' mkdir $out cd $out binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); } binsize64k() { local s=$(stat -L -c %s $1); echo $(($s + 0x10000 &(~0xffff))); } hex() { printf "0x%x" $1; } + rootfsStart=${toString cfg.loadAddress} rootfsSize=$(binsize64k ${o.rootfs} ) rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} )) - dtbStart=$(($rootfsStart + $rootfsSize)) - imageSize=$(binsize ${image}) ln -s ${o.manifest} manifest - ln -s ${image} image ln -s ${o.kernel} vmlinux # handy for gdb + # if we are transferring kernel and dtb separately, the + # dtb has to precede the kernel in ram, because zimage + # decompression code will assume that any memory after the + # end of the kernel is free + + dtbStart=$(($rootfsStart + $rootfsSize)) + ${if cfg.compressRoot + then '' + lzma -z9cv ${o.rootfs} > rootfs.lz + rootfsLzStart=$dtbStart + rootfsLzSize=$(binsize rootfs.lz) + dtbStart=$(($dtbStart + $rootfsLzSize)) + '' + else '' + ln -s ${o.rootfs} rootfs + '' + } + cat ${o.dtb} > dtb address_cells=$(fdtget dtb / '#address-cells') size_cells=$(fdtget dtb / '#size-cells') @@ -93,34 +121,40 @@ in { fdtput -p -t s dtb /reserved-memory/$node compatible phram fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize) - dtbSize=$(binsize ./dtb ) - imageStart=$(($dtbStart + $dtbSize)) - ${if cfg.compressRoot - then '' - lzma -z9cv ${o.rootfs} > rootfs.lz - rootfsLzStart=$(($imageStart + $imageSize)) - rootfsLzSize=$(binsize rootfs.lz) - '' - else "ln -s ${o.rootfs} rootfs" - } - cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0"; fdtput -t s dtb /chosen bootargs "$cmd" - # dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1 + dtbSize=$(binsize ./dtb ) + + ${if cfg.appendDTB then '' + imageStart=$dtbStart + # re-package image with updated dtb + cat ${o.kernel} > vmlinux.elf + ${objcopy} --update-section .appended_dtb=dtb vmlinux.elf + ${stripAndZip} + mkimage -A ${arch} -O linux -T kernel -C lzma -a $(hex ${toString hw.loadAddress}) -e $(hex ${toString hw.entryPoint}) -n '${lib.toUpper arch} Liminix Linux tftpboot' -d vmlinux.bin.lzma image + # dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1 + tftpcmd="tftpboot $(hex $imageStart) result/image " + bootcmd="bootm $(hex $imageStart)" + '' else '' + imageStart=$(($dtbStart + $dtbSize)) + tftpcmd="tftpboot $(hex $imageStart) result/image; tftpboot $(hex $dtbStart) result/dtb " + ln -s ${image} image + bootcmd="${bootCommand} $(hex $imageStart) - $(hex $dtbStart)" + ''} cat > boot.scr << EOF setenv serverip ${cfg.serverip} setenv ipaddr ${cfg.ipaddr} - tftpboot $(hex $imageStart) result/image ; ${ + ${ if cfg.compressRoot then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz" else "tftpboot $(hex $rootfsStart) result/rootfs" - }; tftpboot $(hex $dtbStart) result/dtb + }; $tftpcmd ${if cfg.compressRoot then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); " else "" - } ${bootCommand} $(hex $imageStart) - $(hex $dtbStart) + } $bootcmd EOF ''; diff --git a/tests/tftpboot/test.nix b/tests/tftpboot/test.nix @@ -12,7 +12,7 @@ let derivation = (import liminix { img = derivation.outputs.tftpboot; uboot = derivation.outputs.u-boot; pkgsBuild = derivation.pkgs.pkgsBuildBuild; -in pkgsBuild.runCommand "check" { +in pkgsBuild.runCommand "check-${deviceName}" { nativeBuildInputs = with pkgsBuild; [ expect socat @@ -44,4 +44,11 @@ in { mipsLz = check "qemu" { boot.tftp.compressRoot = true; }; + # this works on real hardware but I haven't figured out how + # to make it work on qemu: it says + # "OF: fdt: No chosen node found, continuing without" + + # mipsOldUboot = check "qemu" { + # boot.tftp.appendDTB = true; + # }; }