// SPDX-License-Identifier: GPL-2.0-only
/*
* kexec_handover.c - kexec handover metadata processing
* Copyright (C) 2023 Alexander Graf <graf@amazon.com>
* Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <rppt@kernel.org>
* Copyright (C) 2025 Google LLC, Changyuan Lyu <changyuanl@google.com>
*/
#define pr_fmt(fmt) "KHO: " fmt
#include <linux/cleanup.h>
#include <linux/cma.h>
#include <linux/count_zeros.h>
#include <linux/debugfs.h>
#include <linux/kexec.h>
#include <linux/kexec_handover.h>
#include <linux/libfdt.h>
#include <linux/list.h>
#include <linux/memblock.h>
#include <linux/notifier.h>
#include <linux/page-isolation.h>
#include <linux/vmalloc.h>
#include <asm/early_ioremap.h>
#include "kexec_handover_internal.h"
/*
* KHO is tightly coupled with mm init and needs access to some of mm
* internal APIs.
*/
#include "../mm/internal.h"
#include "kexec_internal.h"
#define KHO_FDT_COMPATIBLE "kho-v1"
#define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
#define PROP_SUB_FDT "fdt"
#define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
/*
* KHO uses page->private, which is an unsigned long, to store page metadata.
* Use it to store both the magic and the order.
*/
union kho_page_info {
unsigned long page_private;
struct {
unsigned int order;
unsigned int magic;
};
};
static_assert(sizeof(union kho_page_info) == sizeof(((struct page *)0)->private));
static bool kho_enable __ro_after_init;
bool kho_is_enabled(void)
{
return kho_enable;
}
EXPORT_SYMBOL_GPL(kho_is_enabled);
static int __init kho_parse_enable(char *p)
{
return kstrtobool(p, &kho_enable);
}
early_param("kho", kho_parse_enable);
/*
* Keep track of memory that is to be preserved across KHO.
*
* The serializing side uses two levels of xarrays to manage chunks of per-order
* PAGE_SIZE byte bitmaps. For instance if PAGE_SIZE = 4096, the entire 1G order
* of a 8TB system would fit inside a single 4096 byte bitmap. For order 0
* allocations each bitmap will cover 128M of address space. Thus, for 16G of
* memory at most 512K of bitmap memory will be needed for order 0.
*
* This approach is fully incremental, as the serialization progresses folios
* can continue be aggregated to the tracker. The final step, immediately prior
* to kexec would serialize the xarray information into a linked list for the
* successor kernel to parse.
*/
#define PRESERVE_BITS (PAGE_SIZE * 8)
struct kho_mem_phys_bits {
DECLARE_BITMAP(preserve, PRESERVE_BITS);
};
static_assert(sizeof(struct kho_mem_phys_bits) == PAGE_SIZE);
struct kho_mem_phys {
/*
* Points to kho_mem_phys_bits, a sparse bitmap array. Each bit is sized
* to order.
*/
struct xarray phys_bits;
};
struct kho_mem_track {
/* Points to kho_mem_phys, each order gets its own bitmap tree */
struct xarray orders;
};
struct khoser_mem_chunk;
struct kho_serialization {
struct page *fdt;
struct list_head fdt_list;
struct dentry *sub_fdt_dir;
struct kho_mem_track track;
/* First chunk of serialized preserved memory map */
struct khoser_mem_chunk *preserved_mem_map;
};
struct kho_out {
struct blocking_notifier_head chain_head;
struct dentry *dir;
struct mutex lock; /* protects KHO FDT finalization */
struct kho_serialization ser;
bool finalized;
};
static struct kho_out kho_out = {
.chain_head = BLOCKING_NOTIFIER_INIT(kho_out.chain_head),
.lock = __MUTEX_INITIA
|