From 7f297f4e999daa43c69b1bffb0a8eaa52203279a Mon Sep 17 00:00:00 2001 From: rajames Date: Mon, 26 Oct 2026 19:28:42 -0400 Subject: [PATCH] docs(v4): the stick, increment 2 plan: the GPT reader Approved by Captain Bob 2026-10-26, with his ruling on review: a GPT disk is never attached whole -- the identity partition is attached in the raw identity drive's place; a GPT disk with no StarshipOS partition, or a damaged one, is not attached. Co-Authored-By: Claude Opus 5.5 --- .../plans/2026-10-26-stick-2-gpt-reader.md | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md diff --git a/docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md b/docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md new file mode 100644 index 00000000..7d360bea --- /dev/null +++ b/docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md @@ -0,0 +1,132 @@ +# The stick, increment 2: the GPT reader — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans (Captain Bob prefers native execution). Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** The kernel reads a disk's GPT and makes a block device of each StarshipOS partition on it, on +every ISA; the v4 boot recognises Zuse on a GPT drive's identity partition, and raw identity drives are +read exactly as now. + +**Architecture:** One kernel file, `gpt.c`: a CRC-32, a reader that checks the protective MBR, the +primary GPT header and its partition-entry array (both CRCs) and lists the partitions, and a thin `blkio` +backend whose block 0 is a partition's first 1 KiB. The v4 boot's drive-arrival path tries GPT first: a +disk with a valid GPT is used through its StarshipOS identity partition, and that partition device -- not +the disk -- is what the block subsystem is given, in the place a raw identity drive has today (everything +past its metadata is the identity's own block storage); a GPT disk with no StarshipOS partition, or a +damaged one, is given to nothing; a disk with no GPT is read raw, exactly as today. Artemis from a partition, the boot disk, and genesis are increments 4 and 5; this one only reads. + +**Tech Stack:** freestanding C99 kernel; QEMU (`blockdev-add`/`device_add` of a GPT test image); +`sfdisk` + `dd` to build the test images; `scripts/qemu_session.py`. + +**Spec:** `docs/v4.0.0/STICK.md` §3 (GPT for boot media and installed disks, raw identity drives still +accepted), §4 (the layout, the GPT reader, partition devices, "the identity and Artemis formats do not +change"), §11 increment 2. + +## Global Constraints + +- As increment 1's plan (`2026-10-26-stick-1-screen-and-keyboard.md`): branch, commits, strict C99, + `-Werror`, v3 unchanged, QEMU/bare-metal parity, no fix outside the plan, full acceptance on every + kernel-affecting task (three `clean qemu` boots to `[zuse@Hera] ok> `, POST 538/538, `dict_hash` + `0xfec8c47cfc6f437f` equal on all three, `make -C v4 test-32` 0 failures, logs committed). +- The two StarshipOS partition type GUIDs (generated 2026-10-26, fixed from here; `mkdiskimage.sh` + uses the same values in increment 3): + - StarshipOS identity: `23BEBFE1-4817-4CF3-A537-9069BF159788` + - StarshipOS Artemis: `BCD7B586-1C8E-4EEF-86A3-273A8D8EE624` +- GPT as UEFI 2.x defines it: protective MBR (type `0xEE` at LBA 0), header at LBA 1, signature + `"EFI PART"`, revision `0x00010000`, header size 92..512, header CRC-32 over `HeaderSize` bytes with + its own CRC field zeroed, entry array CRC-32 over `NumberOfPartitionEntries * SizeOfPartitionEntry` + bytes, entry size a multiple of 128. Little-endian GUIDs on disk (mixed-endian first three fields). +- 512-byte logical sectors only (USB sticks and the data stick). A disk reporting another sector size + is not read as GPT (said once, used raw). 4 KiB-sector disks are `INSTALL`'s, later. +- A partition is used only if its first LBA and its sector count are both even (it starts and ends on + a 1 KiB `blkio` block); otherwise it is skipped with a message. Every image this project builds starts + partitions at 1 MiB. +- No v3 change: the v3 path does not read GPT (`system=v3` is refused on a stick, STICK.md §5, in + increment 4). + +**Ruled (Captain Bob, 2026-10-26, on reviewing this plan):** a GPT disk is never attached whole. The +identity partition device is attached in the raw identity drive's place; a GPT disk with no StarshipOS +partition, or a damaged one, is not attached at all. So a plain GPT data stick does not appear as raw +block storage on v4, as it would on v3 -- a change from today, accepted. + +## Review Focus + +1. **A GPT whose header CRC or entry-array CRC is wrong** is not used: no partition device, not + attached, not read raw either (a damaged stick is not a raw identity drive), one message. Pinned in + Task 1 (`gpt_bad_crc`). +2. **A GPT disk with no StarshipOS partition** (an ordinary data stick) gives no identity, is not + attached, no fault, no prompt. Pinned in Task 1 (`gpt_foreign`). +3. **A partition device reading past its end** is refused (`BLKIO_EINVAL`), never reaching the + neighbouring partition. Covered by the backend's bound and by `gpt_zuse`'s identity check, which reads + offsets taken from the drive. +4. **Raw identity drives** (`disk/zuse-scratch/`) still work: the identity gate's sessions all pass. +5. **The partition device detaches with its disk:** pulling a GPT Zuse drive ends her session exactly + as pulling a raw one does. Pinned in Task 1 (`gpt_hotplug`). + +--- + +### Task 1: The GPT reader, and Zuse from a GPT identity partition + +**Files:** +- Create: `kernel/src/gpt.c`, `kernel/include/starkernel/gpt.h` +- Modify: `kernel/Makefile` (add `$(KERNEL_SRC)/gpt.c` beside `homeblocks_sig.c` in both source lists) +- Modify: `kernel/src/v4/sk_v4_identity.c` (`sk_v4_drive_arrived` / `sk_v4_drive_left`) +- Modify: `scripts/qemu_session.py` (modes below; it builds the GPT test images) + +**Interfaces (produces):** +- `extern const uint8_t gpt_type_starship_identity[16], gpt_type_starship_artemis[16];` — the on-disk + (mixed-endian) bytes of the two GUIDs above. +- `typedef struct { uint8_t type[16]; uint64_t first_lba; uint64_t last_lba; } gpt_part_t;` +- `int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count);` — returns + `GPT_OK` (0, a valid GPT; `*count` partitions with a non-zero type, at most `max`), `GPT_NONE` (no + protective MBR or no `"EFI PART"`: not a GPT disk), or `GPT_BAD` (a GPT, but a CRC, the revision, the + sizes or the geometry is wrong). Reads only; uses `blkio_read`. +- `int gpt_find(const gpt_part_t *parts, unsigned count, const uint8_t type[16]);` — the index of + the first partition of that type, or -1. +- `typedef struct { blkio_dev_t *disk; uint32_t first_fblock; uint32_t fblocks; } gpt_part_dev_t;` +- `int gpt_part_open(blkio_dev_t *dev, gpt_part_dev_t *st, blkio_dev_t *disk, const gpt_part_t *p);` + — opens `dev` as that partition: `fblock` n is the disk's `first_lba/2 + n`; `total_blocks` is the + sector count / 2; reads and writes past the end are `BLKIO_EINVAL`; `flush`/`info` pass through. + `BLKIO_EINVAL` for an odd first LBA or sector count. +- `uint32_t gpt_crc32(const void *buf, size_t len);` — IEEE 802.3, reflected, poly `0xEDB88320`, + init and final XOR `0xFFFFFFFF` (`gpt_crc32("123456789", 9) == 0xCBF43926`). + +**Interfaces (the v4 drive path, `sk_v4_identity.c`):** on arrival, after `blkio_usb_open_msc`, `gpt_read` +on the raw device: +- `GPT_OK` with a StarshipOS identity partition: open the partition device (kept per slot beside the + raw `blkio_dev_t`); give the **partition device** to the block subsystem (`blk_subsys_attach_device`), + never the raw disk; run the signature check and `capsule_zuse_boot_check` on the partition device; + the device remembered as Zuse's is the partition device. +- `GPT_OK` with no StarshipOS partition: nothing attached, no identity check, no message. +- `GPT_BAD`: "GPT: a USB drive has a damaged partition table -- not used"; nothing attached. +- `GPT_NONE`: exactly today's raw path. +- On detach (`sk_v4_drive_left`, called with the raw slot): if that slot has an open partition + device, it is the partition device that is detached from the block subsystem and compared with + Zuse's; the raw device otherwise, as today. A slot that was never attached detaches nothing. + +- [ ] **Step 1: Failing tests** — the harness gains `make_gpt_image(path, parts)` (truncate, `sfdisk` + with the type GUIDs above, `dd` a payload into a partition at its start) and these modes (amd64; then + `gpt_zuse` on aarch64 and riscv64): + - `gpt_zuse`: no drive at boot; hot-add a 72 MiB GPT image whose partition 1 (LBA 2048, 131072 + sectors, type StarshipOS identity) holds `disk/zuse-scratch/zuse-thumb-ident.img`; expect "Zuse: + identity confirmed from attached thumbdrive", `[zuse@Hera] ok> `, `3 4 + .` gives `7 ok`. + Booted with `KERNEL_ARGS=--log-level=info`, the block subsystem's attach line for the partition + (`blk: disk ... LBN ...`, 64 MiB) appears -- the positive control for the two modes below. + Today: FAIL (the raw check at the disk's devblock 1 finds partition entries, not `LAHB`). + - `gpt_hotplug`: as `gpt_zuse`, then `device_del` → "Zuse: session ended"; re-add → prompt. + Today: FAIL. + - `gpt_foreign`: hot-add a GPT image with one Linux-filesystem partition (`0FC63DAF-…`) holding the + Zuse image; no identity, no `ok> ` for 30 s, no fault, and -- booted with + `KERNEL_ARGS=--log-level=info`, since the attach line is an INFO message the v4 boot otherwise hides + after POST -- no block-subsystem attach line for it (the log's `blk: disk ... LBN` lines do not grow). Today: FAIL on the attach (the raw disk is + attached). + - `gpt_bad_crc`: the `gpt_zuse` image with one byte of the primary header's CRC field flipped; no + identity, no `ok> ` for 30 s, the log says the table is damaged, and (booted with + `KERNEL_ARGS=--log-level=info`) no attach line for it. + Today: FAIL on the message and the attach. +- [ ] **Step 2:** Run each; record the failures. +- [ ] **Step 3:** Implement `gpt.c` and the drive-path change as above. +- [ ] **Step 4:** Every mode passes (`gpt_zuse` on all three ISAs); the identity gate's sessions + (`headless`, `zuse`, `hotplug`, `midpull`, `waitpull`, `typeahead`, `emergency`) and increment 1's + (`screen_dark_until_login`, `screen_dark_after_logout`, `kbd_typeahead`) still pass. +- [ ] **Step 5:** Suite; full acceptance; commit `feat(kernel): the GPT reader, and Zuse from a GPT + identity partition` with the logs.