[RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address CMA area
Radhey Shyam Pandey
radhey.shyam.pandey at amd.com
Wed Oct 14 07:57:30 PDT 2026
kexec_alloc_contig() picks the destination of the next kernel out of the
default CMA area, and the next kernel then runs from there in place
instead of being copied out. That is only safe if the area will not exist
at the same address in the kernel being loaded.
For an area whose base came from the device tree, it will. The loaded
kernel parses the same description, declares the range it is executing
from as a shared-dma-pool, and cma_activate_area() releases the pages
holding its own image to the page allocator.
Distinguish the two cases. An area placed by cma_declare_contiguous_nid()
without a fixed base lands wherever this kernel found room, and the next
kernel repeats that search against its own memblock state, so it cannot
collide. Record the difference as CMA_FIXED_BASE and refuse to use such
an area for the payload, which leaves kexec_locate_mem_hole() to fall
back to the memblock hole search as it did before CMA placement existed.
This is what makes the KEXEC_FILE_NO_CMA flag unnecessary on platforms
that describe their CMA pools in the device tree.
Tested on a AMD Versal AI Edge Series Gen 2 VEK385 Evaluation Kit whose
device tree declares three CMA pools, the first of which is
linux,cma-default:
cma_reserved at 800000000 4 GiB at 0x800000000 (default)
cma_reserved at 50000000000 6 GiB at 0x50000000000
cma_reserved at 60000000000 4 GiB at 0x60000000000
All three have a fixed base, so kexec_alloc_contig() declines the default
area and kexec_locate_mem_hole() falls through to the memblock search,
which places the Image segment at 0x9c00000 size 0x1de0000. The kernel
that boots from it reports its image as [0x09c00000-0x0b9e0000] and
14680064K cma-reserved, i.e. all three pools still present and none of
them overlapping the running kernel.
Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey at amd.com>
---
include/linux/cma.h | 10 ++++++++++
kernel/kexec_file.c | 12 ++++++++++++
mm/cma.c | 16 ++++++++++++++++
mm/cma.h | 2 ++
4 files changed, 40 insertions(+)
diff --git a/include/linux/cma.h b/include/linux/cma.h
index 8555d38a97b1..2fe66377e5d3 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -61,4 +61,14 @@ extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end);
extern void cma_reserve_pages_on_error(struct cma *cma);
+
+#ifdef CONFIG_CMA
+bool cma_has_fixed_base(const struct cma *cma);
+#else
+static inline bool cma_has_fixed_base(const struct cma *cma)
+{
+ return false;
+}
+#endif
+
#endif
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b668ce8d68ee..10e770bcee0b 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -28,6 +28,7 @@
#include <linux/vmalloc.h>
#include <linux/dma-map-ops.h>
#include <linux/kexec_handover.h>
+#include <linux/cma.h>
#include "kexec_internal.h"
#ifdef CONFIG_KEXEC_SIG
@@ -691,6 +692,7 @@ static int kexec_walk_resources(struct kexec_buf *kbuf,
static int kexec_alloc_contig(struct kexec_buf *kbuf)
{
size_t nr_pages = kbuf->memsz >> PAGE_SHIFT;
+ struct cma *cma;
unsigned long mem;
struct page *p;
@@ -702,6 +704,16 @@ static int kexec_alloc_contig(struct kexec_buf *kbuf)
if (kbuf->image->type == KEXEC_TYPE_CRASH)
return -EPERM;
+ /*
+ * The next kernel runs from here rather than being copied out, so it
+ * would declare this area again from the same description and release
+ * the pages holding its own image to the page allocator. Only an area
+ * this kernel placed itself is safe to use.
+ */
+ cma = dev_get_cma_area(NULL);
+ if (!cma || cma_has_fixed_base(cma))
+ return -EOPNOTSUPP;
+
p = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf->buf_align), true);
if (!p)
return -ENOMEM;
diff --git a/mm/cma.c b/mm/cma.c
index a10ea37a261d..1f4d3d48a3bb 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -55,6 +55,19 @@ const char *cma_get_name(const struct cma *cma)
}
EXPORT_SYMBOL_GPL(cma_get_name);
+/**
+ * cma_has_fixed_base() - was the area placed at an address it did not choose?
+ * @cma: Contiguous memory region
+ *
+ * Such an area is described outside the kernel, typically by a device tree
+ * reserved-memory node, and so lands at the same address in every kernel
+ * booted with that description.
+ */
+bool cma_has_fixed_base(const struct cma *cma)
+{
+ return test_bit(CMA_FIXED_BASE, &cma->flags);
+}
+
static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
unsigned int align_order)
{
@@ -304,6 +317,7 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
cma->ranges[0].count = cma->count;
cma->nranges = 1;
cma->nid = NUMA_NO_NODE;
+ set_bit(CMA_FIXED_BASE, &cma->flags);
*res_cma = cma;
@@ -517,6 +531,8 @@ static int __init __cma_declare_contiguous_nid(phys_addr_t *basep,
}
(*res_cma)->nid = nid;
+ if (!fixed)
+ clear_bit(CMA_FIXED_BASE, &(*res_cma)->flags);
*basep = base;
return 0;
diff --git a/mm/cma.h b/mm/cma.h
index 68e574b0f951..1bab24033284 100644
--- a/mm/cma.h
+++ b/mm/cma.h
@@ -80,6 +80,8 @@ enum cma_flags {
CMA_ZONES_VALID,
CMA_ZONES_INVALID,
CMA_ACTIVATED,
+ /* Base address was dictated by the caller, not picked by CMA. */
+ CMA_FIXED_BASE,
};
extern struct cma cma_areas[MAX_CMA_AREAS];
--
2.43.0
More information about the kexec
mailing list