From ec43c69b5da74ead8c94ecdf0c66b762c604df2e Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 18 Jun 2015 00:49:08 +0300 Subject: [PATCH 1/8] linux-rpi: Fix modDirVersion This causes build breakage on staging due to #7524. --- pkgs/os-specific/linux/kernel/linux-rpi.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/os-specific/linux/kernel/linux-rpi.nix b/pkgs/os-specific/linux/kernel/linux-rpi.nix index 67a51dc767df..02b297081315 100644 --- a/pkgs/os-specific/linux/kernel/linux-rpi.nix +++ b/pkgs/os-specific/linux/kernel/linux-rpi.nix @@ -7,7 +7,7 @@ let in import ./generic.nix (args // rec { version = "3.18.y-${rev}"; - modDirVersion = "3.18.7-v7"; + modDirVersion = "3.18.7"; src = fetchurl { url = "https://api.github.com/repos/raspberrypi/linux/tarball/${rev}"; From 82d0acaf371d1ea3f497afbe58854198bb3ce6dd Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Wed, 1 Jul 2015 12:36:55 +0300 Subject: [PATCH 2/8] kernel-config: Explicitly enable NAMESPACES Namespace support is required by the `unshare` tool used in `nixos-install`. It's enabled by the x86 defconfig, but not by e.g. multi_v7_defconfig. So enable it here so that `nixos-install` can work on ARM. --- pkgs/os-specific/linux/kernel/common-config.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/os-specific/linux/kernel/common-config.nix b/pkgs/os-specific/linux/kernel/common-config.nix index cb7423f293a4..7063017b3a8c 100644 --- a/pkgs/os-specific/linux/kernel/common-config.nix +++ b/pkgs/os-specific/linux/kernel/common-config.nix @@ -338,6 +338,7 @@ with stdenv.lib; X86_MCE y # Linux containers. + NAMESPACES? y # Required by 'unshare' used by 'nixos-install' RT_GROUP_SCHED? y CGROUP_DEVICE? y ${if versionAtLeast version "3.6" then '' From 1947179036c7473b735f378ea91916a67e73753d Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Wed, 17 Jun 2015 16:39:12 +0300 Subject: [PATCH 3/8] nixos/rogue: Set WorkingDirectory to /tmp Otherwise we can get an ugly /rogue.scr in the root of the filesystem hierarchy. --- nixos/modules/services/misc/rogue.nix | 1 + 1 file changed, 1 insertion(+) 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"; }; }; From 6147909f8e80d7fbf15e73f556ee1f6ab4c93289 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Sat, 6 Jun 2015 14:15:24 +0300 Subject: [PATCH 4/8] extlinux-conf-builder: Properly copy kernels for the default entry When calling addEntry inside a subshell, the filesCopied array would be updated only in the subshell's environment. This would only cause an issue if no -g flag was passed to the script, causing no kernels to be copied. --- .../generic-extlinux-compatible/extlinux-conf-builder.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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..9f32dbcce1a3 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 @@ -106,9 +106,10 @@ cat > $tmpFile <> $tmpFile + # Add up to $numGenerations generations of the system profile to the menu, # in reverse (most recent to least recent) order. for generation in $( From 8496f71e9219e1a5ea01898c1c306ab58cf6773a Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 18 Jun 2015 00:45:46 +0300 Subject: [PATCH 5/8] extlinux-conf-builder: Make it work on non-DTB systems With this, boot.loader.generic-extlinux-compatible can be used with linuxPackages_rpi on the Raspberry Pi. --- .../extlinux-conf-builder.nix | 3 ++- .../extlinux-conf-builder.sh | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) 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 9f32dbcce1a3..642bdf4673bd 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" } From a6c95a3f7d2215965e098f404a9ad544a8fdd99c Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Fri, 19 Jun 2015 03:36:07 +0300 Subject: [PATCH 6/8] extlinux-conf-builder: Set menu title to force prompt display Without a menu title, U-Boot's distro scripts just autoboot the first entry by default. When I initially wrote this, my board wasn't apparently running stock U-Boot but had some local hacks saved in the U-Boot's environment which made it always display the prompt. --- .../loader/generic-extlinux-compatible/extlinux-conf-builder.sh | 1 + 1 file changed, 1 insertion(+) 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 642bdf4673bd..7da9c488f690 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 @@ -109,6 +109,7 @@ cat > $tmpFile < Date: Sat, 25 Jul 2015 23:57:55 +0300 Subject: [PATCH 7/8] extlinux-conf-builder: Fix warning when building in chroot When using extlinux-conf-builder in a nix build using chroots, the following error message could be seen: /nix/store/XXX-extlinux-conf-builder.sh: line 121: cd: /nix/var/nix/profiles: No such file or directory To avoid this, just skip the code path parsing /nix/var/nix/profiles when $numGenerations (passed from the command line) is 0 (which is the only legal value of $numGenerations in a nix build context). --- .../extlinux-conf-builder.sh | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) 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 7da9c488f690..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 @@ -115,16 +115,18 @@ EOF addEntry $default default >> $tmpFile -# 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 +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 From df86813d9727aa3a9e3dcc45347f97b71bd01599 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Tue, 5 May 2015 06:23:28 +0300 Subject: [PATCH 8/8] nixos: Add derivations for SD card installation images on ARM The resulting image can be copied to a SD card with `dd` and is directly bootable by a suitably configured U-Boot. Though depending on the board, some extra steps are required for copying U-Boot itself to the SD card. Inside the image is a partition table, with a FAT32 /boot and a normal writable EXT4 rootfs. It's possible to directly reuse the SD image's partition layout and "install" NixOS on the same SD card by replacing the default configuration.nix and nixos-rebuild, and actually is the preferred way to use these images. To assist in this installation method, the boot scripts on the image automatically resize the rootfs partition to fit the SD card on the first boot. The SD images come in two flavors; one for the ARMv6 Raspberry Pi, and one multiplatform image for all the boards supported by the mainline kernel's multi_v7_defconfig config target. At the moment, these have been tested on: - Raspberry Pi Model B (512MB model) - NVIDIA Jetson TK1 - Linksprite pcDuino3 Nano To build, run: nix-build '' -A config.system.build.sdImage \ -I nixos-config='' --- nixos/lib/make-ext4-fs.nix | 88 ++++++++++++ .../cd-dvd/sd-image-armv7l-multiplatform.nix | 40 ++++++ .../installer/cd-dvd/sd-image-raspberrypi.nix | 46 +++++++ nixos/modules/installer/cd-dvd/sd-image.nix | 127 ++++++++++++++++++ 4 files changed, 301 insertions(+) create mode 100644 nixos/lib/make-ext4-fs.nix create mode 100644 nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix create mode 100644 nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix create mode 100644 nixos/modules/installer/cd-dvd/sd-image.nix 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 <