summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorviric <viric@viric.name>2015-07-29 14:13:57 +0200
committerviric <viric@viric.name>2015-07-29 14:13:57 +0200
commit982ce5ed589987bff22019e19f6feddf3609cb58 (patch)
tree67e1f8b286b3a0069de78dd58ca2651297a30e2d /nixos
parentf5954509122160ed8209a3158e49ae054c12fcf8 (diff)
parentdf86813d9727aa3a9e3dcc45347f97b71bd01599 (diff)
downloadnixlib-982ce5ed589987bff22019e19f6feddf3609cb58.tar
nixlib-982ce5ed589987bff22019e19f6feddf3609cb58.tar.gz
nixlib-982ce5ed589987bff22019e19f6feddf3609cb58.tar.bz2
nixlib-982ce5ed589987bff22019e19f6feddf3609cb58.tar.lz
nixlib-982ce5ed589987bff22019e19f6feddf3609cb58.tar.xz
nixlib-982ce5ed589987bff22019e19f6feddf3609cb58.tar.zst
nixlib-982ce5ed589987bff22019e19f6feddf3609cb58.zip
Merge pull request #8978 from dezgeg/pr-arm-images
ARM SD card image expressions
Diffstat (limited to 'nixos')
-rw-r--r--nixos/lib/make-ext4-fs.nix88
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix40
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix46
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image.nix127
-rw-r--r--nixos/modules/services/misc/rogue.nix1
-rw-r--r--nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix3
-rw-r--r--nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh36
7 files changed, 326 insertions, 15 deletions
diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix
new file mode 100644
index 000000000000..23839ea487db
--- /dev/null
+++ b/nixos/lib/make-ext4-fs.nix
@@ -0,0 +1,88 @@
+# Builds an ext4 image containing a populated /nix/store with the closure
+# of store paths passed in the storePaths parameter. The generated image
+# is sized to only fit its contents, with the expectation that a script
+# resizes the filesystem at boot time.
+{ pkgs
+, storePaths
+, volumeLabel
+}:
+
+pkgs.stdenv.mkDerivation {
+  name = "ext4-fs.img";
+
+  buildInputs = with pkgs; [e2fsprogs libfaketime perl];
+
+  # For obtaining the closure of `storePaths'.
+  exportReferencesGraph =
+    map (x: [("closure-" + baseNameOf x) x]) storePaths;
+
+  buildCommand =
+    ''
+      # Add the closures of the top-level store objects.
+      storePaths=$(perl ${pkgs.pathsFromGraph} closure-*)
+
+      # Also include a manifest of the closures in a format suitable
+      # for nix-store --load-db.
+      printRegistration=1 perl ${pkgs.pathsFromGraph} closure-* > nix-path-registration
+
+      # Make a crude approximation of the size of the target image.
+      # If the script starts failing, increase the fudge factors here.
+      numInodes=$(find $storePaths | wc -l)
+      numDataBlocks=$(du -c -B 4096 --apparent-size $storePaths | awk '$2 == "total" { print int($1 * 1.03) }')
+      bytes=$((2 * 4096 * $numInodes + 4096 * $numDataBlocks))
+      echo "Creating an EXT4 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks)"
+
+      truncate -s $bytes $out
+      faketime "1970-01-01 00:00:00" mkfs.ext4 -L ${volumeLabel} -U 44444444-4444-4444-8888-888888888888 $out
+
+      # Populate the image contents by piping a bunch of commands to the `debugfs` tool from e2fsprogs.
+      # For example, to copy /nix/store/abcd...efg-coreutils-8.23/bin/sleep:
+      #   cd /nix/store/abcd...efg-coreutils-8.23/bin
+      #   write /nix/store/abcd...efg-coreutils-8.23/bin/sleep sleep
+      #   sif sleep mode 040555
+      #   sif sleep gid 30000
+      # In particular, debugfs doesn't handle absolute target paths; you have to 'cd' in the virtual
+      # filesystem first. Likewise the intermediate directories must already exist (using `find`
+      # handles that for us). And when setting the file's permissions, the inode type flags (__S_IFDIR,
+      # __S_IFREG) need to be set as well.
+      (
+        echo write nix-path-registration nix-path-registration
+        echo mkdir nix
+        echo cd /nix
+        echo mkdir store
+
+        # XXX: This explodes in exciting ways if anything in /nix/store has a space in it.
+        find $storePaths -printf '%y %f %h %m\n'| while read -r type file dir perms; do
+          # echo "TYPE=$type DIR=$dir FILE=$file PERMS=$perms" >&2
+
+          echo "cd $dir"
+          case $type in
+            d)
+              echo "mkdir $file"
+              echo sif $file mode $((040000 | 0$perms)) # magic constant is __S_IFDIR
+              ;;
+            f)
+              echo "write $dir/$file $file"
+              echo sif $file mode $((0100000 | 0$perms)) # magic constant is __S_IFREG
+              ;;
+            l)
+              echo "symlink $file $(readlink "$dir/$file")"
+              ;;
+            *)
+              echo "Unknown entry: $type $dir $file $perms" >&2
+              exit 1
+              ;;
+          esac
+
+          echo sif $file gid 30000 # chgrp to nixbld
+        done
+      ) | faketime "1970-01-01 00:00:00" debugfs -w $out -f /dev/stdin > errorlog 2>&1
+
+      # The debugfs tool doesn't terminate on error nor exit with a non-zero status. Check manually.
+      if egrep -q 'Could not allocate|File not found' errorlog; then
+        cat errorlog
+        echo "--- Failed to create EXT4 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks) ---"
+        return 1
+      fi
+    '';
+}
diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
new file mode 100644
index 000000000000..0ca57a4635f4
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+let
+  extlinux-conf-builder =
+    import ../../system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix {
+      inherit pkgs;
+    };
+in
+{
+  imports = [
+    ../../profiles/minimal.nix
+    ../../profiles/installation-device.nix
+    ./sd-image.nix
+  ];
+
+  assertions = lib.singleton {
+    assertion = pkgs.stdenv.system == "armv7l-linux";
+    message = "sd-image-armv7l-multiplatform.nix can be only built natively on ARMv7; " +
+      "it cannot be cross compiled";
+  };
+
+  boot.loader.grub.enable = false;
+  boot.loader.generic-extlinux-compatible.enable = true;
+
+  # FIXME: change this to linuxPackages_latest once v4.2 is out
+  boot.kernelPackages = pkgs.linuxPackages_testing;
+  boot.kernelParams = ["console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"];
+
+  # FIXME: fix manual evaluation on ARM
+  services.nixosManual.enable = lib.mkOverride 0 false;
+
+  # FIXME: this probably should be in installation-device.nix
+  users.extraUsers.root.initialHashedPassword = "";
+
+  sdImage = {
+    populateBootCommands = ''
+        ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
+    '';
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
new file mode 100644
index 000000000000..199a252ad2b5
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+let
+  extlinux-conf-builder =
+    import ../../system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix {
+      inherit pkgs;
+    };
+in
+{
+  imports = [
+    ../../profiles/minimal.nix
+    ../../profiles/installation-device.nix
+    ./sd-image.nix
+  ];
+
+  assertions = lib.singleton {
+    assertion = pkgs.stdenv.system == "armv6l-linux";
+    message = "sd-image-raspberrypi.nix can be only built natively on ARMv6; " +
+      "it cannot be cross compiled";
+  };
+
+  # Needed by RPi firmware
+  nixpkgs.config.allowUnfree = true;
+
+  boot.loader.grub.enable = false;
+  boot.loader.generic-extlinux-compatible.enable = true;
+
+  boot.kernelPackages = pkgs.linuxPackages_rpi;
+
+  # FIXME: fix manual evaluation on ARM
+  services.nixosManual.enable = lib.mkOverride 0 false;
+
+  # FIXME: this probably should be in installation-device.nix
+  users.extraUsers.root.initialHashedPassword = "";
+
+  sdImage = {
+    populateBootCommands = ''
+      for f in bootcode.bin fixup.dat start.elf; do
+        cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/$f boot/
+      done
+      cp ${pkgs.ubootRaspberryPi}/u-boot.bin boot/u-boot-rpi.bin
+      echo 'kernel u-boot-rpi.bin' > boot/config.txt
+      ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
+    '';
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix
new file mode 100644
index 000000000000..12b4f3045614
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/sd-image.nix
@@ -0,0 +1,127 @@
+# This module creates a bootable SD card image containing the given NixOS
+# configuration. The generated image is MBR partitioned, with a FAT /boot
+# 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 derivation for the SD image will be placed in
+# config.system.build.sdImage
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  rootfsImage = import ../../../lib/make-ext4-fs.nix {
+    inherit pkgs;
+    inherit (config.sdImage) storePaths;
+    volumeLabel = "NIXOS_SD";
+  };
+in
+{
+  options.sdImage = {
+    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.
+      '';
+    };
+
+    bootSize = mkOption {
+      type = types.int;
+      default = 128;
+      description = ''
+        Size of the /boot partition, in megabytes.
+      '';
+    };
+
+    populateBootCommands = mkOption {
+      example = literalExample "'' cp \${pkgs.myBootLoader}/u-boot.bin boot/ ''";
+      description = ''
+        Shell commands to populate the ./boot directory.
+        All files in that directory are copied to the
+        /boot partition on the SD image.
+      '';
+    };
+  };
+
+  config = {
+    fileSystems = {
+      "/boot" = {
+        device = "/dev/disk/by-label/NIXOS_BOOT";
+        fsType = "vfat";
+      };
+      "/" = {
+        device = "/dev/disk/by-label/NIXOS_SD";
+        fsType = "ext4";
+      };
+    };
+
+    sdImage.storePaths = [ config.system.build.toplevel ];
+
+    system.build.sdImage = pkgs.stdenv.mkDerivation {
+      name = "sd-image-${pkgs.stdenv.system}.img";
+
+      buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ];
+
+      buildCommand = ''
+        # Create the image file sized to fit /boot and /, plus 4M of slack
+        rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }')
+        bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512))
+        imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 4096 * 1024))
+        truncate -s $imageSize $out
+
+        # type=b is 'W95 FAT32', type=83 is 'Linux'.
+        sfdisk $out <<EOF
+            label: dos
+            label-id: 0x2178694e
+
+            start=1M, size=$bootSizeBlocks, type=b, bootable
+            type=83
+        EOF
+
+        # Copy the rootfs into the SD image
+        eval $(partx $out -o START,SECTORS --nr 2 --pairs)
+        dd conv=notrunc if=${rootfsImage} of=$out seek=$START count=$SECTORS
+
+        # Create a FAT32 /boot partition of suitable size into bootpart.img
+        eval $(partx $out -o START,SECTORS --nr 1 --pairs)
+        truncate -s $((SECTORS * 512)) bootpart.img
+        faketime "1970-01-01 00:00:00" mkfs.vfat -i 0x2178694e -n NIXOS_BOOT bootpart.img
+
+        # Populate the files intended for /boot
+        mkdir boot
+        ${config.sdImage.populateBootCommands}
+
+        # Copy the populated /boot into the SD image
+        (cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::)
+        dd conv=notrunc if=bootpart.img of=$out seek=$START count=$SECTORS
+      '';
+    };
+
+    boot.postBootCommands = ''
+      # On the first boot do some maintenance tasks
+      if [ -f /nix-path-registration ]; then
+        # Figure out device names for the boot device and root filesystem.
+        rootPart=$(readlink -f /dev/disk/by-label/NIXOS_SD)
+        bootDevice=$(lsblk -npo PKNAME $rootPart)
+
+        # Resize the root partition and the filesystem to fit the disk
+        echo ",+," | sfdisk -N2 --no-reread $bootDevice
+        ${pkgs.parted}/bin/partprobe
+        ${pkgs.e2fsprogs}/bin/resize2fs $rootPart
+
+        # Register the contents of the initial Nix store
+        ${config.nix.package}/bin/nix-store --load-db < /nix-path-registration
+
+        # nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
+        touch /etc/NIXOS
+        ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
+
+        # Prevents this from running on later boots.
+        rm -f /nix-path-registration
+      fi
+    '';
+  };
+}
diff --git a/nixos/modules/services/misc/rogue.nix b/nixos/modules/services/misc/rogue.nix
index ed8da8a518ff..aae02e384c97 100644
--- a/nixos/modules/services/misc/rogue.nix
+++ b/nixos/modules/services/misc/rogue.nix
@@ -52,6 +52,7 @@ in
             TTYPath = "/dev/${cfg.tty}";
             TTYReset = true;
             TTYVTDisallocate = true;
+            WorkingDirectory = "/tmp";
             Restart = "always";
           };
       };
diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
index 261192c6d24e..c5c250c14cea 100644
--- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
+++ b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
@@ -3,6 +3,7 @@
 pkgs.substituteAll {
   src = ./extlinux-conf-builder.sh;
   isExecutable = true;
-  inherit (pkgs) bash;
   path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+  inherit (pkgs) bash;
+  kernelDTB = pkgs.stdenv.platform.kernelDTB or false;
 }
diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
index 8f2a496de8b6..b9a42b2a196d 100644
--- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
+++ b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
@@ -75,8 +75,10 @@ addEntry() {
 
     copyToKernelsDir "$path/kernel"; kernel=$result
     copyToKernelsDir "$path/initrd"; initrd=$result
-    # XXX UGLY: maybe the system config should have a top-level "dtbs" entry?
-    copyToKernelsDir $(readlink -m "$path/kernel/../dtbs"); dtbs=$result
+    if [ -n "@kernelDTB@" ]; then
+        # XXX UGLY: maybe the system config should have a top-level "dtbs" entry?
+        copyToKernelsDir $(readlink -m "$path/kernel/../dtbs"); dtbs=$result
+    fi
 
     timestampEpoch=$(stat -L -c '%Z' $path)
 
@@ -93,7 +95,9 @@ addEntry() {
     fi
     echo "  LINUX ../nixos/$(basename $kernel)"
     echo "  INITRD ../nixos/$(basename $initrd)"
-    echo "  FDTDIR ../nixos/$(basename $dtbs)"
+    if [ -n "@kernelDTB@" ]; then
+        echo "  FDTDIR ../nixos/$(basename $dtbs)"
+    fi
     echo "  APPEND systemConfig=$path init=$path/init $extraParams"
 }
 
@@ -105,20 +109,24 @@ cat > $tmpFile <<EOF
 # Change this to e.g. nixos-42 to temporarily boot to an older configuration.
 DEFAULT nixos-default
 
+MENU TITLE ------------------------------------------------------------
 TIMEOUT $timeout
-$(addEntry $default default)
 EOF
 
-# Add up to $numGenerations generations of the system profile to the menu,
-# in reverse (most recent to least recent) order.
-for generation in $(
-        (cd /nix/var/nix/profiles && ls -d system-*-link) \
-        | sed 's/system-\([0-9]\+\)-link/\1/' \
-        | sort -n -r \
-        | head -n $numGenerations); do
-    link=/nix/var/nix/profiles/system-$generation-link
-    addEntry $link $generation
-done >> $tmpFile
+addEntry $default default >> $tmpFile
+
+if [ "$numGenerations" -gt 0 ]; then
+    # Add up to $numGenerations generations of the system profile to the menu,
+    # in reverse (most recent to least recent) order.
+    for generation in $(
+            (cd /nix/var/nix/profiles && ls -d system-*-link) \
+            | sed 's/system-\([0-9]\+\)-link/\1/' \
+            | sort -n -r \
+            | head -n $numGenerations); do
+        link=/nix/var/nix/profiles/system-$generation-link
+        addEntry $link $generation
+    done >> $tmpFile
+fi
 
 mv -f $tmpFile $target/extlinux/extlinux.conf