Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ● Good question to press on — this is exactly the kind of sequence where people cargo-cult a set/way flush. The answer is no, and it's by construction: a D-cache clean before the
- MMU-off window is only needed if something will read or write memory around the cache while dirty lines cover it. The trampoline is deliberately structured so that never happens.
- Let me lay out the argument precisely.
- Dirty cache lines are not "stale" by themselves — they hold the newest data. They only become a hazard when an access bypasses them:
- 1. a non-cacheable read of a location whose newest value sits in a dirty line (reads old memory), or
- 2. a non-cacheable write that lands in memory, later overwritten by the eviction of an older dirty line.
- Now check the trampoline against that (loader/arch/aarch64/handover.asm):
- - Before the window: every value needed later is pulled into registers (handover.asm:53-64) while the MMU and caches are still on — these are ordinary WB hits. Then dsb sy; isb.
- - Inside the window (M=0, data accesses would be Device-nGnRnE): the instruction stream is exclusively mrs/msr/orr/bic/cbz/b/tlbi/dsb/isb — there is not a single load or store
- between the msr sctlr that clears M and the one that sets it again. No data access ⇒ neither hazard case can occur. This is exactly why the struct fields are hoisted into x1–x12 up
- front, and the comment at handover.asm:47-52 states that invariant.
- - Instruction fetches during the window aren't a problem either: with M=0, I=1 they're Write-Through cacheable, and the trampoline code hasn't been modified since it started
- executing, so the I-side is coherent with memory regardless.
- - After the window: the MMU comes back with RAM mapped Normal WB — the same attributes everything was written with. The dirty lines are simply valid cached data again; subsequent
- accesses hit them and see correct values. Same for the table walker: TCR.IRGN/ORGN = WB matches how the loader wrote the page tables, so walks correctly hit any still-dirty table
- lines.
- - Evictions during the window are harmless in themselves — an eviction writes back correct data; there's no non-cacheable write for it to clobber.
- So the "flush before disabling" recipe applies to a different design: it's mandatory when you're going to stay non-cacheable — i.e., hand off with caches disabled or RAM mapped NC,
- where the kernel's non-cacheable reads would bypass dirty lines forever. That was precisely the old Hyper handoff (SCTLR = M|SA, everything Normal-NC, NC walker), which did not
- clean the cache and was therefore genuinely broken on real hardware — dirty lines from the UEFI phase (page tables, kernel image, boot context) would have been invisible to the
- kernel's NC reads and could evict on top of its writes at any time. QEMU/TCG masked it. Switching to a matching-WB handoff is what made the no-flush trampoline legitimate; the
- alternative fix would have been keeping the NC handoff and adding a full clean+invalidate by VA over every loader-written structure — strictly worse (set/way cleans are also
- architecturally fragile outside of single-core, caches-off contexts).
- The one memory-coherency obligation that does survive in this design is the I-side vs D-side one for freshly written code — the kernel image was produced by data writes, and
- I-fetch doesn't snoop dirty D-lines on real cores. That's handled separately in handover_prepare_for() (dc cvau → dsb ish → ic ivau → dsb ish; isb over the image,
- loader/arch/aarch64/handover_impl.c:167), while the mappings are still up.
- So: no change needed — but it's a sharp check, and the two invariants it rests on (no loads/stores in the MMU-off window, matching WB attributes on both sides) are both documented
- in the trampoline comments precisely because breaking either one silently reintroduces the flush requirement.
Advertisement
Add Comment
Please, Sign In to add comment