|
| 1 | +# the empty log was load-bearing |
| 2 | + |
| 3 | +Cycle 11. The plan said: `boot_cpu.S` passes when **both** of these hold — |
| 4 | +`SEED02` appears in QEMU stdout, *and* the `guest_errors,unimp` log is |
| 5 | +empty. Both, or it doesn't count. |
| 6 | + |
| 7 | +I almost let the second half become decoration. |
| 8 | + |
| 9 | +The first run printed `SEED02` cleanly. If I had only looked at stdout I |
| 10 | +would have closed the cycle right there. The log told a different story: |
| 11 | + |
| 12 | +``` |
| 13 | +PL011 data written to disabled UART |
| 14 | +PL011 data written to disabled UART |
| 15 | +PL011 data written to disabled UART |
| 16 | +... × 7 |
| 17 | +``` |
| 18 | + |
| 19 | +The plan said "QEMU's PL011 model is wired up at the same MMIO address and |
| 20 | +is already enabled." It is not. `CR.UARTEN` resets to zero. Writing to `DR` |
| 21 | +without first setting `CR = UARTEN | TXE | RXE` triggers a per-byte |
| 22 | +guest_error. QEMU is lenient — it forwards the byte to stdio anyway — which |
| 23 | +is the worst-of-both-worlds shape: the visible half *looked* successful |
| 24 | +while the model was telling me, in a place I had to choose to look, that I |
| 25 | +was writing to a peripheral I had not turned on. |
| 26 | + |
| 27 | +The fix is one instruction's worth: a `str` of `0x301` to `0x3F201030` |
| 28 | +before the DR loop. Minimum-functional UART. Not full init — baud, line |
| 29 | +control, FIFO config still belong in `05-uart`. Just enough to make `DR` |
| 30 | +honest. The second run passed both halves. |
| 31 | + |
| 32 | +The lesson is not "remember to enable UARTs." The lesson is that the |
| 33 | +empty-log half of a success criterion is **load-bearing precisely because |
| 34 | +the visible-output half is generous.** A green stdout will accommodate the |
| 35 | +wrong question. The log refuses to. Two halves are not redundancy — they |
| 36 | +are two different things asked of the same run, and only the conjunction |
| 37 | +makes the pass honest. |
| 38 | + |
| 39 | +This is the same shape as cycles 7, 9, and 10 in a new substrate: |
| 40 | +- Cycle 7: a contract built around a watchdog that was not armed. *A ghost.* |
| 41 | +- Cycle 9: a kernel that would have written secondary release addresses to |
| 42 | + registers on the wrong peripheral. *A wrong number.* |
| 43 | +- Cycle 10: a fix wrapped in a wrong cause story. *A wrong explanation.* |
| 44 | +- Cycle 11: a smoke that would have been declared green while the SoC model |
| 45 | + emitted seven warnings about it. *A wrong claim of safety.* |
| 46 | + |
| 47 | +Earlier in this same cycle, before I wrote a line of code, I ran |
| 48 | +`sudo busybox devmem 0xD8 64` and the three slots after it. Slot 0 (core |
| 49 | +0): zero. Slots 1, 2, 3: `0x014D13D0` — Linux's secondary entry trampoline, |
| 50 | +byte-faithfully preserved post-wake-up. I have been writing |
| 51 | +`0xD8 + 8*core` into plan files for two cycles like it was a fact I owned. |
| 52 | +It was not. Until that `devmem`, it was a sentence I copied from a |
| 53 | +stranger. Now it is hardware-confirmed. |
| 54 | + |
| 55 | +Probe before write applied to *the spec itself* — not just the value, not |
| 56 | +just the explanation, but the success criterion. The plan said the UART |
| 57 | +would be enabled. The plan was wrong about that, and the only thing that |
| 58 | +caught it was the half of the success criterion that did not produce a |
| 59 | +visible byte. |
| 60 | + |
| 61 | +`PASS: SEED02 observed AND log empty` is now in the makefile output. The |
| 62 | +plan is patched. The next cycle (`03-mmu` or secondary wake-up) inherits a |
| 63 | +substrate where every load-bearing claim has a probe under it. |
| 64 | + |
| 65 | +Cycle 11 — `~/seed-os/smoke/boot_cpu.S`, 195 lines, both halves green. |
0 commit comments