* [PATCH 1/4] kexec: do not place the next kernel in a fixed-address CMA area
2026-11-03 16:24 [PATCH 0/4] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
@ 2026-11-03 16:24 ` Radhey Shyam Pandey
2026-11-03 16:24 ` [PATCH 2/4] of: reserved_mem: keep the reservation when node_init returns -EBUSY Radhey Shyam Pandey
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-11-03 16:24 UTC (permalink / raw)
To: robh, saravanak, akpm, david, ljs, liam, vbabka, rppt, surenb,
mhocko, m.szyprowski, robin.murphy, baoquan.he, pasha.tatashin,
pratyush
Cc: devicetree, linux-kernel, linux-mm, iommu, kexec, Radhey Shyam Pandey
kexec_alloc_contig() takes the next kernel's destination from the default
CMA area and the next kernel runs from there in place, which is safe only
if that area does not exist at the same address in the kernel being loaded.
For a device-tree area it does: the loaded kernel declares the range it is
executing from as a shared-dma-pool and cma_activate_area() frees its own
image to the page allocator. Record in CMA_DYNAMIC_BASE which areas CMA
placed itself and accept only those, letting kexec_locate_mem_hole() fall
back to the memblock search. "cma=nn" works, "cma=nn@ss" no longer does.
Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
Suggested-by: Mike Rapoport <rppt@kernel.org>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes since RFC:
- Use CMA_DYNAMIC_BASE flag instead of CMA_FIXED_BASE.
- Drop Pratyush reviewed-by tag as there is implementation change.
---
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..d4683a4ff3cc 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_dynamic_base(const struct cma *cma);
+#else
+static inline bool cma_has_dynamic_base(const struct cma *cma)
+{
+ return false;
+}
+#endif
+
#endif
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b668ce8d68ee..763dee85558f 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_dynamic_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 9ec1d1fd5dff..9c53b2b70dc8 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_dynamic_base() - was the area placed at an address CMA chose?
+ * @cma: Contiguous memory region
+ *
+ * An area without a dynamic base was placed where something outside the kernel
+ * said it should go, typically a device tree reserved-memory node, and so lands
+ * at the same address in every kernel booted with that description.
+ */
+bool cma_has_dynamic_base(const struct cma *cma)
+{
+ return test_bit(CMA_DYNAMIC_BASE, &cma->flags);
+}
+
static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
unsigned int align_order)
{
@@ -519,6 +532,8 @@ static int __init __cma_declare_contiguous_nid(phys_addr_t *basep,
}
(*res_cma)->nid = nid;
+ if (!fixed)
+ set_bit(CMA_DYNAMIC_BASE, &(*res_cma)->flags);
*basep = base;
return 0;
@@ -708,6 +723,7 @@ int __init cma_declare_contiguous_multi(phys_addr_t total_size,
cma->nranges = nr;
cma->nid = nid;
+ set_bit(CMA_DYNAMIC_BASE, &cma->flags);
*res_cma = cma;
out:
diff --git a/mm/cma.h b/mm/cma.h
index 68e574b0f951..4703e0958b01 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 chosen by CMA, not dictated by the caller. */
+ CMA_DYNAMIC_BASE,
};
extern struct cma cma_areas[MAX_CMA_AREAS];
--
2.44.4
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/4] of: reserved_mem: keep the reservation when node_init returns -EBUSY
2026-11-03 16:24 [PATCH 0/4] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
2026-11-03 16:24 ` [PATCH 1/4] kexec: do not place the next kernel in a fixed-address " Radhey Shyam Pandey
@ 2026-11-03 16:24 ` Radhey Shyam Pandey
2026-11-03 16:24 ` [PATCH 3/4] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
2026-11-03 16:24 ` [PATCH 4/4] mm/cma: drop the duplicate alignment check in rmem_cma_setup() Radhey Shyam Pandey
3 siblings, 0 replies; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-11-03 16:24 UTC (permalink / raw)
To: robh, saravanak, akpm, david, ljs, liam, vbabka, rppt, surenb,
mhocko, m.szyprowski, robin.murphy, baoquan.he, pasha.tatashin,
pratyush
Cc: devicetree, linux-kernel, linux-mm, iommu, kexec, Radhey Shyam Pandey
fdt_init_reserved_mem_node() treats any error from a region's init function
as "this node is unusable" and calls memblock_phys_free() on the whole
range, although the range was reserved earlier during the FDT scan by code
unrelated to that init function. Give -EBUSY the meaning "cannot be used as
described, but must stay reserved" and skip the release for it. Nothing
returns -EBUSY from node_init today. The next patch does, to refuse a CMA
pool overlapping the kernel image, which without this would free
[_text, _end) rather than protect it.
Suggested-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes since RFC:
- New patch
---
drivers/of/of_reserved_mem.c | 14 ++++++++++++++
include/linux/of_reserved_mem.h | 5 +++++
2 files changed, 19 insertions(+)
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 0fd1683cba01..d67a2b3a4d44 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -621,6 +621,20 @@ static void __init fdt_init_reserved_mem_node(unsigned long node, const char *un
err = __reserved_mem_init_node(rmem, node);
if (err != 0 && err != -ENODEV) {
+ /*
+ * The region was reserved before its init function ran, so it
+ * is not ours to release. -EBUSY says the init function is
+ * declining it for a reason that outlives this node; freeing
+ * it would hand the range to the page allocator even though
+ * something else still owns it.
+ */
+ if (err == -EBUSY) {
+ pr_info("node %s init failed, keeping reservation\n",
+ rmem->name);
+ rmem->name = NULL;
+ return;
+ }
+
pr_info("node %s compatible matching fail\n", rmem->name);
rmem->name = NULL;
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 49da515859f2..4bf32f80785c 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -21,6 +21,11 @@ struct reserved_mem_ops {
int (*node_validate)(unsigned long fdt_node, phys_addr_t *align);
int (*node_fixup)(unsigned long fdt_node, phys_addr_t base,
phys_addr_t size);
+ /*
+ * Returns 0 on success, -ENODEV if the node is not handled by these
+ * ops, or -EBUSY if the region cannot be used but must stay reserved.
+ * Any other error releases the region.
+ */
int (*node_init)(unsigned long fdt_node, struct reserved_mem *rmem);
int (*device_init)(struct reserved_mem *rmem,
struct device *dev);
--
2.44.4
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/4] mm/cma: reject CMA areas that overlap the kernel image
2026-11-03 16:24 [PATCH 0/4] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
2026-11-03 16:24 ` [PATCH 1/4] kexec: do not place the next kernel in a fixed-address " Radhey Shyam Pandey
2026-11-03 16:24 ` [PATCH 2/4] of: reserved_mem: keep the reservation when node_init returns -EBUSY Radhey Shyam Pandey
@ 2026-11-03 16:24 ` Radhey Shyam Pandey
2026-11-08 11:05 ` Mike Rapoport
2026-11-03 16:24 ` [PATCH 4/4] mm/cma: drop the duplicate alignment check in rmem_cma_setup() Radhey Shyam Pandey
3 siblings, 1 reply; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-11-03 16:24 UTC (permalink / raw)
To: robh, saravanak, akpm, david, ljs, liam, vbabka, rppt, surenb,
mhocko, m.szyprowski, robin.murphy, baoquan.he, pasha.tatashin,
pratyush
Cc: devicetree, linux-kernel, linux-mm, iommu, kexec, Radhey Shyam Pandey
cma_init_reserved_mem() takes a base chosen by its caller and only requires
that the range already be reserved, so an overlap with the kernel image is
indistinguishable from the normal case. cma_activate_area() then clears
PageReserved on those pages and frees them into MIGRATE_CMA, overriding the
memblock_reserve() of [_text, _end), so live kernel text reaches userspace.
kexec reached this until the fix this series builds on; a device tree
placing a shared-dma-pool over where the bootloader loaded the kernel still
can. Reject the overlap rather than let such a caller corrupt the kernel.
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes since RFC:
- Drop Pratyush reviewed-by tag as there were few changes.
- Trim the commit description.
- Drop the redundant comment as suggested by Mike.
- cma_overlaps_kernel_image() is kept as helpers in asm-generic/sections.h
are virtual-address helpers.
---
mm/cma.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/mm/cma.c b/mm/cma.c
index 9c53b2b70dc8..3231a53161cd 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -31,6 +31,8 @@
#include <linux/kmemleak.h>
#include <trace/events/cma.h>
+#include <asm/sections.h>
+
#include "internal.h"
#include "cma.h"
#include "mm_init.h"
@@ -68,6 +70,16 @@ bool cma_has_dynamic_base(const struct cma *cma)
return test_bit(CMA_DYNAMIC_BASE, &cma->flags);
}
+static bool cma_overlaps_kernel_image(phys_addr_t base, phys_addr_t size)
+{
+ phys_addr_t end;
+
+ if (check_add_overflow(base, size, &end))
+ return true;
+
+ return base < __pa_symbol(_end) && __pa_symbol(_text) < end;
+}
+
static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
unsigned int align_order)
{
@@ -295,6 +307,15 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
if (!size || !memblock_is_region_reserved(base, size))
return -EINVAL;
+ if (cma_overlaps_kernel_image(base, size)) {
+ phys_addr_t text = __pa_symbol(_text);
+ phys_addr_t end = __pa_symbol(_end);
+
+ pr_err("area %s (%pa, size %pa) overlaps kernel image [%pa-%pa]\n",
+ name ?: "(unnamed)", &base, &size, &text, &end);
+ return -EBUSY;
+ }
+
/*
* CMA uses CMA_MIN_ALIGNMENT_BYTES as alignment requirement which
* needs pageblock_order to be initialized. Let's enforce it.
--
2.44.4
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 3/4] mm/cma: reject CMA areas that overlap the kernel image
2026-11-03 16:24 ` [PATCH 3/4] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
@ 2026-11-08 11:05 ` Mike Rapoport
0 siblings, 0 replies; 6+ messages in thread
From: Mike Rapoport @ 2026-11-08 11:05 UTC (permalink / raw)
To: Radhey Shyam Pandey
Cc: robh, saravanak, akpm, david, ljs, liam, vbabka, surenb, mhocko,
m.szyprowski, robin.murphy, baoquan.he, pasha.tatashin, pratyush,
devicetree, linux-kernel, linux-mm, iommu, kexec
Hi,
On Tue, Nov 03, 2026 at 09:54:28PM +0530, Radhey Shyam Pandey wrote:
> cma_init_reserved_mem() takes a base chosen by its caller and only requires
> that the range already be reserved, so an overlap with the kernel image is
> indistinguishable from the normal case. cma_activate_area() then clears
> PageReserved on those pages and frees them into MIGRATE_CMA, overriding the
> memblock_reserve() of [_text, _end), so live kernel text reaches userspace.
> kexec reached this until the fix this series builds on; a device tree
> placing a shared-dma-pool over where the bootloader loaded the kernel still
> can. Reject the overlap rather than let such a caller corrupt the kernel.
>
> Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
> ---
> Changes since RFC:
> - Drop Pratyush reviewed-by tag as there were few changes.
> - Trim the commit description.
> - Drop the redundant comment as suggested by Mike.
> - cma_overlaps_kernel_image() is kept as helpers in asm-generic/sections.h
> are virtual-address helpers.
> ---
> mm/cma.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/mm/cma.c b/mm/cma.c
> index 9c53b2b70dc8..3231a53161cd 100644
> --- a/mm/cma.c
> +++ b/mm/cma.c
> @@ -31,6 +31,8 @@
> #include <linux/kmemleak.h>
> #include <trace/events/cma.h>
>
> +#include <asm/sections.h>
You probably don't need this.
> +
> #include "internal.h"
> #include "cma.h"
> #include "mm_init.h"
> @@ -68,6 +70,16 @@ bool cma_has_dynamic_base(const struct cma *cma)
> return test_bit(CMA_DYNAMIC_BASE, &cma->flags);
> }
>
> +static bool cma_overlaps_kernel_image(phys_addr_t base, phys_addr_t size)
> +{
> + phys_addr_t end;
> +
> + if (check_add_overflow(base, size, &end))
> + return true;
> +
> + return base < __pa_symbol(_end) && __pa_symbol(_text) < end;
> +}
> +
> static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
> unsigned int align_order)
> {
> @@ -295,6 +307,15 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
> if (!size || !memblock_is_region_reserved(base, size))
> return -EINVAL;
>
> + if (cma_overlaps_kernel_image(base, size)) {
> + phys_addr_t text = __pa_symbol(_text);
> + phys_addr_t end = __pa_symbol(_end);
> +
> + pr_err("area %s (%pa, size %pa) overlaps kernel image [%pa-%pa]\n",
> + name ?: "(unnamed)", &base, &size, &text, &end);
We don't want to expose addresses of the kernel image.
I'd drop text, end, and probably even base from the printout.
> + return -EBUSY;
> + }
> +
> /*
> * CMA uses CMA_MIN_ALIGNMENT_BYTES as alignment requirement which
> * needs pageblock_order to be initialized. Let's enforce it.
> --
> 2.44.4
>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 4/4] mm/cma: drop the duplicate alignment check in rmem_cma_setup()
2026-11-03 16:24 [PATCH 0/4] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
` (2 preceding siblings ...)
2026-11-03 16:24 ` [PATCH 3/4] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
@ 2026-11-03 16:24 ` Radhey Shyam Pandey
3 siblings, 0 replies; 6+ messages in thread
From: Radhey Shyam Pandey @ 2026-11-03 16:24 UTC (permalink / raw)
To: robh, saravanak, akpm, david, ljs, liam, vbabka, rppt, surenb,
mhocko, m.szyprowski, robin.murphy, baoquan.he, pasha.tatashin,
pratyush
Cc: devicetree, linux-kernel, linux-mm, iommu, kexec, Radhey Shyam Pandey
cma_init_reserved_mem() already refuses a base or size that is not aligned
to CMA_MIN_ALIGNMENT_BYTES, so the identical test in rmem_cma_setup() only
repeats it one call earlier. Remove it and report the failure from
cma_init_reserved_mem() instead, where the area name is available: the
message now says which pool is misaligned, and every caller gets the
diagnostic rather than just the device-tree path.
Suggested-by: Mike Rapoport <rppt@kernel.org>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
Changes since RFC:
- New patch
---
kernel/dma/contiguous.c | 5 -----
mm/cma.c | 6 +++++-
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 90937dc7c627..14450cf0f832 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -552,11 +552,6 @@ static int __init rmem_cma_setup(unsigned long node, struct reserved_mem *rmem)
if (ret)
return ret;
- if (!IS_ALIGNED(rmem->base | rmem->size, CMA_MIN_ALIGNMENT_BYTES)) {
- pr_err("Reserved memory: incorrect alignment of CMA region\n");
- return -EINVAL;
- }
-
ret = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
if (ret) {
pr_err("Reserved memory: unable to setup CMA region\n");
diff --git a/mm/cma.c b/mm/cma.c
index 3231a53161cd..68dea7070d69 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -326,8 +326,12 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
}
/* ensure minimal alignment required by mm core */
- if (!IS_ALIGNED(base | size, CMA_MIN_ALIGNMENT_BYTES))
+ if (!IS_ALIGNED(base | size, CMA_MIN_ALIGNMENT_BYTES)) {
+ pr_err("area %s (%pa, size %pa) is not aligned to %lu bytes\n",
+ name ?: "(unnamed)", &base, &size,
+ (unsigned long)CMA_MIN_ALIGNMENT_BYTES);
return -EINVAL;
+ }
ret = cma_new_area(name, size, order_per_bit, &cma);
if (ret != 0)
--
2.44.4
^ permalink raw reply [flat|nested] 6+ messages in thread