Skip to content

Commit

Permalink
Merge pull request #8978 from dezgeg/pr-arm-images
Browse files Browse the repository at this point in the history
ARM SD card image expressions
  • Loading branch information
viric committed Jul 29, 2015
2 parents f595450 + df86813 commit 982ce5e
Show file tree
Hide file tree
Showing 9 changed files with 328 additions and 16 deletions.
88 changes: 88 additions & 0 deletions nixos/lib/make-ext4-fs.nix
Original file line number Diff line number Diff line change
@@ -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
'';
}
40 changes: 40 additions & 0 deletions nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
Original file line number Diff line number Diff line change
@@ -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
'';
};
}
46 changes: 46 additions & 0 deletions nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
Original file line number Diff line number Diff line change
@@ -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
'';
};
}
127 changes: 127 additions & 0 deletions nixos/modules/installer/cd-dvd/sd-image.nix
Original file line number Diff line number Diff line change
@@ -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
'';
};
}
1 change: 1 addition & 0 deletions nixos/modules/services/misc/rogue.nix
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ in
TTYPath = "/dev/${cfg.tty}";
TTYReset = true;
TTYVTDisallocate = true;
WorkingDirectory = "/tmp";
Restart = "always";
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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"
}

Expand All @@ -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

Expand Down
1 change: 1 addition & 0 deletions pkgs/os-specific/linux/kernel/common-config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 ''
Expand Down
2 changes: 1 addition & 1 deletion pkgs/os-specific/linux/kernel/linux-rpi.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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}";
Expand Down

0 comments on commit 982ce5e

Please sign in to comment.