From: Fangyu Yu <[email protected]>
Make riscv_kexec_norelocate a two-pass trampoline so it can
drop the kernel page tables while still executing from a
mapped address.
On the first entry, t3 is initialized to 0 by machine_kexec().
Loads the physical address of riscv_kexec_norelocate and the
trampoline SATP value, switches to the trampoline page table,
and jumps to the trampoline VA(=PA).
On the second entry, t3 contains the physical address of
riscv_kexec_norelocate, so the PC comparison matches and
execution continues under trampoline VA(=PA).
Since the trampoline page table is already active, replace the
previous stvec-based handoff with a direct jump to the target
entry (jr a2).
Signed-off-by: Fangyu Yu <[email protected]>
---
arch/riscv/kernel/kexec_relocate.S | 30 +++++++++++++++-----
arch/riscv/kernel/machine_kexec.c | 44 +++++++++++++++++++++++++++---
2 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/arch/riscv/kernel/kexec_relocate.S b/arch/riscv/kernel/kexec_relocate.S
index af6b99f5b0fd..8cfdf6f4032a 100644
--- a/arch/riscv/kernel/kexec_relocate.S
+++ b/arch/riscv/kernel/kexec_relocate.S
@@ -147,13 +147,35 @@ riscv_kexec_relocate_end:
/* Used for jumping to crashkernel */
+.extern kexec_tramp_satp
+.extern riscv_kexec_norelocate_pa
.section ".kexec.tramp.text", "ax"
SYM_CODE_START(riscv_kexec_norelocate)
+ /*
+ * Two-pass entry:
+ * - 1st entry: t3 == 0 (initialized by machine_kexec()).
+ *
+ * - 2nd entry: t3 holds the physical address of
+ * riscv_kexec_norelocate, so auipc matches t3 and we fall through
+ * to label 1 to continue execution under trampoline VA(=PA).
+ */
+ auipc t0, 0
+ beq t0, t3, 1f
+
+ la t0, riscv_kexec_norelocate_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
/*
* s0: (const) Phys address to jump to
* s1: (const) Phys address of the FDT image
* s2: (const) The hartid of the current hart
*/
+1:
mv s0, a1
mv s1, a2
mv s2, a3
@@ -198,14 +220,8 @@ SYM_CODE_START(riscv_kexec_norelocate)
csrw CSR_SCAUSE, zero
csrw CSR_SSCRATCH, zero
- /*
- * Switch to physical addressing
- * This will also trigger a jump to CSR_STVEC
- * which in this case is the address of the new
- * kernel.
- */
- csrw CSR_STVEC, a2
csrw CSR_SATP, zero
+ jr a2
SYM_CODE_END(riscv_kexec_norelocate)
diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c
index 1947b7bdf5c4..72817bba5d3b 100644
--- a/arch/riscv/kernel/machine_kexec.c
+++ b/arch/riscv/kernel/machine_kexec.c
@@ -18,6 +18,8 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
+unsigned long kexec_tramp_satp;
+unsigned long riscv_kexec_norelocate_pa;
/*
* Trampoline page tables. Both the VA(trampoline)->PA and the
* PA(trampoline)->PA identity mapping are installed in this single
@@ -155,11 +157,17 @@ machine_kexec_prepare(struct kimage *image)
} else {
/*
* Crash kexec uses riscv_kexec_norelocate as a trampoline.
- * Pre-build the trampoline page tables here so the panic
- * path only has to switch satp and jump.
+ * Pre-build the trampoline page tables and capture the
+ * trampoline SATP value plus the physical address of
+ * riscv_kexec_norelocate so that the panic path only has
+ * to switch satp and jump.
*/
riscv_kexec_build_tramp((unsigned long)__kexec_tramp_text_start,
__pa_symbol(__kexec_tramp_text_start));
+ WRITE_ONCE(riscv_kexec_norelocate_pa,
+ __pa_symbol(&riscv_kexec_norelocate));
+ WRITE_ONCE(kexec_tramp_satp,
+ PFN_DOWN(__pa_symbol(kexec_tramp_pgd)) | satp_mode);
}
return 0;
@@ -276,7 +284,35 @@ machine_kexec(struct kimage *image)
/* Jump to the relocation code */
pr_notice("Bye...\n");
- kexec_method(first_ind_entry, jump_addr, fdt_addr,
- this_hart_id, kernel_map.va_pa_offset);
+ /*
+ * Hand off to the trampoline. For KEXEC_TYPE_CRASH we go into
+ * riscv_kexec_norelocate, which uses t3 as the 1st/2nd-pass
+ * discriminator (must be 0 on first entry). A bare
+ * asm volatile ("li t3, 0" ::: "t3")
+ * before the C call only declares t3 *modified*; the compiler is
+ * free to use t3 as scratch when materialising args. Pin t3 = 0
+ * (and the args) via local register variables and perform the
+ * indirect jump inside the same inline asm so t3 == 0 is
+ * guaranteed at the moment control leaves machine_kexec().
+ */
+ {
+ register unsigned long a0_val asm("a0") = first_ind_entry;
+ register unsigned long a1_val asm("a1") = jump_addr;
+ register unsigned long a2_val asm("a2") = fdt_addr;
+ register unsigned long a3_val asm("a3") = this_hart_id;
+ register unsigned long a4_val asm("a4") = kernel_map.va_pa_offset;
+ register unsigned long t3_zero asm("t3") = 0;
+ register riscv_kexec_method m asm("t6") = kexec_method;
+
+ asm volatile (
+ "jr %[m]"
+ :
+ : "r" (a0_val), "r" (a1_val), "r" (a2_val),
+ "r" (a3_val), "r" (a4_val),
+ "r" (t3_zero),
+ [m] "r" (m)
+ : "memory"
+ );
+ }
unreachable();
}
--
2.50.1
--
kvm-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kvm-riscv
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.