feat(boot): the loader tells the kernel its boot disk; system=v3 is refused on a stick

uefi.h: the UEFI device path and Block I/O protocols, a typed LocateDevicePath, and BootDisk
{gpt, is_stick, disk_guid[16]} appended to BootInfo after args (boot_info_offsets.h unchanged).
The loader, before ExitBootServices, cuts its image's device path at the partition's HardDrive
node, locates the whole disk's Block I/O (whole path consumed, not a logical partition, 512-byte
blocks), reads it as 1 KiB blocks through a loader-side blkio adapter, and runs the kernel's own
gpt_read: is it a StarshipOS stick (identity and Artemis partitions), and its DiskGUID
(gpt_disk_guid, new). No HardDrive node (an ISO/CD boot), no GPT, or any failure: not a stick.
Monolithic builds only (the non-monolithic loader does not link gpt.c, and does not build today:
kernel/src/vm/vm_core.c -- reported, not fixed).

The kernel prints 'Boot disk: a StarshipOS stick' or '... not a StarshipOS stick', and on a stick
system=v3 gives 'System: v3 is not on this stick -- StarForth v4' and boots v4.

Tests: stick_v3 (new), stick_boot and zuse x3 failed on the new lines before, pass after; v3_keys
still boots v3 off a stick. Acceptance: amd64, aarch64, riscv64 to [zuse@Hera] ok>, POST 538/538,
dict_hash 0xfec8c47cfc6f437f; v4 suite and test-32 0 failures.

Plan: docs/v4.0.0/plans/2026-10-27-stick-4-one-disk.md, Task 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-28 12:08:48 -04:00
co-authored by Claude Opus 5.5
parent a321c537f7
commit 1b45f5f85f
34 changed files with 20202 additions and 5 deletions
+102
View File
@@ -55,6 +55,9 @@
#include "starkernel/boot_info_offsets.h"
#include "starkernel/fdt.h"
#include "starkernel/fdt_uart.h"
#ifdef MONOLITHIC_BUILD
#include "starkernel/gpt.h"
#endif
#include <stddef.h>
/* Verify BootInfo field offsets match boot_info_offsets.h (BOOT_INFO_OFFSETS) */
@@ -508,6 +511,100 @@ static int load_cfg_from_esp(EFI_HANDLE ImageHandle, EFI_BOOT_SERVICES *BS,
return read_size > 0;
}
#ifdef MONOLITHIC_BUILD
/* The boot disk (docs/v4.0.0/plans/2026-10-27-stick-4-one-disk.md, Task 3).
* The firmware's Block I/O for the whole disk the loader was read from,
* seen as a 1 KiB-block device (block n = LBAs 2n and 2n+1), so the kernel's
* own gpt.c reads its GPT: the kernel is then told whether it booted from a
* StarshipOS stick, and which one (the header's DiskGUID). Reads only, before
* ExitBootServices. Monolithic builds only: the non-monolithic loader does
* not link gpt.c (and does not build, 2026-10-28: kernel/src/vm/vm_core.c). */
static EFI_BLOCK_IO_PROTOCOL *g_boot_bio;
static uint8_t g_boot_bounce[BLKIO_FORTH_BLOCK_SIZE] __attribute__((aligned(4096)));
static int bootdisk_open(blkio_dev_t *dev, const blkio_params_t *p) { (void)dev; (void)p; return BLKIO_OK; }
static int bootdisk_close(blkio_dev_t *dev) { (void)dev; return BLKIO_OK; }
static int bootdisk_read(blkio_dev_t *dev, uint32_t fblock, void *dst)
{
uint8_t *d = (uint8_t *)dst;
unsigned i;
(void)dev;
if (g_boot_bio->ReadBlocks(g_boot_bio, g_boot_bio->Media->MediaId, (UINT64)fblock * 2u,
BLKIO_FORTH_BLOCK_SIZE, g_boot_bounce) != EFI_SUCCESS)
return BLKIO_EIO;
for (i = 0; i < BLKIO_FORTH_BLOCK_SIZE; i++) d[i] = g_boot_bounce[i];
return BLKIO_OK;
}
static int bootdisk_write(blkio_dev_t *dev, uint32_t fblock, const void *src) { (void)dev; (void)fblock; (void)src; return BLKIO_ENOSUP; }
static int bootdisk_flush(blkio_dev_t *dev) { (void)dev; return BLKIO_OK; }
static int bootdisk_info(blkio_dev_t *dev, blkio_info_t *out)
{
out->forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
out->total_blocks = dev->total_blocks;
out->phys_sector_size = 512u;
out->phys_size_bytes = (uint64_t)dev->total_blocks * BLKIO_FORTH_BLOCK_SIZE;
out->read_only = 1u;
return BLKIO_OK;
}
static const blkio_vtable_t g_bootdisk_vt = {
bootdisk_open, bootdisk_close, bootdisk_read, bootdisk_write, bootdisk_flush, bootdisk_info
};
static void probe_boot_disk(EFI_HANDLE ImageHandle, EFI_BOOT_SERVICES *BS, BootDisk *out)
{
static uint8_t path[512]; /* the boot device's path, up to its HardDrive node */
EFI_LOADED_IMAGE_PROTOCOL *li = NULL;
EFI_DEVICE_PATH_PROTOCOL *dp = NULL, *rest;
EFI_HANDLE disk = NULL;
blkio_dev_t dev;
blkio_params_t prm;
gpt_part_t parts[16];
unsigned n = 0, used = 0, len, i;
const uint8_t *node;
UINT64 blocks;
for (i = 0; i < sizeof *out; i++) ((uint8_t *)out)[i] = 0;
if (BS->HandleProtocol(ImageHandle, (EFI_GUID *)&EFI_LOADED_IMAGE_PROTOCOL_GUID, (void **)&li) != EFI_SUCCESS || !li)
return;
if (BS->HandleProtocol(li->DeviceHandle, (EFI_GUID *)&EFI_DEVICE_PATH_PROTOCOL_GUID, (void **)&dp) != EFI_SUCCESS || !dp)
return;
/* copy the path up to (not including) its first Media/HardDrive node --
* the partition -- and end it there: what is left names the whole disk.
* No HardDrive node (a CD/ISO boot): not a disk with a GPT to read. */
for (node = (const uint8_t *)dp; ; node += len) {
len = (unsigned)node[2] | (unsigned)node[3] << 8;
if (node[0] == EFI_DEVICE_PATH_TYPE_END || len < 4u) return;
if (node[0] == EFI_DEVICE_PATH_TYPE_MEDIA && node[1] == EFI_DEVICE_PATH_SUBTYPE_HARDDRIVE) break;
if (used + len + 4u > sizeof path) return;
for (i = 0; i < len; i++) path[used + i] = node[i];
used += len;
}
path[used] = EFI_DEVICE_PATH_TYPE_END; path[used + 1] = EFI_DEVICE_PATH_SUBTYPE_END_ENTIRE;
path[used + 2] = 4; path[used + 3] = 0;
rest = (EFI_DEVICE_PATH_PROTOCOL *)path;
if (BS->LocateDevicePath((EFI_GUID *)&EFI_BLOCK_IO_PROTOCOL_GUID, &rest, &disk) != EFI_SUCCESS || !disk)
return;
if (rest->Type != EFI_DEVICE_PATH_TYPE_END) return; /* a handle for less than the whole path: not the disk */
if (BS->HandleProtocol(disk, (EFI_GUID *)&EFI_BLOCK_IO_PROTOCOL_GUID, (void **)&g_boot_bio) != EFI_SUCCESS ||
!g_boot_bio || !g_boot_bio->Media)
return;
if (g_boot_bio->Media->LogicalPartition || !g_boot_bio->Media->MediaPresent ||
g_boot_bio->Media->BlockSize != 512u || g_boot_bio->Media->IoAlign > 4096u)
return;
blocks = (g_boot_bio->Media->LastBlock + 1u) / 2u;
prm.forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
prm.total_blocks = blocks > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)blocks;
prm.opaque = NULL;
if (blkio_open(&dev, &g_bootdisk_vt, &prm) != BLKIO_OK) return;
if (gpt_read(&dev, parts, 16u, &n) != GPT_OK) return;
out->gpt = 1;
gpt_disk_guid(out->disk_guid);
out->is_stick = gpt_find(parts, n, gpt_type_starship_identity) >= 0 &&
gpt_find(parts, n, gpt_type_starship_artemis) >= 0;
}
#endif
#ifndef MONOLITHIC_BUILD
/* Kernel entry point signature */
typedef void (*KernelEntry)(BootInfo *boot_info);
@@ -866,6 +963,11 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
}
debug_checkpoint(SystemTable, 5, L"Kernel stack allocation decided");
#ifdef MONOLITHIC_BUILD
/* The boot disk, while the firmware's Block I/O is still there */
probe_boot_disk(ImageHandle, BS, &g_boot_info.boot_disk);
#endif
/* Fill BootInfo fields that do NOT require EBS first */
g_boot_info.runtime_services = SystemTable->RuntimeServices;
g_boot_info.acpi_table = NULL;
+7
View File
@@ -50,6 +50,7 @@ static uint64_t le64(const uint8_t *p) { return (uint64_t)le32(p) | (uint64_t)le
static uint8_t g_gpt_hdr_block[BLKIO_FORTH_BLOCK_SIZE];
static uint8_t g_gpt_entries[GPT_ENTRIES_MAX];
static uint8_t g_gpt_disk_guid[16]; /* the DiskGUID of the last gpt_read that gave GPT_OK */
int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count)
{
@@ -123,9 +124,15 @@ int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count
n++;
}
*count = n;
memcpy(g_gpt_disk_guid, h + 56, 16);
return GPT_OK;
}
void gpt_disk_guid(uint8_t guid[16])
{
memcpy(guid, g_gpt_disk_guid, 16);
}
int gpt_find(const gpt_part_t *parts, unsigned count, const uint8_t type[16])
{
unsigned i;
+12 -2
View File
@@ -622,10 +622,19 @@ static void kernel_main_deep(BootInfo *boot_info) {
* arguments (starforth.cfg on the ESP, cmdline.txt on the Pi 5) picks,
* else the build's default (SK_V4_DEFAULT: on -- v4 -- for every build,
* QEMU and board images alike, since 2026-10-14: v3 is being retired). */
/* The disk the loader was booted from (uefi_loader.c's probe): a
* StarshipOS stick carries v4 only (docs/v4.0.0/STICK.md 1, "v4 only"),
* so system=v3 is refused on one (plan 2026-10-27-stick-4-one-disk.md,
* Task 3). An ISO, CD or ESP-image boot is not a stick. */
console_println(boot_info->boot_disk.is_stick ? "Boot disk: a StarshipOS stick"
: "Boot disk: not a StarshipOS stick");
#ifdef STARFORTH_V4
if (boot_info->args.system == KARGS_SYSTEM_V4 ||
{
const int v3_refused = boot_info->boot_disk.is_stick && boot_info->args.system == KARGS_SYSTEM_V3;
if (boot_info->args.system == KARGS_SYSTEM_V4 || v3_refused ||
(boot_info->args.system == KARGS_SYSTEM_DEFAULT && SK_V4_DEFAULT)) {
console_println(boot_info->args.system == KARGS_SYSTEM_V4
console_println(v3_refused ? "System: v3 is not on this stick -- StarForth v4"
: boot_info->args.system == KARGS_SYSTEM_V4
? "System: StarForth v4 (system=v4)"
: "System: StarForth v4 (this build's default)");
/* StarForth v4 at a single prompt: one host node of the F18-derived
@@ -634,6 +643,7 @@ static void kernel_main_deep(BootInfo *boot_info) {
sk_v4_boot_info = boot_info;
sk_v4_run(sk_v4_block_chain);
}
}
console_println(boot_info->args.system == KARGS_SYSTEM_V3
? "System: StarForth v3 (system=v3)"
: "System: StarForth v3 (this build's default; system=v4 for v4)");