# This module creates a bootable SD card image containing the given NixOS # configuration. The generated image is MBR partitioned, with a FAT # /boot/firmware partition, and ext4 root partition. The generated image # is sized to fit its contents, and a boot script automatically resizes # the root partition to fit the device on the first boot. # # The firmware partition is built with expectation to hold the Raspberry # Pi firmware and bootloader, and be removed and replaced with a firmware # build for the target SoC for other board families. # # The derivation for the SD image will be placed in # config.system.build.sdImage { config, lib, pkgs, ... }: with lib; let rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({ inherit (config.sdImage) storePaths; populateImageCommands = config.sdImage.populateRootCommands; volumeLabel = "NIXOS_SD"; } // optionalAttrs (config.sdImage.rootPartitionUUID != null) { uuid = config.sdImage.rootPartitionUUID; }); in { options.sdImage = { imageName = mkOption { default = "${config.sdImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}.img"; description = '' Name of the generated image file. ''; }; imageBaseName = mkOption { default = "nixos-sd-image"; description = '' Prefix of the name of the generated image file. ''; }; storePaths = mkOption { type = with types; listOf package; example = literalExample "[ pkgs.stdenv ]"; description = '' Derivations to be included in the Nix store in the generated SD image. ''; }; firmwarePartitionID = mkOption { type = types.string; default = "0x2178694e"; description = '' Volume ID for the /boot/firmware partition on the SD card. This value must be a 32-bit hexadecimal number. ''; }; rootPartitionUUID = mkOption { type = types.nullOr types.string; default = null; example = "14e19a7b-0ae0-484d-9d54-43bd6fdc20c7"; description = '' UUID for the main NixOS partition on the SD card. ''; }; firmwareSize = mkOption { type = types.int; # As of 2019-05-31 the Raspberry pi firmware + u-bot takes ~13MiB default = 20; description = '' Size of the /boot/firmware partition, in megabytes. ''; }; populateFirmwareCommands = mkOption { example = literalExample "'' cp \${pkgs.myBootLoader}/u-boot.bin firmware/ ''"; description = '' Shell commands to populate the ./firmware directory. All files in that directory are copied to the /boot/firmware partition on the SD image. ''; }; populateRootCommands = mkOption { example = literalExample "''\${extlinux-conf-builder} -t 3 -c \${config.system.build.toplevel} -d ./files/boot''"; description = '' Shell commands to populate the ./files directory. All files in that directory are copied to the root (/) partition on the SD image. Use this to populate the ./files/boot (/boot) directory. ''; }; }; config = { fileSystems = { "/boot/firmware" = { device = "/dev/disk/by-label/FIRMWARE"; fsType = "vfat"; # Alternatively, this could be removed from the configuration. # The filesystem is not needed at runtime, it could be treated # as an opaque blob instead of a discrete FAT32 filesystem. options = [ "nofail" "noauto" ]; }; "/" = { device = "/dev/disk/by-label/NIXOS_SD"; fsType = "ext4"; }; }; sdImage.storePaths = [ config.system.build.toplevel ]; system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, mtools, libfaketime, utillinux }: stdenv.mkDerivation { name = config.sdImage.imageName; nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux ]; buildCommand = '' mkdir -p $out/nix-support $out/sd-image export img=$out/sd-image/${config.sdImage.imageName} echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system echo "file sd-image $img" >> $out/nix-support/hydra-build-products # Create the image file sized to fit /boot/firmware and /, plus 20M of slack rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }') firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512)) imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + 20 * 1024 * 1024)) truncate -s $imageSize $img # type=b is 'W95 FAT32', type=83 is 'Linux'. # The "bootable" partition is where u-boot will look file for the bootloader # information (dtbs, extlinux.conf file). sfdisk $img <