From: Fangyu Yu <[email protected]> Add riscv_kexec_relocate_entry to .kexec.tramp.text and the two asm-visible globals (riscv_kexec_relocate_entry_pa and riscv_kexec_cc_buffer_pa) that the wrapper consumes.
The wrapper performs the same two-step transition used by the crash path: switch to the trampoline pgd, jump to the PA of self, then drop the MMU with PC already on a PA. It finally jumps to the PA of control_code_buffer. machine_kexec_prepare() publishes the wrapper PA via WRITE_ONCE for non-crash images. The per-image control_code_buffer PA is published later, at dispatch time, so a load failure between prepare() and the kexec_image swap cannot leave the global pointing at a freed page. Nothing routes to the wrapper yet; the switchover happens in the follow-up patch. Signed-off-by: Fangyu Yu <[email protected]> --- arch/riscv/include/asm/kexec.h | 1 + arch/riscv/kernel/kexec_relocate.S | 36 ++++++++++++++++++++++++++++++ arch/riscv/kernel/machine_kexec.c | 5 +++++ 3 files changed, 42 insertions(+) diff --git a/arch/riscv/include/asm/kexec.h b/arch/riscv/include/asm/kexec.h index 6466c1f00d41..b75cab959e53 100644 --- a/arch/riscv/include/asm/kexec.h +++ b/arch/riscv/include/asm/kexec.h @@ -53,6 +53,7 @@ typedef void (*riscv_kexec_method)(unsigned long first_ind_entry, unsigned long va_pa_off); extern riscv_kexec_method riscv_kexec_norelocate; +extern riscv_kexec_method riscv_kexec_relocate_entry; #ifdef CONFIG_KEXEC_FILE extern const struct kexec_file_ops elf_kexec_ops; diff --git a/arch/riscv/kernel/kexec_relocate.S b/arch/riscv/kernel/kexec_relocate.S index 8cfdf6f4032a..6c624560c9ac 100644 --- a/arch/riscv/kernel/kexec_relocate.S +++ b/arch/riscv/kernel/kexec_relocate.S @@ -225,6 +225,42 @@ SYM_CODE_START(riscv_kexec_norelocate) SYM_CODE_END(riscv_kexec_norelocate) +.extern riscv_kexec_relocate_entry_pa +.extern riscv_kexec_cc_buffer_pa +.section ".kexec.tramp.text", "ax" +SYM_CODE_START(riscv_kexec_relocate_entry) + /* + * Two-pass entry, identical in shape to riscv_kexec_norelocate: + * - 1st entry: t3 == 0 (initialized by machine_kexec()). + * - 2nd entry: t3 == PA of riscv_kexec_relocate_entry, so auipc + * matches t3 and we fall through to label 1. + * Args a0..a4 are passed through unchanged to riscv_kexec_relocate. + */ + auipc t0, 0 + beq t0, t3, 1f + + la t0, riscv_kexec_relocate_entry_pa + REG_L t3, 0(t0) + la t0, kexec_tramp_satp + REG_L t1, 0(t0) + csrw CSR_SATP, t1 + sfence.vma x0, x0 + + jr t3 +1: + /* + * Now executing at the PA of this wrapper with the trampoline pgd + * installed (identity-mapped). Drop the MMU; PC stays valid because + * it is already a PA. + */ + csrw CSR_SATP, zero + + /* Jump to the PA of control_code_buffer to run the relocate body. */ + la t0, riscv_kexec_cc_buffer_pa + REG_L t0, 0(t0) + jr t0 +SYM_CODE_END(riscv_kexec_relocate_entry) + .section ".rodata" SYM_DATA(riscv_kexec_relocate_size, .long riscv_kexec_relocate_end - riscv_kexec_relocate) diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index d82f45fb44b6..71688c63af65 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -20,6 +20,8 @@ unsigned long kexec_tramp_satp; unsigned long riscv_kexec_norelocate_pa; +unsigned long riscv_kexec_relocate_entry_pa; +unsigned long riscv_kexec_cc_buffer_pa; /* * Trampoline page tables. Both the VA(trampoline)->PA and the * PA(trampoline)->PA identity mapping are installed in this single @@ -164,6 +166,9 @@ machine_kexec_prepare(struct kimage *image) /* Mark the control page executable */ set_memory_x((unsigned long) control_code_buffer, 1); + + WRITE_ONCE(riscv_kexec_relocate_entry_pa, + __pa_symbol(&riscv_kexec_relocate_entry)); } else { WRITE_ONCE(riscv_kexec_norelocate_pa, __pa_symbol(&riscv_kexec_norelocate)); -- 2.50.1
