diff --git a/docs/v4.0.0/STICK.md b/docs/v4.0.0/STICK.md index 867924da..f3edfd16 100644 --- a/docs/v4.0.0/STICK.md +++ b/docs/v4.0.0/STICK.md @@ -122,9 +122,13 @@ gains the partitions below). Three partitions: - **The minimum stick: 8 GB.** Sticks on the shelf are 16–64 GB; 8 GB is the floor. - **The identity partition stays 500 MiB.** About half a million 1 KiB blocks for Zuse's keys and her own pool. - - **Artemis grows to fill the stick at its first boot (§5a),** up to **2 TiB** for the partition - (the block layer numbers 1 KiB blocks with 32 bits: 4 TiB is its hard limit; 2 TiB keeps a margin - and is past any thumb drive). Space past 2 TiB is left unused, and the boot says so. + - **Artemis grows to fill the stick at its first boot (§5a),** up to **256 GiB** for the partition + (**amended 2026-10-29**, Captain Bob: "256 GiB is ruled" -- plan + `plans/2026-10-29-stick-5-proxy-zuse-and-real-zuse.md`, R1. This said 2 TiB: the block layer + numbers 1 KiB blocks with 32 bits, so 4 TiB is its hard limit, but the block subsystem keeps two + bytes of kernel heap per block and the heap is 2 GiB, so a 2 TiB Artemis could not attach; 256 GiB + takes about 384 MiB. The stick's Artemis is formatted with a BAM sized for 256 GiB, so growth + writes metadata only.) Space past 256 GiB is left unused, and the boot says so. - **A stick under 8 GB** is not grown: it runs at the 2 GiB it was written with, and the boot says so. - **The Artemis partition is formatted when the image is built (my call),** not on first boot: @@ -168,7 +172,7 @@ Before genesis, on a stick whose GPT ends where the 2 GiB image ended and whose first boot of a freshly written stick), the kernel grows Artemis: - It moves the backup GPT header and entry array to the disk's end, extends the Artemis partition's - last LBA to the end of the usable space (capped at 2 TiB of partition), and rewrites both headers + last LBA to the end of the usable space (capped at 256 GiB of partition, amended -- §4), and rewrites both headers with their CRCs (the GPT writer; `INSTALL` reuses it). - It grows Artemis's volume to the partition: its `STFR` header's size, its free map for the new blocks, and its metadata fence (with the genesis marker's slot) moved to the new top, as the block diff --git a/experiments/bare_metal/runs/doe-amd64-20261108-115815.csv b/experiments/bare_metal/runs/doe-amd64-20261108-115815.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261108-120001.csv b/experiments/bare_metal/runs/doe-amd64-20261108-120001.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261108-120143.csv b/experiments/bare_metal/runs/doe-amd64-20261108-120143.csv new file mode 100644 index 00000000..e69de29b diff --git a/kernel/Makefile b/kernel/Makefile index 98c73223..6a6c0c0a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -270,6 +270,14 @@ STICK_IMAGE_MIB := 2048 STICK_ESP_MIB := 64 STICK_IDENT_MIB := 500 DATASTICK_MIB := 1536 +# Growth (STICK.md 5a; plan 2026-10-29-stick-5, R1: "256 GiB is ruled"): a stick of at least +# STICK_GROW_MIN_BYTES grows its Artemis partition to fill it, at most STICK_ARTEMIS_CAP_GIB; the +# stick's and data stick's Artemis are formatted with a BAM sized for that cap, so growth writes +# metadata only. The kernel is built with both (-D, below). +STICK_GROW_MIN_BYTES := 8000000000 +STICK_ARTEMIS_CAP_GIB := 256 +STICK_ARTEMIS_CAP_SECTORS := 536870912 +# (256 GiB in 512-byte sectors: keep the two in step) DATASTICK_IMG = $(BUILD_DIR)/datastick.img # STICK_PAIR -- a minted pair (e.g. disk/zuse-scratch) whose identity and Artemis images become # the PC stick's partitions, byte for byte, instead of a blank identity and a fresh Artemis. @@ -706,7 +714,8 @@ endif V4_DEFAULT ?= 1 ifeq ($(STARFORTH_V4),1) V4_DEFS := -DSTARFORTH_V4=1 -DSK_V4_DEFAULT=$(V4_DEFAULT) -Iv4/include \ - -DV4_CELL_BITS=64 -DV4_NODE_WORDS=16384 -DV4_DATA_RING=30 -DV4_RET_RING=31 -DV4_ROM_WORDS=4096 + -DV4_CELL_BITS=64 -DV4_NODE_WORDS=16384 -DV4_DATA_RING=30 -DV4_RET_RING=31 -DV4_ROM_WORDS=4096 \ + -DSTICK_GROW_MIN_BYTES=$(STICK_GROW_MIN_BYTES)ULL -DSTICK_ARTEMIS_CAP_GIB=$(STICK_ARTEMIS_CAP_GIB)ULL KERNEL_CFLAGS += $(V4_DEFS) LOADER_CFLAGS += $(V4_DEFS) @@ -714,8 +723,8 @@ V4_ENGINE_SRCS := $(addprefix v4/src/,node.c exec.c stack.c iword.c heat.c guard V4_ROM_C := $(BUILD_DIR)/v4_rom_64.c V4_MAP_C := $(BUILD_DIR)/v4_map_64.c -LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c $(KERNEL_SRC)/v4/sk_v4_identity.c $(KERNEL_SRC)/v4/sk_v4_marker.c -KERNEL_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c $(KERNEL_SRC)/v4/sk_v4_identity.c $(KERNEL_SRC)/v4/sk_v4_marker.c +LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c $(KERNEL_SRC)/v4/sk_v4_identity.c $(KERNEL_SRC)/v4/sk_v4_marker.c $(KERNEL_SRC)/v4/sk_v4_grow.c +KERNEL_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c $(KERNEL_SRC)/v4/sk_v4_identity.c $(KERNEL_SRC)/v4/sk_v4_marker.c $(KERNEL_SRC)/v4/sk_v4_grow.c # v4/system/boot.c is the boot the hosted v4 system runs too: the ROM # assembles the system, then the capsules from this kernel's own capsule # directory, then the prompt. @@ -816,7 +825,8 @@ $(MKARTEMIS_BIN): $(MKARTEMIS_SRC) $(wildcard v3/include/*.h) kernel/include/sta .PHONY: datastick datastick: $(MKARTEMIS_BIN) @bash scripts/mkdiskimage.sh --out $(DATASTICK_IMG) --table gpt --size-mib $(DATASTICK_MIB) \ - --label DATASTICK --starship $(STICK_IDENT_MIB) --mkartemis $(MKARTEMIS_BIN) --esp-mib 0 + --label DATASTICK --starship $(STICK_IDENT_MIB) --mkartemis $(MKARTEMIS_BIN) --esp-mib 0 \ + --bam-for $(STICK_ARTEMIS_CAP_SECTORS) # zusestick -- Zuse's minted pair as a stick, fresh every time it is made (the ZUSEDISK default). .PHONY: zusestick @@ -1493,7 +1503,8 @@ boot_image: all $(if $(BOARD_STICK),$(MKARTEMIS_BIN)) @bash scripts/mkdiskimage.sh --out $(BOARD_OUT)/$(BOARD_IMAGE) --table gpt \ $(if $(BOARD_STICK),$(if $(STICK_PAIR),--esp-mib $(STICK_ESP_MIB) --starship-from \ $(STICK_PAIR)/zuse-thumb-ident.img $(STICK_PAIR)/artemis.img,--size-mib $(STICK_IMAGE_MIB) \ - --starship $(STICK_IDENT_MIB) --mkartemis $(MKARTEMIS_BIN) --esp-mib $(STICK_ESP_MIB)), \ + --starship $(STICK_IDENT_MIB) --mkartemis $(MKARTEMIS_BIN) --esp-mib $(STICK_ESP_MIB) \ + --bam-for $(STICK_ARTEMIS_CAP_SECTORS)), \ --size-mib $(BOOT_IMAGE_SIZE_MIB)) \ --label LITHOS \ $(LOADER_EFI):/EFI/BOOT/$(EFI_BOOTNAME) \ diff --git a/kernel/include/starkernel/gpt.h b/kernel/include/starkernel/gpt.h index cad5da82..05b413ed 100644 --- a/kernel/include/starkernel/gpt.h +++ b/kernel/include/starkernel/gpt.h @@ -33,6 +33,22 @@ int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count * returned GPT_OK: which disk it was, so a stick can be told from another. */ void gpt_disk_guid(uint8_t guid[16]); +/* The backup header's LBA (header bytes 32..39) of the last gpt_read that + * returned GPT_OK: a disk whose backup header is not its last sector is larger + * than the table written to it. */ +uint64_t gpt_alternate_lba(void); + +/* Grow the partition of `type` -- which must be the disk's last -- to the end + * of `disk` (plan 2026-10-29-stick-5, Task 2): the backup entry array and + * header moved to the disk's last sectors, the partition's last LBA to the + * last usable sector before them (on a whole MiB, at most `max_part_sectors` + * long), both headers rewritten with their CRCs -- the backup first, so a + * write torn between them leaves the old primary valid -- and the protective + * MBR's size. GPT_OK with `*new_last_lba` the partition's last LBA (unchanged, + * and nothing written, when the backup header is already the disk's last + * sector); GPT_BAD or GPT_EIO otherwise. */ +int gpt_grow_last(blkio_dev_t *disk, const uint8_t type[16], uint64_t max_part_sectors, uint64_t *new_last_lba); + /* 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]); diff --git a/kernel/include/starkernel/v4/sk_v4_grow.h b/kernel/include/starkernel/v4/sk_v4_grow.h new file mode 100644 index 00000000..365ff63c --- /dev/null +++ b/kernel/include/starkernel/v4/sk_v4_grow.h @@ -0,0 +1,23 @@ +/* sk_v4_grow.h -- Artemis grows to fill the stick at its first boot + * (docs/v4.0.0/STICK.md 5a; plan 2026-10-29-stick-5, Task 2). + * + * Built only with STARFORTH_V4=1 (Kconfig.kernel). + */ +#ifndef STARKERNEL_V4_SK_V4_GROW_H +#define STARKERNEL_V4_SK_V4_GROW_H + +#include +#include "blkio.h" + +/* How far the Artemis partition `art` may grow, in 512-byte sectors: no + * further than `cap_sectors`, nor than its volume's BAM can track. 0 when + * `art` holds no STFR v2 volume. Reads only. */ +uint64_t sk_v4_grow_cap_sectors(blkio_dev_t *art, uint64_t cap_sectors); + +/* Grow the volume on `art` to fill its partition: its fence (the Artemis + * signature, Zuse's genesis marker) moved to the new top, then its header. + * Metadata only. 1 when grown, 0 when it already fills the partition, -1 on + * an error -- the old header then still names the old volume, whole. */ +int sk_v4_grow_artemis(blkio_dev_t *art); + +#endif /* STARKERNEL_V4_SK_V4_GROW_H */ diff --git a/kernel/src/gpt.c b/kernel/src/gpt.c index 2f4df899..88700e5c 100644 --- a/kernel/src/gpt.c +++ b/kernel/src/gpt.c @@ -7,8 +7,13 @@ * 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. + * drive today. 512-byte logical sectors only (the stick and its data stick); + * docs/v4.0.0/plans/2026-10-26-stick-2-gpt-reader.md. + * + * The writer (plan 2026-10-29-stick-5, Task 2; STICK.md 5a): the stick's last + * partition grown to the end of a disk larger than the image written to it -- + * the backup header and entry array moved to the disk's end, both headers + * rewritten with their CRCs, the backup before the primary. */ #include "starkernel/gpt.h" #include "console.h" @@ -51,6 +56,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 */ +static uint64_t g_gpt_alternate_lba; /* and its backup header's LBA */ int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count) { @@ -125,6 +131,7 @@ int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count } *count = n; memcpy(g_gpt_disk_guid, h + 56, 16); + g_gpt_alternate_lba = le64(h + 32); return GPT_OK; } @@ -140,6 +147,122 @@ int gpt_find(const gpt_part_t *parts, unsigned count, const uint8_t type[16]) return -1; } +/* ---- the writer: growing the last partition to the disk's end ---------- */ + +static void put_le32(uint8_t *p, uint32_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); } +static void put_le64(uint8_t *p, uint64_t v) { put_le32(p, (uint32_t)v); put_le32(p + 4, (uint32_t)(v >> 32)); } + +static uint8_t g_gpt_rmw[BLKIO_FORTH_BLOCK_SIZE]; + +/* Write `n` 512-byte sectors from `src` at sector `lba`: each through a + * read-modify-write of the 1 KiB block that holds it, so a sector at an odd + * LBA (the backup header, the backup entry array) is written alone. */ +static int put_sectors(blkio_dev_t *disk, uint64_t lba, const uint8_t *src, uint32_t n) +{ + uint32_t i; + for (i = 0; i < n; i++) { + uint64_t s = lba + i; + uint32_t blk = (uint32_t)(s / 2u); + if (blkio_read(disk, blk, g_gpt_rmw) != BLKIO_OK) return -1; + memcpy(g_gpt_rmw + (s % 2u) * GPT_SECTOR, src + (size_t)i * GPT_SECTOR, GPT_SECTOR); + if (blkio_write(disk, blk, g_gpt_rmw) != BLKIO_OK) return -1; + } + return 0; +} + +uint64_t gpt_alternate_lba(void) +{ + return g_gpt_alternate_lba; +} + +int gpt_grow_last(blkio_dev_t *disk, const uint8_t type[16], uint64_t max_part_sectors, uint64_t *new_last_lba) +{ + static gpt_part_t parts[16]; + static uint8_t hdr[GPT_SECTOR], bak[GPT_SECTOR]; + const uint8_t *h = g_gpt_hdr_block + GPT_SECTOR; + uint8_t *mbr = g_gpt_hdr_block, *e = (uint8_t *)0; + uint64_t disk_sectors, alt, ent_lba, new_alt, new_ent, new_last_usable, first, last, want, top = 0; + uint32_t nent, esize, ent_sectors, i; + unsigned count; + size_t ebytes; + int rc; + + rc = gpt_read(disk, parts, 16u, &count); + if (rc != GPT_OK) return rc == GPT_NONE ? GPT_BAD : rc; + nent = le32(h + 80); + esize = le32(h + 84); + ebytes = (size_t)nent * esize; + ent_sectors = (uint32_t)((ebytes + GPT_SECTOR - 1u) / GPT_SECTOR); + ent_lba = le64(h + 72); + alt = le64(h + 32); + disk_sectors = (uint64_t)disk->total_blocks * (BLKIO_FORTH_BLOCK_SIZE / GPT_SECTOR); + + /* the partition of `type`, which must be the last on the disk */ + for (i = 0; i < nent; i++) { + uint8_t *x = g_gpt_entries + (size_t)i * esize; + static const uint8_t zero[16]; + if (memcmp(x, zero, 16) == 0) continue; + if (le64(x + 40) > top) top = le64(x + 40); + if (!e && memcmp(x, type, 16) == 0) e = x; + } + if (!e) return GPT_BAD; + first = le64(e + 32); + last = le64(e + 40); + *new_last_lba = last; + if (alt == disk_sectors - 1u) return GPT_OK; /* already ends at the disk's end */ + if (alt > disk_sectors - 1u || last != top) return GPT_BAD; + + /* the backup entry array and header at the disk's end; the partition to + * the last usable sector before them, on whole MiB, at most max sectors */ + new_alt = disk_sectors - 1u; + new_ent = new_alt - ent_sectors; + new_last_usable = new_ent - 1u; + if (new_last_usable < last) return GPT_BAD; + want = new_last_usable - first + 1u; + if (want > max_part_sectors) want = max_part_sectors; + want -= want % 2048u; + if (first + want - 1u > last) { + last = first + want - 1u; + put_le64(e + 40, last); + } + + /* the primary header, then the backup: each with its own LBAs and CRC */ + memcpy(hdr, h, GPT_SECTOR); + put_le64(hdr + 32, new_alt); + put_le64(hdr + 48, new_last_usable); + put_le32(hdr + 88, gpt_crc32(g_gpt_entries, ebytes)); + memcpy(bak, hdr, GPT_SECTOR); + put_le64(bak + 24, new_alt); + put_le64(bak + 32, 1u); + put_le64(bak + 72, new_ent); + put_le32(hdr + 16, 0u); + put_le32(hdr + 16, gpt_crc32(hdr, le32(hdr + 12))); + put_le32(bak + 16, 0u); + put_le32(bak + 16, gpt_crc32(bak, le32(bak + 12))); + + /* the backup before the primary: a write torn between them leaves the old + * primary, which still checks out, and the next boot grows again */ + if (put_sectors(disk, new_ent, g_gpt_entries, ent_sectors) != 0 || + put_sectors(disk, new_alt, bak, 1u) != 0 || disk->vt->flush(disk) != BLKIO_OK) + return GPT_EIO; + if (put_sectors(disk, ent_lba, g_gpt_entries, ent_sectors) != 0 || + put_sectors(disk, 1u, hdr, 1u) != 0 || disk->vt->flush(disk) != BLKIO_OK) + return GPT_EIO; + + /* the protective MBR's partition covers the disk (as many sectors as fit) */ + for (i = 0; i < 4u; i++) { + uint8_t *p = mbr + 446u + 16u * i; + if (p[4] == 0xEEu) { + put_le32(p + 12, disk_sectors - 1u > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)(disk_sectors - 1u)); + break; + } + } + if (put_sectors(disk, 0u, mbr, 1u) != 0 || disk->vt->flush(disk) != BLKIO_OK) return GPT_EIO; + + *new_last_lba = last; + return GPT_OK; +} + /* ---- a partition as a blkio device ------------------------------------- */ static int part_open(blkio_dev_t *dev, const blkio_params_t *p) diff --git a/kernel/src/v4/sk_v4_grow.c b/kernel/src/v4/sk_v4_grow.c new file mode 100644 index 00000000..4f25ec4a --- /dev/null +++ b/kernel/src/v4/sk_v4_grow.c @@ -0,0 +1,94 @@ +/* sk_v4_grow.c -- Artemis grows to fill the stick at its first boot + * (docs/v4.0.0/STICK.md 5a; plan 2026-10-29-stick-5, Task 2). + * + * Metadata only: the volume's header, and its fence -- the top-of-volume + * system metadata, which holds the Artemis signature and Zuse's genesis + * marker (artemis_sig.c, blk_meta_zone_read/write) -- moved to the new top. + * The volume's data blocks are never written, and its BAM is not resized: the + * image is built with a BAM sized for the cap (mkartemis --bam-for), so the + * new blocks are tracked already and read as free. + * + * The block subsystem places a volume's fence by its device's size, not by + * its header (blk_format_or_load_disk), so a volume always fills its + * partition: the partition is grown no further than the BAM can track + * (sk_v4_grow_cap_sectors), then the volume to the partition. + */ +#include "starkernel/v4/sk_v4_grow.h" +#include "block_subsystem.h" +#include + +static union { + uint8_t bytes[4096]; + blk_volume_meta_t meta; +} g_hdr; +static uint8_t g_move[4096]; + +static int dev_rw4k(blkio_dev_t *dev, uint64_t devblock, uint8_t *buf, int write) +{ + uint32_t base = (uint32_t)devblock * 4u, k; + for (k = 0; k < 4u; k++) { + int rc = write ? blkio_write(dev, base + k, buf + k * 1024u) + : blkio_read(dev, base + k, buf + k * 1024u); + if (rc != BLKIO_OK) return -1; + } + return 0; +} + +static int read_header(blkio_dev_t *art) +{ + if (dev_rw4k(art, 0u, g_hdr.bytes, 0) != 0) return -1; + if (g_hdr.meta.magic != 0x53544652u || g_hdr.meta.version != 2u || + g_hdr.meta.bam_devblocks == 0u || g_hdr.meta.meta_fence_blocks == 0u) + return -1; + return 0; +} + +/* The most devblocks a volume with this header's BAM can be: no more Forth + * blocks in its payload than the BAM tracks (compute_totals_from_B). */ +static uint64_t bam_cover_devblocks(const blk_volume_meta_t *m) +{ + uint64_t tracked = 32768ULL * m->bam_devblocks; + return tracked / 3u + 1u + m->bam_devblocks + m->reloc_devblocks + m->meta_fence_blocks; +} + +uint64_t sk_v4_grow_cap_sectors(blkio_dev_t *art, uint64_t cap_sectors) +{ + uint64_t cover; + if (read_header(art) != 0) return 0; + cover = bam_cover_devblocks(&g_hdr.meta) * 8u; /* 4 KiB devblocks, in 512-byte sectors */ + return cover < cap_sectors ? cover : cap_sectors; +} + +int sk_v4_grow_artemis(blkio_dev_t *art) +{ + blkio_info_t info; + uint64_t old_t, new_t, f, k, old_total; + + if (read_header(art) != 0) return -1; + if (blkio_info(art, &info) != BLKIO_OK) return -1; + old_t = g_hdr.meta.total_devblocks; + new_t = info.total_blocks / 4u; + if (new_t <= old_t) return 0; /* not grown: it fills its partition */ + if (new_t > bam_cover_devblocks(&g_hdr.meta)) return -1; + f = g_hdr.meta.meta_fence_blocks; + /* the old and new fences never overlap (a partition grows by whole MiB, + * 256 devblocks, past a 128-devblock fence), so a copy torn by a power + * loss is simply done again from the old top at the next boot */ + if (f >= old_t || new_t - old_t < f) return -1; + + /* the fence to the new top, devblock by devblock */ + for (k = 0; k < f; k++) { + if (dev_rw4k(art, old_t - 1u - k, g_move, 0) != 0) return -1; + if (dev_rw4k(art, new_t - 1u - k, g_move, 1) != 0) return -1; + } + if (blkio_flush(art) != BLKIO_OK) return -1; + + /* the header last: until it is written, the old header names the old top */ + old_total = g_hdr.meta.total_blocks; + g_hdr.meta.total_devblocks = new_t; + blk_volume_compute_totals(&g_hdr.meta); + g_hdr.meta.free_blocks += g_hdr.meta.total_blocks - old_total; + if (dev_rw4k(art, 0u, g_hdr.bytes, 1) != 0) return -1; + if (blkio_flush(art) != BLKIO_OK) return -1; + return 1; +} diff --git a/kernel/src/v4/sk_v4_identity.c b/kernel/src/v4/sk_v4_identity.c index e6a0e721..b1d266a3 100644 --- a/kernel/src/v4/sk_v4_identity.c +++ b/kernel/src/v4/sk_v4_identity.c @@ -22,6 +22,12 @@ * Zuse exists -- reads her back, then writes the version 2 marker * (sk_v4_marker.c) naming her. * + * Growth (plan 2026-10-29-stick-5, Task 2; STICK.md 5a): a stick of at least + * 8 GB whose GPT ends short of the disk -- a stick larger than the image + * written to it -- has its Artemis partition grown to fill it (capped at + * 256 GiB, and at what its BAM tracks: sk_v4_grow.c), then Artemis's volume + * grown to the partition, before anything on it is read. Metadata only. + * * Not here (the plan's "not in this gate"): WIREBIND identities, EJECT, * ACL-ZUSE-BOOT. */ @@ -39,6 +45,7 @@ #include "starkernel/kbd_input.h" #include "starkernel/gpt.h" #include "starkernel/v4/sk_v4_marker.h" +#include "starkernel/v4/sk_v4_grow.h" #ifdef ARCH_AMD64 #include "starkernel/ioapic.h" #include "starkernel/i8042.h" @@ -149,6 +156,45 @@ static int sk_v4_is_this_stick(unsigned nparts) return !g_stick_in; } +/* Growth's limits, from kernel/Makefile (-D): the stick at least this big, + * Artemis's partition at most this big */ +#define SK_V4_GROW_MIN_BYTES STICK_GROW_MIN_BYTES +#define SK_V4_CAP_SECTORS (STICK_ARTEMIS_CAP_GIB * 2097152ULL) + +/* This system's stick, its GPT just read (g_gpt_parts, *nparts): when the + * stick is larger than its GPT (its backup header is not the disk's last + * sector) and at least 8 GB, the Artemis partition is grown to fill it, and + * the GPT read again. Returns 0, or -1 when the GPT cannot be read again. */ +static int sk_v4_grow_stick(sk_v4_drive_t *d, unsigned *nparts) +{ + static int said_small; + uint64_t disk_sectors = (uint64_t)d->raw.total_blocks * (BLKIO_FORTH_BLOCK_SIZE / 512u); + uint64_t cap, new_last, first; + int ai = gpt_find(g_gpt_parts, *nparts, gpt_type_starship_artemis); + + if (ai < 0 || gpt_alternate_lba() == disk_sectors - 1u) return 0; /* the GPT fills the stick */ + if (disk_sectors * 512u < SK_V4_GROW_MIN_BYTES) { + if (!said_small) { + console_println("Artemis: the stick is under 8 GB -- not grown"); + said_small = 1; + } + return 0; + } + first = g_gpt_parts[ai].first_lba; + if (gpt_part_open(&d->art, &d->art_state, &d->raw, &g_gpt_parts[ai]) != BLKIO_OK) return 0; + cap = sk_v4_grow_cap_sectors(&d->art, SK_V4_CAP_SECTORS); + if (cap == 0) return 0; /* no Artemis volume: the signature check refuses it */ + if (gpt_grow_last(&d->raw, gpt_type_starship_artemis, cap, &new_last) != GPT_OK) + console_println("GPT: the stick's table could not be grown"); + else if (new_last - first + 1u >= SK_V4_CAP_SECTORS && first + SK_V4_CAP_SECTORS + 2048u < disk_sectors) + console_println("Artemis: past 256 GiB of the stick is left unused"); + if (gpt_read(&d->raw, g_gpt_parts, SK_V4_GPT_PARTS, nparts) != GPT_OK || + gpt_find(g_gpt_parts, *nparts, gpt_type_starship_identity) < 0 || + gpt_find(g_gpt_parts, *nparts, gpt_type_starship_artemis) < 0) + return -1; + return 0; +} + /* Zuse's session begins on @p dev: the console is hers, and the screen with * it (docs/v4.0.0/STICK.md 2). @p line says how she was found. */ static void sk_v4_login(blkio_dev_t *dev, const uint8_t seed[32], const uint8_t pubkey[32], @@ -241,6 +287,10 @@ static int sk_v4_drive_arrived(sk_v4_drive_t *d) console_println("USB: a drive that is not this system's stick -- not used"); return 1; } + if (sk_v4_grow_stick(d, &nparts) != 0) { + console_println("GPT: the stick's grown table does not read back"); + return 1; + } if (gpt_part_open(&d->art, &d->art_state, &d->raw, &g_gpt_parts[gpt_find(g_gpt_parts, nparts, gpt_type_starship_artemis)]) != BLKIO_OK || gpt_part_open(&d->part, &d->part_state, &d->raw, @@ -249,6 +299,13 @@ static int sk_v4_drive_arrived(sk_v4_drive_t *d) return 1; } + /* Artemis's volume fills its partition, grown with it (STICK.md 5a) */ + { + int g = sk_v4_grow_artemis(&d->art); + if (g > 0) console_println("Artemis: grown to fill the stick"); + else if (g < 0) console_println("Artemis: the stick's volume could not be grown"); + } + /* Artemis first: formatted and signed at build (STICK.md 4); a * signature that does not check out is not used, nor is the stick */ if (artemis_sig_check(&d->art, &asig) != ARTEMIS_SIG_OK) { diff --git a/logs/20261108-115815/amd64/qemu-amd64-20261108-115815.log b/logs/20261108-115815/amd64/qemu-amd64-20261108-115815.log new file mode 100644 index 00000000..99a1d8c4 --- /dev/null +++ b/logs/20261108-115815/amd64/qemu-amd64-20261108-115815.log @@ -0,0 +1,165 @@ +[=3h[=3h[=3hBdsDxe: loading Boot0001 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:03.0-1" from PciRoot(0x0)/Pci(0x3,0x0)/USB(0x0,0x0) +BdsDxe: starting Boot0001 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:03.0-1" from PciRoot(0x0)/Pci(0x3,0x0)/USB(0x0,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +RAW SERIAL UP +[CKPT 002] Serial (COM1) initialized +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: from starforth.cfg +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +EBS... +EBS OK +Calling kernel_main (monolithic)... + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v4.0.0-alpha.3 amd64 +Architecture: amd64 +Build: Nov 8 2026 11:58:08 + +UEFI BootServices: EXITED +Console: serial line is a 16550 at port 0x00000000000003f8 (COM1, probed) + +=== StarKernel Boot Information === +Memory map entries: 128 +Total memory: 1023 MB +Usable memory: 965 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 248623 + Free pages : 247096 + Used pages : 1527 + Total MB : 971 + Free MB : 965 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +APIC: IA32_APIC_BASE MSR=0x00000000fee00900 +APIC: stale-ISR drain: 0 EOI(s) issued +APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear) +APIC: init done + +I/O APIC: init... +I/O APIC: base=0xfec00000, gsi_base=0, overrides=5 + override: source=00 gsi=2 flags=0x0000 + override: source=05 gsi=5 flags=0x000d + override: source=09 gsi=9 flags=0x000d + override: source=0a gsi=10 flags=0x000d + override: source=0b gsi=11 flags=0x000d +i8042: keyboard ACKed enable-scanning +i8042: IRQ1 enabled in controller config byte +I/O APIC: keyboard IRQ1 routed (masked) + +Timer: init... +Timer: init start +Timer: PM_TMR_BLK discovered from FADT at port 1544 +Timer: VM mode detected (hypervisor present). +Timer: HPET calibration disabled (VM-exit MMIO would poison timing). +Timer: WARNING: invariant TSC not present under hypervisor. +Timer: continuing in RELATIVE mode (no determinism guarantees). +Timer: RDTSCP not present; using RDTSC (less serialized). +Timer: CPUID frequency unavailable; trying PM Timer... +Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2048803536 Hz +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 24641536 + Heap end addr: 561512448 + +Heartbeat: init... +APIC Timer: calibrating... +APIC Timer: apic_hz=980690917, tick_hz=100, initial_count=9806909 +APIC Timer: configured (masked, ready to start) +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +Boot disk: a StarshipOS stick +System: StarForth v4 (system=v4) +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:sys:map-64.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:map-64.f18 capsule_hash=0x949eb6a1e9260e49 +V4: capsule v4:sys:core.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:core.f18 capsule_hash=0xb8f167892ec18c28 +V4: capsule v4:sys:input.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:input.f18 capsule_hash=0x85d3ec486d929a79 +V4: capsule v4:sys:dict.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:dict.f18 capsule_hash=0x1379bd3b5de811dc +V4: capsule v4:sys:codegen.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:codegen.f18 capsule_hash=0x36d2ae84d2911b90 +V4: capsule v4:sys:compile.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:compile.f18 capsule_hash=0x11145c4c2ab819d8 +V4: capsule v4:sys:quit.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:quit.f18 capsule_hash=0x43cadf3b21ddbf3e +V4: capsule v4:sys:forth.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:forth.f18 capsule_hash=0x16cab4a1268b85da +V4: capsule v4:sys:numout.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:numout.f18 capsule_hash=0x85a240ad3fb2b299 +V4: capsule v4:sys:words.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:words.f18 capsule_hash=0xb1d381f927d9f14e +V4: capsule v4:sys:system.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:system.f18 capsule_hash=0xd92203664c2915bd +V4: capsule v4:sys:qmath.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:qmath.f18 capsule_hash=0xdb4140713dd857f3 +V4: capsule v4:sys:blocks.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:blocks.f18 capsule_hash=0x76307ef48678dc2a +V4: capsule v4:sys:log.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:log.f18 capsule_hash=0xfc6540e6b824a52b +V4: capsule v4:sys:acl.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:acl.f18 capsule_hash=0x6845aa048db7eb22 +V4: capsule v4:sys:end.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:end.f18 capsule_hash=0x5ef01312c07b79cc +PARITY:V4_ROM words=2372 rom_hash=0x87b23d3ae2643c8d instructions=59145258 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x033e1c05c09a1df8 +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_SYSTEM word_count=317 dict_hash=0xfec8c47cfc6f437f +PARITY:OK +POST: PASSED +[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks) +PCI: init... +PCI: ECAM mapped (amd64) +virtio-rng: no device on PCI bus 0 +rng: backend = rdrand +entropy: ready +virtio-input: no device on PCI bus 0 +Starting heartbeat... +APIC Timer: started (LVT=0x00020020 ICR=9806909) +I/O APIC: keyboard IRQ1 unmasked +Heartbeat running. +blkio_usb: MSC device ready +Artemis: grown to fill the stick +[HADES][INFO ] [KRELTSC: 16230636740] blk: disk 'StarForth Volume' v2 LBN 3072..12132441 (12129370 us +Artemis: the stick's partition attached +[HADES][INFO ] [KRELTSC: 16247512130] blk: disk 'StarForth Volume' v2 LBN 12132442..12515983 (383542 +[HADES][INFO ] [KRELTSC: 16269128656] MINT verify: identity reads back correctly +[Hera] Zuse: genesis minted onto attached thumbdrive +[zuse@Hera] ok>  \ No newline at end of file diff --git a/logs/20261108-120001/amd64/qemu-amd64-20261108-120001.log b/logs/20261108-120001/amd64/qemu-amd64-20261108-120001.log new file mode 100644 index 00000000..b5c17a6f --- /dev/null +++ b/logs/20261108-120001/amd64/qemu-amd64-20261108-120001.log @@ -0,0 +1,163 @@ +[=3h[=3h[=3hBdsDxe: loading Boot0001 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:03.0-1" from PciRoot(0x0)/Pci(0x3,0x0)/USB(0x0,0x0) +BdsDxe: starting Boot0001 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:03.0-1" from PciRoot(0x0)/Pci(0x3,0x0)/USB(0x0,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +RAW SERIAL UP +[CKPT 002] Serial (COM1) initialized +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: from starforth.cfg +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +EBS... +EBS OK +Calling kernel_main (monolithic)... + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v4.0.0-alpha.3 amd64 +Architecture: amd64 +Build: Nov 8 2026 11:58:08 + +UEFI BootServices: EXITED +Console: serial line is a 16550 at port 0x00000000000003f8 (COM1, probed) + +=== StarKernel Boot Information === +Memory map entries: 128 +Total memory: 1023 MB +Usable memory: 965 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 248623 + Free pages : 247096 + Used pages : 1527 + Total MB : 971 + Free MB : 965 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +APIC: IA32_APIC_BASE MSR=0x00000000fee00900 +APIC: stale-ISR drain: 0 EOI(s) issued +APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear) +APIC: init done + +I/O APIC: init... +I/O APIC: base=0xfec00000, gsi_base=0, overrides=5 + override: source=00 gsi=2 flags=0x0000 + override: source=05 gsi=5 flags=0x000d + override: source=09 gsi=9 flags=0x000d + override: source=0a gsi=10 flags=0x000d + override: source=0b gsi=11 flags=0x000d +i8042: keyboard ACKed enable-scanning +i8042: IRQ1 enabled in controller config byte +I/O APIC: keyboard IRQ1 routed (masked) + +Timer: init... +Timer: init start +Timer: PM_TMR_BLK discovered from FADT at port 1544 +Timer: VM mode detected (hypervisor present). +Timer: HPET calibration disabled (VM-exit MMIO would poison timing). +Timer: WARNING: invariant TSC not present under hypervisor. +Timer: continuing in RELATIVE mode (no determinism guarantees). +Timer: RDTSCP not present; using RDTSC (less serialized). +Timer: CPUID frequency unavailable; trying PM Timer... +Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2031442736 Hz +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 24641536 + Heap end addr: 561512448 + +Heartbeat: init... +APIC Timer: calibrating... +APIC Timer: apic_hz=975597517, tick_hz=100, initial_count=9755975 +APIC Timer: configured (masked, ready to start) +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +Boot disk: a StarshipOS stick +System: StarForth v4 (system=v4) +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:sys:map-64.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:map-64.f18 capsule_hash=0x949eb6a1e9260e49 +V4: capsule v4:sys:core.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:core.f18 capsule_hash=0xb8f167892ec18c28 +V4: capsule v4:sys:input.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:input.f18 capsule_hash=0x85d3ec486d929a79 +V4: capsule v4:sys:dict.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:dict.f18 capsule_hash=0x1379bd3b5de811dc +V4: capsule v4:sys:codegen.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:codegen.f18 capsule_hash=0x36d2ae84d2911b90 +V4: capsule v4:sys:compile.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:compile.f18 capsule_hash=0x11145c4c2ab819d8 +V4: capsule v4:sys:quit.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:quit.f18 capsule_hash=0x43cadf3b21ddbf3e +V4: capsule v4:sys:forth.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:forth.f18 capsule_hash=0x16cab4a1268b85da +V4: capsule v4:sys:numout.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:numout.f18 capsule_hash=0x85a240ad3fb2b299 +V4: capsule v4:sys:words.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:words.f18 capsule_hash=0xb1d381f927d9f14e +V4: capsule v4:sys:system.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:system.f18 capsule_hash=0xd92203664c2915bd +V4: capsule v4:sys:qmath.f18 signature: missing (unsigned) +PARRITY:V4_F18 name=v4:sys:qmath.f18 capsule_hash=0xdb4140713dd857f3 +V4: capsule v4:sys:blocks.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:blocks.f18 capsule_hash=0x76307ef48678dc2a +V4: capsule v4:sys:log.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:log.f18 capsule_hash=0xfc6540e6b824a52b +V4: capsule v4:sys:acl.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:acl.f18 capsule_hash=0x6845aa048db7eb22 +V4: capsule v4:sys:end.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:end.f18 capsule_hash=0x5ef01312c07b79cc +PARITY:V4_ROM words=2372 rom_hash=0x87b23d3ae2643c8d instructions=59145258 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x033e1c05c09a1df8 +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_SYSTEM word_count=317 dict_hash=0xfec8c47cfc6f437f +PARITY:OK +POST: PASSED +[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks) +PCI: init... +PCI: ECAM mapped (amd64) +virtio-rng: no device on PCI bus 0 +rng: backend = rdrand +entropy: ready +virtio-input: no device on PCI bus 0 +Starting heartbeat... +APIC Timer: started (LVT=0x00020020 ICR=9755975) +I/O APIC: keyboard IRQ1 unmasked +Heartbeat running. +blkio_usb: MSC device ready +[HADES][INFO ] [KRELTSC: 15850556752] blk: disk 'StarForth Volume' v2 LBN 3072..12132441 (12129370 us +Artemis: the stick's partition attached +[HADES][INFO ] [KRELTSC: 15868168832] blk: disk 'StarForth Volume' v2 LBN 12132442..12515983 (383542 +[Hera] Zuse: identity confirmed from attached thumbdrive +[zuse@Hera] ok>  \ No newline at end of file diff --git a/logs/20261108-120143/amd64/qemu-amd64-20261108-120143.log b/logs/20261108-120143/amd64/qemu-amd64-20261108-120143.log new file mode 100644 index 00000000..6c89fcdb --- /dev/null +++ b/logs/20261108-120143/amd64/qemu-amd64-20261108-120143.log @@ -0,0 +1,165 @@ +[=3h[=3h[=3hBdsDxe: loading Boot0001 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:03.0-1" from PciRoot(0x0)/Pci(0x3,0x0)/USB(0x0,0x0) +BdsDxe: starting Boot0001 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:03.0-1" from PciRoot(0x0)/Pci(0x3,0x0)/USB(0x0,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +RAW SERIAL UP +[CKPT 002] Serial (COM1) initialized +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: from starforth.cfg +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +EBS... +EBS OK +Calling kernel_main (monolithic)... + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v4.0.0-alpha.3 amd64 +Architecture: amd64 +Build: Nov 8 2026 12:01:37 + +UEFI BootServices: EXITED +Console: serial line is a 16550 at port 0x00000000000003f8 (COM1, probed) + +=== StarKernel Boot Information === +Memory map entries: 128 +Total memory: 1023 MB +Usable memory: 965 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 248623 + Free pages : 247096 + Used pages : 1527 + Total MB : 971 + Free MB : 965 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +APIC: IA32_APIC_BASE MSR=0x00000000fee00900 +APIC: stale-ISR drain: 0 EOI(s) issued +APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear) +APIC: init done + +I/O APIC: init... +I/O APIC: base=0xfec00000, gsi_base=0, overrides=5 + override: source=00 gsi=2 flags=0x0000 + override: source=05 gsi=5 flags=0x000d + override: source=09 gsi=9 flags=0x000d + override: source=0a gsi=10 flags=0x000d + override: source=0b gsi=11 flags=0x000d +i8042: keyboard ACKed enable-scanning +i8042: IRQ1 enabled in controller config byte +I/O APIC: keyboard IRQ1 routed (masked) + +Timer: init... +Timer: init start +Timer: PM_TMR_BLK discovered from FADT at port 1544 +Timer: VM mode detected (hypervisor present). +Timer: HPET calibration disabled (VM-exit MMIO would poison timing). +Timer: WARNING: invariant TSC not present under hypervisor. +Timer: continuing in RELATIVE mode (no determinism guarantees). +Timer: RDTSCP not present; using RDTSC (less serialized). +Timer: CPUID frequency unavailable; trying PM Timer... +Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2090125821 Hz +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 24641536 + Heap end addr: 561512448 + +Heartbeat: init... +APIC Timer: calibrating... +APIC Timer: apic_hz=999200110, tick_hz=100, initial_count=9992001 +APIC Timer: configured (masked, ready to start) +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +Boot disk: a StarshipOS stick +System: StarForth v4 (system=v4) +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:sys:map-64.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:map-64.f18 capsule_hash=0x949eb6a1e9260e49 +V4: capsule v4:sys:core.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:core.f18 capsule_hash=0xb8f167892ec18c28 +V4: capsule v4:sys:input.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:input.f18 capsule_hash=0x85d3ec486d929a79 +V4: capsule v4:sys:dict.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:dict.f18 capsule_hash=0x1379bd3b5de811dc +V4: capsule v4:sys:codegen.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:codegen.f18 capsule_hash=0x36d2ae84d2911b90 +V4: capsule v4:sys:compile.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:compile.f18 capsule_hash=0x11145c4c2ab819d8 +V4: capsule v4:sys:quit.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:quit.f18 capsule_hash=0x43cadf3b21ddbf3e +V4: capsule v4:sys:forth.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:forth.f18 capsule_hash=0x16cab4a1268b85da +V4: capsule v4:sys:numout.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:numout.f18 capsule_hash=0x85a240ad3fb2b299 +V4: capsule v4:sys:words.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:words.f18 capsule_hash=0xb1d381f927d9f14e +V4: capsule v4:sys:system.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:system.f18 capsule_hash=0xd92203664c2915bd +V4: capsule v4:sys:qmath.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:qmath.f18 capsule_hash=0xdb4140713dd857f3 +V4: capsule v4:sys:blocks.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:blocks.f18 capsule_hash=0x76307ef48678dc2a +V4: capsule v4:sys:log.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:log.f18 capsule_hash=0xfc6540e6b824a52b +V4: capsule v4:sys:acl.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:acl.f18 capsule_hash=0x6845aa048db7eb22 +V4: capsule v4:sys:end.f18 signature: missing (unsigned) +PARITY:V4_F18 name=v4:sys:end.f18 capsule_hash=0x5ef01312c07b79cc +PARITY:V4_ROM words=2372 rom_hash=0x87b23d3ae2643c8d instructions=59145258 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x033e1c05c09a1df8 +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_SYSTEM word_count=317 dict_hash=0xfec8c47cfc6f437f +PARITY:OK +POST: PASSED +[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks) +PCI: init... +PCI: ECAM mapped (amd64) +virtio-rng: no device on PCI bus 0 +rng: backend = rdrand +entropy: ready +virtio-input: no device on PCI bus 0 +Starting heartbeat... +APIC Timer: started (LVT=0x00020020 ICR=9992001) +I/O APIC: keyboard IRQ1 unmasked +Heartbeat running. +blkio_usb: MSC device ready +Artemis: the stick is under 8 GB -- not grown +[HADES][INFO ] [KRELTSC: 15571534696] blk: disk 'StarForth Volume' v2 LBN 3072..1122393 (1119322 user +Artemis: the stick's partition attached +[HADES][INFO ] [KRELTSC: 15591872270] blk: disk 'StarForth Volume' v2 LBN 1122394..1505935 (383542 us +[HADES][INFO ] [KRELTSC: 15623585180] MINT verify: identity reads back correctly +[Hera] Zuse: genesis minted onto attached thumbdrive +[zuse@Hera] ok>  \ No newline at end of file diff --git a/scripts/mkdiskimage.sh b/scripts/mkdiskimage.sh index 17f74ee5..b0995042 100644 --- a/scripts/mkdiskimage.sh +++ b/scripts/mkdiskimage.sh @@ -17,11 +17,12 @@ # --table gpt -> partition type "EFI System" (UEFI firmware, U-Boot bootefi) # # The StarshipOS stick and data stick (docs/v4.0.0/STICK.md 4; --table gpt only): -# --starship IDENT_MIB --mkartemis TOOL --esp-mib E +# --starship IDENT_MIB --mkartemis TOOL --esp-mib E [--bam-for SECTORS] # lays out an ESP of E MiB holding the SRC:DEST files (E=0: no ESP, no files), # then a StarshipOS identity partition of IDENT_MIB MiB (all zeros), then a # StarshipOS Artemis partition to the last whole MiB before the backup GPT, -# formatted and signed by TOOL (tools/mkartemis.c). Every partition starts and +# formatted and signed by TOOL (tools/mkartemis.c) -- with --bam-for, its BAM sized for an Artemis +# of SECTORS, so it can grow that far on a larger stick (STICK.md 5a). Every partition starts and # ends on a whole MiB, so on 1 KiB blocks, as the kernel's GPT reader needs. # # Zuse's minted pair as a stick (docs/v4.0.0/plans/2026-10-27-stick-4-one-disk.md, Task 1): @@ -38,7 +39,7 @@ # root, no loop devices. set -euo pipefail -OUT="" TABLE="" SIZE_MIB="" LABEL="" IDENT_MIB="" MKARTEMIS="" ESP_MIB="" FROM_IDENT="" FROM_ART="" +OUT="" TABLE="" SIZE_MIB="" LABEL="" IDENT_MIB="" MKARTEMIS="" ESP_MIB="" FROM_IDENT="" FROM_ART="" BAM_FOR="" while [ $# -gt 0 ]; do case "$1" in --out) OUT="$2"; shift 2 ;; @@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do --starship) IDENT_MIB="$2"; shift 2 ;; --mkartemis) MKARTEMIS="$2"; shift 2 ;; --esp-mib) ESP_MIB="$2"; shift 2 ;; + --bam-for) BAM_FOR="$2"; shift 2 ;; --starship-from) FROM_IDENT="$2"; FROM_ART="$3"; shift 3 ;; --) shift; break ;; -*) echo "mkdiskimage.sh: unknown option $1" >&2; exit 2 ;; @@ -119,7 +121,8 @@ if [ -n "$IDENT_MIB" ]; then dd if="$FROM_IDENT" of="$OUT" bs=1M seek=$(( IDENT_START / 2048 )) conv=notrunc,sparse status=none dd if="$FROM_ART" of="$OUT" bs=1M seek=$(( ART_START / 2048 )) conv=notrunc,sparse status=none else - "$MKARTEMIS" --format "$OUT" --first-lba "$ART_START" --sectors "$ART_SECTORS" >/dev/null || { + "$MKARTEMIS" --format "$OUT" --first-lba "$ART_START" --sectors "$ART_SECTORS" \ + ${BAM_FOR:+--bam-for "$BAM_FOR"} >/dev/null || { echo "mkdiskimage.sh: formatting Artemis failed" >&2; rm -f "$OUT"; exit 1; } fi if [ "$ESP_MIB" = 0 ]; then diff --git a/tools/mkartemis.c b/tools/mkartemis.c index cab7d282..a81ba1ba 100644 --- a/tools/mkartemis.c +++ b/tools/mkartemis.c @@ -10,10 +10,14 @@ * kernel writes. Zuse's genesis marker is not written: the first boot does * that. docs/v4.0.0/plans/2026-10-27-stick-3-images.md. * - * mkartemis --format IMG --first-lba L --sectors N + * mkartemis --format IMG --first-lba L --sectors N [--bam-for M] * mkartemis --check IMG --first-lba L --sectors N * - * L and N count 512-byte sectors; both must be even (1 KiB blocks). + * L, N and M count 512-byte sectors; L and N must be even (1 KiB blocks). + * --bam-for M sizes the volume's BAM as for a volume of M sectors, so it can + * grow that far on a larger stick with no BAM to move (docs/v4.0.0/STICK.md + * 5a; plan 2026-10-29-stick-5, R1 and Task 2). --check reports the BAM's + * size in 4 KiB devblocks. */ #define _GNU_SOURCE #include @@ -111,7 +115,7 @@ static uint8_t g_ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE]; static int usage(void) { - fprintf(stderr, "usage: mkartemis --format|--check IMG --first-lba L --sectors N\n"); + fprintf(stderr, "usage: mkartemis --format|--check IMG --first-lba L --sectors N [--bam-for M]\n"); return 2; } @@ -140,6 +144,8 @@ static int attach(blkio_dev_t *dev, uint32_t *start_lbn, uint32_t *count) * header is read from the disk itself and judged as attach judges it * (block_subsystem.c blk_format_or_load_disk): blk_get_volume_meta would give * the geometry computed in memory for a blank disk, magic and all. */ +static uint32_t g_bam_devblocks; /* the BAM's size, as check_volume read it */ + static int check_volume(blkio_dev_t *dev, uint32_t *count, artemis_sig_t *sig) { static uint8_t hdr[BLK_DEVICE_SECTOR]; @@ -160,6 +166,7 @@ static int check_volume(blkio_dev_t *dev, uint32_t *count, artemis_sig_t *sig) fprintf(stderr, "mkartemis: the Artemis signature does not check out (%d)\n", (int)src); else rc = 0; + g_bam_devblocks = meta.bam_devblocks; blk_subsys_shutdown(); return rc; } @@ -167,7 +174,7 @@ static int check_volume(blkio_dev_t *dev, uint32_t *count, artemis_sig_t *sig) int main(int argc, char **argv) { const char *mode, *img; - unsigned long long first_lba, sectors; + unsigned long long first_lba, sectors, bam_for = 0; range_t r; blkio_dev_t dev; blkio_params_t params; @@ -175,7 +182,8 @@ int main(int argc, char **argv) artemis_sig_t sig; int i, format; - if (argc != 7 || strcmp(argv[3], "--first-lba") != 0 || strcmp(argv[5], "--sectors") != 0) return usage(); + if ((argc != 7 && argc != 9) || strcmp(argv[3], "--first-lba") != 0 || strcmp(argv[5], "--sectors") != 0) + return usage(); mode = argv[1]; img = argv[2]; if (strcmp(mode, "--format") == 0) format = 1; @@ -185,6 +193,12 @@ int main(int argc, char **argv) fprintf(stderr, "mkartemis: --first-lba and --sectors take a number of sectors\n"); return 1; } + if (argc == 9) { + if (!format || strcmp(argv[7], "--bam-for") != 0 || parse_count(argv[8], &bam_for) != 0 || bam_for < sectors) { + fprintf(stderr, "mkartemis: --bam-for takes a number of sectors, at least --sectors, with --format\n"); + return 1; + } + } if (first_lba % 2u != 0u || sectors % 2u != 0u || sectors == 0u || sectors / 2u > 0xFFFFFFFFull) { fprintf(stderr, "mkartemis: the partition must start and end on a 1 KiB block (even LBA and sector count)\n"); return 1; @@ -202,6 +216,7 @@ int main(int argc, char **argv) sf_time_init(); if (format) { + blk_subsys_set_format_bam_for(bam_for / 8u); /* sectors -> 4 KiB devblocks */ if (attach(&dev, &start_lbn, &count) != 0) return 1; if (artemis_sig_check(&dev, &sig) != ARTEMIS_SIG_BLANK) { fprintf(stderr, "mkartemis: the partition is not blank -- not formatted\n"); @@ -221,7 +236,8 @@ int main(int argc, char **argv) printf("mkartemis: formatted %u blocks, STFR v2, signature stamped, read back ok\n", r.blocks); } else { if (check_volume(&dev, &count, &sig) != 0) return 1; - printf("mkartemis: STFR v2, %u user blocks, signature ok, disk_uuid ", count); + printf("mkartemis: STFR v2, %u user blocks, bam %u devblocks, signature ok, disk_uuid ", + count, g_bam_devblocks); for (i = 0; i < 16; i++) printf("%02x", sig.disk_uuid[i]); printf("\n"); } diff --git a/v3/include/block_subsystem.h b/v3/include/block_subsystem.h index 328c540a..23fd242d 100644 --- a/v3/include/block_subsystem.h +++ b/v3/include/block_subsystem.h @@ -591,6 +591,18 @@ int blk_subsys_add_raw_device(uint8_t *buf, uint32_t nblocks); * by the disk's owner before any write to that slot will succeed. */ int blk_subsys_confirm_format(uint32_t lbn); +/* blk_volume_compute_totals - a volume's tracked/total block counts from its + * total_devblocks, bam_devblocks, reloc_devblocks and meta_fence_blocks, as a + * fresh format computes them. For a volume grown to fill a larger partition + * (the v4 stick, plan 2026-10-29-stick-5, Task 2). */ +void blk_volume_compute_totals(blk_volume_meta_t *m); + +/* blk_subsys_set_format_bam_for - size the BAM of the next fresh format for a + * volume of `total_devblocks` 4 KiB devblocks, when that is more than the + * disk's own: the volume can later grow that far with no BAM of its own to + * move (mkartemis --bam-for). 0, the default, sizes it for the disk. */ +void blk_subsys_set_format_bam_for(uint64_t total_devblocks); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/v3/src/block_subsystem.c b/v3/src/block_subsystem.c index d8f04d2f..6c3afca6 100644 --- a/v3/src/block_subsystem.c +++ b/v3/src/block_subsystem.c @@ -386,6 +386,20 @@ static void compute_totals_from_B(blk_volume_meta_t *m) { * blk_compute_fresh_geometry()). */ } +/* compute_totals_from_B, for a volume grown on its disk (the v4 stick: + * docs/v4.0.0/plans/2026-10-29-stick-5-proxy-zuse-and-real-zuse.md, Task 2). */ +void blk_volume_compute_totals(blk_volume_meta_t *m) { + if (m) compute_totals_from_B(m); +} + +/* The BAM a fresh format sizes: for this many devblocks when it is more than + * the disk's own (0: the disk's). Only mkartemis sets it (--bam-for). */ +static uint64_t g_format_bam_for; + +void blk_subsys_set_format_bam_for(uint64_t total_devblocks) { + g_format_bam_for = total_devblocks; +} + /* ===== physical BAM I/O (sync to/from slot->bam[]) ===== */ /* @@ -530,7 +544,8 @@ static void blk_compute_fresh_geometry(blk_dev_slot_t *slot) { strncpy(slot->vol_meta.label, "StarForth Volume", sizeof(slot->vol_meta.label) - 1); slot->vol_meta.total_devblocks = (uint64_t) udiv_floor(slot->total_blkio_blocks_1k, 4); slot->vol_meta.bam_start = 1; - slot->vol_meta.bam_devblocks = choose_B(slot->vol_meta.total_devblocks); + slot->vol_meta.bam_devblocks = choose_B(g_format_bam_for > slot->vol_meta.total_devblocks + ? g_format_bam_for : slot->vol_meta.total_devblocks); slot->vol_meta.reloc_start = slot->vol_meta.bam_start + slot->vol_meta.bam_devblocks; /* One devblock (4 KiB): a 4-byte count prefix + up to BLK_RELOC_MAX_ENTRIES * 8-byte {home_lbn, actual_lbn} pairs -- see this file's own doc comment