#include "../kexec_internal.h"
#include "kexec_handover_internal.h"
+/*
+ * This is the minimal alignment required by deferred struct page init.
+ * deferred_init_memmap_chunk frees memory to the buddy allocator, which looks
+ * at the neighboring pages (up to MAX_PAGE_ORDER) to merge them.
+ * If KHO scratch is not aligned to that value, buddy can access uninitialized
+ * struct pages, which can cause a crash.
+ */
+#define SCRATCH_ALIGNMENT_BYTES (PAGE_SIZE * MAX_ORDER_NR_PAGES)
+static_assert(SCRATCH_ALIGNMENT_BYTES >= CMA_MIN_ALIGNMENT_BYTES);
+
/* The magic token for preserved pages */
#define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
* Scratch areas are released as MIGRATE_CMA. Round them up to the right
* size.
*/
- scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES);
- scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES);
+ scratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES);
+ scratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES);
}
static phys_addr_t __init scratch_size_node(int nid)
size = scratch_size_pernode;
}
- return round_up(size, CMA_MIN_ALIGNMENT_BYTES);
+ return round_up(size, SCRATCH_ALIGNMENT_BYTES);
}
/**
* next kernel
*/
size = scratch_size_lowmem;
- addr = memblock_phys_alloc_range(size, CMA_MIN_ALIGNMENT_BYTES, 0,
+ addr = memblock_phys_alloc_range(size, SCRATCH_ALIGNMENT_BYTES, 0,
ARCH_LOW_ADDRESS_LIMIT);
if (!addr) {
pr_err("Failed to reserve lowmem scratch buffer\n");
/* reserve large contiguous area for allocations without nid */
size = scratch_size_global;
- addr = memblock_phys_alloc(size, CMA_MIN_ALIGNMENT_BYTES);
+ addr = memblock_phys_alloc(size, SCRATCH_ALIGNMENT_BYTES);
if (!addr) {
pr_err("Failed to reserve global scratch buffer\n");
goto err_free_scratch_areas;
*/
for_each_node_state(nid, N_MEMORY) {
size = scratch_size_node(nid);
- addr = memblock_alloc_range_nid(size, CMA_MIN_ALIGNMENT_BYTES,
+ addr = memblock_alloc_range_nid(size, SCRATCH_ALIGNMENT_BYTES,
0, MEMBLOCK_ALLOC_ACCESSIBLE,
nid, true);
if (!addr) {