I4 step 2: a write that is checked

Status: done and gated. A boot writes a file, reads it back and compares it byte for byte. Steps 1, 1b and 3 — write-back, the barrier, and the across-reboot check — are not done.

Why this before the mechanism

The plan lists write-back first. It is done second here, and the reason is in I2's own note: the block cache was left write-through deliberately, "because turning write-back on would trade a verified property for an unverified one, and write-back belongs with I4, where writes are read back and proved". Turning it on before the proof exists would be doing exactly that. So the proof lands first and the mechanism gets checked by it.

What it checks

A 1300-byte file, written, read back, compared.

Every byte carries its own offset. A constant survives every interesting failure this can have — a write that landed a sector early, a read that returned a neighbouring sector, a transfer that lost its last sector and left the previous contents — because all of them hand back bytes equal to what was expected. An offset-dependent pattern fails all three and says where.

1300 is not a multiple of 512 on purpose. A file of exactly N sectors never exercises the tail, and the tail is where a length confused with a byte count shows up. This project has had that defect once already, in the FAT reader that returned the size the directory claimed.

The buffer is cleared before the read, so a read that returns nothing at all cannot pass by leaving behind what was just written into it.

What it does not prove

That the bytes reached the medium. Everything here could be served from the block cache; with I2's write-through policy the device was written too, but this check cannot tell the difference. Only a reboot can, and that is step 3.

Three things it found immediately

The write refused, and the refusal said nothing. fat_write_file_locked had seven return -1s: a full disk, a name that is not 8.3, a directory with no free slot and a medium that would not take the sector all arrived at the caller as the same value. They name themselves now.

The parent did not exist. The first version wrote to DOCS/, and at mount time the root holds exactly EFI and STARTUP.NSH — the docs directory staged on the host is not presented to the guest at all, and DOCS/NOTES.TXT exists later only because the boot script creates it. The check was wrong about the medium, not about the kernel, and the refusal now says parent_directory_not_found rather than the earlier path_or_name_refused that sent the investigation looking at the name.

An [EXEC] line was assembled without bracketing, and CI caught it before this was committed:

[EXEC] interpreter EFI/BOOT/LDMUSL.SO[HW][SYS] write(ring3): SVC_EXIT svc-stress 10 0

Five serial_puts calls are five critical sections, and that one runs inside execve while services start on other cores. The boot gate's interleaved_lines check failed the run — which is the check working exactly as designed, since a cut line can invent a failure or hide one.

A scan for its siblings reported forty-nine more. Most are false positives: the scanner treats an unlock in an early-return branch as ending the critical section, so a correctly bracketed block like the [CRASH] dump reads as unbracketed. That is why the forty-nine were checked rather than fixed — a blanket edit of forty-nine sites on a bad number would have been the expensive kind of confident wrong answer.

Confirmed by breaking it

Skipping the data-sector writes while still returning success:

[IO] WRITE_PROOF first_bad_offset=0x0 got=0x30 want=0x5a
reason=invariant_failed:write_proof:FAILED:_contents_differ

That is the failure the phase is named for: a boot writes about thirty sectors through the shell's mkdir and nothing checked any of them, so a write path that acknowledged without writing was invisible until some later boot could not mount.

And the checks caught their own author

check-assertions-covered.py, written earlier the same session, failed on write_proof_missing the moment the assertion was added — before the phase was committed. The case was written; the count went back to its baseline. That is the whole of what that check is for, working on the first new assertion after it existed.


I4 steps 1 and 1b

write_many. Both drivers already had the multi-sector machinery — virtio_blk_rw_n and ahci_xfer each take a write flag — and neither exposed it, so the block layer's adapter wrote a run one sector at a time. The read side learned that lesson expensively: a 2 MiB transfer done a sector at a time under a lock that masks interrupts was indistinguishable from a hang and was reported as one. There was no equivalent on the write side only because until now nothing wrote enough to notice — a boot's entire writing is about thirty sectors.

The barrier. vibeos_blk_barrier(device) is not a flush, and the distinction is the point: a flush empties a cache, this only orders. They belong to different layers — the block cache above is emptied by vibeos_blockcache_flush, and what this does is tell the device to stop holding writes in its own volatile cache. A real drive acknowledges a write as soon as it reaches that cache, so a caller that has written and not called this has no ordering at all.

virtio-blk issues VIRTIO_BLK_T_FLUSH, which carries no data — the chain is the header and the status byte with nothing between them, so it could not be built out of virtio_blk_rw_n. AHCI issues FLUSH CACHE EXT, a command that had been defined in that file from the beginning and never issued: one more of the declared-and-unused family.

A device with no barrier is refused, not quietly granted. A null hook answers NO_DEVICE. A caller told "yes" would build on an ordering that is not there, which for a journal is the difference between a recoverable medium and a corrupt one.

The write proof asks for one before reading back, so the primitive is exercised on every boot rather than merely existing, and the gate asserts both that it was asked for at least once and that none failed.

What is left in I4

Step 3, the across-reboot check — the only test that separates a write that reached the medium from one that reached a cache. It needs a medium that survives a boot, and QEMU's synthesised FAT is not one: the guest does not even see the docs directory staged on the host. That makes it a job for the real disk images and VirtualBox rather than for the boot gate.

Write-back in the block cache is also still off. The policy note in I2 said it belongs here, and it does — but it belongs after step 3, because write-back is precisely the thing that makes "the bytes are on the medium" stop following from "the write returned". Turning it on while the only check is within a single boot would remove the property the check is testing.