Merge pull request #8978 from dezgeg/pr-arm-images

ARM SD card image expressions

viric 982ce5ed f5954509

Changed files
+328 -16
nixos
lib
modules
installer
services
misc
system
boot
loader
pkgs
os-specific
+88
nixos/lib/make-ext4-fs.nix
···
+
# 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
+
'';
+
}
+40
nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
···
+
{ 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
+
'';
+
};
+
}
+46
nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
···
+
{ 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
+
'';
+
};
+
}
+127
nixos/modules/installer/cd-dvd/sd-image.nix
···
+
# 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
+
'';
+
};
+
}
+1
nixos/modules/services/misc/rogue.nix
···
TTYPath = "/dev/${cfg.tty}";
TTYReset = true;
TTYVTDisallocate = true;
+
WorkingDirectory = "/tmp";
Restart = "always";
};
};
+2 -1
nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
···
pkgs.substituteAll {
src = ./extlinux-conf-builder.sh;
isExecutable = true;
+
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
inherit (pkgs) bash;
-
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+
kernelDTB = pkgs.stdenv.platform.kernelDTB or false;
}
+22 -14
nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
···
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)
···
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"
}
···
# 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
+1
pkgs/os-specific/linux/kernel/common-config.nix
···
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 ''
+1 -1
pkgs/os-specific/linux/kernel/linux-rpi.nix
···
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}";