fix(v4): the kernel's fatal stops halt, interrupts off, instead of spinning (#177)

sk_v4_run's three stops -- "not started", "did not let go of POST's
blocks", "stopped on a fault" -- ended in for (;;) { }: a core spinning at
100%, the thing #151 removed from the prompt. Each now calls sk_v4_stop()
(kernel/src/v4/sk_v4_stop.c): interrupts off, then arch_halt() for ever. Each
still says why first. sk_v4_stop.c added to the loader's and the kernel's v4
sources.

kernel/tests/test_sk_v4_stop.c, with arch_halt and arch_disable_interrupts
faked (the halt jumps back out after three calls; an alarm fails a spin):
against the stop as it was -- "spun for 2 s without halting"; now -- 2
checks, 0 failures: halts in a loop, interrupts off before the first halt.
Built for amd64, aarch64 and riscv64. #177's "seen on the three
architectures": no boot reaches these stops, so it is shown on the host only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
Claude
2026-11-12 18:04:31 +00:00
parent 8649463a58
commit c778f6e0e7
7 changed files with 80 additions and 6 deletions
+2 -2
View File
@@ -730,8 +730,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_SRC)/v4/sk_v4_grow.c $(KERNEL_SRC)/v4/sk_v4_mint.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 $(KERNEL_SRC)/v4/sk_v4_mint.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_SRC)/v4/sk_v4_mint.c $(KERNEL_SRC)/v4/sk_v4_stop.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 $(KERNEL_SRC)/v4/sk_v4_mint.c $(KERNEL_SRC)/v4/sk_v4_stop.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.
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* sk_v4_stop.h -- the v4 kernel's fatal stop (#177): what sk_v4_run does when the system cannot
* go on ("not started", "did not let go of POST's blocks", "stopped on a fault"). Never returns.
*/
#ifndef STARKERNEL_V4_SK_V4_STOP_H
#define STARKERNEL_V4_SK_V4_STOP_H
void sk_v4_stop(void) __attribute__((noreturn));
#endif
+4 -3
View File
@@ -26,6 +26,7 @@
#include "starkernel/v4/sk_v4_mint.h"
#include "starkernel/v4/sk_v4.h"
#include "starkernel/v4/sk_v4_identity.h"
#include "starkernel/v4/sk_v4_stop.h"
#include "starkernel/kbd_input.h"
#include "starkernel/console.h"
#include "starkernel/arch.h"
@@ -159,7 +160,7 @@ void sk_v4_run(void (*chain)(void), const BootDisk *boot_disk)
blk_subsys_init(sk_v4_post_blk_ram, sizeof(sk_v4_post_blk_ram));
if (!v4_boot_run(&boot)) {
console_println("StarForth v4: not started");
for (;;) { }
sk_v4_stop(); /* #177: halts, not a 100% spin */
}
if (chain) {
/* the blocks are other blocks now: what the node has in its two
@@ -167,7 +168,7 @@ void sk_v4_run(void (*chain)(void), const BootDisk *boot_disk)
chain();
if (v4_boot_line(&boot, "EMPTY-BUFFERS", 13) != V4_TEXT_COMPLETED) {
console_println("StarForth v4: the node did not let go of POST's blocks");
for (;;) { }
sk_v4_stop(); /* #177: halts, not a 100% spin */
}
}
sk_v4_identity_boot(boot_disk);
@@ -230,7 +231,7 @@ void sk_v4_run(void (*chain)(void), const BootDisk *boot_disk)
else if (how == V4_BOOT_LINE_REFUSED) console_puts("Message refused\n ERROR\n"); /* the node's wire is full: error 19's words */
else if (how == V4_BOOT_LINE_STOPPED) {
console_println("StarForth v4: the node stopped on a fault");
for (;;) { }
sk_v4_stop(); /* #177: halts, not a 100% spin */
}
}
}
+14
View File
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* sk_v4_stop.c -- see sk_v4_stop.h. */
#include "starkernel/v4/sk_v4_stop.h"
#include "starkernel/arch.h"
/* Interrupts off, then halt for ever: the core sleeps instead of spinning at 100% (#177), and no
* timer or device wakes it to do anything. sk_v4_run's three stops each said why first. */
void sk_v4_stop(void)
{
arch_disable_interrupts();
for (;;) arch_halt();
}
+2 -1
View File
@@ -19,7 +19,8 @@ CC ?= cc
CFLAGS := -std=c99 -Wall -Wextra -Werror -O2 -g -I$(HERE) -I$(ROOT)/kernel/include \
-I$(ROOT)/kernel/include/starkernel -I$(ROOT)/v3/include
TESTS := test_gpt test_usb_config
TESTS := test_gpt test_usb_config test_sk_v4_stop
SRC_test_sk_v4_stop := $(ROOT)/kernel/src/v4/sk_v4_stop.c
SRC_test_gpt := $(ROOT)/kernel/src/gpt.c
SRC_test_usb_config := $(ROOT)/kernel/src/usb/usb_config.c
+43
View File
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* test_sk_v4_stop.c -- kernel/src/v4/sk_v4_stop.c on the host (#177): the v4 kernel's fatal stops
* halt -- interrupts off, then arch_halt() in a loop -- instead of spinning a core at 100%.
* arch_halt() and arch_disable_interrupts() are faked: the fake halt counts, and after a few calls
* jumps back out of the loop. A stop that spins never calls it: an alarm ends the test as failed.
*/
#define _POSIX_C_SOURCE 200809L
#include "harness.h"
#include "starkernel/v4/sk_v4_stop.h"
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
static jmp_buf out;
static int halts, irq_off, irq_off_before_first_halt = -1;
void arch_disable_interrupts(void) { irq_off = 1; }
void arch_enable_interrupts(void) { irq_off = 0; }
void arch_halt(void)
{
if (irq_off_before_first_halt < 0) irq_off_before_first_halt = irq_off;
if (++halts >= 3) longjmp(out, 1); /* it is halting, again and again: enough */
}
static void spun(int sig)
{
(void)sig;
static const char msg[] = "FAIL sk_v4_stop spun for 2 s without halting (#177)\n 1 checks, 1 failures\n";
(void)!write(1, msg, sizeof msg - 1); /* not printf: _exit would not flush it */
_exit(1);
}
int main(void)
{
signal(SIGALRM, spun);
alarm(2);
if (setjmp(out) == 0) sk_v4_stop();
alarm(0);
CHECK(halts == 3, "#177: the stop halts in a loop (arch_halt called %d times before the test stopped it)", halts);
CHECK(irq_off_before_first_halt == 1, "#177: interrupts off before the first halt, so nothing wakes it to spin again");
return DONE();
}
Binary file not shown.