Why split. The first run of docs/tasks/6.5a-ii.md stopped under rule 3
at 411 changed non-spec lines with part (a) unfinished and nothing
committed. Its draft, report and boot log are kept for reference, not to be
copied blindly, at
/tmp/claude-1000/-workspace-tree/a0ae0c06-f036-4a7e-a6d5-e2cc56695fb6/scratchpad/6.5a-ii-attempt1/
(tracked.diff, untracked.tar, 6.5a-ii-report.md, 6.5a-ii-a-new.log).
Part (a) splits along the seam that run found: the firmware standing up on
its own, then loading an application. Part (b) is unchanged. Three
branches, one after another, each from trunk after the previous one is
merged: slice/6.5a-ii-1, slice/6.5a-ii-2, slice/6.5a-ii-3, each one
check-in.
docs/tasks/6.5a-ii.md stays the specification for everything not restated
here: its Read first, Shape, Constraints and Demonstration apply to
every part. Read it first.
The first run's findings, which the parts must avoid.
- The firmware never ran. Glint placed it ("Guest image: guest.efi, 44
KiB at 0x40200000, entry 0x40201000, 0 relocations") and the guest
faulted on its first fetch at
0x200: the image jumped to an address from its link layout, not where it was placed. The test guest is placed the same way and works, so build the firmware exactly asbuild_test_guestbuilds the test guest (same linker script, same position independence), or give it base relocations that 3.6b applies. Show which in the check-in, withllvm-objdumpof its entry. - The test application's PE was refused by 3.6's parser with
EFI_LOAD_ERROR. Find which check refused it before changing either the build or the parser, and say which. If the parser is right, fix the build; if the parser is too strict for a valid image, fix the parser and add a spec for that header shape. - A function grew to 2,976 bytes (
GuestEFI.build). Build tables in small functions from the start. - x86_64 was built inside the seed because a new host spec required
spec/image_initialization_spec.cr. Don't require it from a new spec; run it on its own. - One spec expectation was wrong (14 UCS-2 units read for the
15-character
console=ttyAMA0). Derive lengths from the string in the spec, not by hand.
Part 1 (slice/6.5a-ii-1): the firmware stands up
The firmware is Glint's guest. Glint loads it as it loads the test guest
(bin/glint-run aarch64 --mode hypervisor --guest
build/aarch64/guest/efi.efi), with 6.5a (i)'s tree in x0. It builds its
system table, ConOut over the PL011 with 4.5's filter rule, ConIn "not
ready", the configuration table with the devicetree GUID, and every other
boot service returning EFI_UNSUPPORTED. Then, with no application yet, it
calls its own ConOut to print guest firmware: system table ok,
devicetree in configuration table and powers off through PSCI. Host specs:
the table headers (signatures, revisions, sizes, CRC32s) read back; the
UCS-2 filter. About 200 non-spec lines.
Part 2 (slice/6.5a-ii-2): it starts an EFI application
Glint places the application's file bytes in guest memory (where, and how
the firmware learns it, stated: a /chosen property such as
glint,efi-app with address and size is one way). The firmware loads it
with 3.6's rules, shared and not copied, installs the loaded-image protocol
on its handle (base, size, load options from /chosen/bootargs as UCS-2),
and calls its entry. bin/glint-run --guest-efi FILE and bin/glint-build
efi-app aarch64 as in the original task; the application prints its three
lines (efi app: started, efi app: devicetree found, efi app: load
options "<bootargs>"), returns, and the firmware powers off. Specs: the
application's PE parsed and loaded on the host; the loaded-image fields;
the boot. About 200 non-spec lines.
Part 3 (slice/6.5a-ii-3): memory, ExitBootServices, runtime services
Exactly part (b) of docs/tasks/6.5a-ii.md.
Check-in comments. Rule 2's four parts, one per part.
Part 2, split again (Nov 11): 2a, then 2b
The first run of Part 2 stopped under rule 3 at 402 changed non-spec lines,
with the application not yet started and nothing committed. Its draft and
report are at
/tmp/claude-1000/-workspace-tree/a0ae0c06-f036-4a7e-a6d5-e2cc56695fb6/scratchpad/6.5a-ii-2-attempt1/
(tracked.diff, untracked.tar, 6.5a-ii-2-report.md). It found finding
2's cause: the test application's .data section is all BSS (SizeOfRawData
0), but boot/crt0.S writes its PointerToRawData as _data_rva (16,384),
past the end of the 12,800-byte file, and 3.6's parser rightly refuses a
section whose raw span starts outside the file. The draft worked around it
by adding a data byte to the application; don't. Fix the header instead.
Two branches, one after the other, each one check-in.
2a (slice/6.5a-ii-2a): the shared PE loader and the header fix. About
100 non-spec lines.
boot/efi.ldsandboot/crt0.S: when a section has no raw data, its PointerToRawData is 0, as the PE format specifies for uninitialized data. Glint's own images and the existing guests must come out byte-identical (their.datahas raw bytes); check withcmpand say so. A guest built with an all-BSS.datanow parses.boot/pe_loader.cr: the section copy and relocation placement thatGuestSpace#loaddoes today, extracted so the firmware can use the same code over its own memory.GuestSpace#loadcalls it; no change in what it places (3.6b's CRC spec and every guest boot unchanged).- Specs: 3.6's parser over a header with an all-BSS section (accepted when PointerToRawData is 0, still refused when it points past the file); the extracted loader over the existing fixtures.
2b (slice/6.5a-ii-2b): starting the application. Part 2 as above, on
2a's loader, without the data-byte workaround, and with two fixes the
draft's report names: the loaded-image protocol answers only for the one
handle the firmware installed (compare the handle, don't trust a field
inside whatever it points to); and guest_efi_main stays under 2 KiB in
the release build (the draft's grew to 4,020 bytes by inlining; keep the
pieces as separate functions and check the built sizes, not the source).
Run boots one at a time: the draft's two runs collided on the shared ESP
image. About 250 non-spec lines.