* [PATCH v4] kho: validate preserved memory map during population
@ 2025-12-23 14:01 Pasha Tatashin
2025-12-23 14:11 ` Pratyush Yadav
2026-01-08 18:03 ` Breno Leitao
0 siblings, 2 replies; 3+ messages in thread
From: Pasha Tatashin @ 2025-12-23 14:01 UTC (permalink / raw)
To: akpm, pasha.tatashin, rppt, graf, linux-kernel, kexec, linux-mm,
pratyush, ricardo.neri-calderon
If the previous kernel enabled KHO but did not call kho_finalize()
(e.g., CONFIG_LIVEUPDATE=n or userspace skipped the finalization step),
the 'preserved-memory-map' property in the FDT remains empty/zero.
Previously, kho_populate() would succeed regardless of the memory map's
state, reserving the incoming scratch regions in memblock. However,
kho_memory_init() would later fail to deserialize the empty map. By that
time, the scratch regions were already registered, leading to partial
initialization and subsequent list corruption (freeing scratch area
twice) during kho_init().
Move the validation of the preserved memory map earlier into
kho_populate(). If the memory map is empty/NULL:
1. Abort kho_populate() immediately with -ENOENT.
2. Do not register or reserve the incoming scratch memory, allowing the new
kernel to reclaim those pages as standard free memory.
3. Leave the global 'kho_in' state uninitialized.
Consequently, kho_memory_init() sees no active KHO context
(kho_in.mem_chunks_phys is 0) and falls back to kho_reserve_scratch(),
allocating fresh scratch memory as if it were a standard cold boot.
Fixes: de51999e687c ("kho: allow memory preservation state updates after finalization")
Reported-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
Closes: https://lore.kernel.org/all/20251218215613.GA17304@ranerica-svr.sc.intel.com
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Tested-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
---
Changes v4:
- Addressed Tested-by
- Addressed review comments from Pratyush.
kernel/liveupdate/kexec_handover.c | 37 +++++++++++++++---------------
1 file changed, 19 insertions(+), 18 deletions(-)
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 9dc51fab604f..d4482b6e3cae 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -460,27 +460,23 @@ static void __init deserialize_bitmap(unsigned int order,
}
}
-/* Return true if memory was deserizlied */
-static bool __init kho_mem_deserialize(const void *fdt)
+/* Returns physical address of the preserved memory map from FDT */
+static phys_addr_t __init kho_get_mem_map_phys(const void *fdt)
{
- struct khoser_mem_chunk *chunk;
const void *mem_ptr;
- u64 mem;
int len;
mem_ptr = fdt_getprop(fdt, 0, PROP_PRESERVED_MEMORY_MAP, &len);
if (!mem_ptr || len != sizeof(u64)) {
pr_err("failed to get preserved memory bitmaps\n");
- return false;
+ return 0;
}
- mem = get_unaligned((const u64 *)mem_ptr);
- chunk = mem ? phys_to_virt(mem) : NULL;
-
- /* No preserved physical pages were passed, no deserialization */
- if (!chunk)
- return false;
+ return get_unaligned((const u64 *)mem_ptr);
+}
+static void __init kho_mem_deserialize(struct khoser_mem_chunk *chunk)
+{
while (chunk) {
unsigned int i;
@@ -489,8 +485,6 @@ static bool __init kho_mem_deserialize(const void *fdt)
&chunk->bitmaps[i]);
chunk = KHOSER_LOAD_PTR(chunk->hdr.next);
}
-
- return true;
}
/*
@@ -1253,6 +1247,7 @@ bool kho_finalized(void)
struct kho_in {
phys_addr_t fdt_phys;
phys_addr_t scratch_phys;
+ phys_addr_t mem_map_phys;
struct kho_debugfs dbg;
};
@@ -1434,12 +1429,10 @@ static void __init kho_release_scratch(void)
void __init kho_memory_init(void)
{
- if (kho_in.scratch_phys) {
+ if (kho_in.mem_map_phys) {
kho_scratch = phys_to_virt(kho_in.scratch_phys);
kho_release_scratch();
-
- if (!kho_mem_deserialize(kho_get_fdt()))
- kho_in.fdt_phys = 0;
+ kho_mem_deserialize(phys_to_virt(kho_in.mem_map_phys));
} else {
kho_reserve_scratch();
}
@@ -1448,8 +1441,9 @@ void __init kho_memory_init(void)
void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
phys_addr_t scratch_phys, u64 scratch_len)
{
- void *fdt = NULL;
struct kho_scratch *scratch = NULL;
+ phys_addr_t mem_map_phys;
+ void *fdt = NULL;
int err = 0;
unsigned int scratch_cnt = scratch_len / sizeof(*kho_scratch);
@@ -1475,6 +1469,12 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
goto out;
}
+ mem_map_phys = kho_get_mem_map_phys(fdt);
+ if (!mem_map_phys) {
+ err = -ENOENT;
+ goto out;
+ }
+
scratch = early_memremap(scratch_phys, scratch_len);
if (!scratch) {
pr_warn("setup: failed to memremap scratch (phys=0x%llx, len=%lld)\n",
@@ -1515,6 +1515,7 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
kho_in.fdt_phys = fdt_phys;
kho_in.scratch_phys = scratch_phys;
+ kho_in.mem_map_phys = mem_map_phys;
kho_scratch_cnt = scratch_cnt;
pr_info("found kexec handover data.\n");
base-commit: cc3aa43b44bdb43dfbac0fcb51c56594a11338a8
--
2.52.0.351.gbe84eed79e-goog
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v4] kho: validate preserved memory map during population
2025-12-23 14:01 [PATCH v4] kho: validate preserved memory map during population Pasha Tatashin
@ 2025-12-23 14:11 ` Pratyush Yadav
2026-01-08 18:03 ` Breno Leitao
1 sibling, 0 replies; 3+ messages in thread
From: Pratyush Yadav @ 2025-12-23 14:11 UTC (permalink / raw)
To: Pasha Tatashin
Cc: akpm, rppt, graf, linux-kernel, kexec, linux-mm, pratyush,
ricardo.neri-calderon
On Tue, Dec 23 2025, Pasha Tatashin wrote:
> If the previous kernel enabled KHO but did not call kho_finalize()
> (e.g., CONFIG_LIVEUPDATE=n or userspace skipped the finalization step),
> the 'preserved-memory-map' property in the FDT remains empty/zero.
>
> Previously, kho_populate() would succeed regardless of the memory map's
> state, reserving the incoming scratch regions in memblock. However,
> kho_memory_init() would later fail to deserialize the empty map. By that
> time, the scratch regions were already registered, leading to partial
> initialization and subsequent list corruption (freeing scratch area
> twice) during kho_init().
>
> Move the validation of the preserved memory map earlier into
> kho_populate(). If the memory map is empty/NULL:
> 1. Abort kho_populate() immediately with -ENOENT.
> 2. Do not register or reserve the incoming scratch memory, allowing the new
> kernel to reclaim those pages as standard free memory.
> 3. Leave the global 'kho_in' state uninitialized.
>
> Consequently, kho_memory_init() sees no active KHO context
> (kho_in.mem_chunks_phys is 0) and falls back to kho_reserve_scratch(),
> allocating fresh scratch memory as if it were a standard cold boot.
>
> Fixes: de51999e687c ("kho: allow memory preservation state updates after finalization")
> Reported-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
> Closes: https://lore.kernel.org/all/20251218215613.GA17304@ranerica-svr.sc.intel.com
> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> Tested-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
[...]
--
Regards,
Pratyush Yadav
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v4] kho: validate preserved memory map during population
2025-12-23 14:01 [PATCH v4] kho: validate preserved memory map during population Pasha Tatashin
2025-12-23 14:11 ` Pratyush Yadav
@ 2026-01-08 18:03 ` Breno Leitao
1 sibling, 0 replies; 3+ messages in thread
From: Breno Leitao @ 2026-01-08 18:03 UTC (permalink / raw)
To: Pasha Tatashin
Cc: akpm, rppt, graf, linux-kernel, kexec, linux-mm, pratyush,
ricardo.neri-calderon, kernel-team
Hello Pasha,
On Tue, Dec 23, 2025 at 09:01:40AM -0500, Pasha Tatashin wrote:
> If the previous kernel enabled KHO but did not call kho_finalize()
> (e.g., CONFIG_LIVEUPDATE=n or userspace skipped the finalization step),
> the 'preserved-memory-map' property in the FDT remains empty/zero.
>
> Previously, kho_populate() would succeed regardless of the memory map's
> state, reserving the incoming scratch regions in memblock. However,
> kho_memory_init() would later fail to deserialize the empty map. By that
> time, the scratch regions were already registered, leading to partial
> initialization and subsequent list corruption (freeing scratch area
> twice) during kho_init().
While trying my new patchset [0] on top of this patch, I got the
following issue:
[ 0.000000] KHO: disabling KHO revival: -2
Trying to solve it, I come up with a change in kho_get_mem_map_phys() to
distinguish no memory and error, see the patch attached later.
This is what I used to test [0] on top of linux-next. Is this useful?
Link: https://lore.kernel.org/all/20260108-kho-v3-1-b1d6b7a89342@debian.org/ [0]
thanks
--breno
commit 5d7855fede8110d74942e1b67056ba589a1cb54a
Author: Breno Leitao <leitao@debian.org>
Date: Thu Jan 8 07:44:08 2026 -0800
kho: allow KHO to work when no memory is preserved
Fix KHO initialization failing when no memory pages were preserved by
the previous kernel.
Commit eda79a683a0a ("kho: validate preserved memory map during
population") introduced kho_get_mem_map_phys() which returns the physical
address of the preserved memory map directly as its return value. The
caller then validates it with:
mem_map_phys = kho_get_mem_map_phys(fdt);
if (!mem_map_phys) {
err = -ENOENT;
goto out;
}
This creates an ambiguity: physical address 0 is used both as an error
indicator (property missing/malformed) and as a valid value (property
exists with value 0, meaning no memory was preserved).
"No memory preserved" is a legitimate state. KHO provides features beyond
memory page preservation, such as previous kernel version tracking and
kexec count tracking. When the previous kernel enables KHO but doesn't
preserve any memory pages, it sets 'preserved-memory-map' to 0. This is
semantically different from "KHO not initialized" - it means "KHO is
active, there's just nothing in the memory map."
Before eda79a683a0a, the code handled this gracefully in
kho_mem_deserialize():
chunk = mem ? phys_to_virt(mem) : NULL;
if (!chunk)
return false; // No pages, but KHO could still work
After eda79a683a0a, the early validation conflated "no property" with
"property value is 0", causing KHO to be completely disabled in both
cases.
Fix this by changing kho_get_mem_map_phys() to return an error code and
pass the physical address via pointer. This allows distinguishing between:
- Property missing/malformed: return -ENOENT (KHO fails)
- Property exists with value 0: return 0 (KHO succeeds, no memory to
restore)
Fixes: eda79a683a0a ("kho: validate preserved memory map during population")
Signed-off-by: Breno Leitao <leitao@debian.org>
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 271d90198a08..3cf2dc6840c9 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -471,8 +471,8 @@ static void __init deserialize_bitmap(unsigned int order,
}
}
-/* Returns physical address of the preserved memory map from FDT */
-static phys_addr_t __init kho_get_mem_map_phys(const void *fdt)
+/* Returns 0 on success and stores physical address in *phys_out */
+static int __init kho_get_mem_map_phys(const void *fdt, phys_addr_t *phys_out)
{
const void *mem_ptr;
int len;
@@ -480,10 +480,11 @@ static phys_addr_t __init kho_get_mem_map_phys(const void *fdt)
mem_ptr = fdt_getprop(fdt, 0, KHO_FDT_MEMORY_MAP_PROP_NAME, &len);
if (!mem_ptr || len != sizeof(u64)) {
pr_err("failed to get preserved memory bitmaps\n");
- return 0;
+ return -ENOENT;
}
- return get_unaligned((const u64 *)mem_ptr);
+ *phys_out = get_unaligned((const u64 *)mem_ptr);
+ return 0;
}
static void __init kho_mem_deserialize(struct khoser_mem_chunk *chunk)
@@ -1439,7 +1440,7 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
phys_addr_t scratch_phys, u64 scratch_len)
{
struct kho_scratch *scratch = NULL;
- phys_addr_t mem_map_phys;
+ phys_addr_t mem_map_phys = 0;
void *fdt = NULL;
int err = 0;
unsigned int scratch_cnt = scratch_len / sizeof(*kho_scratch);
@@ -1466,11 +1467,9 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
goto out;
}
- mem_map_phys = kho_get_mem_map_phys(fdt);
- if (!mem_map_phys) {
- err = -ENOENT;
+ err = kho_get_mem_map_phys(fdt, &mem_map_phys);
+ if (err)
goto out;
- }
scratch = early_memremap(scratch_phys, scratch_len);
if (!scratch) {
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-01-08 18:03 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-23 14:01 [PATCH v4] kho: validate preserved memory map during population Pasha Tatashin
2025-12-23 14:11 ` Pratyush Yadav
2026-01-08 18:03 ` Breno Leitao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox