···
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.
7
+
# The derivation for the SD image will be placed in
8
+
# config.system.build.sdImage
10
+
{ config, lib, pkgs, ... }:
15
+
rootfsImage = import ../../../lib/make-ext4-fs.nix {
17
+
inherit (config.sdImage) storePaths;
18
+
volumeLabel = "NIXOS_SD";
23
+
storePaths = mkOption {
24
+
type = with types; listOf package;
25
+
example = literalExample "[ pkgs.stdenv ]";
27
+
Derivations to be included in the Nix store in the generated SD image.
31
+
bootSize = mkOption {
35
+
Size of the /boot partition, in megabytes.
39
+
populateBootCommands = mkOption {
40
+
example = literalExample "'' cp \${pkgs.myBootLoader}/u-boot.bin boot/ ''";
42
+
Shell commands to populate the ./boot directory.
43
+
All files in that directory are copied to the
44
+
/boot partition on the SD image.
52
+
device = "/dev/disk/by-label/NIXOS_BOOT";
56
+
device = "/dev/disk/by-label/NIXOS_SD";
61
+
sdImage.storePaths = [ config.system.build.toplevel ];
63
+
system.build.sdImage = pkgs.stdenv.mkDerivation {
64
+
name = "sd-image-${pkgs.stdenv.system}.img";
66
+
buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ];
69
+
# Create the image file sized to fit /boot and /, plus 4M of slack
70
+
rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }')
71
+
bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512))
72
+
imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 4096 * 1024))
73
+
truncate -s $imageSize $out
75
+
# type=b is 'W95 FAT32', type=83 is 'Linux'.
78
+
label-id: 0x2178694e
80
+
start=1M, size=$bootSizeBlocks, type=b, bootable
84
+
# Copy the rootfs into the SD image
85
+
eval $(partx $out -o START,SECTORS --nr 2 --pairs)
86
+
dd conv=notrunc if=${rootfsImage} of=$out seek=$START count=$SECTORS
88
+
# Create a FAT32 /boot partition of suitable size into bootpart.img
89
+
eval $(partx $out -o START,SECTORS --nr 1 --pairs)
90
+
truncate -s $((SECTORS * 512)) bootpart.img
91
+
faketime "1970-01-01 00:00:00" mkfs.vfat -i 0x2178694e -n NIXOS_BOOT bootpart.img
93
+
# Populate the files intended for /boot
95
+
${config.sdImage.populateBootCommands}
97
+
# Copy the populated /boot into the SD image
98
+
(cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::)
99
+
dd conv=notrunc if=bootpart.img of=$out seek=$START count=$SECTORS
103
+
boot.postBootCommands = ''
104
+
# On the first boot do some maintenance tasks
105
+
if [ -f /nix-path-registration ]; then
106
+
# Figure out device names for the boot device and root filesystem.
107
+
rootPart=$(readlink -f /dev/disk/by-label/NIXOS_SD)
108
+
bootDevice=$(lsblk -npo PKNAME $rootPart)
110
+
# Resize the root partition and the filesystem to fit the disk
111
+
echo ",+," | sfdisk -N2 --no-reread $bootDevice
112
+
${pkgs.parted}/bin/partprobe
113
+
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
115
+
# Register the contents of the initial Nix store
116
+
${config.nix.package}/bin/nix-store --load-db < /nix-path-registration
118
+
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
120
+
${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
122
+
# Prevents this from running on later boots.
123
+
rm -f /nix-path-registration