arch_chain_load() is PANIC_UNIMPLEMENTED on arm64 (arch/arm64/arch.c:129), so the four callers — app/lkboot (two sites), app/moot, and the chain_load console command in lib/debugcommands — cannot work on any arm64 target. arm/arm, arm-m and x86 have real implementations.
PR #226 (2018, Payam Moradshahi, tested on a Khadas VIM2) was phase 1 of this and is being closed in favour of this issue; it is 1550 commits behind and no longer cherry-picks cleanly. It is worth reading for the shape of the solution, but it only covers the easy half. Recording what the hard parts actually are:
Tearing down the MMU. The PR's arm_chain_load does:
mrs x6, sctlr_el1
bic x6, x6, #(1 << 0) // Disable MMU
msr sctlr_el1, x6
isb
br x5
That is thin. Turning the MMU off means the caches must be cleaned to PoC and invalidated first, with the right barriers, and the code doing the disabling has to be running from a mapping that stays valid across the transition — i.e. identity mapped, since after SCTLR.M clears, VA==PA. The PR handles that last part by creating a scratch aspace and mapping the loader's surrounding 2MB at its physical address, which is the right idea, but there is no dsb around the msr, no TLB invalidate, and SCTLR.C/SCTLR.I are left alone.
Exception level. The asm hardcodes sctlr_el1 with a comment saying "LK runs in EL1". LK gets there via arm64_elX_to_el1, so by the time anything chain loads, the original EL is gone. The next stage generally expects to be entered at the EL the previous stage was entered at — a kernel handed control at EL2 wants EL2 back. The PR's author called this out explicitly as phase 2 and it was never written. Getting back up an EL is not symmetric with dropping down one; it needs either a stashed return path or firmware help.
Neither of these is arm64-specific trivia — they are the whole problem, which is why this has sat unimplemented. Anyone picking it up should probably also decide whether arch_chain_load is the right interface at all, given how much the state teardown differs per architecture.
arch_chain_load()isPANIC_UNIMPLEMENTEDon arm64 (arch/arm64/arch.c:129), so the four callers —app/lkboot(two sites),app/moot, and thechain_loadconsole command inlib/debugcommands— cannot work on any arm64 target. arm/arm, arm-m and x86 have real implementations.PR #226 (2018, Payam Moradshahi, tested on a Khadas VIM2) was phase 1 of this and is being closed in favour of this issue; it is 1550 commits behind and no longer cherry-picks cleanly. It is worth reading for the shape of the solution, but it only covers the easy half. Recording what the hard parts actually are:
Tearing down the MMU. The PR's
arm_chain_loaddoes:That is thin. Turning the MMU off means the caches must be cleaned to PoC and invalidated first, with the right barriers, and the code doing the disabling has to be running from a mapping that stays valid across the transition — i.e. identity mapped, since after
SCTLR.Mclears, VA==PA. The PR handles that last part by creating a scratch aspace and mapping the loader's surrounding 2MB at its physical address, which is the right idea, but there is nodsbaround themsr, no TLB invalidate, andSCTLR.C/SCTLR.Iare left alone.Exception level. The asm hardcodes
sctlr_el1with a comment saying "LK runs in EL1". LK gets there viaarm64_elX_to_el1, so by the time anything chain loads, the original EL is gone. The next stage generally expects to be entered at the EL the previous stage was entered at — a kernel handed control at EL2 wants EL2 back. The PR's author called this out explicitly as phase 2 and it was never written. Getting back up an EL is not symmetric with dropping down one; it needs either a stashed return path or firmware help.Neither of these is arm64-specific trivia — they are the whole problem, which is why this has sat unimplemented. Anyone picking it up should probably also decide whether
arch_chain_loadis the right interface at all, given how much the state teardown differs per architecture.