@@ -8,6 +8,13 @@ SPDX-License-Identifier: Apache-2.0
88
99## Build Chain
1010
11+ ### x86 (vendored)
12+
13+ No build step — hex0 source files are included directly:
14+ * ` builder-hex0-x86-stage1-bios.hex0 ` — stage 1 (MBR boot + hex0 compiler)
15+ * ` builder-hex0-x86-stage2.hex0 ` — stage 2 (full builder with shell)
16+ * ` builder-hex0-x86-mini.hex0 ` — minimal hex0-only compiler (512 bytes)
17+
1118### RISC-V 64-bit
1219
1320```
@@ -29,21 +36,26 @@ hex2_word.c, so AArch64 pre-resolves all offsets in Python).
2936
3037## Source Files
3138
32- | File | Purpose |
33- | ------| ---------|
34- | builder-hex0-riscv64-stage1-virt.S | RISC-V QEMU virt stage 1 (VirtIO) |
35- | builder-hex0-riscv64-stage1-sifive_u.S | RISC-V SiFive sifive_u stage 1 (SPI+SD) |
36- | builder-hex0-riscv64-stage2.S | RISC-V portable stage 2 kernel |
37- | rv64-asm2hex2.py | RISC-V assembly-to-hex2 converter |
38- | asm.py | RISC-V instruction encoder library |
39- | builder-hex0-aarch64-stage1-virt.S | AArch64 QEMU virt stage 1 (VirtIO) |
40- | builder-hex0-aarch64-stage1-raspi3b.S | AArch64 RPi 3B stage 1 (SDHCI, core parking) |
41- | builder-hex0-aarch64-stage2.S | AArch64 portable stage 2 kernel |
42- | a64-asm2hex2.py | AArch64 assembly-to-hex2 converter (two-pass) |
43- | a64_asm.py | AArch64 instruction encoder library |
44- | hex2tohex0.py | hex2-to-hex0 converter with comments (shared) |
45- | hex2/ | Vendored hex2 linker (C source, shared) |
46- | Makefile | Multi-architecture build (` ARCH=riscv64\|aarch64 ` ) |
39+ | File | Purpose |
40+ | ----------------------------------------| -------------------------------------------------|
41+ | builder-hex0-x86-stage1-bios.hex0 | x86 BIOS stage 1 (vendored, Rick Masters) |
42+ | builder-hex0-x86-stage2.hex0 | x86 full builder (vendored, Rick Masters) |
43+ | builder-hex0-x86-mini.hex0 | x86 mini hex0 compiler (vendored, Rick Masters) |
44+ | builder-hex0-riscv64-stage1-virt.S | RISC-V QEMU virt stage 1 (VirtIO) |
45+ | builder-hex0-riscv64-stage1-sifive_u.S | RISC-V SiFive sifive_u stage 1 (SPI+SD) |
46+ | builder-hex0-riscv64-stage2.S | RISC-V portable stage 2 kernel |
47+ | rv64-asm2hex2.py | RISC-V assembly-to-hex2 converter |
48+ | asm.py | RISC-V instruction encoder library |
49+ | builder-hex0-aarch64-stage1-virt.S | AArch64 QEMU virt stage 1 (VirtIO) |
50+ | builder-hex0-aarch64-stage1-raspi3b.S | AArch64 RPi 3B stage 1 (SDHCI, core parking) |
51+ | builder-hex0-aarch64-stage2.S | AArch64 portable stage 2 kernel |
52+ | a64-asm2hex2.py | AArch64 assembly-to-hex2 converter (two-pass) |
53+ | a64_asm.py | AArch64 instruction encoder library |
54+ | hex2tohex0.py | hex2-to-hex0 converter with comments (shared) |
55+ | hex0-to-src.sh | Generate shell script for hex0 self-compilation |
56+ | build-self.sh | Boot kernel and hex0-compile a source file |
57+ | hex2/ | Vendored hex2 linker (C source, shared) |
58+ | Makefile | Multi-architecture build with per-board targets |
4759
4860
4961## Two-Stage Boot
@@ -52,12 +64,12 @@ hex2_word.c, so AArch64 pre-resolves all offsets in Python).
5264
5365Each board has its own stage 1. Stage 1 is architecture- and board-specific.
5466
55- | | RISC-V | AArch64 (virt) | AArch64 (raspi3b) |
56- | ---| ---| ---| ---|
57- | Load address | ` 0x80200000 ` (by OpenSBI) | ` 0x40080000 ` (by QEMU) | ` 0x00080000 ` (by QEMU) |
58- | Entry state | ` a0 ` =hartid, ` a1 ` =dtb | ` x0 ` =dtb | EL2, all 4 cores |
59- | Stage 2 address | ` 0x80210000 ` | ` 0x40210000 ` | ` 0x00210000 ` |
60- | Exit convention | ` a0 ` =sector, ` a1 ` =DTB | ` x0 ` =sector, ` x1 ` =DTB | ` x0 ` =sector, ` x1 ` =0, ` x2 ` =SDHCI base, ` x3 ` =3 |
67+ | | RISC-V | AArch64 (virt) | AArch64 (raspi3b) |
68+ | ----------------- | --------------------------- | ------------------------ | ------------------------------------------- ---|
69+ | Load address | ` 0x80200000 ` (by OpenSBI) | ` 0x40080000 ` (by QEMU) | ` 0x00080000 ` (by QEMU) |
70+ | Entry state | ` a0 ` =hartid, ` a1 ` =dtb | ` x0 ` =dtb | EL2, all 4 cores |
71+ | Stage 2 address | ` 0x80210000 ` | ` 0x40210000 ` | ` 0x00210000 ` |
72+ | Exit convention | ` a0 ` =sector, ` a1 ` =DTB | ` x0 ` =sector, ` x1 ` =DTB | ` x0 ` =sector, ` x1 ` =0, ` x2 ` =SDHCI base, ` x3 ` =3 |
6173
6274Responsibilities:
63751 . Find and initialize the storage device (board-specific)
@@ -148,6 +160,47 @@ uses PSCI HVC, AArch64 raspi3b uses the BCM2835 power management watchdog
148160(writing PM_RSTS, PM_WDOG, PM_RSTC with the PM password ` 0x5A ` ).
149161
150162
163+ ## Self-Build Reproducibility
164+
165+ Each architecture can prove its hex0 compiler produces identical output to the
166+ host toolchain. The ` make self-test ` targets automate this.
167+
168+ ### x86
169+
170+ The x86 chain follows the original builder-hex0 pattern:
171+
172+ 1 . Host compiles ` builder-hex0-x86-mini.hex0 ` to binary using ` xxd ` (seed)
173+ 2 . Mini seed boots in QEMU, compiles ` stage1-bios.hex0 ` → writes to disk
174+ 3 . Host also compiles ` stage1-bios.hex0 ` using ` xxd `
175+ 4 . Diff the two binaries — proves mini's hex0 compiler matches host
176+
177+ The x86 also has a "full builds full" chain (the full builder can compile its
178+ own hex0 source via the ` hex0 ` shell command), matching the original
179+ builder-hex0 Makefile.
180+
181+ ### RISC-V and AArch64
182+
183+ These architectures don't have a "mini" variant. Stage 1 IS the hex0 compiler,
184+ but it can't self-build in isolation because:
185+ - It needs QEMU ` -kernel ` to load (not MBR boot)
186+ - The compiled output goes to a fixed RAM address, not back to disk
187+ - Stage 1 has no "write to disk" capability
188+
189+ Instead, self-builds go through the full stage 2 kernel:
190+
191+ 1 . Host compiles stage 1 and stage 2 from hex0 source (seed binaries)
192+ 2 . Stage 1 seed boots → compiles stage 2 hex0 → stage 2 kernel runs
193+ 3 . Internal shell loads a hex0 file via ` src ` , compiles it via ` hex0 ` , flushes
194+ to disk via ` f `
195+ 4 . Extract the result from the disk image and diff against the seed binary
196+
197+ This is one extra layer compared to x86 mini, but achieves the same proof:
198+ the hex0 compiler (embedded in the kernel) reproduces the binary from source.
199+
200+ Both stage 1 and stage 2 are tested:
201+ - ` make self-test-{arch}-{board} ` runs both stage1 and stage2 self-builds
202+
203+
151204## Porting
152205
153206### New board (same architecture)
@@ -157,7 +210,7 @@ uses PSCI HVC, AArch64 raspi3b uses the BCM2835 power management watchdog
1572103 . If using a new storage type, add a driver to stage 2 implementing
158211 ` xx_read_sector(a0=sector, a1=buffer) ` and ` xx_write_sector `
1592124 . Add detection logic in ` fdt_find_storage ` for the new DTB node pattern
160- 5 . Add a test target (` test-boot -{board} ` )
213+ 5 . Add a test target (` test-{arch}-{board} ` ) and self-test target ( ` self-test-{arch} -{board}` )
161214
162215### New architecture
163216
@@ -172,12 +225,16 @@ uses PSCI HVC, AArch64 raspi3b uses the BCM2835 power management watchdog
172225
173226```
174227make clean && make # all architectures
228+ make x86 # x86 (vendored, no build step)
175229make riscv64 # riscv64 only
176230make aarch64 # aarch64 only
177231
178- make test # all architectures
179- make test-riscv64 # riscv64 only
180- make test-aarch64 # aarch64 only
232+ make test # boot tests, all arch+board combos
233+ make test-x86-bios # one board
234+ make test-aarch64-raspi3b # one board
235+
236+ make self-test # self-build reproducibility, all
237+ make self-test-aarch64-virt # one board
181238```
182239
183240The RISC-V sifive_u test requires QEMU >= 10.1.
0 commit comments