From 1687593a4c90e549f40b9116004bfa075e038450 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 7 Aug 2026 11:54:25 -0400 Subject: [PATCH 1/5] doc/reproducible-builds.md: fix SOURCE_DATE_EPOCH wording and document output files The prior wording "git log always falls back to 0" is misleading -- git log does not fall back; it fails when .git is missing, and the || echo 0 shell construct in modules/musl-cross-make sets the value. Describe the actual mechanism: the build system cannot derive a commit timestamp from extracted tarballs, so modules/musl-cross-make falls back to echo 0 when git log fails. Also add a new 'Output files' section in doc/reproducible-builds.md describing what each hash-related file (hashes.txt, sizes.txt, sha256sum.txt) contains and how they relate to reproducibility verification. Signed-off-by: Thierry Laurion --- doc/reproducible-builds.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/reproducible-builds.md b/doc/reproducible-builds.md index 82e74a9b6..4f69dcf2a 100644 --- a/doc/reproducible-builds.md +++ b/doc/reproducible-builds.md @@ -13,7 +13,9 @@ produce the same build triplet instead of probing the Docker host kernel. `-Wa,--no-pad-sections` prevents gas from padding section ends (non-deterministic alignment). `--with-debug-prefix-map=$(pwd)=.` normalizes build paths in debug info. `--enable-compressed-debug-sections=no` disables zlib debug-section -compression. `SOURCE_DATE_EPOCH` from the pinned musl-cross-make commit epoch +compression. `SOURCE_DATE_EPOCH=0` (extracted tarballs lack .git; the build system cannot +derive a commit timestamp from extracted tarballs, so `modules/musl-cross-make` falls back to +`echo 0` when `git log` fails) prevents `__DATE__`/`__TIME__` embedding during the GCC build. ## Userland compiler flags @@ -123,6 +125,18 @@ fetch_source_archive.sh. "https://output.circle-artifacts.com/output/job/circleci-job-id/artifacts/0/build/x86/EOL_t480-hotp-maximized/hashes.txt" ``` +### Output files + +A build produces these hash-related files under `build///`: + +| File | Content | +|---|---| +| `hashes.txt` | SHA-256 of every build artifact (cpio archives, bzImage, ROM) plus per-file hashes inside each cpio. Reset at each `make` invocation; appended by each build rule. The authoritative source for reproducibility verification. | +| `sizes.txt` | Byte sizes of each artifact, matching the hashes.txt entries. | +| `sha256sum.txt` | SHA-256 of the final ROM only. Packaged inside the update zip for integrity checks during flash updates. | + +Both `hashes.txt` and `sha256sum.txt` are included in the update zip for offline reproducibility verification. + ### Understanding hashes.txt `build/$ARCH/$BOARD/hashes.txt` records the SHA256 of **every file inside every From d5b88854f329a52897e842cea160f63b5dd777ac Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 7 Aug 2026 11:55:12 -0400 Subject: [PATCH 2/5] modules/linux: improve bzImage UNCHANGED guard (no tmp hash noise, no cmp stderr) The FORCE prerequisite (already in master) ensures the rule always runs, but the do-copy call produced sha256sum/stat noise for the temporary file, and cmp emitted stderr on first build when the target did not exist. - Replace do-copy with plain cp -a + INSTALL progress line (no tmp hash/stat noise in build log) - Guard cmp with [ ! -f "$@" ] || so the first build takes the "changed" branch without invoking cmp at all Signed-off-by: Thierry Laurion --- modules/linux | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/linux b/modules/linux index 6685d6530..d7a067416 100644 --- a/modules/linux +++ b/modules/linux @@ -201,8 +201,10 @@ $(build)/$(BOARD)/modules.cpio: $(build)/$(linux_dir)/.build FORCE # linux build directory. We need to copy it into our board # specific directory for ease of locating it later. $(build)/$(BOARD)/$(LINUX_IMAGE_FILE): $(build)/$(linux_dir)/.build FORCE - $(call do-copy,$(dir $<)/$(linux_output),$@.tmp) - @if ! cmp --quiet "$@.tmp" "$@" ; then \ + $(call do,INSTALL ,$< => $@.tmp,\ + cp -a "$(dir $<)/$(linux_output)" "$@.tmp" \ + ) + @if [ ! -f "$@" ] || ! cmp --quiet "$@.tmp" "$@" ; then \ mv "$@.tmp" "$@" ; \ touch "$@" ; \ else \ From 851328b292848c60a86956e675679c184b519c41 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 7 Aug 2026 11:55:22 -0400 Subject: [PATCH 3/5] modules/busybox: use musl-cross compiler for busybox.links generation mkll only runs the preprocessor (-E) on already-generated config headers, producing a text-only applet list -- no binaries, no cross-compilation needed. The host gcc and the musl-cross compiler produce identical preprocessor output for this task. Replace HOSTCC="$(busybox_hostcc)" (host gcc via variable) with HOSTCC="$(heads_cc)" (musl-cross compiler used by every other build step). The variable was unnecessary indirection; the cross-compiler keeps the build chain self-contained with no host tool assumptions. Signed-off-by: Thierry Laurion --- modules/busybox | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/busybox b/modules/busybox index 07c51effa..462f72f9e 100644 --- a/modules/busybox +++ b/modules/busybox @@ -11,8 +11,6 @@ busybox_hash := b8cc24c9574d809e7279c3be349795c5d5ceb6fdf19ca709f80cde50e47de314 busybox_configure := $(MAKE) CC="$(heads_cc)" oldconfig busybox_config := config/busybox.config busybox_output := busybox -# Host compiler used by applets/busybox.mkll (busybox's Makefile uses gcc too) -busybox_hostcc ?= gcc busybox_target := \ $(CROSS_TOOLS) \ $(MAKE_JOBS) \ @@ -36,11 +34,15 @@ endif $(initrd_bin_dir)/busybox: $(build)/$(busybox_dir)/.build # Regenerate busybox.links (may be missing after clean/cache restore) + # mkll only runs the preprocessor (-E): cross-compiler is + # identical to host gcc for text-only header parsing and keeps + # the build chain self-contained (no host tools assumed) $(call do,INSTALL,bin/busybox,\ cp $(build)/$(busybox_dir)/busybox \ $(initrd_bin_dir)/busybox && \ cd $(build)/$(busybox_dir) && \ - HOSTCC="$(busybox_hostcc)" $(SHELL) applets/busybox.mkll include/autoconf.h include/applets.h > busybox.links && \ + HOSTCC="$(heads_cc)" \ + $(SHELL) applets/busybox.mkll include/autoconf.h include/applets.h > busybox.links && \ test -s busybox.links && \ $(SHELL) applets/install.sh $(initrd_bin_dir)/.. --symlinks \ ) From 79f291b66f05970b0f1d36c46004fc36ca8ef532 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 7 Aug 2026 11:56:34 -0400 Subject: [PATCH 4/5] all: use relative paths in all hashes.txt and sizes.txt entries All hashes.txt and sizes.txt writers now use consistent relative paths. initrd.cpio.xz already stripped the $(pwd)/ prefix; the remaining writers did not, producing path-prefix diffs between CI and local builds. - modules/linux: bzImage sha256sum/stat stripped $(pwd)/ prefix from $@ - Makefile do-cpio: strip $(pwd)/ prefix from $1 - Makefile all: ROM hash/size: strip $(pwd)/ prefix from $(board_build) - Makefile all payload: hash/size: strip $(pwd)/ prefix from $< Now every hashes.txt and sizes.txt entry uses a consistent relative path format, eliminating path-prefix diffs between CI and local builds. Signed-off-by: Thierry Laurion --- Makefile | 12 ++++++------ modules/linux | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index d9ed41b2c..c41137696 100644 --- a/Makefile +++ b/Makefile @@ -281,8 +281,8 @@ $(board_build)/$(CB_UPDATE_PKG_FILE): $(board_build)/$(CB_OUTPUT_FILE) # Only add the hash and size if split_8mb4mb.mk is not included ifeq ($(wildcard split_8mb4mb.mk),) all: $(board_build)/$(CB_OUTPUT_FILE) $(board_build)/$(CB_UPDATE_PKG_FILE) - @sha256sum $(board_build)/$(CB_OUTPUT_FILE) | tee -a "$(HASHES)" - @stat -c "%8s:%n" $(board_build)/$(CB_OUTPUT_FILE) | tee -a "$(SIZES)" + @sha256sum $(board_build:$(pwd)/%=%)/$(CB_OUTPUT_FILE) | tee -a "$(HASHES)" + @stat -c "%8s:%n" $(board_build:$(pwd)/%=%)/$(CB_OUTPUT_FILE) | tee -a "$(SIZES)" else all: $(board_build)/$(CB_OUTPUT_FILE) $(board_build)/$(CB_UPDATE_PKG_FILE) endif @@ -300,8 +300,8 @@ $(error "$(BOARD): neither CONFIG_COREBOOT nor CONFIG_LINUXBOOT is set?") endif all payload: - @sha256sum $< | tee -a "$(HASHES)" - @stat -c "%8s:%n" $< | tee -a "$(SIZES)" + @sha256sum $(<:$(pwd)/%=%) | tee -a "$(HASHES)" + @stat -c "%8s:%n" $(<:$(pwd)/%=%) | tee -a "$(SIZES)" # Validate coreboot CBFS size against IFD BIOS region validate_cbfs_ifd: @@ -407,8 +407,8 @@ define do-cpio = echo "$(DATE) UNCHANGED $(1:$(pwd)/%=%)" ; \ rm "$1.tmp" ; \ fi - @sha256sum "$1" | tee -a "$(HASHES)" - @stat -c "%8s:%n" "$1" | tee -a "$(SIZES)" + @sha256sum "$(1:$(pwd)/%=%)" | tee -a "$(HASHES)" + @stat -c "%8s:%n" "$(1:$(pwd)/%=%)" | tee -a "$(SIZES)" $(call do,HASHES , $1,\ ( cd "$2"; \ echo "-----" ; \ diff --git a/modules/linux b/modules/linux index d7a067416..9cef3100e 100644 --- a/modules/linux +++ b/modules/linux @@ -211,8 +211,8 @@ $(build)/$(BOARD)/$(LINUX_IMAGE_FILE): $(build)/$(linux_dir)/.build FORCE echo "$(DATE) UNCHANGED $(@:$(pwd)/%=%)" ; \ rm "$@.tmp" ; \ fi - @sha256sum "$@" | tee -a "$(HASHES)" - @stat -c "%8s:%n" "$@" | tee -a "$(SIZES)" + @sha256sum "$(@:$(pwd)/%=%)" | tee -a "$(HASHES)" + @stat -c "%8s:%n" "$(@:$(pwd)/%=%)" | tee -a "$(SIZES)" # Build kernel second time, now that initrd is built. $(build)/$(BOARD)/$(LINUX_IMAGE_FILE).bundled: \ From 8632a835059061a3ed1dabec92dde1762aa0bf96 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 7 Aug 2026 11:56:46 -0400 Subject: [PATCH 5/5] Makefile: include hashes.txt in update zip for future introspection Including hashes.txt in the update ZIP enables future work: a tool that hashes files in the running firmware and compares them against the update's hashes.txt, instantly showing which files are identical and which differ. For the same commit, all files match, confirming the same ROM is already installed. This commit only makes hashes.txt available in the ZIP; the tool itself is deferred. --- Makefile | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c41137696..ef27d9697 100644 --- a/Makefile +++ b/Makefile @@ -271,12 +271,24 @@ else # verification before flashing (see flash-gui.sh). The ZIP package format # allows other metadata that might be needed to added in the future without # breaking backward compatibility. +# --- UPDATE PACKAGE (ZIP) --- +# +# The update zip contains three files: +# - The Heads ROM image (16 MiB flashable image) +# sha256sum.txt - SHA-256 of the ROM only (for update integrity checks) +# hashes.txt - Per-file hash manifest for reproducibility +# verification (same-commit CI comparison) and +# future flash-gui.sh introspection: compare the +# current ROM's files against the update ZIP's +# hashes.txt to report which scripts, modules, +# or kernel changed before deciding to flash $(board_build)/$(CB_UPDATE_PKG_FILE): $(board_build)/$(CB_OUTPUT_FILE) rm -rf "$(board_build)/update_pkg" mkdir -p "$(board_build)/update_pkg" cp "$<" "$(board_build)/update_pkg/" + cp "$(HASHES)" "$(board_build)/update_pkg/" cd "$(board_build)/update_pkg" && sha256sum "$(CB_OUTPUT_FILE)" >sha256sum.txt - cd "$(board_build)/update_pkg" && zip -9 "$@" "$(CB_OUTPUT_FILE)" sha256sum.txt + cd "$(board_build)/update_pkg" && zip -9 "$@" "$(CB_OUTPUT_FILE)" sha256sum.txt "$(notdir $(HASHES))" # Only add the hash and size if split_8mb4mb.mk is not included ifeq ($(wildcard split_8mb4mb.mk),)