feat(kernel): the GPT reader, and Zuse from a GPT identity partition

The stick, increment 2 (plan 2026-10-26-stick-2-gpt-reader; docs/v4.0.0/
STICK.md 3 and 4: GPT for boot media and installed disks). kernel/src/gpt.c:
a CRC-32; gpt_read(), which checks the protective MBR, the primary header and
the partition-entry array (both CRCs, the revision, the sizes, the geometry);
and a partition as a blkio device, refusing blocks past its end. The two
StarshipOS partition types: identity 23BEBFE1-4817-4CF3-A537-9069BF159788,
Artemis BCD7B586-1C8E-4EEF-86A3-273A8D8EE624.

The v4 drive path: a GPT disk is used only through its StarshipOS identity
partition, and that partition -- never the disk -- is given to the block
subsystem, in a raw identity drive's place; a GPT disk with no StarshipOS
partition, or a damaged table ("GPT: a USB drive has a damaged partition
table -- not used"), is not attached (Captain Bob's ruling on the plan); a
disk with no GPT is read raw, as always. Pulling the drive detaches the
partition and ends her session. Reads only.

Red (amd64): gpt_zuse 193158, gpt_hotplug 193804, gpt_foreign 194713,
gpt_bad_crc 194947. Green: 195409, 195627, 195854, 200136; gpt_zuse aarch64
200424, riscv64 200839 (the 64 MiB partition attached, 48724 user blocks,
not the 72 MiB disk); the identity gate's and increment 1's sessions,
201119-203403, all pass.
Accepted (with the KERNEL_ARGS fix that follows): suite EXIT 0; 214300,
214507, 214911, POST 538/538, dict_hash 0xfec8c47cfc6f437f; test-32, 39
modules, 0 failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-26 21:56:31 -04:00
co-authored by Claude Opus 5.5
parent 03099f2c51
commit 3cc4937c93
43 changed files with 3428 additions and 12 deletions
+2
View File
@@ -515,6 +515,7 @@ LOADER_SRCS_BASE := \
$(KERNEL_SRC)/doe_log.c \
$(KERNEL_SRC)/heartbeat.c \
$(KERNEL_SRC)/homeblocks_sig.c \
$(KERNEL_SRC)/gpt.c \
$(KERNEL_SRC)/artemis_sig.c \
$(KERNEL_SRC)/log_region.c
@@ -573,6 +574,7 @@ KERNEL_SRCS_BASE := \
$(KERNEL_SRC)/doe_log.c \
$(KERNEL_SRC)/heartbeat.c \
$(KERNEL_SRC)/homeblocks_sig.c \
$(KERNEL_SRC)/gpt.c \
$(KERNEL_SRC)/artemis_sig.c \
$(KERNEL_SRC)/log_region.c
+50
View File
@@ -0,0 +1,50 @@
/* gpt.h -- the GPT reader, and a block device of one partition.
* docs/v4.0.0/STICK.md 3 and 4; docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md.
*/
#ifndef STARKERNEL_GPT_H
#define STARKERNEL_GPT_H
#include <stdint.h>
#include <stddef.h>
#include "blkio.h"
/* The two StarshipOS partition types, as their GUIDs are on disk. */
extern const uint8_t gpt_type_starship_identity[16]; /* 23BEBFE1-4817-4CF3-A537-9069BF159788 */
extern const uint8_t gpt_type_starship_artemis[16]; /* BCD7B586-1C8E-4EEF-86A3-273A8D8EE624 */
typedef struct {
uint8_t type[16];
uint64_t first_lba;
uint64_t last_lba;
} gpt_part_t;
#define GPT_OK 0 /* a valid GPT */
#define GPT_NONE 1 /* not a GPT disk (or one whose sectors are not 512 bytes): read it raw */
#define GPT_BAD 2 /* a GPT, but its header or entry array does not check out: not used */
/* Read `disk`'s protective MBR, primary GPT header and partition-entry array,
* checking both CRC-32s, the revision, the sizes and the geometry. On GPT_OK,
* `*count` partitions with a non-zero type (at most `max`) are in `parts`.
* Reads only. */
int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count);
/* The index of the first partition of `type`, or -1. */
int gpt_find(const gpt_part_t *parts, unsigned count, const uint8_t type[16]);
/* A partition as a block device: its block n is the disk's block
* first_fblock + n; blocks past fblocks are refused. */
typedef struct {
blkio_dev_t *disk;
uint32_t first_fblock;
uint32_t fblocks;
} gpt_part_dev_t;
/* Open `dev` as partition `p` of `disk`, its state in `st` (which must live
* as long as `dev`). BLKIO_EINVAL for a partition that does not start and end
* on a 1 KiB block, or that runs past the disk. */
int gpt_part_open(blkio_dev_t *dev, gpt_part_dev_t *st, blkio_dev_t *disk, const gpt_part_t *p);
/* CRC-32 (IEEE 802.3, reflected, polynomial 0xEDB88320), as GPT uses. */
uint32_t gpt_crc32(const void *buf, size_t len);
#endif /* STARKERNEL_GPT_H */
+195
View File
@@ -0,0 +1,195 @@
/* gpt.c -- the GPT reader, and a block device of one partition.
*
* GPT for boot media and installed disks (Captain Bob, 2026-10-26;
* docs/v4.0.0/STICK.md 3 and 4, overturning FABRIC-2.md F.8 for those two
* kinds of media). The kernel reads a disk's GPT -- the protective MBR, the
* primary header and its partition-entry array, both CRC-checked -- and makes
* a block device of a StarshipOS partition: its block 0 is the partition's
* first 1 KiB, and nothing past its end can be reached through it. The
* identity and Artemis formats inside a partition are exactly those of a raw
* drive today. Reads only. 512-byte logical sectors only (the stick and its
* data stick); docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md.
*/
#include "starkernel/gpt.h"
#include "console.h"
#include <stddef.h>
#include <string.h>
/* The two StarshipOS partition type GUIDs, as they are on disk: the first
* three fields little-endian, the last eight bytes as written.
* Identity 23BEBFE1-4817-4CF3-A537-9069BF159788,
* Artemis BCD7B586-1C8E-4EEF-86A3-273A8D8EE624. */
const uint8_t gpt_type_starship_identity[16] = {
0xE1, 0xBF, 0xBE, 0x23, 0x17, 0x48, 0xF3, 0x4C,
0xA5, 0x37, 0x90, 0x69, 0xBF, 0x15, 0x97, 0x88
};
const uint8_t gpt_type_starship_artemis[16] = {
0x86, 0xB5, 0xD7, 0xBC, 0x8E, 0x1C, 0xEF, 0x4E,
0x86, 0xA3, 0x27, 0x3A, 0x8D, 0x8E, 0xE6, 0x24
};
uint32_t gpt_crc32(const void *buf, size_t len)
{
const uint8_t *p = (const uint8_t *)buf;
uint32_t crc = 0xFFFFFFFFu;
size_t i;
int k;
for (i = 0; i < len; i++) {
crc ^= p[i];
for (k = 0; k < 8; k++) crc = (crc >> 1) ^ (0xEDB88320u & (0u - (crc & 1u)));
}
return crc ^ 0xFFFFFFFFu;
}
static uint32_t le32(const uint8_t *p) { return (uint32_t)p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24; }
static uint64_t le64(const uint8_t *p) { return (uint64_t)le32(p) | (uint64_t)le32(p + 4) << 32; }
#define GPT_SECTOR 512u
#define GPT_ENTRIES_MAX (32u * 1024u) /* the entry array read, at most (128 entries of 128 bytes is 16 KiB) */
static uint8_t g_gpt_hdr_block[BLKIO_FORTH_BLOCK_SIZE];
static uint8_t g_gpt_entries[GPT_ENTRIES_MAX];
int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count)
{
const uint8_t *mbr = g_gpt_hdr_block, *h = g_gpt_hdr_block + GPT_SECTOR;
uint32_t hsize, hcrc, nent, esize, ecrc, i;
uint64_t my_lba, first_usable, last_usable, ent_lba, disk_sectors;
uint8_t hdr[GPT_SECTOR];
size_t ebytes;
unsigned n = 0;
*count = 0;
if (!disk || disk->forth_block_size != BLKIO_FORTH_BLOCK_SIZE) return GPT_NONE;
{
static int said;
blkio_info_t info;
if (disk->vt->info(disk, &info) == BLKIO_OK && info.phys_sector_size != 0u &&
info.phys_sector_size != GPT_SECTOR) {
if (!said) { console_println("GPT: a drive with sectors other than 512 bytes is read without its partition table"); said = 1; }
return GPT_NONE;
}
}
if (blkio_read(disk, 0u, g_gpt_hdr_block) != BLKIO_OK) return GPT_NONE;
/* the protective MBR: 0x55AA, and a partition of type 0xEE */
if (mbr[510] != 0x55u || mbr[511] != 0xAAu) return GPT_NONE;
for (i = 0; i < 4u; i++) if (mbr[446u + 16u * i + 4u] == 0xEEu) break;
if (i == 4u) return GPT_NONE;
if (memcmp(h, "EFI PART", 8) != 0) return GPT_NONE;
/* from here it is a GPT disk: anything wrong is GPT_BAD */
if (le32(h + 8) != 0x00010000u) return GPT_BAD;
hsize = le32(h + 12);
if (hsize < 92u || hsize > GPT_SECTOR) return GPT_BAD;
memcpy(hdr, h, GPT_SECTOR);
hcrc = le32(hdr + 16);
memset(hdr + 16, 0, 4);
if (gpt_crc32(hdr, hsize) != hcrc) return GPT_BAD;
my_lba = le64(h + 24);
first_usable = le64(h + 40);
last_usable = le64(h + 48);
ent_lba = le64(h + 72);
nent = le32(h + 80);
esize = le32(h + 84);
ecrc = le32(h + 88);
disk_sectors = (uint64_t)disk->total_blocks * (BLKIO_FORTH_BLOCK_SIZE / GPT_SECTOR);
if (my_lba != 1u || esize < 128u || esize % 128u != 0u || nent == 0u) return GPT_BAD;
if (first_usable > last_usable || last_usable >= disk_sectors) return GPT_BAD;
ebytes = (size_t)nent * esize;
if (ebytes > GPT_ENTRIES_MAX || ent_lba < 2u || (ent_lba % 2u) != 0u) return GPT_BAD;
if (ent_lba + (ebytes + GPT_SECTOR - 1u) / GPT_SECTOR > first_usable) return GPT_BAD;
/* the entry array, a 1 KiB block at a time */
for (i = 0; (size_t)i * BLKIO_FORTH_BLOCK_SIZE < ebytes; i++) {
if (blkio_read(disk, (uint32_t)(ent_lba / 2u) + i, g_gpt_entries + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK)
return GPT_BAD;
}
if (gpt_crc32(g_gpt_entries, ebytes) != ecrc) return GPT_BAD;
for (i = 0; i < nent && n < max; i++) {
const uint8_t *e = g_gpt_entries + (size_t)i * esize;
static const uint8_t zero[16];
uint64_t first = le64(e + 32), last = le64(e + 40);
if (memcmp(e, zero, 16) == 0) continue; /* an unused entry */
if (first < first_usable || last > last_usable || first > last) return GPT_BAD;
memcpy(parts[n].type, e, 16);
parts[n].first_lba = first;
parts[n].last_lba = last;
n++;
}
*count = n;
return GPT_OK;
}
int gpt_find(const gpt_part_t *parts, unsigned count, const uint8_t type[16])
{
unsigned i;
for (i = 0; i < count; i++) if (memcmp(parts[i].type, type, 16) == 0) return (int)i;
return -1;
}
/* ---- a partition as a blkio device ------------------------------------- */
static int part_open(blkio_dev_t *dev, const blkio_params_t *p)
{
gpt_part_dev_t *st = (gpt_part_dev_t *)p->opaque;
dev->state = st;
dev->forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
dev->total_blocks = st->fblocks;
return BLKIO_OK;
}
static int part_close(blkio_dev_t *dev) { (void)dev; return BLKIO_OK; }
static int part_read(blkio_dev_t *dev, uint32_t fblock, void *dst)
{
gpt_part_dev_t *st = (gpt_part_dev_t *)dev->state;
if (fblock >= st->fblocks) return BLKIO_EINVAL;
return blkio_read(st->disk, st->first_fblock + fblock, dst);
}
static int part_write(blkio_dev_t *dev, uint32_t fblock, const void *src)
{
gpt_part_dev_t *st = (gpt_part_dev_t *)dev->state;
if (fblock >= st->fblocks) return BLKIO_EINVAL;
return blkio_write(st->disk, st->first_fblock + fblock, src);
}
static int part_flush(blkio_dev_t *dev)
{
gpt_part_dev_t *st = (gpt_part_dev_t *)dev->state;
return st->disk->vt->flush(st->disk);
}
static int part_info(blkio_dev_t *dev, blkio_info_t *out)
{
gpt_part_dev_t *st = (gpt_part_dev_t *)dev->state;
int rc = st->disk->vt->info(st->disk, out);
if (rc != BLKIO_OK) return rc;
out->total_blocks = st->fblocks;
out->phys_size_bytes = (uint64_t)st->fblocks * BLKIO_FORTH_BLOCK_SIZE;
return BLKIO_OK;
}
static const blkio_vtable_t g_part_vt = { part_open, part_close, part_read, part_write, part_flush, part_info };
int gpt_part_open(blkio_dev_t *dev, gpt_part_dev_t *st, blkio_dev_t *disk, const gpt_part_t *p)
{
blkio_params_t params;
uint64_t sectors;
if (!dev || !st || !disk || !p) return BLKIO_EINVAL;
sectors = p->last_lba - p->first_lba + 1u;
if ((p->first_lba % 2u) != 0u || (sectors % 2u) != 0u) return BLKIO_EINVAL;
if (p->first_lba / 2u + sectors / 2u > disk->total_blocks) return BLKIO_EINVAL;
st->disk = disk;
st->first_fblock = (uint32_t)(p->first_lba / 2u);
st->fblocks = (uint32_t)(sectors / 2u);
params.forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
params.total_blocks = st->fblocks;
params.opaque = st;
return blkio_open(dev, &g_part_vt, &params);
}
+53 -9
View File
@@ -24,6 +24,7 @@
#include "starkernel/homeblocks_sig.h"
#include "starkernel/capsule_zuse_boot.h"
#include "starkernel/kbd_input.h"
#include "starkernel/gpt.h"
#ifdef ARCH_AMD64
#include "starkernel/ioapic.h"
#include "starkernel/i8042.h"
@@ -43,12 +44,26 @@ static uint8_t g_zuse_root_pubkey[32];
static int g_zuse_root_known;
static struct blkio_dev *g_zuse_dev;
/* A blkio_dev per slot, per xHCI controller, as repl.c's g_usb_ctrl. */
/* A drive in a USB slot: the raw disk; its StarshipOS identity partition as
* a device of its own, when it has a GPT with one; and the one of the two
* given to the block subsystem, if either was (a GPT disk is never given
* whole: docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md, ruled). */
typedef struct {
blkio_dev_t raw;
blkio_dev_t part;
gpt_part_dev_t part_state;
blkio_dev_t *used;
} sk_v4_drive_t;
/* One per slot, per xHCI controller, as repl.c's g_usb_ctrl. */
static struct {
blkio_dev_t *slots;
uint32_t count;
sk_v4_drive_t *slots;
uint32_t count;
} g_usb[XHCI_MAX_CONTROLLERS];
#define SK_V4_GPT_PARTS 16u
static gpt_part_t g_gpt_parts[SK_V4_GPT_PARTS];
static uint64_t g_last_beat;
static int g_said_no_genesis;
@@ -93,17 +108,42 @@ void sk_v4_identity_boot(void)
/* A drive that has arrived: given to the block subsystem (Artemis's, the
* kernel's), then, if it is Zuse's, her session begins. */
static int sk_v4_drive_arrived(blkio_dev_t *dev)
static int sk_v4_drive_arrived(sk_v4_drive_t *d)
{
blkio_dev_t *dev = &d->raw;
homeblocks_sig_t sig;
homeblocks_sig_result_t sig_rc = homeblocks_sig_check(dev, HOMEBLOCKS_SIG_START_FBLOCK, &sig);
homeblocks_sig_result_t sig_rc;
uint8_t seed[32], pubkey[32];
zuse_drive_t which;
unsigned nparts;
int gpt_rc, idx;
/* GPT first (docs/v4.0.0/STICK.md 4): a disk with a valid GPT is used
* through its StarshipOS identity partition only; one with no such
* partition, or a damaged table, is not used at all; a disk with no GPT
* is read raw, as an identity drive always has been. */
d->used = (blkio_dev_t *)0;
gpt_rc = gpt_read(&d->raw, g_gpt_parts, SK_V4_GPT_PARTS, &nparts);
if (gpt_rc == GPT_BAD) {
console_println("GPT: a USB drive has a damaged partition table -- not used");
return 1;
}
if (gpt_rc == GPT_OK) {
idx = gpt_find(g_gpt_parts, nparts, gpt_type_starship_identity);
if (idx < 0) return 1; /* no StarshipOS partition: not ours to use */
if (gpt_part_open(&d->part, &d->part_state, &d->raw, &g_gpt_parts[idx]) != BLKIO_OK) {
console_println("GPT: a USB drive's identity partition is not on 1 KiB blocks -- not used");
return 1;
}
dev = &d->part;
}
sig_rc = homeblocks_sig_check(dev, HOMEBLOCKS_SIG_START_FBLOCK, &sig);
if (blk_subsys_attach_device(dev) != BLK_OK) {
console_println("xhci: USB MSC block-subsystem attach failed");
return 0;
}
d->used = dev;
if (g_zuse_session) return 1;
which = capsule_zuse_boot_check(dev, sig_rc, &sig, seed, pubkey);
@@ -134,8 +174,12 @@ static int sk_v4_drive_arrived(blkio_dev_t *dev)
return 1;
}
static void sk_v4_drive_left(blkio_dev_t *dev)
static void sk_v4_drive_left(sk_v4_drive_t *d)
{
blkio_dev_t *dev = d->used;
if (!dev) return; /* never given to the block subsystem */
d->used = (blkio_dev_t *)0;
blk_subsys_detach_device(dev);
if (dev != g_zuse_dev) return;
g_zuse_session = 0;
@@ -164,8 +208,8 @@ static void sk_v4_idle(void)
xhci_dev_t *xdev = xhci_dev_at(xc);
if (!xdev) continue;
if (!g_usb[xc].slots || g_usb[xc].count < xdev->max_slots + 1) {
size_t bytes = (size_t)(xdev->max_slots + 1) * sizeof(blkio_dev_t);
blkio_dev_t *fresh = (blkio_dev_t *)kmalloc_aligned(bytes, 64);
size_t bytes = (size_t)(xdev->max_slots + 1) * sizeof(sk_v4_drive_t);
sk_v4_drive_t *fresh = (sk_v4_drive_t *)kmalloc_aligned(bytes, 64);
if (!fresh) continue;
memset(fresh, 0, bytes);
g_usb[xc].slots = fresh;
@@ -175,7 +219,7 @@ static void sk_v4_idle(void)
xhci_msc_slot_t *ms = xhci_msc_slot_for(xdev, slot);
if (!ms || !ms->bot_msc_attach_pending) continue;
ms->bot_msc_attach_pending = 0;
if (blkio_usb_open_msc(&g_usb[xc].slots[slot], xdev, slot) == 0 &&
if (blkio_usb_open_msc(&g_usb[xc].slots[slot].raw, xdev, slot) == 0 &&
sk_v4_drive_arrived(&g_usb[xc].slots[slot]))
ms->bot_msc_attached = 1;
}