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 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-26 19:28:42 -04:00
co-authored by Claude Opus 5.5
parent a78cc56c31
commit 7f297f4e99
@@ -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.