tftpboot.nix (6170B)
1 { 2 config 3 , pkgs 4 , lib 5 , ... 6 }: 7 let 8 inherit (lib) mkOption types concatStringsSep; 9 cfg = config.boot.tftp; 10 hw = config.hardware; 11 arch = pkgs.stdenv.hostPlatform.linuxArch; 12 in { 13 imports = [ ../ramdisk.nix ]; 14 options.boot.tftp = { 15 freeSpaceBytes = mkOption { 16 type = types.int; 17 default = 0; 18 }; 19 kernelFormat = mkOption { 20 type = types.enum [ "zimage" "uimage" ]; 21 default = "uimage"; 22 }; 23 compressRoot = mkOption { 24 type = types.bool; 25 default = false; 26 }; 27 appendDTB = mkOption { 28 type = types.bool; 29 default = false; 30 }; 31 }; 32 options.system.outputs = { 33 tftpboot = mkOption { 34 type = types.package; 35 description = '' 36 tftpboot 37 ******** 38 39 This output is intended for developing on a new device. 40 It assumes you have a serial connection and a 41 network connection to the device and that your 42 build machine is running a TFTP server. 43 44 The output is a directory containing kernel and 45 root filesystem image, and a script :file:`boot.scr` of U-Boot 46 commands that will load the images into memory and 47 run them directly, 48 instead of first writing them to flash. This saves 49 time and erase cycles. 50 51 It uses the Linux `phram <https://github.com/torvalds/linux/blob/master/drivers/mtd/devices/phram.c>`_ driver to emulate a flash device using a segment of physical RAM. 52 ''; 53 }; 54 }; 55 config = { 56 boot.ramdisk.enable = true; 57 58 system.outputs = rec { 59 tftpboot = 60 let 61 inherit (pkgs.lib.trivial) toHexString; 62 o = config.system.outputs; 63 image = let choices = { 64 uimage = o.uimage; 65 zimage = o.zimage; 66 }; in choices.${cfg.kernelFormat}; 67 bootCommand = let choices = { 68 uimage = "bootm"; 69 zimage = "bootz"; 70 }; in choices.${cfg.kernelFormat}; 71 72 cmdline = concatStringsSep " " config.boot.commandLine; 73 objcopy = "${pkgs.stdenv.cc.bintools.targetPrefix}objcopy"; 74 stripAndZip = '' 75 ${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin 76 rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin 77 ''; 78 in 79 pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc pkgs.stdenv.cc ubootTools ]; } '' 80 mkdir $out 81 cd $out 82 binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); } 83 binsize64k() { local s=$(stat -L -c %s $1); echo $(($s + 0x10000 &(~0xffff))); } 84 hex() { printf "0x%x" $1; } 85 86 rootfsStart=${toString cfg.loadAddress} 87 rootfsSize=$(binsize64k ${o.rootfs} ) 88 rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} )) 89 90 ln -s ${o.manifest} manifest 91 ln -s ${o.kernel} vmlinux # handy for gdb 92 93 # if we are transferring kernel and dtb separately, the 94 # dtb has to precede the kernel in ram, because zimage 95 # decompression code will assume that any memory after the 96 # end of the kernel is free 97 98 dtbStart=$(($rootfsStart + $rootfsSize)) 99 ${if cfg.compressRoot 100 then '' 101 lzma -z9cv ${o.rootfs} > rootfs.lz 102 rootfsLzStart=$dtbStart 103 rootfsLzSize=$(binsize rootfs.lz) 104 dtbStart=$(($dtbStart + $rootfsLzSize)) 105 '' 106 else '' 107 ln -s ${o.rootfs} rootfs 108 '' 109 } 110 111 cat ${o.dtb} > dtb 112 address_cells=$(fdtget dtb / '#address-cells') 113 size_cells=$(fdtget dtb / '#size-cells') 114 if [ $address_cells -gt 1 ]; then ac_prefix=0; fi 115 if [ $size_cells -gt 1 ]; then sz_prefix=0; fi 116 117 fdtput -p dtb /reserved-memory '#address-cells' $address_cells 118 fdtput -p dtb /reserved-memory '#size-cells' $size_cells 119 fdtput -p dtb /reserved-memory ranges 120 node=$(printf "phram-rootfs@%x" $rootfsStart) 121 fdtput -p -t s dtb /reserved-memory/$node compatible phram 122 fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize) 123 124 cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0"; 125 fdtput -t s dtb /chosen bootargs "$cmd" 126 127 dtbSize=$(binsize ./dtb ) 128 129 ${if cfg.appendDTB then '' 130 imageStart=$dtbStart 131 # re-package image with updated dtb 132 cat ${o.kernel} > vmlinux.elf 133 ${objcopy} --update-section .appended_dtb=dtb vmlinux.elf 134 ${stripAndZip} 135 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 136 # dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1 137 tftpcmd="tftpboot $(hex $imageStart) result/image " 138 bootcmd="bootm $(hex $imageStart)" 139 '' else '' 140 imageStart=$(($dtbStart + $dtbSize)) 141 tftpcmd="tftpboot $(hex $imageStart) result/image; tftpboot $(hex $dtbStart) result/dtb " 142 ln -s ${image} image 143 bootcmd="${bootCommand} $(hex $imageStart) - $(hex $dtbStart)" 144 ''} 145 146 cat > boot.scr << EOF 147 setenv serverip ${cfg.serverip} 148 setenv ipaddr ${cfg.ipaddr} 149 ${ 150 if cfg.compressRoot 151 then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz" 152 else "tftpboot $(hex $rootfsStart) result/rootfs" 153 }; $tftpcmd 154 ${if cfg.compressRoot 155 then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); " 156 else "" 157 } $bootcmd 158 EOF 159 ''; 160 161 }; 162 }; 163 }