Real per-VM log persistence: source attribution + ACL pin (FABRIC-3.md §XXVII)
Wires the previously-unused vm_log_attributed_vm() into LOG-APPEND's kernel primitive so persisted log records carry a trustworthy source (the real attributed VM's registry name, or "HADES" pseudo-source) instead of a caller-supplied, trivially forgeable string. Drops src-addr/src-u from LOG-APPEND's stack signature accordingly. Pins LOG-APPEND via bare ACL-PIN in Artemis's own init.4th, matching BIRTH/CAPSULE-BIRTH's precedent for a privileged word that can't reach the shared, host-portable ACL.4th. Also fixes two console-banner nitpicks: a mis-rendering em dash (U+2014) in the boot banner, and drops "Emergency" from the CLI banner text. Doc corrections to artemis_sig.h/zuse_eligibility_list.h reconciling the three fixed devblock ranges now in play. LOG-FLUSH (the intended normal entry point) and level-aware log eviction remain open, flagged not fixed. Re-verified clean boot to ok> on all 3 architectures after every change. riscv64 showed one new, unrelated virtio_blk write-timeout anomaly during Artemis's early physics self-test (self-recovered, boot unaffected, sector doesn't map to the log region) -- flagged, not investigated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
61755fde78
commit
2a30212bd3
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* log_region.c - Growable per-VM log-persistence ring on Artemis's disk.
|
||||
* See starkernel/log_region.h for the format/interface design.
|
||||
*
|
||||
* Deliberately never calls log_message() (or anything documented to call
|
||||
* it) -- this runs inside Artemis's own dictionary context during message
|
||||
* delivery (LOG-APPEND, dispatched by a sender's MSG-SEND/MSG-TICK); a log
|
||||
* call here would append to Artemis's own buffer and eventually flush back
|
||||
* through this same path.
|
||||
*/
|
||||
|
||||
#include "starkernel/log_region.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "block_subsystem.h" /* blk_meta_zone_read/write, compute_crc64 */
|
||||
|
||||
static uint64_t log_region_ctrl_compute_crc(const log_region_ctrl_t *ctrl) {
|
||||
size_t crc_span = offsetof(log_region_ctrl_t, hdr_crc);
|
||||
return compute_crc64((const uint8_t *)ctrl, crc_span);
|
||||
}
|
||||
|
||||
/* Reads the control header; returns 0 and *out populated only if magic,
|
||||
* version, and CRC all check out. Any other result (blank, foreign,
|
||||
* corrupt, or a read failure) is treated identically by the caller: "not
|
||||
* yet initialized," matching genesis_marker_read()'s own discipline. */
|
||||
static int log_region_ctrl_read(log_region_ctrl_t *out) {
|
||||
if (blk_meta_zone_read(LOG_REGION_DEVBLOCK_FROM_TOP_BASE, (uint8_t *)out) != 0) return -1;
|
||||
if (LOG_REGION_GET_MAGIC(out->magic) != (uint32_t)(LOG_REGION_MAGIC & 0xFFFFFFFFull)) return -1;
|
||||
if (LOG_REGION_GET_VERSION(out->magic) != LOG_REGION_VERSION_0) return -1;
|
||||
uint64_t want_crc = log_region_ctrl_compute_crc(out);
|
||||
if (want_crc != out->hdr_crc) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int log_region_ctrl_write(log_region_ctrl_t *ctrl) {
|
||||
ctrl->hdr_crc = log_region_ctrl_compute_crc(ctrl);
|
||||
return blk_meta_zone_write(LOG_REGION_DEVBLOCK_FROM_TOP_BASE, (const uint8_t *)ctrl) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
static void log_region_ctrl_init_fresh(log_region_ctrl_t *ctrl) {
|
||||
memset(ctrl, 0, sizeof(*ctrl));
|
||||
ctrl->magic = LOG_REGION_PACK(LOG_REGION_VERSION_0);
|
||||
ctrl->devblocks = LOG_REGION_INITIAL_DEVBLOCKS;
|
||||
ctrl->head_slot = 0;
|
||||
ctrl->tail_slot = 0;
|
||||
ctrl->record_count = 0;
|
||||
}
|
||||
|
||||
/* Reads/writes the devblock holding slot_index (LOG_SLOTS_PER_DEVBLOCK
|
||||
* slots per devblock), one devblock_from_top past the control header for
|
||||
* every LOG_SLOTS_PER_DEVBLOCK slots. */
|
||||
static uint32_t log_region_slot_devblock_from_top(uint32_t slot_index) {
|
||||
return LOG_REGION_DEVBLOCK_FROM_TOP_BASE + 1u + (slot_index / LOG_SLOTS_PER_DEVBLOCK);
|
||||
}
|
||||
|
||||
static int log_region_write_slot(uint32_t slot_index, const log_slot_t *slot) {
|
||||
uint8_t devblock_buf[4096];
|
||||
uint32_t dft = log_region_slot_devblock_from_top(slot_index);
|
||||
uint32_t slot_in_devblock = slot_index % LOG_SLOTS_PER_DEVBLOCK;
|
||||
|
||||
/* Read-modify-write: blk_meta_zone_*() only operates at whole-devblock
|
||||
* granularity, and a devblock holds LOG_SLOTS_PER_DEVBLOCK slots, so a
|
||||
* single-slot write must preserve its siblings. A read failure on a
|
||||
* never-yet-written devblock (blank fence content) is not fatal here --
|
||||
* proceed with a zeroed buffer, matching zuse_eligibility.c's own
|
||||
* "blank fence content is indistinguishable from absent, treat as
|
||||
* absent" discipline. */
|
||||
if (blk_meta_zone_read(dft, devblock_buf) != 0) {
|
||||
memset(devblock_buf, 0, sizeof(devblock_buf));
|
||||
}
|
||||
memcpy(devblock_buf + (size_t)slot_in_devblock * LOG_SLOT_SIZE, slot, sizeof(*slot));
|
||||
return blk_meta_zone_write(dft, devblock_buf) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int log_region_append(uint8_t level, uint64_t timestamp,
|
||||
const char *source, uint32_t source_len,
|
||||
const char *msg, uint32_t msg_len) {
|
||||
log_region_ctrl_t ctrl;
|
||||
if (log_region_ctrl_read(&ctrl) != 0) {
|
||||
log_region_ctrl_init_fresh(&ctrl);
|
||||
}
|
||||
|
||||
uint32_t total_slots = ctrl.devblocks * LOG_SLOTS_PER_DEVBLOCK;
|
||||
if (ctrl.record_count >= total_slots) {
|
||||
if (ctrl.devblocks < LOG_REGION_MAX_DEVBLOCKS) {
|
||||
/* Grow: new slots appear at indices [old_total_slots ..
|
||||
* new_total_slots-1], nothing already written moves -- safe
|
||||
* to do at any point in the ring's lifecycle regardless of
|
||||
* wrap state, since head/tail are slot INDICES modulo
|
||||
* total_slots, and the modulus is simply larger from here on. */
|
||||
ctrl.devblocks += LOG_REGION_GROWTH_INCREMENT;
|
||||
if (ctrl.devblocks > LOG_REGION_MAX_DEVBLOCKS) ctrl.devblocks = LOG_REGION_MAX_DEVBLOCKS;
|
||||
total_slots = ctrl.devblocks * LOG_SLOTS_PER_DEVBLOCK;
|
||||
}
|
||||
if (ctrl.record_count >= total_slots) {
|
||||
/* Still full (growth ceiling already reached) -- evict oldest. */
|
||||
ctrl.head_slot = (ctrl.head_slot + 1u) % total_slots;
|
||||
ctrl.record_count--;
|
||||
}
|
||||
}
|
||||
|
||||
log_slot_t slot;
|
||||
memset(&slot, 0, sizeof(slot));
|
||||
slot.timestamp = timestamp;
|
||||
slot.level = level;
|
||||
|
||||
uint32_t src_n = source_len;
|
||||
if (src_n > LOG_SLOT_SOURCE_MAX) src_n = LOG_SLOT_SOURCE_MAX;
|
||||
memcpy(slot.source, source, src_n);
|
||||
|
||||
uint32_t msg_n = msg_len;
|
||||
if (msg_n > LOG_SLOT_MSG_MAX) msg_n = LOG_SLOT_MSG_MAX;
|
||||
memcpy(slot.msg, msg, msg_n);
|
||||
slot.msg_len = (uint16_t)msg_n;
|
||||
|
||||
if (log_region_write_slot(ctrl.tail_slot, &slot) != 0) return -1;
|
||||
|
||||
ctrl.tail_slot = (ctrl.tail_slot + 1u) % total_slots;
|
||||
if (ctrl.record_count < total_slots) ctrl.record_count++;
|
||||
|
||||
return log_region_ctrl_write(&ctrl);
|
||||
}
|
||||
@@ -1492,8 +1492,8 @@ void sk_repl(VM *vm)
|
||||
console_println(lithos_version);
|
||||
console_puts("StarForth Version "); console_println(STARFORTH_VERSION);
|
||||
console_println("");
|
||||
console_println("StarForth Emergency CLI");
|
||||
console_println("FORTH-79 interpreter — type BYE or power off to exit");
|
||||
console_println("StarForth CLI");
|
||||
console_println("FORTH-79 interpreter type BYE or power off to exit");
|
||||
console_println("");
|
||||
|
||||
sk_repl_run(vm);
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "../include/vm_host.h"
|
||||
#ifdef __STARKERNEL__
|
||||
#include "starkernel/vm/arena.h"
|
||||
#include "starkernel/log_attrib.h" /* vm_log_attributed_vm() */
|
||||
#include "starkernel/console.h" /* g_sk_fault_word */
|
||||
#include "platform_alloc.h" /* sf_free for call_stack */
|
||||
#include "starkernel/vm/stadium_words.h" /* item 4.1: word patrons on the Stadium */
|
||||
@@ -1029,11 +1030,24 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
|
||||
* @param vm Pointer to VM instance
|
||||
* @param input String containing Forth code to interpret
|
||||
*/
|
||||
/* FABRIC-3.md §XXVI follow-on, Step 4 (log_attrib.h): which VM's dictionary
|
||||
* context is currently executing, for log persistence attribution. Save/
|
||||
* restore (not a plain set) so a nested vm_interpret() call -- VM-EXEC
|
||||
* dispatching into a different VM's own dictionary, mid-interpret --
|
||||
* attributes to whichever VM is actually running when it returns to the
|
||||
* outer frame, not silently to the inner one. */
|
||||
static VM *g_log_attrib_vm; /* zero-initialized (BSS) */
|
||||
|
||||
VM *vm_log_attributed_vm(void) { return g_log_attrib_vm; }
|
||||
|
||||
void vm_interpret(VM* vm, const char* input)
|
||||
{
|
||||
if (!vm || !input) return;
|
||||
vm_assert_interpreter_enabled(vm, "vm_interpret");
|
||||
|
||||
VM *prev_attrib_vm = g_log_attrib_vm;
|
||||
g_log_attrib_vm = vm;
|
||||
|
||||
/* Load into input buffer (cap + NUL) */
|
||||
size_t n = 0, cap = INPUT_BUFFER_SIZE ? INPUT_BUFFER_SIZE - 1 : 0;
|
||||
while (n < cap)
|
||||
@@ -1064,6 +1078,8 @@ void vm_interpret(VM* vm, const char* input)
|
||||
vm->heartbeat.check_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_log_attrib_vm = prev_attrib_vm;
|
||||
}
|
||||
|
||||
/* ====================== VM memory helpers ======================= */
|
||||
|
||||
Reference in New Issue
Block a user