summary refs log tree commit diff
path: root/host/initramfs
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2022-01-09 12:46:48 +0000
committerAlyssa Ross <hi@alyssa.is>2022-01-11 14:34:13 +0000
commitec07104a10246130219fda67b91f4afdcd7c0537 (patch)
treeba25cc5bc640cd0e2f497f20dcf2d22d5e782c46 /host/initramfs
parentf7c31462751b3536f88acc2aac07b2fd4a2eaf4c (diff)
downloadspectrum-ec07104a10246130219fda67b91f4afdcd7c0537.tar
spectrum-ec07104a10246130219fda67b91f4afdcd7c0537.tar.gz
spectrum-ec07104a10246130219fda67b91f4afdcd7c0537.tar.bz2
spectrum-ec07104a10246130219fda67b91f4afdcd7c0537.tar.lz
spectrum-ec07104a10246130219fda67b91f4afdcd7c0537.tar.xz
spectrum-ec07104a10246130219fda67b91f4afdcd7c0537.tar.zst
spectrum-ec07104a10246130219fda67b91f4afdcd7c0537.zip
host/initramfs: run QEMU from Make
For better portability, and faster iteration cycles in nix-shell.
I've brought back and modified the make-gpt.sh script that used to be
part of rootfs.
Diffstat (limited to 'host/initramfs')
-rw-r--r--host/initramfs/Makefile65
-rw-r--r--host/initramfs/default.nix12
-rw-r--r--host/initramfs/live.nix102
-rwxr-xr-xhost/initramfs/run9
-rwxr-xr-xhost/initramfs/scripts/format-uuid.sh6
-rwxr-xr-xhost/initramfs/scripts/make-gpt.sh59
-rw-r--r--host/initramfs/scripts/sfdisk-field.awk20
-rw-r--r--host/initramfs/shell.nix10
8 files changed, 191 insertions, 92 deletions
diff --git a/host/initramfs/Makefile b/host/initramfs/Makefile
index a001c36..77919c9 100644
--- a/host/initramfs/Makefile
+++ b/host/initramfs/Makefile
@@ -1,8 +1,19 @@
 # SPDX-License-Identifier: EUPL-1.2
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+
+# qemu-kvm is non-standard, but is present in at least Fedora and
+# Nixpkgs.  If you don't have qemu-kvm, you'll need to set e.g.
+# QEMU_KVM = qemu-system-x86_64 -enable-kvm.
+QEMU_KVM = qemu-kvm
 
 CPIO = cpio
 CPIOFLAGS = --reproducible -R +0:+0 -H newc
+MCOPY = mcopy
+MKFS_FAT = mkfs.fat
+MMD = mmd
+OBJCOPY = objcopy
+TRUNCATE = truncate
+VERITYSETUP = veritysetup
 
 build/initramfs: build/local.cpio $(PACKAGES_CPIO)
 	cat build/local.cpio $(PACKAGES_CPIO) | gzip -9n > $@
@@ -29,6 +40,58 @@ build/mountpoints:
 	cd build/mountpoints && mkdir -p $(MOUNTPOINTS)
 	find build/mountpoints -mindepth 1 -exec touch -d @0 {} ';'
 
+build/cmdline: build/rootfs.verity.roothash
+	printf "ro console=ttyS0 roothash=" > $@
+	cat build/rootfs.verity.roothash >> $@
+
+build/bootx64.efi: etc/os-release build/cmdline build/initramfs
+	$(OBJCOPY) --add-section .osrel=etc/os-release --change-section-vma .osrel=0x20000 \
+	    --add-section .cmdline=build/cmdline --change-section-vma .cmdline=0x30000 \
+	    --add-section .linux=$(KERNEL) --change-section-vma .linux=0x40000 \
+	    --add-section .initrd=build/initramfs --change-section-vma .initrd=0x3000000 \
+	    $(EFI_STUB) $@
+
+build/boot.fat: build/bootx64.efi
+	$(TRUNCATE) -s 157286400 $@
+	$(MKFS_FAT) $@
+	$(MMD) -i $@ ::/EFI ::/EFI/BOOT
+	$(MCOPY) -i $@ build/bootx64.efi ::/EFI/BOOT
+
+# veritysetup format produces two files, but Make only (portably)
+# supports one output per rule, so we combine the two outputs then
+# define two more rules to separate them again.
+build/rootfs.verity: $(ROOT_FS)
+	mkdir -p build
+	$(VERITYSETUP) format $(ROOT_FS) build/rootfs.verity.superblock.tmp \
+	    | awk -F ':[[:blank:]]*' '$$1 == "Root hash" {print $$2; exit}' \
+	    > build/rootfs.verity.roothash.tmp
+	cat build/rootfs.verity.roothash.tmp build/rootfs.verity.superblock.tmp \
+	    > $@
+	rm build/rootfs.verity.roothash.tmp build/rootfs.verity.superblock.tmp
+build/rootfs.verity.roothash: build/rootfs.verity
+	head -n 1 build/rootfs.verity > $@
+build/rootfs.verity.superblock: build/rootfs.verity
+	tail -n +2 build/rootfs.verity > $@
+
+build/live.img: scripts/format-uuid.sh scripts/make-gpt.sh build/boot.fat build/rootfs.verity.superblock build/rootfs.verity.roothash $(ROOT_FS) $(EXT_FS)
+	scripts/make-gpt.sh $@.tmp \
+	    build/boot.fat:c12a7328-f81f-11d2-ba4b-00a0c93ec93b \
+	    build/rootfs.verity.superblock:2c7357ed-ebd2-46d9-aec1-23d437ec2bf5:$$(scripts/format-uuid.sh "$$(dd if=build/rootfs.verity.roothash bs=32 skip=1 count=1 status=none)") \
+	    $(ROOT_FS):4f68bce3-e8cd-4db1-96e7-fbcaf984b709:$$(scripts/format-uuid.sh "$$(head -c 32 build/rootfs.verity.roothash)") \
+	    $(EXT_FS):9293e1ff-cee4-4658-88be-898ec863944f
+	mv $@.tmp $@
+
 clean:
 	rm -rf build
 .PHONY: clean
+
+run: build/live.img
+	$(QEMU_KVM) -m 4G \
+	    -bios $(OVMF_FD) \
+	    -cpu host \
+	    -display gtk,gl=on \
+	    -device virtio-vga-gl \
+	    -device qemu-xhci \
+	    -device usb-storage,drive=drive1,removable=true \
+	    -drive file=build/live.img,id=drive1,format=raw,if=none,readonly=true
+.PHONY: run
diff --git a/host/initramfs/default.nix b/host/initramfs/default.nix
index ba6ede2..833da69 100644
--- a/host/initramfs/default.nix
+++ b/host/initramfs/default.nix
@@ -1,7 +1,10 @@
-{ pkgs ? import <nixpkgs> {} }: pkgs.callPackage (
+{ pkgs ? import <nixpkgs> {}
+, rootfs ? import ../rootfs { inherit pkgs; }
+}:
 
+pkgs.callPackage (
 { lib, stdenv, runCommand, writeReferencesToFile, pkgsStatic
-, busybox, cpio, cryptsetup, linux, lvm2
+, busybox, cpio, cryptsetup, lvm2
 }:
 
 let
@@ -11,6 +14,7 @@ let
   inherit (lib) cleanSource cleanSourceWith concatMapStringsSep;
 
   cryptsetup = cryptsetup'.override { lvm2 = lvm2.override { udev = null; }; };
+  linux = rootfs.kernel;
 
   packages = [
     cryptsetup pkgsStatic.mdevd pkgsStatic.execline
@@ -49,7 +53,7 @@ stdenv.mkDerivation {
   name = "initramfs";
 
   src = cleanSourceWith {
-    filter = name: _type: name != "${toString ./.}/build" && name != "${toString ./.}/spectrum-live";
+    filter = name: _type: name != "${toString ./.}/build";
     src = cleanSource ./.;
   };
 
@@ -58,7 +62,9 @@ stdenv.mkDerivation {
   nativeBuildInputs = [ cpio ];
 
   installPhase = ''
+    runHook preInstall
     cp build/initramfs $out
+    runHook postInstall
   '';
 
   enableParallelBuilding = true;
diff --git a/host/initramfs/live.nix b/host/initramfs/live.nix
index 80463d7..2c520d8 100644
--- a/host/initramfs/live.nix
+++ b/host/initramfs/live.nix
@@ -1,89 +1,33 @@
-{ pkgs ? import <nixpkgs> {} }: pkgs.callPackage (
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
 
-{ stdenv, runCommand, runCommandCC, callPackage, pkgsStatic
-, cryptsetup, dosfstools, jq, mtools, systemd, util-linux
-}:
+{ pkgs ? import <nixpkgs> {} }:
 
 let
-  initramfs = (import ./. { inherit pkgs; }).override { linux = kernel; };
-  host-rootfs = import ../rootfs { inherit pkgs; };
-  extfs = pkgsStatic.callPackage ./extfs.nix { inherit pkgs; };
-
-  inherit (host-rootfs) kernel;
-  kernelTarget = stdenv.hostPlatform.linux-kernel.target;
-
-  uki = runCommandCC "spectrum-uki" {
-    passAsFile = [ "cmdline" ];
-    cmdline = "ro console=ttyS0";
-    inherit initramfs;
-  } ''
-    roothash="$(awk -F ':[[:blank:]]*' '$1 == "Root hash" {print $2; exit}' ${verity.table})"
-    echo "ro console=ttyS0 roothash=$roothash" > cmdline
-    objcopy --add-section .osrel=${etc/os-release} --change-section-vma .osrel=0x20000 \
-            --add-section .cmdline=cmdline --change-section-vma .cmdline=0x30000 \
-            --add-section .linux=${kernel}/${kernelTarget} --change-section-vma .linux=0x40000 \
-            --add-section .initrd=$initramfs --change-section-vma .initrd=0x3000000 \
-            ${systemd}/lib/systemd/boot/efi/linuxx64.efi.stub $out
-  '';
-
-  efi = runCommand "spectrum-efi" {
-    nativeBuildInputs = [ dosfstools mtools ];
-    passthru = { inherit uki; };
-  } ''
-    truncate -s ${toString (150 * 1024 * 1024)} $out
-    mkfs.vfat $out
-    mmd -i $out ::/EFI ::/EFI/BOOT
-    mcopy -i $out ${uki} ::/EFI/BOOT/BOOTX64.EFI
-  '';
-
-  verity = runCommand "spectrum-verity" {
-    nativeBuildInputs = [ cryptsetup ];
-    outputs = [ "out" "table" ];
-  } ''
-    veritysetup format ${host-rootfs} $out > $table
-  '';
+  extfs = pkgs.pkgsStatic.callPackage ./extfs.nix { inherit pkgs; };
+  rootfs = import ../rootfs { inherit pkgs; };
+  initramfs = import ./. { inherit pkgs rootfs; };
 in
 
-runCommand "spectrum-live" {
-  nativeBuildInputs = [ jq util-linux ];
-  passthru = {
-    inherit efi verity;
-    rootfs = host-rootfs;
-  };
-} ''
-  blockSize() {
-      wc -c "$1" | awk '{printf "%d\n", ($1 + 511) / 512}'
-  }
+with pkgs;
 
-  fillPartition() {
-      read start size < <(sfdisk -J "$1" | jq -r --argjson index "$2" \
-          '.partitiontable.partitions[$index] | "\(.start) \(.size)"')
-      dd if="$3" of="$1" seek="$start" count="$size" conv=notrunc
-  }
+initramfs.overrideAttrs ({ buildFlags ? "", nativeBuildInputs ? [], ... }: {
+  name = "spectrum-live.img";
 
-  formatUuid() {
-      printf "%s\n" "''${1:0:8}-''${1:8:4}-''${1:12:4}-''${1:16:4}-''${1:20}"
-  }
+  nativeBuildInputs = nativeBuildInputs ++ [
+    cryptsetup dosfstools jq mtools util-linux
+  ];
 
-  roothash="$(awk -F ':[[:blank:]]*' '$1 == "Root hash" {print $2; exit}' ${verity.table})"
+  EFI_STUB = "${systemd}/lib/systemd/boot/efi/linuxx64.efi.stub";
+  EXT_FS = extfs;
+  KERNEL = "${rootfs.kernel}/${stdenv.hostPlatform.linux-kernel.target}";
+  ROOT_FS = rootfs;
 
-  efiSize="$(blockSize ${efi})"
-  veritySize="$(blockSize ${verity})"
-  rootfsSize="$(blockSize ${host-rootfs})"
-  extSize="$(blockSize ${extfs})"
+  buildFlags = "${toString buildFlags} build/live.img";
 
-  truncate -s $(((4 * 2048 + $efiSize + $veritySize + $rootfsSize + $extSize) * 512)) $out
-  sfdisk $out <<EOF
-  label: gpt
-  size=$efiSize,    type=c12a7328-f81f-11d2-ba4b-00a0c93ec93b
-  size=$veritySize, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, uuid=$(formatUuid "$(printf "%s" "$roothash" | tail -c 32)")
-  size=$rootfsSize, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, uuid=$(formatUuid "$(printf "%s" "$roothash" | head -c 32)")
-  size=$extSize,    type=9293e1ff-cee4-4658-88be-898ec863944f
-  EOF
-
-  fillPartition $out 0 ${efi}
-  fillPartition $out 1 ${verity}
-  fillPartition $out 2 ${host-rootfs}
-  fillPartition $out 3 ${extfs}
-''
-) {}
+  installPhase = ''
+    runHook preInstall
+    mv build/live.img $out
+    runHook postInstall
+  '';
+})
diff --git a/host/initramfs/run b/host/initramfs/run
deleted file mode 100755
index 300ae63..0000000
--- a/host/initramfs/run
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh -eux
-qemu-kvm -m 4G \
-	-bios $(nix-build --no-out-link -A OVMF.fd '<nixpkgs>')/FV/OVMF.fd \
-	-cpu host \
-	-display gtk,gl=on \
-	-device virtio-vga-gl \
-	-device qemu-xhci \
-	-device usb-storage,drive=drive1,removable=true \
-	-drive file=$(nix-build --no-out-link live.nix),id=drive1,format=raw,if=none,readonly=true
diff --git a/host/initramfs/scripts/format-uuid.sh b/host/initramfs/scripts/format-uuid.sh
new file mode 100755
index 0000000..bada8ce
--- /dev/null
+++ b/host/initramfs/scripts/format-uuid.sh
@@ -0,0 +1,6 @@
+#!/bin/sh -eu
+#
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+# SPDX-License-Identifier: EUPL-1.2
+
+printf "%s\n" "${1:0:8}-${1:8:4}-${1:12:4}-${1:16:4}-${1:20}"
diff --git a/host/initramfs/scripts/make-gpt.sh b/host/initramfs/scripts/make-gpt.sh
new file mode 100755
index 0000000..6097d04
--- /dev/null
+++ b/host/initramfs/scripts/make-gpt.sh
@@ -0,0 +1,59 @@
+#!/bin/sh -eu
+#
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+# SPDX-License-Identifier: EUPL-1.2
+#
+# usage: make-gpt.sh GPT_PATH PATH:PARTTYPE[:PARTUUID]...
+
+ONE_MiB=1048576
+TWO_MiB=2097152
+
+# Prints the number of 1MiB blocks required to store the file named
+# $1.  We use 1MiB blocks because that's what sfdisk uses for
+# alignment.  It would be possible to get a slightly smaller image
+# using actual normal-sized 512-byte blocks, but it's probably not
+# worth it to configure sfdisk to do that.
+sizeMiB() {
+	wc -c "$1" | awk -v ONE_MiB=$ONE_MiB \
+		'{printf "%d\n", ($1 + ONE_MiB - 1) / ONE_MiB}'
+}
+
+# Copies from path $3 into partition number $2 in partition table $1.
+fillPartition() {
+	sfdisk -J "$1" | jq -r --argjson index "$2" \
+		'.partitiontable.partitions[$index] | "\(.start) \(.size)"' |
+		(read start size;
+		 dd if="$3" of="$1" seek="$start" count="$size" conv=notrunc)
+}
+
+# Prints the partition path from a PATH:PARTTYPE[:PARTUUID] string.
+partitionPath() {
+	awk -F: '{print $1}' <<EOF
+$1
+EOF
+}
+
+out="$1"
+shift
+
+nl=$'\n'
+table="label: gpt"
+
+# Keep 1MiB free at the start, and 1MiB free at the end.
+gptBytes=$TWO_MiB
+for partition; do
+	sizeMiB="$(sizeMiB "$(partitionPath "$partition")")"
+	table="$table${nl}size=${sizeMiB}MiB,$(awk -f scripts/sfdisk-field.awk -v partition="$partition")"
+	gptBytes="$(expr "$gptBytes" + "$sizeMiB" \* $ONE_MiB)"
+done
+
+truncate -s "$gptBytes" "$out"
+sfdisk "$out" <<EOF
+$table
+EOF
+
+n=0
+for partition; do
+	fillPartition "$out" "$n" "$(partitionPath "$partition")"
+	n="$(expr "$n" + 1)"
+done
diff --git a/host/initramfs/scripts/sfdisk-field.awk b/host/initramfs/scripts/sfdisk-field.awk
new file mode 100644
index 0000000..c2d9e5d
--- /dev/null
+++ b/host/initramfs/scripts/sfdisk-field.awk
@@ -0,0 +1,20 @@
+#!/usr/bin/awk -f
+#
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is>
+
+BEGIN {
+    # Field #1 is the partition path, which make-gpt.sh will turn into
+    # the size field.  Since it's handled elsewhere, we skip that
+    # first field.
+    skip=1
+
+    split("type uuid", keys)
+    split(partition, fields, ":")
+
+    for (n in fields) {
+	if (n <= skip)
+	    continue
+	printf "%s=%s,", keys[n - skip], fields[n]
+    }
+}
diff --git a/host/initramfs/shell.nix b/host/initramfs/shell.nix
new file mode 100644
index 0000000..02aa685
--- /dev/null
+++ b/host/initramfs/shell.nix
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+
+{ pkgs ? import <nixpkgs> {} }:
+
+with pkgs;
+
+(import ./live.nix { inherit pkgs; }).overrideAttrs ({ ... }: {
+  OVMF_FD = "${OVMF.fd}/FV/OVMF.fd";
+})