boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 25566ce26c643bae70bcc9d77b8eb0d3dbcdc409
parent 5dbea4132c4faacb1c5a1056b67a75ce698c36bf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 30 Apr 2026 10:38:49 -0700

pokem rm

Diffstat:
MMakefile | 17+++++------------
Dpokem/pokem.P1 | 263-------------------------------------------------------------------------------
Mscripts/boot2.sh | 5++---
3 files changed, 7 insertions(+), 278 deletions(-)

diff --git a/Makefile b/Makefile @@ -11,9 +11,8 @@ # scripts/Containerfile.busybox. # # Common entrypoints: -# make all (m1pp + pokem for ARCH) +# make all (m1pp for ARCH) # make m1pp build the m1pp expander for ARCH -# make pokem build pokem for ARCH # make scheme1 build the scheme1 interpreter for ARCH # make cc catm the cc compiler source for ARCH # make tcc-flat flatten upstream tcc.c into one TU @@ -70,10 +69,10 @@ PODMAN = podman run --rm --pull=never --platform $(PLATFORM_$(1)) \ # --- Targets -------------------------------------------------------------- -.PHONY: all m1pp pokem scheme1 cc test image tools tables \ +.PHONY: all m1pp scheme1 cc test image tools tables \ tools-native cloc clean help tcc-boot2 tcc-flat tcc-gcc -all: m1pp pokem +all: m1pp help: @sed -n '/^# Common entrypoints:/,/^$$/p' Makefile | sed 's/^# *//' @@ -141,12 +140,12 @@ $(TOOLS_M0): build/%/tools/M0: scripts/boot1.sh build/%/.image \ # --- Pre-pruned P1 backend tables ----------------------------------------- # # Generated once from P1/gen/p1_gen.py and pruned to just the DEFINEs -# referenced by M1pp/pokem/.M1 fixtures. Result is checked in to +# referenced by M1pp/.M1 fixtures. Result is checked in to # P1/P1-<arch>.M1; routine builds skip the prune step. Re-run `make # tables` after editing P1/gen/*.py or any of the prune-source files # below, then commit the updated P1/*.M1. -P1_PRUNE_SRCS := M1pp/M1pp.P1 pokem/pokem.P1 $(wildcard tests/P1/*.P1) +P1_PRUNE_SRCS := M1pp/M1pp.P1 $(wildcard tests/P1/*.P1) tables: $(foreach a,$(ALL_ARCHES),P1/P1-$(a).M1) @@ -163,7 +162,6 @@ P1/P1-%.M1: build/%/P1/P1.M1 scripts/prune-p1-table.sh $(P1_PRUNE_SRCS) # --- Programs (per arch) -------------------------------------------------- M1PP_BINS := $(foreach a,$(ALL_ARCHES),build/$(a)/M1pp/M1pp) -POKEM_BINS := $(foreach a,$(ALL_ARCHES),build/$(a)/pokem/pokem) SCHEME1_SRC := scheme1/scheme1.P1pp SCHEME1_BINS := $(foreach a,$(ALL_ARCHES),build/$(a)/scheme1/scheme1) @@ -174,7 +172,6 @@ CC_SRCS := scheme1/prelude.scm cc/cc.scm cc/main.scm CC_BINS := $(foreach a,$(ALL_ARCHES),build/$(a)/cc/cc.scm) m1pp: $(OUT_DIR)/M1pp/M1pp -pokem: $(OUT_DIR)/pokem/pokem scheme1: $(OUT_DIR)/scheme1/scheme1 cc: $(OUT_DIR)/cc/cc.scm @@ -193,10 +190,6 @@ $(M1PP_BINS): build/%/M1pp/M1pp: M1pp/M1pp.P1 $(P1_BUILD_DEPS) ARCH=$* sh scripts/lint.sh M1pp/M1pp.P1 $(call PODMAN,$*) sh scripts/boot-build-p1.sh M1pp/M1pp.P1 $@ -$(POKEM_BINS): build/%/pokem/pokem: pokem/pokem.P1 $(P1_BUILD_DEPS) - ARCH=$* sh scripts/lint.sh pokem/pokem.P1 - $(call PODMAN,$*) sh scripts/boot-build-p1.sh pokem/pokem.P1 $@ - $(SCHEME1_BINS): build/%/scheme1/scheme1: $(SCHEME1_SRC) $(P1PP_BUILD_DEPS) $(call PODMAN,$*) sh scripts/boot-build-p1pp.sh $@ $(SCHEME1_SRC) diff --git a/pokem/pokem.P1 b/pokem/pokem.P1 @@ -1,263 +0,0 @@ -## pokem.M1 — raw-byte file patcher, P1. -## -## Runtime shape: pokem file offset hex_bytes -## -## Opens `file` with O_RDWR, advances the file position by `offset` -## bytes via discard reads, then writes the ASCII-hex-decoded payload -## bytes in place. Primary use case is patching ELF header fields -## after assembly — e.g. bumping p_memsz to reserve a BSS region -## without emitting literal zero bytes in the source. -## -## Arguments: -## file path to the file to patch in place. -## offset byte offset, decimal ASCII, unsigned. -## hex_bytes 2*N ASCII hex nibbles (upper or lower case). Each pair -## becomes one raw byte. Empty string is a no-op. -## -## Bootstrap-tool posture: no input validation and no syscall error -## checking. Callers are expected to pass well-formed inputs, and the -## tool is invoked by build scripts, not end users. Bad input produces -## either a bogus patch, a crash, or a silently extended file; we trade -## defensive code for auditability. -## -## Pipeline: -## p1_main stash argv; decode offset (parse_u64) and -## payload (parse_hex_bytes into patch_buf); -## openat O_RDWR; advance via read into scratch_buf -## until offset bytes consumed; write patch_buf; -## close; return 0 (stub sys_exits with it). -## parse_u64 leaf. Decimal ASCII -> u64 in a0. -## parse_hex_bytes leaf. ASCII hex -> patch_buf; returns byte count -## in a0. Uses a branchless nibble decode that is -## correct for [0-9A-Fa-f] and garbage otherwise. -## -## P1 ABI: a0..a3 arg/return, t0..t2 caller-saved temps, s0..s3 -## callee-saved (unused here). Entry is the portable p1_main -## (a0=argc, a1=argv). The backend-owned :_start stub captures argc/argv -## from the native entry state and sys_exits p1_main's return value. - -## --- Constants & sizing ------------------------------------------------------ - -## patch_buf cap: 256 bytes. ELF-header patch fields are 4 or 8 bytes; -## this leaves generous slack for longer fixups. -DEFINE POKEM_PATCH_CAP 0001000000000000 - -## scratch_buf cap: 256 bytes. Used to absorb offset bytes before the -## write. Each advance iteration reads up to this many to amortize -## syscall cost on large offsets. -DEFINE POKEM_SCRATCH_CAP 0001000000000000 - -## openat flags. O_RDWR = 2 so a single fd handles both the position- -## advance reads and the in-place write. -DEFINE O_RDWR 0200000000000000 -DEFINE AT_FDCWD 9CFFFFFFFFFFFFFF - -DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' -DEFINE ZERO8 '0000000000000000' - -## --- Runtime shell: argv, open, advance, write, exit ------------------------- - -:p1_main - enter_0 - # a0 = argc, a1 = argv (pointer to argv[0]). Stash argv[1..3] into - # memory before anything clobbers a1. - # file_path = argv[1] - ld_t0,a1,8 - la_a2 &file_path - st_t0,a2,0 - # offset_str = argv[2] - ld_t0,a1,16 - la_a2 &offset_str - st_t0,a2,0 - # hex_str = argv[3] - ld_t0,a1,24 - la_a2 &hex_str - st_t0,a2,0 - - # offset = parse_u64(offset_str) - la_a0 &offset_str - ld_a0,a0,0 - la_br &parse_u64 - call - la_a1 &offset_val - st_a0,a1,0 - - # patch_len = parse_hex_bytes(hex_str) — bytes land in patch_buf - la_a0 &hex_str - ld_a0,a0,0 - la_br &parse_hex_bytes - call - la_a1 &patch_len - st_a0,a1,0 - - # fd = openat(AT_FDCWD, file_path, O_RDWR, 0) - la_a0 &file_path - ld_a2,a0,0 - li_a0 sys_openat - li_a1 AT_FDCWD - li_a3 O_RDWR - li_t0 %0 %0 - syscall - la_a1 &file_fd - st_a0,a1,0 - - # Advance the file position by offset bytes via discard reads into - # scratch_buf. Each iteration reads min(remaining, SCRATCH_CAP). - la_a0 &offset_val - ld_t0,a0,0 # t0 = remaining - -:advance_loop - la_br &advance_done - beqz_t0 - - # chunk = min(remaining, SCRATCH_CAP) -> a3 - mov_a3,t0 # default: chunk = remaining - li_t1 POKEM_SCRATCH_CAP - la_br &advance_do_read - bltu_t0,t1 # if remaining < cap, keep a3 = remaining - mov_a3,t1 # else chunk = cap - -:advance_do_read - la_a0 &file_fd - ld_a1,a0,0 - la_a2 &scratch_buf - li_a0 sys_read - syscall - - # remaining -= n (short read with n == 0 loops forever; callers - # must pass offset <= file_size) - sub_t0,t0,a0 - la_br &advance_loop - b - -:advance_done - # Write patch_buf (patch_len bytes) at the current position. - la_a0 &patch_len - ld_t0,a0,0 # t0 = patch_len (total) - li_t1 %0 %0 # t1 = written - -:write_loop - la_br &write_done - beq_t1,t0 - - # n = write(fd, patch_buf + written, patch_len - written) - la_a0 &file_fd - ld_a1,a0,0 - la_a2 &patch_buf - add_a2,a2,t1 - sub_a3,t0,t1 - li_a0 sys_write - syscall - - # written += n - add_t1,t1,a0 - la_br &write_loop - b - -:write_done - # close(fd); return 0 (backend :_start stub sys_exits with a0) - la_a0 &file_fd - ld_a1,a0,0 - li_a0 sys_close - syscall - - li_a0 %0 %0 - eret - -## --- parse_u64(a0=str) -> a0=value -------------------------------------------- -## Leaf. Decimal ASCII -> u64. Empty string returns 0. Non-digit bytes -## produce garbage — caller's responsibility to pass a digit-only, -## NUL-terminated string. - -:parse_u64 - mov_t0,a0 # cursor - li_t1 %0 %0 # result = 0 - li_a3 %10 %0 # constant multiplier - -:parse_u64_loop - lb_a1,t0,0 # c - la_br &parse_u64_done - beqz_a1 - - # result = result * 10 + (c - 48) - mul_t1,t1,a3 - addi_a1,a1,neg48 - add_t1,t1,a1 - - addi_t0,t0,1 - la_br &parse_u64_loop - b - -:parse_u64_done - mov_a0,t1 - ret - -## --- parse_hex_bytes(a0=str) -> a0=byte_count --------------------------------- -## Leaf. Walks the ASCII hex string two chars at a time into patch_buf. -## Empty input returns 0. Odd length or non-hex characters produce -## garbage bytes; no validation. -## -## Branchless nibble decode: for any c in [0-9A-Fa-f], -## nibble = (c & 15) + 9 * (c >> 6) -## Digits (48..57) have bit 6 clear, so `9 * (c>>6)` is 0 and -## `c & 15` is the value. Letters (65..70 / 97..102) have bit 6 set, -## so we add 9 to the low nibble (1..6) to land on 10..15. - -:parse_hex_bytes - mov_t0,a0 # cursor - la_t1 &patch_buf # dst base - li_t2 %0 %0 # count - -:phb_loop - lb_a0,t0,0 # c0 = *cursor - la_br &phb_done - beqz_a0 - - lb_a1,t0,1 # c1 = *(cursor+1) - li_a3 %9 %0 - - # hi = (c0 & 15) + 9 * (c0 >> 6) - andi_a2,a0,15 - shri_a0,a0,6 - mul_a0,a0,a3 - add_a0,a2,a0 - - # lo = (c1 & 15) + 9 * (c1 >> 6) - andi_a2,a1,15 - shri_a1,a1,6 - mul_a1,a1,a3 - add_a1,a2,a1 - - # byte = (hi << 4) | lo; patch_buf[count] = byte - shli_a0,a0,4 - or_a0,a0,a1 - add_a2,t1,t2 - sb_a0,a2,0 - - addi_t2,t2,1 # count += 1 - addi_t0,t0,2 # cursor += 2 - la_br &phb_loop - b - -:phb_done - mov_a0,t2 - ret - -## --- BSS --------------------------------------------------------------------- - -:file_path ZERO8 -:offset_str ZERO8 -:hex_str ZERO8 -:offset_val ZERO8 -:patch_len ZERO8 -:file_fd ZERO8 - -## scratch_buf (POKEM_SCRATCH_CAP bytes) — discard-read target. -:scratch_buf -ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 - -## patch_buf (POKEM_PATCH_CAP bytes) — decoded payload. -:patch_buf -ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 - -:ELF_end diff --git a/scripts/boot2.sh b/scripts/boot2.sh @@ -1,11 +1,11 @@ #!/bin/sh ## boot2.sh — stage 2 of the bootstrap chain. ## -## In-container script. Builds the M1pp expander and pokem ELFs from the +## In-container script. Builds the M1pp expander ELF from the ## checked-in pre-pruned P1 backend table (P1/P1-$ARCH.M1) plus their ## sources, by calling scripts/boot-build-p1.sh. ## -## Outputs: build/$ARCH/M1pp/M1pp, build/$ARCH/pokem/pokem +## Outputs: build/$ARCH/M1pp/M1pp ## ## Env: ARCH=aarch64|amd64|riscv64 @@ -19,4 +19,3 @@ case "$ARCH" in esac sh scripts/boot-build-p1.sh M1pp/M1pp.P1 build/$ARCH/M1pp/M1pp -sh scripts/boot-build-p1.sh pokem/pokem.P1 build/$ARCH/pokem/pokem