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>
This commit is contained in:
Daniel Barlow 2024-02-15 20:12:03 +00:00
parent 7e7171556f
commit c373152673
2 changed files with 61 additions and 20 deletions

View file

@ -7,6 +7,8 @@
let let
inherit (lib) mkOption types concatStringsSep; inherit (lib) mkOption types concatStringsSep;
cfg = config.boot.tftp; cfg = config.boot.tftp;
hw = config.hardware;
arch = pkgs.stdenv.hostPlatform.linuxArch;
in { in {
imports = [ ../ramdisk.nix ]; imports = [ ../ramdisk.nix ];
options.boot.tftp = { options.boot.tftp = {
@ -22,6 +24,10 @@ in {
type = types.bool; type = types.bool;
default = false; default = false;
}; };
appendDTB = mkOption {
type = types.bool;
default = false;
};
}; };
options.system.outputs = { options.system.outputs = {
tftpboot = mkOption { tftpboot = mkOption {
@ -62,24 +68,46 @@ in {
uimage = "bootm"; uimage = "bootm";
zimage = "bootz"; zimage = "bootz";
}; in choices.${cfg.kernelFormat}; }; in choices.${cfg.kernelFormat};
cmdline = concatStringsSep " " config.boot.commandLine; 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 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 mkdir $out
cd $out cd $out
binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); } binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); }
binsize64k() { local s=$(stat -L -c %s $1); echo $(($s + 0x10000 &(~0xffff))); } binsize64k() { local s=$(stat -L -c %s $1); echo $(($s + 0x10000 &(~0xffff))); }
hex() { printf "0x%x" $1; } hex() { printf "0x%x" $1; }
rootfsStart=${toString cfg.loadAddress} rootfsStart=${toString cfg.loadAddress}
rootfsSize=$(binsize64k ${o.rootfs} ) rootfsSize=$(binsize64k ${o.rootfs} )
rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} )) rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} ))
dtbStart=$(($rootfsStart + $rootfsSize))
imageSize=$(binsize ${image})
ln -s ${o.manifest} manifest ln -s ${o.manifest} manifest
ln -s ${image} image
ln -s ${o.kernel} vmlinux # handy for gdb 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 cat ${o.dtb} > dtb
address_cells=$(fdtget dtb / '#address-cells') address_cells=$(fdtget dtb / '#address-cells')
size_cells=$(fdtget dtb / '#size-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 s dtb /reserved-memory/$node compatible phram
fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize) 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"; 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" fdtput -t s dtb /chosen bootargs "$cmd"
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 # 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 cat > boot.scr << EOF
setenv serverip ${cfg.serverip} setenv serverip ${cfg.serverip}
setenv ipaddr ${cfg.ipaddr} setenv ipaddr ${cfg.ipaddr}
tftpboot $(hex $imageStart) result/image ; ${ ${
if cfg.compressRoot if cfg.compressRoot
then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz" then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz"
else "tftpboot $(hex $rootfsStart) result/rootfs" else "tftpboot $(hex $rootfsStart) result/rootfs"
}; tftpboot $(hex $dtbStart) result/dtb }; $tftpcmd
${if cfg.compressRoot ${if cfg.compressRoot
then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); " then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); "
else "" else ""
} ${bootCommand} $(hex $imageStart) - $(hex $dtbStart) } $bootcmd
EOF EOF
''; '';

View file

@ -12,7 +12,7 @@ let derivation = (import liminix {
img = derivation.outputs.tftpboot; img = derivation.outputs.tftpboot;
uboot = derivation.outputs.u-boot; uboot = derivation.outputs.u-boot;
pkgsBuild = derivation.pkgs.pkgsBuildBuild; pkgsBuild = derivation.pkgs.pkgsBuildBuild;
in pkgsBuild.runCommand "check" { in pkgsBuild.runCommand "check-${deviceName}" {
nativeBuildInputs = with pkgsBuild; [ nativeBuildInputs = with pkgsBuild; [
expect expect
socat socat
@ -44,4 +44,11 @@ in {
mipsLz = check "qemu" { mipsLz = check "qemu" {
boot.tftp.compressRoot = true; 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;
# };
} }