feat(build): the stick and the data stick, Artemis formatted and signed at build
The stick's increment 3, Task 1 (docs/v4.0.0/plans/2026-10-27-stick-3-images.md; STICK.md 4). make boot_image TARGET=PC now writes the 2 GiB stick: ESP 64 MiB (BOOTX64.EFI, startup.nsh, starforth.cfg), StarshipOS identity 500 MiB (zeros), StarshipOS Artemis to the end. make -f kernel/Makefile ARCH=<arch> datastick writes the 1.5 GiB data stick, identity and Artemis, fresh every time. Sizes in kernel/Makefile; every partition on whole MiB. tools/mkartemis formats and signs an Artemis partition at build with the kernel's own code -- v3's block_subsystem.c (blk_subsys_confirm_format) and artemis_sig.c (artemis_sig_genesis_stamp) -- through a blkio device over a byte range of the image; --check reads one back the same way (read-only). disk_uuid from getrandom(2): each image its own. mkdiskimage.sh gains --starship, --mkartemis, --esp-mib; Milk-V and the Pi images are unchanged. The kernel is not changed. Red: scripts/check_images.sh FAIL (no datastick, the PC image one partition). Green: 23/23. Mutation: --check on a range shifted by one block fails. Accepted: suite EXIT 0; 133146, 133417, 133815, POST 538/538, dict_hash 0xfec8c47cfc6f437f; test-32, 0 failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a4221d8b8c
commit
e177c5c6b3
@@ -0,0 +1,184 @@
|
||||
/* mkartemis.c -- format, or check, an Artemis partition inside a disk image.
|
||||
*
|
||||
* The stick's Artemis partition is formatted when the image is built, not on
|
||||
* first boot (docs/v4.0.0/STICK.md 4): its STFR v2 volume header, BAM and
|
||||
* relocation table, and its artemis_sig_t genesis signature. This tool does
|
||||
* it with the kernel's own code -- v3's block_subsystem.c
|
||||
* (blk_subsys_confirm_format) and kernel/src/artemis_sig.c
|
||||
* (artemis_sig_genesis_stamp, artemis_sig_check) -- through a blkio device
|
||||
* over a byte range of the image file, so the bytes are exactly those the
|
||||
* 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 --check IMG --first-lba L --sectors N
|
||||
*
|
||||
* L and N count 512-byte sectors; both must be even (1 KiB blocks).
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/random.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "blkio.h"
|
||||
#include "block_subsystem.h"
|
||||
#include "starkernel/artemis_sig.h"
|
||||
#include "starkernel/rng.h"
|
||||
|
||||
/* The kernel draws disk_uuid from its entropy backend; on the build machine
|
||||
* that is the host's own, getrandom(2). */
|
||||
int rng_get_bytes(uint8_t *buf, size_t n)
|
||||
{
|
||||
while (n > 0) {
|
||||
ssize_t got = getrandom(buf, n, 0);
|
||||
if (got < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
buf += got;
|
||||
n -= (size_t)got;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- a blkio device over [first_lba, first_lba + sectors) of a file ---- */
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
off_t base; /* first_lba * 512 */
|
||||
uint32_t blocks; /* sectors / 2 */
|
||||
int read_only;
|
||||
} range_t;
|
||||
|
||||
static int range_open(blkio_dev_t *dev, const blkio_params_t *p)
|
||||
{
|
||||
range_t *r = (range_t *)p->opaque;
|
||||
dev->state = r;
|
||||
dev->forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
|
||||
dev->total_blocks = r->blocks;
|
||||
return BLKIO_OK;
|
||||
}
|
||||
|
||||
static int range_close(blkio_dev_t *dev) { (void)dev; return BLKIO_OK; }
|
||||
|
||||
static int range_read(blkio_dev_t *dev, uint32_t fblock, void *dst)
|
||||
{
|
||||
range_t *r = (range_t *)dev->state;
|
||||
if (fblock >= r->blocks) return BLKIO_EINVAL;
|
||||
if (pread(r->fd, dst, BLKIO_FORTH_BLOCK_SIZE, r->base + (off_t)fblock * BLKIO_FORTH_BLOCK_SIZE)
|
||||
!= (ssize_t)BLKIO_FORTH_BLOCK_SIZE) return BLKIO_EIO;
|
||||
return BLKIO_OK;
|
||||
}
|
||||
|
||||
static int range_write(blkio_dev_t *dev, uint32_t fblock, const void *src)
|
||||
{
|
||||
range_t *r = (range_t *)dev->state;
|
||||
if (fblock >= r->blocks) return BLKIO_EINVAL;
|
||||
if (r->read_only) return BLKIO_EIO;
|
||||
if (pwrite(r->fd, src, BLKIO_FORTH_BLOCK_SIZE, r->base + (off_t)fblock * BLKIO_FORTH_BLOCK_SIZE)
|
||||
!= (ssize_t)BLKIO_FORTH_BLOCK_SIZE) return BLKIO_EIO;
|
||||
return BLKIO_OK;
|
||||
}
|
||||
|
||||
static int range_flush(blkio_dev_t *dev)
|
||||
{
|
||||
range_t *r = (range_t *)dev->state;
|
||||
if (r->read_only) return BLKIO_OK;
|
||||
return fsync(r->fd) == 0 ? BLKIO_OK : BLKIO_EIO;
|
||||
}
|
||||
|
||||
static int range_info(blkio_dev_t *dev, blkio_info_t *out)
|
||||
{
|
||||
range_t *r = (range_t *)dev->state;
|
||||
out->forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
|
||||
out->total_blocks = r->blocks;
|
||||
out->phys_sector_size = 512u;
|
||||
out->phys_size_bytes = (uint64_t)r->blocks * BLKIO_FORTH_BLOCK_SIZE;
|
||||
out->read_only = (uint8_t)r->read_only;
|
||||
return BLKIO_OK;
|
||||
}
|
||||
|
||||
static const blkio_vtable_t g_range_vt = { range_open, range_close, range_read, range_write, range_flush, range_info };
|
||||
|
||||
extern void sf_time_init(void); /* v3/src/platform/platform_init.c: the clock the volume header's times come from */
|
||||
|
||||
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");
|
||||
return 2;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *mode, *img;
|
||||
unsigned long long first_lba, sectors;
|
||||
range_t r;
|
||||
blkio_dev_t dev;
|
||||
blkio_params_t params;
|
||||
uint32_t start_lbn = 0, count = 0;
|
||||
artemis_sig_t sig;
|
||||
int i, format;
|
||||
|
||||
if (argc != 7 || 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;
|
||||
else if (strcmp(mode, "--check") == 0) format = 0;
|
||||
else return usage();
|
||||
first_lba = strtoull(argv[4], NULL, 10);
|
||||
sectors = strtoull(argv[6], NULL, 10);
|
||||
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;
|
||||
}
|
||||
|
||||
r.fd = open(img, format ? O_RDWR : O_RDONLY);
|
||||
if (r.fd < 0) { fprintf(stderr, "mkartemis: %s: %s\n", img, strerror(errno)); return 1; }
|
||||
r.base = (off_t)first_lba * 512;
|
||||
r.blocks = (uint32_t)(sectors / 2u);
|
||||
r.read_only = !format;
|
||||
params.forth_block_size = BLKIO_FORTH_BLOCK_SIZE;
|
||||
params.total_blocks = r.blocks;
|
||||
params.opaque = &r;
|
||||
if (blkio_open(&dev, &g_range_vt, ¶ms) != BLKIO_OK) { fprintf(stderr, "mkartemis: cannot open the range\n"); return 1; }
|
||||
|
||||
sf_time_init();
|
||||
if (blk_subsys_init(g_ram, sizeof(g_ram)) != BLK_OK || blk_subsys_attach_device(&dev) != BLK_OK ||
|
||||
blk_get_device_range(&dev, &start_lbn, &count) != BLK_OK) {
|
||||
fprintf(stderr, "mkartemis: the block subsystem would not take the partition\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (format) {
|
||||
if (artemis_sig_check(&dev, &sig) != ARTEMIS_SIG_BLANK) {
|
||||
fprintf(stderr, "mkartemis: the partition is not blank -- not formatted\n");
|
||||
return 1;
|
||||
}
|
||||
if (blk_subsys_confirm_format(start_lbn) != BLK_OK) { fprintf(stderr, "mkartemis: format failed\n"); return 1; }
|
||||
blk_subsys_shutdown();
|
||||
if (artemis_sig_genesis_stamp(&dev) != 0) { fprintf(stderr, "mkartemis: the signature stamp failed\n"); return 1; }
|
||||
printf("mkartemis: formatted %u blocks, STFR v2, signature stamped\n", r.blocks);
|
||||
} else {
|
||||
blk_volume_meta_t meta;
|
||||
artemis_sig_result_t src;
|
||||
if (blk_get_volume_meta(&meta) != BLK_OK || meta.magic != 0x53544652u || meta.version != 2u) {
|
||||
fprintf(stderr, "mkartemis: no STFR v2 volume header\n");
|
||||
return 1;
|
||||
}
|
||||
src = artemis_sig_check(&dev, &sig);
|
||||
if (src != ARTEMIS_SIG_OK) { fprintf(stderr, "mkartemis: the Artemis signature does not check out (%d)\n", (int)src); return 1; }
|
||||
printf("mkartemis: STFR v2, %u user blocks, signature ok, disk_uuid ", count);
|
||||
for (i = 0; i < 16; i++) printf("%02x", sig.disk_uuid[i]);
|
||||
printf("\n");
|
||||
blk_subsys_shutdown();
|
||||
}
|
||||
close(r.fd);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user