-
-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8978 from dezgeg/pr-arm-images
ARM SD card image expressions
- Loading branch information
Showing
9 changed files
with
328 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
40
nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
''; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
''; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
''; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters