* [PATCH RESEND 0/3] Fix kexec_file segment placement on RISC-V
@ 2026-09-02 9:08 Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 1/3] riscv: kexec_file: constrain extra segments to the Sv39 direct map Yufan Dou
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Yufan Dou @ 2026-09-02 9:08 UTC (permalink / raw)
To: pjw, palmer, aou
Cc: alex, leitao, akpm, ajones, lizhengyu3, liaochang1,
songshuaishuai, bjorn, gaohan, douyufan, linux-riscv,
linux-kernel, yang.yicong, weidong.wd, geshijian
This patchset fixes three ways in which kexec_file_load() can place
segments where the next kernel cannot use them:
- Patch 1/3 keeps the extra segments within the direct map of the next
kernel, which is narrower when an Sv48/Sv57 kernel loads an Sv39 one
- Patch 2/3 sizes the ELF placement search by the extent the image
occupies in memory rather than by the length of the file
- Patch 3/3 reserves the PMD-aligned tail of the kernel image that the
next kernel reserves under CONFIG_STRICT_KERNEL_RWX
Patch 1/3 is independent. Patch 3/3 depends on patch 2/3, and both are
marked for stable.
Resending rebased onto current mainline (v7.3-rc1); no code changes
since the previous posting.
Yufan Dou (3):
riscv: kexec_file: constrain extra segments to the Sv39 direct map
riscv: kexec_file: size the ELF placement search by the load extent
riscv: kexec: reserve the PMD-aligned kernel image range
arch/riscv/kernel/kexec_elf.c | 38 ++++++++++++++++++++++----
arch/riscv/kernel/kexec_image.c | 7 ++++-
arch/riscv/kernel/machine_kexec_file.c | 25 ++++++++++++++++-
3 files changed, 62 insertions(+), 8 deletions(-)
base-commit: 89a312991dc6e638a36adc43ccb91dbc25504c04
--
2.34.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH RESEND 1/3] riscv: kexec_file: constrain extra segments to the Sv39 direct map
2026-09-02 9:08 [PATCH RESEND 0/3] Fix kexec_file segment placement on RISC-V Yufan Dou
@ 2026-09-02 9:08 ` Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 2/3] riscv: kexec_file: size the ELF placement search by the load extent Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 3/3] riscv: kexec: reserve the PMD-aligned kernel image range Yufan Dou
2 siblings, 0 replies; 4+ messages in thread
From: Yufan Dou @ 2026-09-02 9:08 UTC (permalink / raw)
To: pjw, palmer, aou
Cc: alex, leitao, akpm, ajones, lizhengyu3, liaochang1,
songshuaishuai, bjorn, gaohan, douyufan, linux-riscv,
linux-kernel, yang.yicong, weidong.wd, geshijian
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@picoheart.com>
Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
Signed-off-by: Yufan Dou <douyufan@picoheart.com>
---
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 26cd2a8bd0cd..15a3c180c558 100644
--- a/arch/riscv/kernel/machine_kexec_file.c
+++ b/arch/riscv/kernel/machine_kexec_file.c
@@ -239,6 +239,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,
@@ -252,7 +275,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
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH RESEND 2/3] riscv: kexec_file: size the ELF placement search by the load extent
2026-09-02 9:08 [PATCH RESEND 0/3] Fix kexec_file segment placement on RISC-V Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 1/3] riscv: kexec_file: constrain extra segments to the Sv39 direct map Yufan Dou
@ 2026-09-02 9:08 ` Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 3/3] riscv: kexec: reserve the PMD-aligned kernel image range Yufan Dou
2 siblings, 0 replies; 4+ messages in thread
From: Yufan Dou @ 2026-09-02 9:08 UTC (permalink / raw)
To: pjw, palmer, aou
Cc: alex, leitao, akpm, ajones, lizhengyu3, liaochang1,
songshuaishuai, bjorn, gaohan, douyufan, linux-riscv,
linux-kernel, yang.yicong, weidong.wd, geshijian
elf_find_pbase() searches for a memory hole to place the whole kernel
image, and the segments are then added at fixed addresses derived from
the base it returns. kexec_add_buffer() skips the memory hole check for
a segment whose address is already known.
The search is sized by the length of the ELF file, which does not
reflect the extent the image occupies in memory: a PT_LOAD segment can
have a memory size larger than its file size, so a stripped vmlinux can
end up needing more memory than the file length accounts for. The tail
of the image is then placed without any check that the memory is
available.
Size the search by the extent between the lowest and the highest
physical address of the PT_LOAD segments instead, and drop the now
unused kernel_len argument.
Fixes: 6261586e0c91 ("RISC-V: Add kexec_file support")
Cc: stable@vger.kernel.org
Co-developed-by: Yicong Yang <yang.yicong@picoheart.com>
Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
Signed-off-by: Yufan Dou <douyufan@picoheart.com>
---
arch/riscv/kernel/kexec_elf.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c
index 3e9a32acb8f2..d84548f9d289 100644
--- a/arch/riscv/kernel/kexec_elf.c
+++ b/arch/riscv/kernel/kexec_elf.c
@@ -54,9 +54,9 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
* Go through the available phsyical memory regions and find one that hold
* an image of the specified size.
*/
-static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
- struct elfhdr *ehdr, struct kexec_elf_info *elf_info,
- unsigned long *old_pbase, unsigned long *new_pbase)
+static int elf_find_pbase(struct kimage *image, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info, unsigned long *old_pbase,
+ unsigned long *new_pbase)
{
int i;
int ret;
@@ -64,6 +64,7 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
const struct elf_phdr *phdr;
unsigned long lowest_paddr = ULONG_MAX;
unsigned long lowest_vaddr = ULONG_MAX;
+ unsigned long highest_paddr = 0;
for (i = 0; i < ehdr->e_phnum; i++) {
phdr = &elf_info->proghdrs[i];
@@ -75,6 +76,9 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
if (lowest_vaddr > phdr->p_vaddr)
lowest_vaddr = phdr->p_vaddr;
+
+ highest_paddr = max(highest_paddr,
+ (unsigned long)(phdr->p_paddr + phdr->p_memsz));
}
kbuf.image = image;
@@ -88,7 +92,12 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
*/
kbuf.buf_align = PMD_SIZE;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
- kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE);
+ /*
+ * The segments are added at fixed addresses later on, which makes
+ * kexec_add_buffer() skip the memory hole check, so the range searched
+ * here has to cover the whole extent the image occupies in memory.
+ */
+ kbuf.memsz = ALIGN(highest_paddr - lowest_paddr, PAGE_SIZE);
kbuf.cma = NULL;
kbuf.top_down = false;
ret = arch_kexec_locate_mem_hole(&kbuf);
@@ -115,8 +124,8 @@ static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
if (ret)
return ERR_PTR(ret);
- ret = elf_find_pbase(image, kernel_len, &ehdr, &elf_info,
- &old_kernel_pbase, &new_kernel_pbase);
+ ret = elf_find_pbase(image, &ehdr, &elf_info, &old_kernel_pbase,
+ &new_kernel_pbase);
if (ret)
goto out;
--
2.34.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH RESEND 3/3] riscv: kexec: reserve the PMD-aligned kernel image range
2026-09-02 9:08 [PATCH RESEND 0/3] Fix kexec_file segment placement on RISC-V Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 1/3] riscv: kexec_file: constrain extra segments to the Sv39 direct map Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 2/3] riscv: kexec_file: size the ELF placement search by the load extent Yufan Dou
@ 2026-09-02 9:08 ` Yufan Dou
2 siblings, 0 replies; 4+ messages in thread
From: Yufan Dou @ 2026-09-02 9:08 UTC (permalink / raw)
To: pjw, palmer, aou
Cc: alex, leitao, akpm, ajones, lizhengyu3, liaochang1,
songshuaishuai, bjorn, gaohan, douyufan, linux-riscv,
linux-kernel, yang.yicong, weidong.wd, geshijian
When strict kernel permissions are enabled, the next kernel extends
its image reservation from _start to the PMD-aligned address following
_end. If kexec only reserves the exact image range, a later segment can
be placed in this aligned tail and overlap the next kernel's
reservation.
For a flat Image, extend the decoded image size to the next PMD
boundary.
An ELF image contains multiple PT_LOAD segments. Extending every
segment can make an intermediate segment overlap the following one.
Find the PT_LOAD segment with the highest physical end address and
extend only that segment to the next PMD boundary. Align the extent
searched by elf_find_pbase() as well, so that the expanded tail stays
within the range validated against the available memory.
This reserves the range expected by the next kernel without changing
the layout of intermediate ELF segments.
Fixes: 6261586e0c91 ("RISC-V: Add kexec_file support")
Fixes: 809a11eea8e8 ("riscv: kexec_file: Support loading Image binary file")
Cc: stable@vger.kernel.org
Co-developed-by: Yicong Yang <yang.yicong@picoheart.com>
Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
Signed-off-by: Yufan Dou <douyufan@picoheart.com>
---
arch/riscv/kernel/kexec_elf.c | 21 +++++++++++++++++++--
arch/riscv/kernel/kexec_image.c | 7 ++++++-
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c
index d84548f9d289..da5679bf1d0c 100644
--- a/arch/riscv/kernel/kexec_elf.c
+++ b/arch/riscv/kernel/kexec_elf.c
@@ -28,6 +28,15 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
int ret = 0;
struct kexec_buf kbuf = {};
const struct elf_phdr *phdr;
+ unsigned long highest_paddr = 0;
+
+ /* Find the highest physical end address of the kernel image. */
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &elf_info->proghdrs[i];
+ if (phdr->p_type == PT_LOAD)
+ highest_paddr = max(highest_paddr,
+ (unsigned long)(phdr->p_paddr + phdr->p_memsz));
+ }
kbuf.image = image;
@@ -41,6 +50,13 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
kbuf.buf_align = phdr->p_align;
kbuf.mem = phdr->p_paddr - old_pbase + new_pbase;
kbuf.memsz = phdr->p_memsz;
+ /*
+ * The next kernel aligns its image reservation on PMD_SIZE, as
+ * explained by a comment in setup_bootmem(). Reserve that tail
+ * so subsequent segments do not overlap it.
+ */
+ if (phdr->p_paddr + phdr->p_memsz == highest_paddr)
+ kbuf.memsz = ALIGN(kbuf.mem + phdr->p_memsz, PMD_SIZE) - kbuf.mem;
kbuf.top_down = false;
ret = kexec_add_buffer(&kbuf);
if (ret)
@@ -95,9 +111,10 @@ static int elf_find_pbase(struct kimage *image, struct elfhdr *ehdr,
/*
* The segments are added at fixed addresses later on, which makes
* kexec_add_buffer() skip the memory hole check, so the range searched
- * here has to cover the whole extent the image occupies in memory.
+ * here has to cover the whole extent the image occupies in memory,
+ * including the PMD-aligned tail of the last segment.
*/
- kbuf.memsz = ALIGN(highest_paddr - lowest_paddr, PAGE_SIZE);
+ kbuf.memsz = ALIGN(highest_paddr - lowest_paddr, PMD_SIZE);
kbuf.cma = NULL;
kbuf.top_down = false;
ret = arch_kexec_locate_mem_hole(&kbuf);
diff --git a/arch/riscv/kernel/kexec_image.c b/arch/riscv/kernel/kexec_image.c
index 51dc89259f16..52678b3044fd 100644
--- a/arch/riscv/kernel/kexec_image.c
+++ b/arch/riscv/kernel/kexec_image.c
@@ -69,7 +69,12 @@ static void *image_load(struct kimage *image,
kbuf.buffer = kernel;
kbuf.bufsz = kernel_len;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
- kbuf.memsz = le64_to_cpu(h->image_size);
+ /*
+ * The next kernel aligns its image reservation on PMD_SIZE, as
+ * explained by a comment in setup_bootmem(). Reserve that tail so
+ * subsequent segments do not overlap it.
+ */
+ kbuf.memsz = ALIGN(le64_to_cpu(h->image_size), PMD_SIZE);
kbuf.buf_align = le64_to_cpu(h->text_offset);
ret = kexec_add_buffer(&kbuf);
--
2.34.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-02 9:09 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 9:08 [PATCH RESEND 0/3] Fix kexec_file segment placement on RISC-V Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 1/3] riscv: kexec_file: constrain extra segments to the Sv39 direct map Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 2/3] riscv: kexec_file: size the ELF placement search by the load extent Yufan Dou
2026-09-02 9:08 ` [PATCH RESEND 3/3] riscv: kexec: reserve the PMD-aligned kernel image range Yufan Dou
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox