65816-llvm-mos/demos/gnoForkWaitProbe.c
2026-07-07 18:33:48 -05:00

56 lines
2.5 KiB
C

// Process-lifecycle core regression for GNO: fork a leaf child that _exit(42)s,
// have the parent wait() and assert the real exit status propagates. Covers two
// fixed bugs at once:
// 1. Kernel dispatch vector: gnoKernel.s called $E10000 (the standard Tool
// Locator, toolset $03 = Misc Tools) instead of GNO's udispatch $E10008, so
// EVERY K* primitive hung. Latent until this probe -- fork is the first K*
// call ever exercised under GNO.
// 2. Exit status: _exit routed QUIT through __gnoGsosCall, which loaded
// A = callNum (0x2029) right before the trap, so GNO's StackGSOS recorded
// 0x2029 as the exit code and wait() decoded WEXITSTATUS = 0x29 (41).
// __gnoQuit now leaves A = status, so wait() sees 42 (0x2A).
//
// GNO fork is a thread-spawn: the child begins at child() on a fresh stack/DP, so
// child() is a LEAF using only absolute-long stores + _exit (no DP scratch).
// Bank-2 absolute markers (DBR-independent), per the gnoHeapProbe convention.
// 0x025000 C0DE reached end (no hang in fork/wait)
// 0x025010 C41D child was scheduled + ran
// 0x025014 002A WEXITSTATUS == 42 (the status propagated)
// 0x025016 0001 wait() reaped the same pid fork() returned
// 0x025018 600D exit status is NOT the old 41/0x29 miscompile
#include <stdint.h>
#include <sys/wait.h>
extern int fork(void *subr); // GNO thread-spawn: subr = (void *)&childFn
extern int wait(int *status);
extern __attribute__((noreturn)) void _exit(int status);
#define M(a) (*(volatile uint16_t *)(a))
static void child(void) {
M(0x025010UL) = 0xC41D; // child was scheduled + entered child()
_exit(42); // exit code 42 -> status word 0x2A00
}
int main(void) {
M(0x025000UL) = 0x0000;
int pid = fork((void *)child);
if (pid <= 0) {
M(0x025016UL) = 0xDEAD; // fork failed / returned no child
M(0x025000UL) = 0xC0DE;
for (volatile unsigned long j = 0; j < 300000UL; j++) {
}
return 0;
}
int st = 0;
int w = wait(&st); // blocks until the child dies (race-free)
M(0x025014UL) = WIFEXITED(st) ? (uint16_t)WEXITSTATUS(st) : 0xEEEE; // == 0x002A
M(0x025016UL) = (w == pid) ? 0x0001 : 0xDEAD; // pid round-trip
M(0x025018UL) = (WEXITSTATUS(st) == 41) ? 0xDEAD : 0x600D; // guard old bug
M(0x025000UL) = 0xC0DE; // reached end
for (volatile unsigned long j = 0; j < 300000UL; j++) {
}
return 0;
}