A block write the device refuses after the kernel has taken the node's copy: the node was told "Storage refused" while the kernel's cache kept the new data, to be read back and perhaps written later. The kernel now puts its own copy back as it was. Tested with a device whose writes can be made to fail; it failed first. POST's runner: a case whose starting state could not be set says so, with what the node said, where it showed nothing; and a case that ends with QUIT fails, as it did when the cases were a capsule, where the runner had taken it for a completed line. Both tests failed first. The v4 boot says "Artemis: virtio-blk attached" only when the attach worked. make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and riscv64 boot, POST 538 of 538, dict_hash 0x5f0a949a6fc8ef2b on all six: logs/20261007-135741, -140004, -140329. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
4.6 KiB
C
121 lines
4.6 KiB
C
/* post.c -- POST's runner: the kernel feeds a case to the node and judges it
|
|
* from outside. See post.h; docs/v4.0.0/NUCLEUS.md section 6.3.
|
|
*
|
|
* Nothing here uses the C library: the bare-metal kernel links this file.
|
|
*/
|
|
#include "v4/post.h"
|
|
#include "v4/message.h"
|
|
|
|
#define SHOWN_OUT 255u /* how much of a failing case's printing is shown */
|
|
#define SHOWN_STACK 32u /* and how many of the values it left */
|
|
|
|
static const char state_line[] = "DECIMAL FORTH DEFINITIONS";
|
|
|
|
static unsigned length(const char *s)
|
|
{
|
|
unsigned n = 0;
|
|
while (s[n]) n++;
|
|
return n;
|
|
}
|
|
|
|
static void say(const v4_post_host *h, const char *s)
|
|
{
|
|
h->say(h->self, s, length(s));
|
|
}
|
|
|
|
static void say_number(const v4_post_host *h, long long v)
|
|
{
|
|
char text[24];
|
|
unsigned at = sizeof text;
|
|
unsigned long long u = v < 0 ? 0ull - (unsigned long long)v : (unsigned long long)v;
|
|
|
|
do { text[--at] = (char)('0' + u % 10u); u /= 10u; } while (u);
|
|
if (v < 0) text[--at] = '-';
|
|
h->say(h->self, text + at, (unsigned)sizeof text - at);
|
|
}
|
|
|
|
int v4_post_run(const v4_post_host *h, const v4_post_case *cases, unsigned count, v4_post_tally *tally)
|
|
{
|
|
static char out[V4_POST_OUT]; /* what the case being run has printed */
|
|
v4_cell left[SHOWN_STACK]; /* what it left on the stack, the top first */
|
|
unsigned i, k;
|
|
|
|
tally->tests = 0; tally->pass = 0; tally->fail = 0;
|
|
for (i = 0; i < count; i++) {
|
|
const v4_post_case *c = &cases[i];
|
|
unsigned kept = 0, total = 0, len = 0, depth;
|
|
int errored = 0, came_back = 1, lost = 0, no_state = 0, quit = 0, ok, how;
|
|
|
|
/* every case starts from the same state */
|
|
v4_dstack_reset(&h->n->ds);
|
|
how = h->line(h->self, state_line, (unsigned)sizeof state_line - 1u, out, V4_POST_OUT, &len);
|
|
if (how < 0) lost = 1;
|
|
if (how != V4_TEXT_COMPLETED) { /* what the node said of it is what is shown */
|
|
came_back = 0; no_state = 1;
|
|
total = len; kept = len < V4_POST_OUT ? len : V4_POST_OUT;
|
|
}
|
|
v4_dstack_reset(&h->n->ds);
|
|
|
|
for (k = 0; came_back && k < c->line_count; k++) {
|
|
len = 0;
|
|
how = h->line(h->self, c->lines[k], length(c->lines[k]), out + kept, V4_POST_OUT - kept, &len);
|
|
if (how < 0) { came_back = 0; lost = 1; break; }
|
|
if (how == V4_TEXT_ERROR) errored = 1;
|
|
if (how == V4_TEXT_QUIT) quit = 1; /* it went back to the terminal: it did not complete */
|
|
total += len;
|
|
kept = total < V4_POST_OUT ? total : V4_POST_OUT;
|
|
}
|
|
|
|
/* what it left, read before anything is judged */
|
|
depth = h->n->ds.depth;
|
|
for (k = 0; k < depth; k++) {
|
|
v4_cell v = v4_dstack_pop(&h->n->ds);
|
|
if (k < SHOWN_STACK) left[k] = v;
|
|
}
|
|
v4_dstack_reset(&h->n->ds);
|
|
|
|
if (!came_back || quit) {
|
|
ok = 0;
|
|
} else if (c->expect == V4_POST_ERROR) {
|
|
ok = errored;
|
|
} else if (errored) {
|
|
ok = 0;
|
|
} else {
|
|
ok = depth == c->depth;
|
|
if (ok && c->expect == V4_POST_STACK) {
|
|
if (depth > SHOWN_STACK) ok = 0;
|
|
for (k = 0; ok && k < depth; k++) ok = left[k] == (v4_cell)c->stack[depth - 1u - k];
|
|
}
|
|
if (ok && c->expect != V4_POST_DEPTH) {
|
|
ok = total == c->output_len && total <= V4_POST_OUT;
|
|
for (k = 0; ok && k < total; k++) ok = out[k] == c->output[k];
|
|
}
|
|
}
|
|
|
|
tally->tests++;
|
|
if (ok) { tally->pass++; continue; }
|
|
tally->fail++;
|
|
say(h, "POST FAIL: "); say(h, c->name);
|
|
if (no_state) say(h, " could not be put in its starting state:");
|
|
if (quit) say(h, " ended with QUIT:");
|
|
say(h, " out<"); h->say(h->self, out, kept < SHOWN_OUT ? kept : SHOWN_OUT);
|
|
say(h, "> stack<");
|
|
for (k = depth < SHOWN_STACK ? depth : SHOWN_STACK; k-- > 0; ) { say_number(h, (long long)left[k]); say(h, " "); }
|
|
say(h, ">\n");
|
|
|
|
/* The node did not come back from a line: it is stopped, or will
|
|
* run for ever. Nothing more can be asked of it, so POST ends
|
|
* here and says which case it was. */
|
|
if (lost) {
|
|
say(h, "POST: the node did not come back from "); say(h, c->name);
|
|
say(h, "; "); say_number(h, (long long)(count - i - 1u)); say(h, " not run\n");
|
|
break;
|
|
}
|
|
}
|
|
say(h, "PARITY:V4_POST tests="); say_number(h, (long long)tally->tests);
|
|
say(h, " pass="); say_number(h, (long long)tally->pass);
|
|
say(h, " fail="); say_number(h, (long long)tally->fail);
|
|
say(h, "\n");
|
|
return tally->fail == 0;
|
|
}
|