/* boot.c -- the machine's ROM assembles the system, then its capsules, then * the prompt. See boot.h. * * Nothing here uses the C library: the bare-metal kernel links this file. * It is not part of the engine (v4/src): it needs the capsule directory, * which only a whole system has. */ #include "v4/post.h" #include "v4/rom_entries.h" #include "v4/blocks.h" #include "v4/boot.h" #include "v4/rom_errors.h" #include "v4/sys_files.h" #include "v4/message.h" #include "v4/wire.h" #include "starkernel/capsule.h" #include "starkernel/capsule_generated.h" #include "starkernel/capsule_sig.h" #include "starkernel/capsule_blocks.h" /* The capsules, in the order they are loaded. */ static const char *const boot_capsules[] = { "v4:forth79.4th", }; /* POST is part of the boot, and is the kernel's (v4/include/v4/post.h; * docs/v4.0.0/NUCLEUS.md 6.3): when the capsules are in and the system is * sealed, the kernel feeds its cases to the node and judges them, puts the * node back to the system as sealed, and the boot passes only if none * failed. -DV4_POST_AT_BOOT=0 leaves it out, for work on a vocabulary * that does not pass yet. */ #ifndef V4_POST_AT_BOOT #define V4_POST_AT_BOOT 1 #endif #define LINE_MAX 1025u /* a line is at most 1024 characters, a block */ #define STEP_LIMIT 4000000000ULL /* instruction words one line may take */ /* ---- printing ------------------------------------------------------------ */ static void say(const v4_boot *b, const char *s) { unsigned len = 0; while (s[len]) len++; b->out(s, len); } static void say_dec(const v4_boot *b, uint32_t v) { char buf[10]; unsigned i = sizeof buf; do { buf[--i] = (char)('0' + v % 10u); v /= 10u; } while (v); b->out(buf + i, (unsigned)sizeof buf - i); } static void say_hex(const v4_boot *b, uint64_t v) { char buf[18]; unsigned i; buf[0] = '0'; buf[1] = 'x'; for (i = 0; i < 16; i++) buf[2 + i] = "0123456789abcdef"[(v >> (60 - 4 * i)) & 15u]; b->out(buf, sizeof buf); } /* ---- hashing: FNV-1a, 64 bits, as kernel/src/vm/parity.c ----------------- */ uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im) { return v4_image_dict_hash(n, im); } /* how many words FORTH holds: the list from LATEST, each entry's link in the * cell before its code */ static uint32_t word_count(const v4_node *n, const v4_image *im) { v4_cell xt = n->mem[im->latest]; uint32_t count = 0; while (xt > 0 && xt < (v4_cell)V4_NODE_WORDS && count < (uint32_t)V4_NODE_WORDS) { count++; xt = n->mem[xt - 1]; } return count; } /* ---- handing the node a line ---------------------------------------------- */ /* The boot is the node's neighbour on two of its ports. On port 0 it is * the kernel: the node writes the number of a request there, a word * (ENGINE.md 3.3) -- for a block (blocks.h), or for one of the host's * words. On port 1 it is the console: it puts a message of text on the * node's wire and takes off it the messages of what the node printed and * of how the text ended (message.h). */ #define KERNEL_PORT 0u #define CONSOLE_PORT 1u #define CONSOLE_ID 1 /* the console's number as a sender; the node's is 0 until it is given one */ /* What the kernel keeps of the node's block window (blocks.h). */ static v4_blocks boot_blocks; /* THE WIRE (docs/v4.0.0/MESH.md 7d.5). The lone node has no fabric: the * boot keeps the two queues of its console's wire, one each way (wire.h), * and does what the node asks of them, as a fabric does for a node of a * mesh (fabric.c). * THE KERNEL'S PORT HAS NO QUEUE (ruled 2026-10-08). The kernel is asked by * words written to port 0 and takes no messages: a message for that port * -- SEND-ON to it, or what a text prints when the way to its console has * been set there -- is refused at once, as on any port with no one to take * messages: the node's error 18, "No one on that port". */ static v4_wire_queue boot_wire[2]; /* the console's: [0] to the node, [1] from it */ #define TO_NODE (&boot_wire[0]) #define FROM_NODE (&boot_wire[1]) /* 1 if `count` words from `addr` are the node's memory and not its ports * or the addresses after them: they may be copied to and from. */ static int span_ok(const v4_node *n, v4_cell addr, unsigned count) { if (count == 0) return 1; if (count > V4_NODE_WORDS || addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0; if (n->port >= 0 && addr <= n->port + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > n->port) return 0; return 1; } /* The node is blocked at an operation on a wire (node.h, v4_node_doing): * do it, whole, and let the node go on; the operations and their answers * are MESH.md 7d.3's, as fabric.c does them. Returns 0 if the node sleeps * on: it asked to sleep (8), and no message has come since it last asked * which wires have one; or for room (9) that is not there, and nothing * has come off its wire either. */ static int boot_do(const v4_boot *bt) { v4_node *n = bt->n; v4_wire_queue *rx = n->wire == CONSOLE_PORT ? TO_NODE : 0, *tx = n->wire == CONSOLE_PORT ? FROM_NODE : 0; v4_cell a = n->wire_a, b = n->wire_b, h[V4_WIRE_HEADER], how = V4_WIRE_NO_ONE; unsigned cells; switch (n->do_op) { case 1: /* look */ if (!rx) break; if (rx->mark >= rx->used) how = V4_WIRE_NO_MESSAGE; else how = span_ok(n, a, V4_WIRE_HEADER) ? v4_wire_look(rx, &n->mem[a]) : V4_WIRE_NOT_A_MESSAGE; break; case 2: /* take */ if (!rx) break; if (!rx->looked || v4_wire_look(rx, h) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; } /* only the message last looked at (wire.h) */ cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER; if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) how = V4_WIRE_NOT_A_MESSAGE; else how = v4_wire_take(rx, &n->mem[a], cells ? &n->mem[b] : &n->mem[0], cells); break; case 3: /* move */ if (!rx) break; if (rx->mark >= rx->used || !rx->looked) how = V4_WIRE_NO_MESSAGE; else how = a == (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, FROM_NODE) : V4_WIRE_NO_ONE; break; case 4: /* drop */ if (rx) how = rx->looked ? v4_wire_drop(rx) : V4_WIRE_NO_MESSAGE; break; case 5: /* put */ if (!tx) break; cells = span_ok(n, a, V4_WIRE_HEADER) ? v4_wire_cells(n->mem[a + 6]) : 0; if (cells == 0 || !span_ok(n, b, cells - V4_WIRE_HEADER)) how = V4_WIRE_NOT_A_MESSAGE; else how = v4_wire_put(tx, &n->mem[a], cells > V4_WIRE_HEADER ? &n->mem[b] : &n->mem[0]); break; case 6: /* first */ if (rx) { v4_wire_first(rx); how = V4_WIRE_DONE; } break; case 7: /* next */ if (rx) how = v4_wire_next(rx); break; case 8: /* sleep */ if (!TO_NODE->arrived) return 0; how = V4_WIRE_DONE; break; case 9: /* sleep for room */ if (!tx) break; if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { how = V4_WIRE_NO_ROOM; break; } if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { how = V4_WIRE_DONE; break; } if (!TO_NODE->arrived && !FROM_NODE->freed) return 0; /* a message has come, or the console has taken one */ FROM_NODE->freed = 0; how = V4_WIRE_NO_ROOM; break; case 10: /* room */ if (!tx) break; how = a < 0 ? V4_WIRE_NOT_A_MESSAGE : a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM; break; default: /* not an operation */ how = V4_WIRE_NOT_A_MESSAGE; break; } v4_node_done(n, how); return 1; } /* The node is told which of its wires have a message and executes one * instruction word; if it fetched which wires have one, it has seen what * had come. The first half of a fabric's step (fabric.c). */ static void boot_exec(const v4_boot *b) { v4_node *n = b->n; v4_node_have(n, TO_NODE->used ? 1u << CONSOLE_PORT : 0u); (void)v4_exec_step_word(n, b->es, b->h); if (n->have_fetched) { TO_NODE->arrived = 0; n->have_fetched = 0; } } /* Time passes for the node, in the order a fabric's step has: that, and * then what it asked of a wire is done. Returns 0 if it is asleep at a * wire and sleeps on. */ static int boot_step(const v4_boot *b) { boot_exec(b); return v4_node_doing(b->n) ? boot_do(b) : 1; } /* Hand the node a line and run it to its end. What it prints goes to the * console; or, if `keep` is given, into `keep`, at most `cap` characters, * and none of it is shown: *printed is then how many it printed in all. * The console takes every message off the node's wire each time round, so * what the node prints never waits for room for long. */ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep, unsigned cap, unsigned *printed) { static v4_message out; /* one line at a time: the boot is not re-entered */ static v4_cell in[V4_WIRE_HEADER], in_text[V4_MSG_MAX_CHARS / 4u]; v4_node *n = b->n; uint64_t steps = 0; unsigned i; if (text) { /* with none, the node has a line waiting on its wire (boot.h) */ if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG; if (v4_wire_put(TO_NODE, out.word, out.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_REFUSED; /* no room: refused at once, as any message is */ } for (;;) { int awake = boot_step(b); if (n->stopped) return V4_BOOT_LINE_STOPPED; v4_wire_first(FROM_NODE); /* what the node has sent its console, in the order it was sent */ while (v4_wire_take(FROM_NODE, in, in_text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) { if (in[2] == V4_MSG_OUTPUT) { for (i = 0; i < (unsigned)in[6]; i++) { char c = (char)(((v4_ucell)in_text[i / 4u] >> (8u * (i % 4u))) & 0xFFu); if (!keep) b->out(&c, 1); else { if (*printed < cap) keep[*printed] = c; (*printed)++; } } } else if (in[2] == V4_MSG_DONE) { /* The text has ended. The node goes on to where it next asks something of a wire, with * nothing of its own on either stack: whoever called may then look at its stacks, or * empty them. */ for (i = 0; i < 4096u && !v4_node_doing(n) && !n->stopped && !n->asking; i++) boot_exec(b); return (int)in_text[0]; } } if (!awake) { if (n->do_op == 9) { /* waiting for room, which the console has just made */ if (++steps > STEP_LIMIT) return V4_BOOT_LINE_STOPPED; continue; } return V4_BOOT_LINE_WAITING; /* waiting for an answer: only the console can end that */ } if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */ if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */ v4_node_port_served(n); } else if (b->serve && n->request >= 1 && n->request <= (v4_cell)b->word_count) { b->serve(n, (unsigned)n->request); v4_node_port_served(n); } else { /* a request no one serves is an error, as any other (D-18): * 12, Argument out of range */ v4_node_port_served(n); v4_node_store(n, n->error_reg, 12); /* a store to NODE-ERROR raises it */ } continue; } if (n->asking) { /* a word written to a port with no one on it: an error on the node */ if (!v4_node_port_gone(n, V4_ERROR_NO_ONE)) return V4_BOOT_LINE_STOPPED; continue; } if (n->reading && !n->given) { /* a word read from a port: no one here writes one */ if (n->read_port != V4_PORT_ANY && v4_node_port_gone(n, V4_ERROR_NO_ONE)) continue; return V4_BOOT_LINE_STOPPED; /* waiting for what will not come */ } if (v4_image_waiting(n, b->im)) { /* the text is reading the keyboard */ int c = b->key ? b->key() : V4_BOOT_KEY_END; unsigned char ch = (unsigned char)c; if (c == V4_BOOT_KEY_END) return V4_BOOT_LINE_NO_INPUT; if (c >= 0) (void)v4_node_console_feed(n, &ch, 1u); continue; } if (++steps > STEP_LIMIT) return V4_BOOT_LINE_STOPPED; } } int v4_boot_line(const v4_boot *b, const char *text, unsigned len) { return run_line(b, text, len, 0, 0u, 0); } #if V4_POST_AT_BOOT /* POST's host (post.h): a line is handed to the node as any line is, and * what the node prints is kept for the runner and not shown. */ static int post_line(void *self, const char *text, unsigned text_len, char *out, unsigned cap, unsigned *len) { int how; *len = 0; how = run_line((const v4_boot *)self, text, text_len, out, cap, len); return (how == V4_TEXT_QUIT || how == V4_TEXT_COMPLETED || how == V4_TEXT_ERROR) ? how : -1; } static void post_say(void *self, const char *text, unsigned len) { ((const v4_boot *)self)->out(text, len); } #endif /* ---- one capsule --------------------------------------------------------- */ /* The capsule `name`: found, its hash checked, its signature verified -- one * that does not verify is refused, a missing one is only reported. Its * payload, or NULL with what was wrong said. */ static const uint8_t *open_capsule(const v4_boot *b, const char *name, const CapsuleDesc **found) { const CapsuleDirHeader *dir = capsule_get_directory(); const CapsuleDesc *descs = capsule_get_descriptors(); const CapsuleNameEntry *names = capsule_get_names(); const uint8_t *arena = capsule_get_arena(); const CapsuleDesc *cap; const uint8_t *p; CapsuleValidateResult vr; CapsuleSigResult sr; cap = capsule_find_by_name(dir, descs, names, name); if (!cap) { say(b, "V4: capsule "); say(b, name); say(b, " is not in this binary\n"); return 0; } vr = capsule_validate(cap, arena, dir->arena_size, 1); if (vr != CAPSULE_VALID) { say(b, "V4: capsule "); say(b, name); say(b, ": "); say(b, capsule_validate_result_str(vr)); say(b, "\n"); return 0; } sr = capsule_verify_signature(descs, names, capsule_get_signatures(), arena, dir->desc_count, (int)(cap - descs)); if (sr != CAPSULE_SIG_OK) { say(b, "V4: capsule "); say(b, name); say(b, " signature: "); say(b, capsule_sig_result_str(sr)); say(b, "\n"); if (sr == CAPSULE_SIG_INVALID) return 0; } p = capsule_get_payload(cap, arena); if (!p) { say(b, "V4: capsule "); say(b, name); say(b, " has no payload\n"); return 0; } *found = cap; return p; } static void say_where(const v4_boot *b, const char *name, uint32_t block, uint32_t line) { say(b, "\nV4: capsule "); say(b, name); say(b, " block "); say_dec(b, block); say(b, " line "); say_dec(b, line); } static int load_capsule(const v4_boot *b, const char *name) { const CapsuleDesc *cap; const uint8_t *p, *end; uint32_t block = 0, line = 0; int in_block = 0; p = open_capsule(b, name, &cap); if (!p) return 0; end = p + cap->length; while (p < end) { const uint8_t *after, *nl; uint32_t num; unsigned len, i; char text[LINE_MAX]; if (capsule_block_header(p, end, &num, &after)) { block = num; line = 0; in_block = 1; p = after; continue; } for (nl = p; nl < end && *nl != '\n'; nl++) { } len = (unsigned)(nl - p); if (len && p[len - 1] == '\r') len--; if (in_block) { line++; if (len >= LINE_MAX) { say_where(b, name, block, line); say(b, " is too long\n"); return 0; } if (len) { for (i = 0; i < len; i++) text[i] = (char)p[i]; int how = v4_boot_line(b, text, len); if (how != V4_TEXT_COMPLETED) { say_where(b, name, block, line); say(b, how == V4_TEXT_ERROR ? " ended in an error: " : how == V4_TEXT_QUIT ? " ended with QUIT: " : how == V4_BOOT_LINE_NO_INPUT ? " reads the keyboard: " : " stopped the node: "); b->out(text, len); say(b, "\n"); return 0; } } } p = (nl < end) ? nl + 1 : end; } say(b, "PARITY:V4_CAPSULE name="); say(b, name); say(b, " capsule_id="); say_hex(b, cap->capsule_id); say(b, " capsule_hash="); say_hex(b, cap->content_hash); say(b, " dict_hash="); say_hex(b, v4_boot_dict_hash(b->n, b->im)); say(b, "\n"); return 1; } /* ---- the system: the machine's ROM assembles it ----------------------------- */ /* The ROM (docs/v4.0.0/MACHINE.md section 2): the machine's one binary, * built by v4/tools/mkrom.c for this cell width. The machine starts at its * entry V4_ROM_START (v4/rom_entries.h), (ROM) (capsules/v4/rom/main.f18), * which reads the system's source from the console, assembles it into RAM * and, at the source's `start`, runs it. */ #if V4_CELL_BITS == 64 extern const v4_cell v4_rom_64[]; extern const unsigned v4_rom_64_words; #define BOOT_ROM v4_rom_64 #define BOOT_ROM_WORDS v4_rom_64_words #else extern const v4_cell v4_rom_32[]; extern const unsigned v4_rom_32_words; #define BOOT_ROM v4_rom_32 #define BOOT_ROM_WORDS v4_rom_32_words #endif #define ROM_STEP_LIMIT 400000000ULL /* instruction words the ROM may take: it takes about 16 million */ #define EOT 4 /* the end of the source: the ROM's (KEY) sees no more */ /* The system's source, in the order the ROM reads it: the memory map, then * the system (v4/sys_files.h), then its end -- the state it is switched on * in, and `start`. capsules/v4/sys; each blob is a capsule like any other. */ #define MAP_NAME_(bits) "v4:sys:map-" #bits ".f18" #define MAP_NAME__(bits) MAP_NAME_(bits) static const char *const system_blobs[] = { #define SYS_BLOB(file) "v4:sys:" #file ".f18", MAP_NAME__(V4_CELL_BITS), V4_SYS_FILES(SYS_BLOB) "v4:sys:end.f18", #undef SYS_BLOB }; #define SYSTEM_BLOBS (sizeof system_blobs / sizeof system_blobs[0]) /* What the ROM's error codes mean (v4/rom_errors.h): the ROM stops with the * code, and the loader, which owns the console, says it. Code c is entry c. */ #define ROM_ERROR_TEXT(name, code, text) text, static const char *const rom_errors[] = { "", V4_ROM_ERRORS(ROM_ERROR_TEXT) }; #undef ROM_ERROR_TEXT #define ROM_ERRORS (sizeof rom_errors / sizeof rom_errors[0]) /* FNV-1a over the ROM, a cell at a time, low byte first, as the dictionary * hash is (image.c). */ static uint64_t rom_hash(void) { uint64_t h = 0xcbf29ce484222325ULL; unsigned w, i; for (w = 0; w < BOOT_ROM_WORDS; w++) for (i = 0; i < V4_CELL_BITS / 8; i++) { h ^= (uint64_t)(((v4_ucell)BOOT_ROM[w] >> (8 * i)) & 0xFFu); h *= 0x00000100000001b3ULL; } return h; } /* The machine starts in its ROM, and the boot is its console: it feeds the * system's source to the console a blob at a time, each found and checked * as any capsule is, with a new line and V4_ROM_BLOB_END after each (where * the ROM ends a blob as text.c ends a file), and then EOT, until the ROM * has assembled the system and started it. What the ROM had not read of * the source when it started the system is the source's, not the system's * console input, and is taken back. Then the node is where the * system waits for a message, and its host reads from the image record * what only the system knows (v4_image_from_node). If the ROM stops on an * error, the blob and line are said, and what the error is. */ static int load_system(const v4_boot *b) { static const uint8_t *payload[SYSTEM_BLOBS]; static uint32_t length[SYSTEM_BLOBS], first_line[SYSTEM_BLOBS]; static const char blob_end[2] = { '\n', V4_ROM_BLOB_END }, eot = EOT; v4_node *n = b->n; uint64_t steps = 0; uint32_t lines = 0, at = 0; unsigned i, blob = 0; int fed_all = 0; for (i = 0; i < SYSTEM_BLOBS; i++) { const CapsuleDesc *cap; uint32_t k; payload[i] = open_capsule(b, system_blobs[i], &cap); if (!payload[i]) return 0; length[i] = (uint32_t)cap->length; first_line[i] = lines; for (k = 0; k < length[i]; k++) if (payload[i][k] == '\n') lines++; lines++; /* the new line after it */ say(b, "PARITY:V4_F18 name="); say(b, system_blobs[i]); say(b, " capsule_hash="); say_hex(b, cap->content_hash); say(b, "\n"); } v4_node_rom_attach(n, BOOT_ROM, BOOT_ROM_WORDS); n->p = V4_ROM_BASE + (v4_cell)V4_ROM_START; while (n->p >= V4_ROM_BASE) { /* the console is given more of the source whenever it has taken * all it had */ if (!fed_all && n->input_pos == n->input_len) { if (blob < SYSTEM_BLOBS && at < length[blob]) { at += v4_node_console_feed(n, payload[blob] + at, length[blob] - at); } else if (blob < SYSTEM_BLOBS) { at += v4_node_console_feed(n, blob_end + (at - length[blob]), length[blob] + 2u - at); if (at == length[blob] + 2u) { blob++; at = 0; } } else if (v4_node_console_feed(n, &eot, 1)) { fed_all = 1; } } (void)v4_exec_step_word(n, b->es, b->h); if (n->stopped || ++steps > ROM_STEP_LIMIT) { say(b, "V4: the machine's ROM did not assemble the system\n"); return 0; } if ((steps & 1023u) == 0 && n->mem[b->im->rom_failed] != 0) { v4_cell code = n->mem[b->im->rom_failed]; uint32_t line = (uint32_t)n->mem[b->im->rom_errline]; for (i = SYSTEM_BLOBS; i-- > 1 && first_line[i] > line; ) { } say(b, "V4: "); say(b, system_blobs[i]); say(b, " line "); say_dec(b, line - first_line[i] + 1u); say(b, ": "); say(b, code > 0 && (unsigned)code < ROM_ERRORS ? rom_errors[code] : "an error the ROM has no words for"); say(b, "\n"); return 0; } } n->input_pos = n->input_len = 0; say(b, "PARITY:V4_ROM words="); say_dec(b, BOOT_ROM_WORDS); say(b, " rom_hash="); say_hex(b, rom_hash()); say(b, " instructions="); say_dec(b, (uint32_t)b->es->anticlock); say(b, "\n"); if (!v4_image_from_node(n, b->im) || n->p != b->im->idle) { say(b, "V4: the system the ROM assembled has no image record, or did not start where it says it waits\n"); return 0; } v4_node_fault_attach(n, b->im->fault_table); /* until it is where the system waits for a message: asleep, no message * having come */ for (;;) { if (!boot_step(b)) { if (n->do_op == 8) break; say(b, "V4: the system did not come to where it waits\n"); return 0; } if (n->stopped || n->asking || ++steps > STEP_LIMIT) { say(b, "V4: the system did not come to where it waits\n"); return 0; } } return 1; } /* ---- the whole boot ------------------------------------------------------ */ int v4_boot_run(const v4_boot *b) { unsigned i; /* a node is born with nothing in it, and its ROM assembles the system */ if (!v4_image_born(b->n, b->es, b->h, b->im)) { say(b, "V4: the memory map is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n"); return 0; } /* its console's wire, with nothing on it */ v4_wire_reset(TO_NODE); v4_wire_reset(FROM_NODE); v4_blocks_init(&boot_blocks, b->im->block_window); if (!load_system(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; } /* the kernel's words, before any capsule that might use one */ for (i = 0; i < b->word_count; i++) { char text[64]; unsigned at = 0, k, num = i + 1, div = 1; while (num / div >= 10) div *= 10; for (; div; div /= 10) text[at++] = (char)('0' + num / div % 10); for (k = 0; " KERNEL-WORD "[k]; k++) text[at++] = " KERNEL-WORD "[k]; for (k = 0; b->words[i][k] && at < sizeof text; k++) text[at++] = b->words[i][k]; if (b->words[i][k] || v4_boot_line(b, text, at) != V4_TEXT_COMPLETED) { say(b, "V4: kernel word "); say(b, b->words[i]); say(b, " could not be made\nPARITY:FAIL\nPOST: FAILED\n"); return 0; } } for (i = 0; i < sizeof boot_capsules / sizeof boot_capsules[0]; i++) if (!load_capsule(b, boot_capsules[i])) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; } /* The kernel's words and the capsules are the system: COLD comes back * to here, and FORGET will not go below it. */ v4_image_seal(b->n, b->im); #if V4_POST_AT_BOOT { static const char cold[] = "COLD"; v4_post_host host; v4_post_tally tally; unsigned printed = 0; int how; host.self = (void *)b; host.n = b->n; host.line = post_line; host.say = post_say; if (!v4_post_run(&host, v4_post_cases, v4_post_case_count, &tally)) { say(b, "V4: POST reported failures\nPARITY:FAIL\nPOST: FAILED\n"); return 0; } /* POST leaves nothing: the node is put back to the system as it was * sealed, as v3 puts the dictionary back after each of its suites * (v3/src/test_runner/test_common.c, run_test_suite). What COLD * prints is not shown. */ v4_dstack_reset(&b->n->ds); how = run_line(b, cold, (unsigned)sizeof cold - 1u, (char *)cold, 0u, &printed); if (how != V4_TEXT_COMPLETED && how != V4_TEXT_QUIT) { say(b, "V4: the node did not come back to the system after POST\nPARITY:FAIL\nPOST: FAILED\n"); return 0; } } #endif say(b, "PARITY:V4_SYSTEM word_count="); say_dec(b, word_count(b->n, b->im)); say(b, " dict_hash="); say_hex(b, v4_boot_dict_hash(b->n, b->im)); say(b, "\n"); #if V4_POST_AT_BOOT say(b, "PARITY:OK\nPOST: PASSED\n"); #else say(b, "PARITY:OK\n"); #endif return 1; }