From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from atuin.qyliss.net (localhost [IPv6:::1]) by atuin.qyliss.net (Postfix) with ESMTP id 278E58115; Wed, 29 Oct 2025 10:14:34 +0000 (UTC) Received: by atuin.qyliss.net (Postfix, from userid 993) id 599A880A1; Wed, 29 Oct 2025 10:14:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on atuin.qyliss.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DMARC_PASS,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 Received: from mail-yw1-x1136.google.com (mail-yw1-x1136.google.com [IPv6:2607:f8b0:4864:20::1136]) by atuin.qyliss.net (Postfix) with ESMTPS id 2EE007FD2 for ; Wed, 29 Oct 2025 10:14:25 +0000 (UTC) Received: by mail-yw1-x1136.google.com with SMTP id 00721157ae682-7849c889ac8so15281467b3.0 for ; Wed, 29 Oct 2025 03:14:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761732864; x=1762337664; darn=spectrum-os.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LSLTl3lJ170ok2k3zX5HdGqwjcX3HGMO2DkBbVn4tOs=; b=BRUWO1tt4DMAcMm0re0wVnI8Gnn6HlymvTNcn87lK5cFZFyfQgjeeHznpu8mL9QosB WUI0y9QLiePiYFwFiVmtj6ZLIVJML2sljC1Mt1XGCaJX4Yyny3mo3FbFt67PIdyJ+li1 ik0nGZNnJeB6Oa1dwdYlMJ0FgDbLltu9aYgIQAGkxLnulnvwur0ZLn403ovzKG73a8dL P+VhHo/WVvh5bz+6tj88g7JD8rmhruEeO9zNusBT0ArX0VHNJ5qsO+n8p60X0Eb6/t2H qrUF506SuL02SVTdYzfBIra7l7HE5cm0SMvG8nrPp9sQltgbO9hI3+LlhfpLOV1Orxh1 xaIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761732864; x=1762337664; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LSLTl3lJ170ok2k3zX5HdGqwjcX3HGMO2DkBbVn4tOs=; b=bYkHkSglcaxdGTyuCFwqY/380IlojGepoY/0IylVkn+W+9y588DHBAmYT21oOMXQ0c tSddSJcJ60VrPggRRZtzbgKOjMB7LMpDL4zW7XS/zGOIziPesfWBJVkzUR/FCnFIkViz IIb3b1wDGN4WrZWwyeUSBMo1BhLAXjg/K6/JKT5U3a9JWDqeUQqg4K5SM57dRvthRXw3 Wak4PqnkGlWw1Rd9gLn9xqdQ7VGJ9Q19NKKdsW2Dd75eSN7n5hKTNxHTMPe9h4gFoZfv m8jCPq5LXpXHdX446MaFwFjPd8GqhqX7uupyzUN9lM9mEGJJKPYkAuhpPujUfDPVd9DJ 2ENw== X-Gm-Message-State: AOJu0YzvGDii6CNOnXLcG6c0efOurNLL50GMIG1XKSg2AhWUrxth4iXi C9fQSI7XnYfqJwSP2LPskON9eO+YkSjugBXDI9ptZbFtbrg5Ln/Fr0X37c8i1Q== X-Gm-Gg: ASbGncu33sarpYXImovrQp2ldWK8JDANsM0tFa2Z0J6NrRtZTncvFR2RQ5/SqZVMwt9 aedHAAf1G+iEEvoyFwguS6BDRVtOaxlJ75aPkNcgb98P5ad1x0wNLYMSzR4OlK5p/t8HVONBCtw 3g5V0fiAB2uxKwzI8ojw5KhIHsmQEY/9SItfpzfssyh8pVYb+Fd7PYm8Mi8oQzwRl6qHpSxgX24 Vfr9uhQD9Bg5aNPQfWxmY9Fhxd3hhvY7B6OtZpPIrT0CUrwuraY1qbB+nzSsT+p7nktOYyztIDn 7rKcHuwJuKCao0Xpi3C8Pf8cxKuVms4mTApxGA/P+z6D2cnY3TaPGuY3EIkg+RrEJjnUYTUb4lD 3c056CgbFNMheDZYcQ0TObIE4lEUV4Hy+dyJt/M9ejcC87o4M0MXuW78Nx5mf7eOjZ2ZgSxItu8 tsdpkSBg4ED0w1S+haFtEIAcaTTyjJZ9Tuf9c4sJp+46eYy51jpuKtV5lxrNlKC3gS7VT2G+4gc H0GKEVcnn72MmWVy9Wu/tG7 X-Google-Smtp-Source: AGHT+IG2bRhHNepXHxD18k82hYGD/wWX479gaeLCXANNybZ8LUgH5dMtDr2GhrjCT5FOY5Rz5uly5g== X-Received: by 2002:a05:690e:144d:b0:63e:2264:2ecd with SMTP id 956f58d0204a3-63f7731d717mr1861705d50.18.1761732863715; Wed, 29 Oct 2025 03:14:23 -0700 (PDT) Received: from localhost.localdomain (h96-60-249-169.cncrtn.broadband.dynamic.tds.net. [96.60.249.169]) by smtp.gmail.com with UTF8SMTPSA id 956f58d0204a3-63f4c3ccc77sm4024186d50.12.2025.10.29.03.14.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Oct 2025 03:14:23 -0700 (PDT) From: Demi Marie Obenour Date: Wed, 29 Oct 2025 06:12:43 -0400 Subject: [PATCH 4/7] Adjust partition layout to support updates MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20251029-updates-v1-4-401c1be2a11b@gmail.com> References: <20251029-updates-v1-0-401c1be2a11b@gmail.com> In-Reply-To: <20251029-updates-v1-0-401c1be2a11b@gmail.com> To: Spectrum OS Development X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1761732759; l=31799; i=demiobenour@gmail.com; s=20250729; h=from:subject:message-id; bh=4LAyotTF6R/i0dajhQaZfW+2cjV9wqj/uBeNJafwydk=; b=52GdgflTyR2sthCN0DdnqeIV29q7Lw+o16FQq6hqsTBi3vhisYIiwqed1TulfUBPXa79Af229 B6qPpArgF3wCtE6f2FUmIOB2cyyaaTtdP2xjHC26WH+m8OAEegLm/4M X-Developer-Key: i=demiobenour@gmail.com; a=ed25519; pk=X57Q4/YQDj9t4SBeKaDwvXYKB6quZJVx/DE2Ly2out0= Message-ID-Hash: 4PTHURUOSTPWEMHMHU7O4BN7D2MF65FM X-Message-ID-Hash: 4PTHURUOSTPWEMHMHU7O4BN7D2MF65FM X-MailFrom: demiobenour@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-devel.spectrum-os.org-0; header-match-devel.spectrum-os.org-1; header-match-devel.spectrum-os.org-2; header-match-devel.spectrum-os.org-3; header-match-devel.spectrum-os.org-4; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Demi Marie Obenour , Alyssa Ross X-Mailman-Version: 3.3.9 Precedence: list List-Id: Patches and low-level development discussion Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: systemd-sysupdate has strict requirements on the partition layout: - The label of the active partition must match the template in the .transfer file. For instance, the root filesystem of Spectrum OS 0.0.0 will be in a partition with label Spectrum_OS_0.0.0. - The label of the inactive partition must either be that of the old version of the OS or "_empty". The former indicates an incomplete update. - The partition type UUID must conform to the Discoverable Partition Specification. After installing an image to a partition, systemd-sysupdate updates the label of the partition to match the image's version. However, it does not update the partition UUID. Therefore, use the partition label, not the partition UUID, to find the root filesystem and its verity metadata. systemd-sysupdate will fail if the OS image does not fit in the partitions that the installer created. Therefor, make the partitions very large so that there is plenty of room for the OS to grow. This requires rewriting the code that calculates the partition sizes. Since the partition label includes the OS version, add an OS version number. Use 0.0.0 to indicate that Spectrum OS is still in very early development and should not be used. Signed-off-by: Demi Marie Obenour --- host/initramfs/Makefile | 17 +++++----- host/initramfs/default.nix | 2 ++ host/initramfs/etc/init | 17 +++------- host/initramfs/etc/probe | 20 ++++++++---- host/initramfs/shell.nix | 1 + host/rootfs/Makefile | 23 ++++++++------ host/rootfs/default.nix | 3 ++ host/rootfs/shell.nix | 1 + img/app/Makefile | 2 +- img/app/default.nix | 5 +-- lib/kcmdline-utils.mk | 10 +++--- lib/version.nix | 15 +++++++++ release/checks/no-roothash.nix | 2 +- release/live/Makefile | 15 ++++----- release/live/default.nix | 11 +++++-- release/live/shell.nix | 4 ++- scripts/format-uuid.awk | 35 ++++++++++++++++++++ scripts/format-uuid.sh | 1 + scripts/make-gpt.bash | 72 ++++++++++++++++++++++++++++++++++++++++++ scripts/make-gpt.sh | 67 ++------------------------------------- scripts/make-live-image.sh | 41 ++++++++++++++++++++++++ scripts/sfdisk-field.awk | 3 +- version | 1 + version.license | 2 ++ vm/sys/net/Makefile | 2 +- vm/sys/net/default.nix | 5 +-- 26 files changed, 252 insertions(+), 125 deletions(-) diff --git a/host/initramfs/Makefile b/host/initramfs/Makefile index cb13fbb35f065b67d291d4a35591d6f12720060c..798f675eb4f2ffde1c2eadc0a7b08ca59b65f347 100644 --- a/host/initramfs/Makefile +++ b/host/initramfs/Makefile @@ -4,6 +4,7 @@ .POSIX: include ../../lib/common.mk +include ../../lib/kcmdline-utils.mk dest = build/initramfs @@ -51,16 +52,13 @@ build/rootfs.verity.roothash: 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 ../../scripts/sfdisk-field.awk build/rootfs.verity.superblock build/rootfs.verity.roothash $(ROOT_FS) - ../../scripts/make-gpt.sh $@.tmp \ - build/rootfs.verity.superblock:verity:$$(../../scripts/format-uuid.sh "$$(dd if=build/rootfs.verity.roothash bs=32 skip=1 count=1 status=none)") \ - $(ROOT_FS):root:$$(../../scripts/format-uuid.sh "$$(head -c 32 build/rootfs.verity.roothash)") - mv $@.tmp $@ +build/live.img: $(LIVE_IMAGE_DEPS) $(ROOT_FS) + ../../scripts/make-live-image.sh live $@ $(ROOT_FS) build/loop.tar: build/live.img $(TAR) -cf $@ build/live.img -build/loop.img: ../../scripts/make-gpt.sh ../../scripts/sfdisk-field.awk build/loop.ext4 +build/loop.img: ../../scripts/make-gpt.sh ../../scripts/make-gpt.bash ../../scripts/sfdisk-field.awk build/loop.ext4 ../../scripts/make-gpt.sh $@.tmp \ build/loop.ext4:56a3bbc3-aefa-43d9-a64d-7b3fd59bbc4e mv $@.tmp $@ @@ -69,12 +67,13 @@ clean: rm -rf build .PHONY: clean -run: $(dest) build/rootfs.verity.roothash $(RUN_IMAGE) - @../../scripts/run-qemu.sh -m 4G \ +run: $(dest) build/rootfs.verity.roothash $(RUN_IMAGE) ../../lib/kcmdline-utils.mk + $(READ_ROOTHASH); \ + ../../scripts/run-qemu.sh -m 4G \ -machine virtualization=on \ -kernel $(KERNEL) \ -initrd $(dest) \ - -append "ro earlycon console=hvc0 intel_iommu=on roothash=$$(< build/rootfs.verity.roothash) nokaslr" \ + -append "ro earlycon console=hvc0 intel_iommu=on nokaslr x-spectrum-roothash=$$roothash x-spectrum-version=$$VERSION" \ -cpu max \ -gdb unix:build/gdb.sock,server,nowait \ -parallel none \ diff --git a/host/initramfs/default.nix b/host/initramfs/default.nix index ac7efbe3e19ee73d757d041a6a1051fe06c1d069..a1a1abbf5395f5650585901e5d2a13a72100ac09 100644 --- a/host/initramfs/default.nix +++ b/host/initramfs/default.nix @@ -99,12 +99,14 @@ stdenvNoCC.mkDerivation { fileset = lib.fileset.intersection src (lib.fileset.unions [ ./. ../../lib/common.mk + ../../lib/kcmdline-utils.mk ]); }; sourceRoot = "source/host/initramfs"; env = { PACKAGES_CPIO = packagesCpio; + VERSION = import ../../lib/version.nix; } // lib.optionalAttrs stdenvNoCC.hostPlatform.isx86_64 { MICROCODE = microcode; }; diff --git a/host/initramfs/etc/init b/host/initramfs/etc/init index 719488741b6d31564c2c17c0e41f15d16b1c0a08..8f36cd68e0450ff1a77ed5338e992323577a9f87 100755 --- a/host/initramfs/etc/init +++ b/host/initramfs/etc/init @@ -6,22 +6,16 @@ export PATH /bin if { mount -a } -piperw 3 4 -if { fdmove 1 4 /etc/getuuids } -fdclose 4 -# head -1 would be clearer, but it might use buffered I/O and consume -# too much from the fifo. Ideally we'd have line(1) from illumos. -backtick ROOTFS_UUID { fdmove 0 3 dd count=1 bs=37 status=none } -backtick VERITY_UUID { fdmove 0 3 dd count=1 bs=37 status=none } -fdclose 3 - if { mkfifo /dev/rootfs.poll } background { - fdclose 3 mdevd -C -b134217728 } -importas -iu mdevd_pid ! + +multisubstitute { + importas -iu mdevd_pid ! + importas -i roothash x-spectrum-roothash +} if { modprobe erofs } @@ -36,7 +30,6 @@ background { kill $mdevd_pid } background { rm /dev/rootfs.poll } if { - importas -Si roothash veritysetup open /dev/rootfs root-verity /dev/verity $roothash } diff --git a/host/initramfs/etc/probe b/host/initramfs/etc/probe index 4cbd00db52c1a7128b5c619a43d415675feaee0b..34e82fe9fa81316f21125b8eb058cc2917de69d7 100755 --- a/host/initramfs/etc/probe +++ b/host/initramfs/etc/probe @@ -14,9 +14,13 @@ if -n { forx -pE module { ext4 loop } modprobe $module } - backtick -E uuid { lsblk -lnpo PARTUUID $mdev } + backtick uuid { lsblk -lnpo PARTUUID $mdev } + multisubstiute { + define mdev_ $mdev + importas -Si uuid + } if { mkdir -p /mnt/${uuid} } - if { mount $mdev /mnt/${uuid} } + if { mount $mdev_ /mnt/${uuid} } find /mnt/${uuid} -name *.img -exec losetup -Pf {} ; @@ -24,11 +28,13 @@ if -n { # Check whether we now have all the partitions we need to boot. -importas -i rootfs_uuid ROOTFS_UUID -importas -i verity_uuid VERITY_UUID - -backtick -E rootfs_dev { findfs PARTUUID=${rootfs_uuid} } -backtick -E verity_dev { findfs PARTUUID=${verity_uuid} } +importas -i version x-spectrum-version +backtick rootfs_dev { findfs PARTLABEL=Spectrum_OS_${version} } +backtick verity_dev { findfs PARTLABEL=Spectrum_OS_${version}.verity } +multisubstitute { + importas -iS rootfs_dev + importas -iS verity_dev +} if { ln -s $rootfs_dev /dev/rootfs } if { ln -s $verity_dev /dev/verity } diff --git a/host/initramfs/shell.nix b/host/initramfs/shell.nix index eeba865e3ac793f67ae1808a92cf5eb1b37d57af..fa628f9c09eb266de247241b233286e756bd01d4 100644 --- a/host/initramfs/shell.nix +++ b/host/initramfs/shell.nix @@ -18,5 +18,6 @@ initramfs.overrideAttrs ({ nativeBuildInputs ? [], env ? {}, ... }: { env = env // { KERNEL = "${rootfs.kernel}/${stdenv.hostPlatform.linux-kernel.target}"; ROOT_FS = rootfs; + VERSION = import ../../lib/version.nix; }; })) (_: {}) diff --git a/host/rootfs/Makefile b/host/rootfs/Makefile index 15752286f5924291768f0655a12b90c702730520..84f1b385198ecfa5905b69e4901e56150ea1b424 100644 --- a/host/rootfs/Makefile +++ b/host/rootfs/Makefile @@ -4,6 +4,7 @@ .POSIX: include ../../lib/common.mk +-include ../../lib/kcmdline-utils.mk include file-list.mk dest = build/rootfs.erofs @@ -38,9 +39,11 @@ DIRS = \ etc/s6-linux-init/run-image/vm/by-id \ etc/s6-linux-init/run-image/vm/by-name \ ext \ + home \ proc \ run \ - sys + sys \ + tmp FIFOS = etc/s6-linux-init/run-image/service/s6-svscan-log/fifo @@ -99,11 +102,8 @@ build/rootfs.verity.roothash: 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 ../../scripts/sfdisk-field.awk build/rootfs.verity.superblock build/rootfs.verity.roothash $(dest) - ../../scripts/make-gpt.sh $@.tmp \ - build/rootfs.verity.superblock:verity:$$(../../scripts/format-uuid.sh "$$(dd if=build/rootfs.verity.roothash bs=32 skip=1 count=1 status=none)") \ - $(dest):root:$$(../../scripts/format-uuid.sh "$$(head -c 32 build/rootfs.verity.roothash)") - mv $@.tmp $@ +build/live.img: $(LIVE_IMAGE_DEPS) $(dest) + ../../scripts/make-live-image.sh live $@ $(dest) debug: $(GDB) -q \ @@ -112,14 +112,17 @@ debug: $(VMLINUX) .PHONY: debug -run: build/live.img $(EXT_FS) build/rootfs.verity.roothash +run: build/live.img $(EXT_FS) build/rootfs.verity.roothash ../../lib/kcmdline-utils.mk @set -x && \ ext="$$(mktemp build/spectrum-rootfs-extfs.XXXXXXXXXX.img)" && \ truncate -s 10G "$$ext" && \ - mkfs.btrfs "$$ext" && \ + dir=$$(mktemp -d) && \ + mkdir -- "$$dir/tmp" "$$dir/home" && \ + mkfs.btrfs --rootdir "$$dir" --subvol tmp --subvol home -- "$$ext" && \ + rm -rf -- "$$dir" && \ exec 3<>"$$ext" && \ rm -f "$$ext" && \ - set +x && \ + set -x && $(READ_ROOTHASH) && \ exec ../../scripts/run-qemu.sh -cpu max -m 4G \ -machine virtualization=on \ -kernel $(KERNEL) \ @@ -133,7 +136,7 @@ run: build/live.img $(EXT_FS) build/rootfs.verity.roothash -device virtconsole,chardev=virtiocon0 \ -drive file=build/live.img,if=virtio,format=raw,readonly=on \ -drive file=/proc/self/fd/3,if=virtio,format=raw \ - -append "earlycon console=hvc0 roothash=$$(< build/rootfs.verity.roothash) intel_iommu=on nokaslr" \ + -append "earlycon console=hvc0 intel_iommu=on nokaslr x-spectrum-roothash=$$roothash x-spectrum-version=$$VERSION" \ -device virtio-keyboard \ -device virtio-mouse \ -device virtio-gpu \ diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix index 00052222507077b9e94a5ed0a3fbddd27caeefc3..bc364b930b30e00c55b17b5e4248a303392cf3a0 100644 --- a/host/rootfs/default.nix +++ b/host/rootfs/default.nix @@ -133,7 +133,9 @@ stdenvNoCC.mkDerivation { fileset = fileset.intersection src (fileset.unions [ ./. ../../lib/common.mk + ../../lib/kcmdline-utils.mk ../../scripts/make-erofs.sh + ../../version ]); }; sourceRoot = "source/host/rootfs"; @@ -145,6 +147,7 @@ stdenvNoCC.mkDerivation { printf "%s\n/\n" ${packagesSysroot} >$out sed p ${writeClosure [ packagesSysroot] } >>$out ''; + VERSION = import ../../lib/version.nix; }; makeFlags = [ "dest=$(out)" ]; diff --git a/host/rootfs/shell.nix b/host/rootfs/shell.nix index 3d986f7327823cb855e5980759ad2a3935793340..bd234e90ee19bdfa6591d29c518cb0dc393b01c8 100644 --- a/host/rootfs/shell.nix +++ b/host/rootfs/shell.nix @@ -20,5 +20,6 @@ rootfs.overrideAttrs ( KERNEL = "${passthru.kernel}/${stdenv.hostPlatform.linux-kernel.target}"; LINUX_SRC = srcOnly passthru.kernel; VMLINUX = "${passthru.kernel.dev}/vmlinux"; + VERSION = import ../../lib/version.nix; }; })) (_: {}) diff --git a/img/app/Makefile b/img/app/Makefile index 981889ebe55d9ba03228977f3dc0ea3f26d5c4fb..e380fc173f580f00e9f4008da36533b645345f9b 100644 --- a/img/app/Makefile +++ b/img/app/Makefile @@ -24,7 +24,7 @@ $(imgdir)/appvm/vmlinux: $(KERNEL) mkdir -p $$(dirname $@) cp $(KERNEL) $@ -$(imgdir)/appvm/blk/root.img: ../../scripts/make-gpt.sh ../../scripts/sfdisk-field.awk build/rootfs.erofs +$(imgdir)/appvm/blk/root.img: ../../scripts/make-gpt.sh ../../scripts/make-gpt.bash ../../scripts/sfdisk-field.awk build/rootfs.erofs mkdir -p $$(dirname $@) ../../scripts/make-gpt.sh $@.tmp \ build/rootfs.erofs:root:5460386f-2203-4911-8694-91400125c604:root diff --git a/img/app/default.nix b/img/app/default.nix index 253fef08f5db29757da9d11fda67ac23fe6040c3..06764356d4126d3a2cd6a3e590accfeab6cffda4 100644 --- a/img/app/default.nix +++ b/img/app/default.nix @@ -8,7 +8,7 @@ pkgsStatic.callPackage ( { lib, stdenvNoCC, runCommand, writeClosure , erofs-utils, jq, s6-rc, util-linux , busybox, cacert, dejavu_fonts, execline, kmod, linux_latest, mdevd, s6 -, s6-linux-init, spectrum-app-tools +, s6-linux-init, spectrum-app-tools, bash }: let @@ -106,13 +106,14 @@ stdenvNoCC.mkDerivation { ./. ../../lib/common.mk ../../scripts/make-erofs.sh + ../../scripts/make-gpt.bash ../../scripts/make-gpt.sh ../../scripts/sfdisk-field.awk ]); }; sourceRoot = "source/img/app"; - nativeBuildInputs = [ erofs-utils jq spectrum-build-tools s6-rc util-linux ]; + nativeBuildInputs = [ erofs-utils jq spectrum-build-tools s6-rc util-linux bash ]; env = { KERNEL = "${kernel}/${baseNameOf kernelTarget}"; diff --git a/lib/kcmdline-utils.mk b/lib/kcmdline-utils.mk index fa228552e583f15fc77a746985060ad5d04cdf2c..7f1ef7d197ccf68c17640f4fdf44c167939fca13 100644 --- a/lib/kcmdline-utils.mk +++ b/lib/kcmdline-utils.mk @@ -1,6 +1,8 @@ # SPDX-License-Identifier: EUPL-1.2+ # SPDX-FileCopyrightText: 2021-2024 Alyssa Ross -READ_ROOTHASH = { \ - set -eufo pipefail; \ - read -r version < ../../version; \ - LC_ALL=C expr "x$$version" : '^\(x0\|x[1-9][0-9]*\)\(\.\(0\|[1-9][0-9]*\)\)\{2\}$$' >/dev/null; } +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour +READ_ROOTHASH = { set -euo pipefail; \ + read -r roothash < build/rootfs.verity.roothash; \ + LC_ALL=C expr "x$$roothash" : '^x[a-f0-9]\{64\}$$' >/dev/null; } + +LIVE_IMAGE_DEPS = ../../scripts/format-uuid.awk ../../scripts/make-gpt.sh ../../scripts/make-gpt.bash ../../scripts/sfdisk-field.awk build/rootfs.verity.superblock build/rootfs.verity.roothash ../../scripts/make-live-image.sh ../../lib/kcmdline-utils.mk diff --git a/lib/version.nix b/lib/version.nix new file mode 100644 index 0000000000000000000000000000000000000000..1c1568137313c37c4e1377a063992f7bf6856e57 --- /dev/null +++ b/lib/version.nix @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +let + raw_version = builtins.readFile ../version; + version_length = builtins.stringLength raw_version - 1; + version = builtins.substring 0 version_length raw_version; + number_re = "(0|[1-9][0-9]{0,2})"; +in +if version_length < 0 || builtins.substring version_length 1 raw_version != "\n" then + builtins.abort "Version file missing trailing newline (contents ${builtins.toJSON raw_version})" +else if builtins.match "^(${number_re}\\.){2}${number_re}$" version == null then + builtins.abort "Version ${builtins.toJSON version} is invalid" +else + version diff --git a/release/checks/no-roothash.nix b/release/checks/no-roothash.nix index 76d1e8b88ba74e6981775f3d4b8d10138c342d84..1d044cb04828ea221e7d1656e5eb7942669fe73c 100644 --- a/release/checks/no-roothash.nix +++ b/release/checks/no-roothash.nix @@ -28,6 +28,6 @@ in { machine = create_machine(flags) machine.start() - machine.wait_for_console_text("roothash invalid or missing") + machine.wait_for_console_text("x-spectrum-roothash not set") ''; }))) (_: {}) diff --git a/release/live/Makefile b/release/live/Makefile index 6dcbdeedda5d6ccf293f60dc62043f46c81ecf83..3072d869f13efbf5ea196d191881aeab85726d2e 100644 --- a/release/live/Makefile +++ b/release/live/Makefile @@ -4,22 +4,21 @@ .POSIX: include ../../lib/common.mk +include ../../lib/kcmdline-utils.mk DTBS ?= build/empty dest = build/live.img -$(dest): ../../scripts/format-uuid.sh ../../scripts/make-gpt.sh ../../scripts/sfdisk-field.awk build/boot.fat build/rootfs.verity.superblock build/rootfs.verity.roothash $(ROOT_FS) - ../../scripts/make-gpt.sh $@.tmp \ - build/boot.fat:c12a7328-f81f-11d2-ba4b-00a0c93ec93b \ - build/rootfs.verity.superblock:verity:$$(../../scripts/format-uuid.sh "$$(dd if=build/rootfs.verity.roothash bs=32 skip=1 count=1 status=none)") \ - $(ROOT_FS):root:$$(../../scripts/format-uuid.sh "$$(head -c 32 build/rootfs.verity.roothash)") - mv $@.tmp $@ +$(dest): $(LIVE_IMAGE_DEPS) build/boot.fat + ../../scripts/make-live-image.sh release $(dest) $(ROOT_FS) build/empty: mkdir -p $@ -build/spectrum.efi: build/rootfs.verity.roothash $(DTBS) $(KERNEL) $(INITRAMFS) +build/spectrum.efi: build/rootfs.verity.roothash $(DTBS) $(KERNEL) $(INITRAMFS) ../../lib/kcmdline-utils.mk + set -euo pipefail; \ + $(READ_ROOTHASH) && \ { \ printf "[UKI]\nDeviceTreeAuto=" && \ find $(DTBS) -name '*.dtb' -print0 | tr '\0' ' ' ;\ @@ -29,7 +28,7 @@ build/spectrum.efi: build/rootfs.verity.roothash $(DTBS) $(KERNEL) $(INITRAMFS) --linux $(KERNEL) \ --initrd $(INITRAMFS) \ --os-release $$'NAME="Spectrum"\n' \ - --cmdline "ro intel_iommu=on roothash=$$(cat build/rootfs.verity.roothash)" + --cmdline "ro intel_iommu=on x-spectrum-roothash=$$roothash x-spectrum-version=$$VERSION" build/boot.fat: $(SYSTEMD_BOOT_EFI) build/spectrum.efi $(TRUNCATE) -s 440401920 $@ diff --git a/release/live/default.nix b/release/live/default.nix index 2a1dc3e1dd939f21edac582bf39737eb4d46eb0c..b5c0c8df31d4c6cb7fdd2337e8169f36655dd1a8 100644 --- a/release/live/default.nix +++ b/release/live/default.nix @@ -1,12 +1,13 @@ # SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021-2023, 2025 Alyssa Ross # SPDX-FileCopyrightText: 2022 Unikie +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour import ../../lib/call-package.nix ( { callSpectrumPackage, spectrum-build-tools, rootfs, src , lib, pkgsStatic, stdenvNoCC , cryptsetup, dosfstools, jq, mtools, util-linux -, systemdUkify +, systemdUkify, bash }: let @@ -32,15 +33,20 @@ stdenv.mkDerivation { fileset = lib.fileset.intersection src (lib.fileset.unions [ ./. ../../lib/common.mk + ../../lib/kcmdline-utils.mk + ../../scripts/format-uuid.awk ../../scripts/format-uuid.sh + ../../scripts/make-gpt.bash ../../scripts/make-gpt.sh + ../../scripts/make-live-image.sh ../../scripts/sfdisk-field.awk + ../../version ]); }; sourceRoot = "source/release/live"; nativeBuildInputs = [ - cryptsetup dosfstools jq spectrum-build-tools mtools systemd util-linux + bash cryptsetup dosfstools jq spectrum-build-tools mtools systemd util-linux ]; env = { @@ -49,6 +55,7 @@ stdenv.mkDerivation { ROOT_FS = rootfs; SYSTEMD_BOOT_EFI = "${systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi"; EFINAME = "BOOT${toUpper efiArch}.EFI"; + VERSION = import ../../lib/version.nix; } // lib.optionalAttrs stdenv.hostPlatform.linux-kernel.DTB or false { DTBS = "${rootfs.kernel}/dtbs"; }; diff --git a/release/live/shell.nix b/release/live/shell.nix index 5acaa8c5b113fd2789aaea9268487b193bab37af..e1e78214871c0e4681bff0d5a894c8ae3b8c3c02 100644 --- a/release/live/shell.nix +++ b/release/live/shell.nix @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021-2024 Alyssa Ross -import ../../lib/call-package.nix ({ callSpectrumPackage, stdenv, qemu_kvm }: +import ../../lib/call-package.nix ({ callSpectrumPackage, stdenv, qemu_kvm, rootfs }: (callSpectrumPackage ./. {}).overrideAttrs ( { nativeBuildInputs ? [], env ? {}, ... }: @@ -9,7 +9,9 @@ import ../../lib/call-package.nix ({ callSpectrumPackage, stdenv, qemu_kvm }: nativeBuildInputs = nativeBuildInputs ++ [ qemu_kvm ]; env = env // { + ROOT_FS = rootfs; OVMF_CODE = "${qemu_kvm}/share/qemu/edk2-${stdenv.hostPlatform.qemuArch}-code.fd"; + VERSION = import ../../lib/version.nix; }; } )) (_: {}) diff --git a/scripts/format-uuid.awk b/scripts/format-uuid.awk new file mode 100644 index 0000000000000000000000000000000000000000..a5349d68a4d29be5f750650236420c9b5a7257eb --- /dev/null +++ b/scripts/format-uuid.awk @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour +function format_uuid(arg) { + if (arg in found_so_far) { + fail("Duplicate UUID, try changing the image (by even 1 bit)"); + } + found_so_far[arg] = 1; + print (substr(arg, 1, 8) "-" \ + substr(arg, 9, 4) "-" \ + substr(arg, 13, 4) "-" \ + substr(arg, 17, 4) "-" \ + substr(arg, 21, 12)); +} + +function fail(msg) { + print msg > "/dev/stderr"; + exit 1; +} + +BEGIN { + FS = ""; + RS = "\n"; + if ((getline) != 1) + fail("Empty input file"); + roothash = $0; + if (roothash !~ /^[a-f0-9]{64}$/) + fail("Invalid root hash"); + if (getline) + fail("Junk after root hash"); + found_so_far[""] = ""; + for (i = 1; i != 49; i += 16) { + format_uuid(substr($0, i, 32)); + } + format_uuid(substr($0, 49, 16) substr($0, 1, 16)); +} diff --git a/scripts/format-uuid.sh b/scripts/format-uuid.sh index 497a5f2daeef88e0143f5021cd64fa2181ffe163..f589b3340252c653df97a82ce429528beee43b1a 100755 --- a/scripts/format-uuid.sh +++ b/scripts/format-uuid.sh @@ -4,6 +4,7 @@ # SPDX-FileCopyrightText: 2022 Unikie # SPDX-License-Identifier: EUPL-1.2+ +set -o pipefail substr () { str=$1 beg=$2 diff --git a/scripts/make-gpt.bash b/scripts/make-gpt.bash new file mode 100644 index 0000000000000000000000000000000000000000..f9d53817e3cc4342cac5d4c832cf4aa129880399 --- /dev/null +++ b/scripts/make-gpt.bash @@ -0,0 +1,72 @@ +#!/usr/bin/bash -- +# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross +# SPDX-FileCopyrightText: 2022 Unikie +# SPDX-License-Identifier: EUPL-1.2+ +# +# usage: make-gpt.sh GPT_PATH PATH:PARTTYPE[:PARTUUID[:PARTLABEL]]... + +set -xeuo pipefail +ONE_MiB=1048576 + +# 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() { + start="$(sfdisk -J "$1" | jq -r --argjson index "$2" \ + '.partitiontable.partitions[$index].start * 512')" + + # GNU cat will use copy_file_range(2) if possible, whereas dd + # will always do a userspace copy, which is significantly slower. + lseek -S 1 "$start" cat "$3" 1<>"$1" +} + +# Prints the partition path from a PATH:PARTTYPE[:PARTUUID[:PARTLABEL]] string. +partitionPath() { + awk -F: '{print $1}' <"$1" -} - -# Prints the partition path from a PATH:PARTTYPE[:PARTUUID[:PARTLABEL]] string. -partitionPath() { - awk -F: '{print $1}' < +exec bash -- "${0%.sh}.bash" "$@" diff --git a/scripts/make-live-image.sh b/scripts/make-live-image.sh new file mode 100755 index 0000000000000000000000000000000000000000..2d8f5140fd23280d0f8ff2c0cb1640875dab4e8e --- /dev/null +++ b/scripts/make-live-image.sh @@ -0,0 +1,41 @@ +#!/bin/sh -- +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2021-2024 Alyssa Ross +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour +set -euo pipefail +if [ ! -f build/rootfs.verity.superblock ]; then + echo 'No superblock found' >&2 + exit 1 +fi +case $0 in +(/*) dir=${0%/*}/;; +(*/*) dir=./${0%/*};; +(*) dir=.;; +esac +usage () { + echo 'Usage: make-live-image.sh [release|live] OUTPUT_FILE ROOT_FILESYSTEM' >&2 + exit 1 +} +if [ "$#" != 3 ]; then usage; fi +file_type=$1 output_file=$2 root_filesystem=$3 +root_hashes=$(LC_ALL=C awk -f "${dir}/format-uuid.awk" < build/rootfs.verity.roothash) +# The awk script produces output that is meant for field splitting +# and has no characters special for globbing. +# shellcheck disable=SC2086 +set -- $root_hashes +case $file_type in +(release) + "$dir/make-gpt.sh" "$output_file.tmp" \ + build/boot.fat:c12a7328-f81f-11d2-ba4b-00a0c93ec93b \ + "build/rootfs.verity.superblock:verity:$1:Spectrum_OS_$VERSION.verity:1024MiB" \ + "$root_filesystem:root:$2:Spectrum_OS_$VERSION:20480MiB" \ + "/dev/null:verity:$3:_empty:1024MiB" \ + "/dev/null:root:$4:_empty:20480MiB" + ;; +(live) + "$dir/make-gpt.sh" "$output_file.tmp" \ + "build/rootfs.verity.superblock:verity:$1:Spectrum_OS_$VERSION.verity" \ + "$root_filesystem:root:$2:Spectrum_OS_$VERSION";; +(*) usage;; +esac +mv -- "$output_file.tmp" "$output_file" diff --git a/scripts/sfdisk-field.awk b/scripts/sfdisk-field.awk index e13c86d2fb11a066eebd043808e659b08dbd269c..72eec9a0a770563d32da14440fe2552eb2e39b68 100644 --- a/scripts/sfdisk-field.awk +++ b/scripts/sfdisk-field.awk @@ -24,6 +24,7 @@ BEGIN { arch = _arch } + comma = "" for (n in fields) { if (n <= skip) continue @@ -33,6 +34,6 @@ BEGIN { fields[n] = uuid } - printf "%s=%s,", keys[n - skip], fields[n] + printf ",%s%s=%s", comma, keys[n - skip], fields[n] } } diff --git a/version b/version new file mode 100644 index 0000000000000000000000000000000000000000..77d6f4ca23711533e724789a0a0045eab28c5ea6 --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.0.0 diff --git a/version.license b/version.license new file mode 100644 index 0000000000000000000000000000000000000000..e9aa5bf149a1b426dba78c7df37b92c0a992a7dd --- /dev/null +++ b/version.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: EUPL-1.2+ +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/vm/sys/net/Makefile b/vm/sys/net/Makefile index b377e12bba8f062026e997de18e19c9af8e07cb5..c29a8faee4e0a7eb170325e4d1eaeeba4532df41 100644 --- a/vm/sys/net/Makefile +++ b/vm/sys/net/Makefile @@ -23,7 +23,7 @@ $(vmdir)/netvm/vmlinux: $(KERNEL) mkdir -p $$(dirname $@) cp $(KERNEL) $@ -$(vmdir)/netvm/blk/root.img: ../../../scripts/make-gpt.sh ../../../scripts/sfdisk-field.awk build/rootfs.erofs +$(vmdir)/netvm/blk/root.img: ../../../scripts/make-gpt.sh ../../../scripts/make-gpt.bash ../../../scripts/sfdisk-field.awk build/rootfs.erofs mkdir -p $$(dirname $@) ../../../scripts/make-gpt.sh $@.tmp \ build/rootfs.erofs:root:ea21da27-0391-48da-9235-9d2ab2ca7844:root diff --git a/vm/sys/net/default.nix b/vm/sys/net/default.nix index 9d9df0b001060085239e00ffa59f8e091f0b88bf..2f3eea176928315ac0cd6e81bb788e965613e3a9 100644 --- a/vm/sys/net/default.nix +++ b/vm/sys/net/default.nix @@ -8,7 +8,7 @@ pkgsStatic.callPackage ( { lib, stdenvNoCC, nixos, runCommand, writeClosure , erofs-utils, jq, s6-rc, util-linux, xorg , busybox, connmanMinimal, dbus, execline, kmod, linux_latest, mdevd, nftables -, s6, s6-linux-init +, s6, s6-linux-init, bash }: let @@ -106,13 +106,14 @@ stdenvNoCC.mkDerivation { ./. ../../../lib/common.mk ../../../scripts/make-erofs.sh + ../../../scripts/make-gpt.bash ../../../scripts/make-gpt.sh ../../../scripts/sfdisk-field.awk ]); }; sourceRoot = "source/vm/sys/net"; - nativeBuildInputs = [ erofs-utils jq spectrum-build-tools s6-rc util-linux ]; + nativeBuildInputs = [ erofs-utils jq spectrum-build-tools s6-rc util-linux bash ]; env = { KERNEL = "${kernel}/${baseNameOf kernelTarget}"; -- 2.51.2