at 18.09-beta 5.7 kB view raw
1# This module creates a bootable SD card image containing the given NixOS 2# configuration. The generated image is MBR partitioned, with a FAT /boot 3# partition, and ext4 root partition. The generated image is sized to fit 4# its contents, and a boot script automatically resizes the root partition 5# to fit the device on the first boot. 6# 7# The derivation for the SD image will be placed in 8# config.system.build.sdImage 9 10{ config, lib, pkgs, ... }: 11 12with lib; 13 14let 15 rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({ 16 inherit (config.sdImage) storePaths; 17 volumeLabel = "NIXOS_SD"; 18 } // optionalAttrs (config.sdImage.rootPartitionUUID != null) { 19 uuid = config.sdImage.rootPartitionUUID; 20 }); 21in 22{ 23 options.sdImage = { 24 imageName = mkOption { 25 default = "${config.sdImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}.img"; 26 description = '' 27 Name of the generated image file. 28 ''; 29 }; 30 31 imageBaseName = mkOption { 32 default = "nixos-sd-image"; 33 description = '' 34 Prefix of the name of the generated image file. 35 ''; 36 }; 37 38 storePaths = mkOption { 39 type = with types; listOf package; 40 example = literalExample "[ pkgs.stdenv ]"; 41 description = '' 42 Derivations to be included in the Nix store in the generated SD image. 43 ''; 44 }; 45 46 bootPartitionID = mkOption { 47 type = types.string; 48 default = "0x2178694e"; 49 description = '' 50 Volume ID for the /boot partition on the SD card. This value must be a 51 32-bit hexadecimal number. 52 ''; 53 }; 54 55 rootPartitionUUID = mkOption { 56 type = types.nullOr types.string; 57 default = null; 58 example = "14e19a7b-0ae0-484d-9d54-43bd6fdc20c7"; 59 description = '' 60 UUID for the main NixOS partition on the SD card. 61 ''; 62 }; 63 64 bootSize = mkOption { 65 type = types.int; 66 default = 120; 67 description = '' 68 Size of the /boot partition, in megabytes. 69 ''; 70 }; 71 72 populateBootCommands = mkOption { 73 example = literalExample "'' cp \${pkgs.myBootLoader}/u-boot.bin boot/ ''"; 74 description = '' 75 Shell commands to populate the ./boot directory. 76 All files in that directory are copied to the 77 /boot partition on the SD image. 78 ''; 79 }; 80 }; 81 82 config = { 83 fileSystems = { 84 "/boot" = { 85 device = "/dev/disk/by-label/NIXOS_BOOT"; 86 fsType = "vfat"; 87 }; 88 "/" = { 89 device = "/dev/disk/by-label/NIXOS_SD"; 90 fsType = "ext4"; 91 }; 92 }; 93 94 sdImage.storePaths = [ config.system.build.toplevel ]; 95 96 system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, mtools, libfaketime, utillinux }: stdenv.mkDerivation { 97 name = config.sdImage.imageName; 98 99 nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux ]; 100 101 buildCommand = '' 102 mkdir -p $out/nix-support $out/sd-image 103 export img=$out/sd-image/${config.sdImage.imageName} 104 105 echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system 106 echo "file sd-image $img" >> $out/nix-support/hydra-build-products 107 108 # Create the image file sized to fit /boot and /, plus 20M of slack 109 rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }') 110 bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512)) 111 imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 20 * 1024 * 1024)) 112 truncate -s $imageSize $img 113 114 # type=b is 'W95 FAT32', type=83 is 'Linux'. 115 sfdisk $img <<EOF 116 label: dos 117 label-id: ${config.sdImage.bootPartitionID} 118 119 start=8M, size=$bootSizeBlocks, type=b, bootable 120 start=${toString (8 + config.sdImage.bootSize)}M, type=83 121 EOF 122 123 # Copy the rootfs into the SD image 124 eval $(partx $img -o START,SECTORS --nr 2 --pairs) 125 dd conv=notrunc if=${rootfsImage} of=$img seek=$START count=$SECTORS 126 127 # Create a FAT32 /boot partition of suitable size into bootpart.img 128 eval $(partx $img -o START,SECTORS --nr 1 --pairs) 129 truncate -s $((SECTORS * 512)) bootpart.img 130 faketime "1970-01-01 00:00:00" mkfs.vfat -i ${config.sdImage.bootPartitionID} -n NIXOS_BOOT bootpart.img 131 132 # Populate the files intended for /boot 133 mkdir boot 134 ${config.sdImage.populateBootCommands} 135 136 # Copy the populated /boot into the SD image 137 (cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::) 138 dd conv=notrunc if=bootpart.img of=$img seek=$START count=$SECTORS 139 ''; 140 }) {}; 141 142 boot.postBootCommands = '' 143 # On the first boot do some maintenance tasks 144 if [ -f /nix-path-registration ]; then 145 # Figure out device names for the boot device and root filesystem. 146 rootPart=$(readlink -f /dev/disk/by-label/NIXOS_SD) 147 bootDevice=$(lsblk -npo PKNAME $rootPart) 148 149 # Resize the root partition and the filesystem to fit the disk 150 echo ",+," | sfdisk -N2 --no-reread $bootDevice 151 ${pkgs.parted}/bin/partprobe 152 ${pkgs.e2fsprogs}/bin/resize2fs $rootPart 153 154 # Register the contents of the initial Nix store 155 ${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration 156 157 # nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag. 158 touch /etc/NIXOS 159 ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system 160 161 # Prevents this from running on later boots. 162 rm -f /nix-path-registration 163 fi 164 ''; 165 }; 166}