[PATCH 1/3] riscv: kexec_file: constrain extra segments to the Sv39 direct map
From: Yufan Dou
Date: Thu Aug 06 2026 - 03:25:15 EST
When an Sv48 or Sv57 kernel loads an Sv39 kernel, top-down allocation
can place the initrd and other extra segments above the direct-map
range supported by the next kernel.
During early boot, setup_bootmem() limits usable memory to
phys_ram_base + KERN_VIRT_SIZE. Any segment placed above the Sv39 limit
is therefore unreachable by the next kernel. In particular, an initrd
outside this range is disabled during boot.
The max_low_pfn limit only reflects the direct map of the loading kernel
and is insufficient when the next kernel uses a narrower address space.
The paging mode of the next kernel is not known at load time, so apply
the Sv39 limit unconditionally. On a machine with more than 128 GiB this
also constrains a next kernel that would run in Sv48 or Sv57.
Limit extra segment placement to the smaller of the loading kernel's
direct-map limit and the end of the Sv39 direct map. The next kernel
derives its direct map from the start of the memory it is given, which
is the crash kernel region for a crash image, so use that region as the
base in that case. A NOMMU kernel has no direct map and keeps the limit
of the loading kernel.
Fixes: b67a1ee0db00 ("riscv: kexec_file: Constrain segment placement to direct map")
Co-developed-by: Yicong Yang <yang.yicong@xxxxxxxxxxxxx>
Signed-off-by: Yicong Yang <yang.yicong@xxxxxxxxxxxxx>
Signed-off-by: Yufan Dou <douyufan@xxxxxxxxxxxxx>
---
arch/riscv/kernel/machine_kexec_file.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c
index 59d4bbc848a8..70d84fc90178 100644
--- a/arch/riscv/kernel/machine_kexec_file.c
+++ b/arch/riscv/kernel/machine_kexec_file.c
@@ -254,6 +254,29 @@ int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
}
+/*
+ * The next kernel may run in Sv39 even when the current kernel runs in Sv48 or
+ * Sv57, in which case the direct map of the next kernel is narrower. Any
+ * segment placed above it is unreachable by the next kernel during early boot.
+ * The next kernel derives its direct map from the start of the memory it is
+ * given, which is the crash kernel region for a crash image. A NOMMU kernel
+ * has no direct map, so only the limit of the current kernel applies.
+ */
+static unsigned long kexec_segment_limit(struct kimage *image)
+{
+ unsigned long limit = PFN_PHYS(max_low_pfn);
+#ifdef CONFIG_MMU
+ unsigned long base = phys_ram_base;
+
+#ifdef CONFIG_CRASH_DUMP
+ if (image->type == KEXEC_TYPE_CRASH)
+ base = crashk_res.start;
+#endif
+ limit = min(limit, base + BIT(VA_BITS_SV39 - 2) - 1);
+#endif
+ return limit;
+}
+
int load_extra_segments(struct kimage *image, unsigned long kernel_start,
unsigned long kernel_len, char *initrd,
unsigned long initrd_len, char *cmdline,
@@ -267,7 +290,7 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start,
kbuf.image = image;
kbuf.buf_min = kernel_start + kernel_len;
- kbuf.buf_max = PFN_PHYS(max_low_pfn);
+ kbuf.buf_max = kexec_segment_limit(image);
#ifdef CONFIG_CRASH_DUMP
/* Add elfcorehdr */
--
2.34.1