Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kho: validate preserved memory map during population

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.

Link: https://lkml.kernel.org/r/20251223140140.2090337-1-pasha.tatashin@soleen.com
Fixes: de51999e687c ("kho: allow memory preservation state updates after finalization")
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reported-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
Closes: https://lore.kernel.org/all/20251218215613.GA17304@ranerica-svr.sc.intel.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>
Cc: Alexander Graf <graf@amazon.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Pasha Tatashin and committed by
Andrew Morton
582f0f38 777a8560

+19 -18
+19 -18
kernel/liveupdate/kexec_handover.c
··· 460 460 } 461 461 } 462 462 463 - /* Return true if memory was deserizlied */ 464 - static bool __init kho_mem_deserialize(const void *fdt) 463 + /* Returns physical address of the preserved memory map from FDT */ 464 + static phys_addr_t __init kho_get_mem_map_phys(const void *fdt) 465 465 { 466 - struct khoser_mem_chunk *chunk; 467 466 const void *mem_ptr; 468 - u64 mem; 469 467 int len; 470 468 471 469 mem_ptr = fdt_getprop(fdt, 0, PROP_PRESERVED_MEMORY_MAP, &len); 472 470 if (!mem_ptr || len != sizeof(u64)) { 473 471 pr_err("failed to get preserved memory bitmaps\n"); 474 - return false; 472 + return 0; 475 473 } 476 474 477 - mem = get_unaligned((const u64 *)mem_ptr); 478 - chunk = mem ? phys_to_virt(mem) : NULL; 475 + return get_unaligned((const u64 *)mem_ptr); 476 + } 479 477 480 - /* No preserved physical pages were passed, no deserialization */ 481 - if (!chunk) 482 - return false; 483 - 478 + static void __init kho_mem_deserialize(struct khoser_mem_chunk *chunk) 479 + { 484 480 while (chunk) { 485 481 unsigned int i; 486 482 ··· 485 489 &chunk->bitmaps[i]); 486 490 chunk = KHOSER_LOAD_PTR(chunk->hdr.next); 487 491 } 488 - 489 - return true; 490 492 } 491 493 492 494 /* ··· 1247 1253 struct kho_in { 1248 1254 phys_addr_t fdt_phys; 1249 1255 phys_addr_t scratch_phys; 1256 + phys_addr_t mem_map_phys; 1250 1257 struct kho_debugfs dbg; 1251 1258 }; 1252 1259 ··· 1429 1434 1430 1435 void __init kho_memory_init(void) 1431 1436 { 1432 - if (kho_in.scratch_phys) { 1437 + if (kho_in.mem_map_phys) { 1433 1438 kho_scratch = phys_to_virt(kho_in.scratch_phys); 1434 1439 kho_release_scratch(); 1435 - 1436 - if (!kho_mem_deserialize(kho_get_fdt())) 1437 - kho_in.fdt_phys = 0; 1440 + kho_mem_deserialize(phys_to_virt(kho_in.mem_map_phys)); 1438 1441 } else { 1439 1442 kho_reserve_scratch(); 1440 1443 } ··· 1441 1448 void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, 1442 1449 phys_addr_t scratch_phys, u64 scratch_len) 1443 1450 { 1444 - void *fdt = NULL; 1445 1451 struct kho_scratch *scratch = NULL; 1452 + phys_addr_t mem_map_phys; 1453 + void *fdt = NULL; 1446 1454 int err = 0; 1447 1455 unsigned int scratch_cnt = scratch_len / sizeof(*kho_scratch); 1448 1456 ··· 1466 1472 pr_warn("setup: handover FDT (0x%llx) is incompatible with '%s': %d\n", 1467 1473 fdt_phys, KHO_FDT_COMPATIBLE, err); 1468 1474 err = -EINVAL; 1475 + goto out; 1476 + } 1477 + 1478 + mem_map_phys = kho_get_mem_map_phys(fdt); 1479 + if (!mem_map_phys) { 1480 + err = -ENOENT; 1469 1481 goto out; 1470 1482 } 1471 1483 ··· 1515 1515 1516 1516 kho_in.fdt_phys = fdt_phys; 1517 1517 kho_in.scratch_phys = scratch_phys; 1518 + kho_in.mem_map_phys = mem_map_phys; 1518 1519 kho_scratch_cnt = scratch_cnt; 1519 1520 pr_info("found kexec handover data.\n"); 1520 1521