2098 lines · 53.9 KB · C
RawDownload
// 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> * Copyright (C) 2025 Pasha Tatashin <pasha.tatashin@soleen.com> * Copyright (C) 2026 Google LLC, Jason Miu <jasonmiu@google.com> */ #define pr_fmt(fmt) "KHO: " fmt #include <linux/cleanup.h>#include <linux/cma.h>#include <linux/kmemleak.h>#include <linux/count_zeros.h>#include <linux/kasan.h>#include <linux/kexec.h>#include <linux/kexec_handover.h>#include <linux/kho_radix_tree.h>#include <linux/utsname.h>#include <linux/kho/abi/kexec_handover.h>#include <linux/kho/abi/kexec_metadata.h>#include <linux/libfdt.h>#include <linux/list.h>#include <linux/memblock.h>#include <linux/page-isolation.h>#include <linux/unaligned.h>#include <linux/vmalloc.h> #include <asm/early_ioremap.h> /* * KHO is tightly coupled with mm init and needs access to some of mm * internal APIs. */#include "../../mm/mm_init.h"#include "../../mm/vmalloc.h"#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' */ /* * 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 = IS_ENABLED(CONFIG_KEXEC_HANDOVER_ENABLE_DEFAULT); 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); struct kho_out {	void *fdt;	struct mutex lock; /* protects KHO FDT */ 	struct kho_radix_tree radix_tree;	struct kho_debugfs dbg;}; static struct kho_out kho_out = {	.lock = __MUTEX_INITIALIZER(kho_out.lock),	.radix_tree = {		.lock = __MUTEX_INITIALIZER(kho_out.radix_tree.lock),	},}; struct kho_in {	phys_addr_t fdt_phys;	phys_addr_t scratch_phys;	char previous_release[__NEW_UTS_LEN + 1];	u32 kexec_count;	struct kho_debugfs dbg;	struct kho_radix_tree radix_tree;}; static struct kho_in kho_in = {}; static const void *kho_get_fdt(void){	return kho_in.fdt_phys ? phys_to_virt(kho_in.fdt_phys) : NULL;} /** * kho_encode_radix_key - Encodes a physical address and order into a radix key. * @phys: The physical address of the page. * @order: The order of the page. * * This function combines a page's physical address and its order into a * single unsigned long, which is used as a key for all radix tree * operations. * * Return: The encoded unsigned long radix key. */static unsigned long kho_encode_radix_key(phys_addr_t phys, unsigned int order){	/* The physical address is encoded by shifting the PFN by its order. */	unsigned long shift = PAGE_SHIFT + order;	/* Order bit goes right before the shifted PFN. */	unsigned long h = 1UL << (64 - shift);	/* Shifted PFN. */	unsigned long l = phys >> shift; 	return h | l;} /** * kho_decode_radix_key - Decodes a radix key back into a physical address and order. * @key: The unsigned long key to decode. * @order: An output parameter, a pointer to an unsigned int where the decoded *         page order will be stored. * * This function reverses the encoding performed by kho_encode_radix_key(), * extracting the original physical address and page order from a given key. * * Return: The decoded physical address. */static phys_addr_t kho_decode_radix_key(unsigned long key, unsigned int *order){	/* fls64() indexes starting from 1. */	unsigned int order_bit = fls64(key) - 1;	phys_addr_t phys; 	/* order bit goes right before the shifted PFN. */	*order = 64 - (PAGE_SHIFT + order_bit);	/* The order bit is discarded by the shift */	phys = key << (PAGE_SHIFT + *order); 	return phys;} static unsigned long kho_radix_get_bitmap_index(unsigned long key){	return key % (1 << KHO_BITMAP_SIZE_LOG2);} static unsigned long kho_radix_get_table_index(unsigned long key,					       unsigned int level){	int s; 	s = ((level - 1) * KHO_TABLE_SIZE_LOG2) + KHO_BITMAP_SIZE_LOG2;	return (key >> s) % (1 << KHO_TABLE_SIZE_LOG2);} static void __ref *kho_radix_alloc_node(void){	struct kho_radix_node *node; 	if (slab_is_available())		node = (struct kho_radix_node *)get_zeroed_page(GFP_KERNEL);	else		node = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 	return node;} static void __ref kho_radix_free_node(struct kho_radix_node *node){	if (slab_is_available())		free_page((unsigned long)node);	else		memblock_free(node, PAGE_SIZE);} /** * kho_radix_add_key - Add a key to the radix tree. * @tree: The KHO radix tree. * @key: The key to add. * * This function traverses the radix tree based on the @key provided. It sets the * corresponding bit in the leaf bitmap to mark the @key as present. If * intermediate nodes do not exist along the path, they are allocated and added * to the tree. * * NOTE: Currently only keys of width up to %KHO_RADIX_KEY_WIDTH are supported. * This limit only exists because current users of the radix tree don't use more * than that. Changing the maximum width requires changing the tree depth, which * needs bumping the ABI version. * * Return: 0 on success, or a negative error code on failure. */int kho_radix_add_key(struct kho_radix_tree *tree, unsigned long key){	/* Newly allocated nodes for error cleanup */	struct kho_radix_node *intermediate_nodes[KHO_TREE_MAX_DEPTH] = { 0 };	struct kho_radix_node *anchor_node = NULL;	struct kho_radix_node *node = tree->root;	struct kho_radix_node *new_node;	unsigned int i, idx, anchor_idx;	struct kho_radix_leaf *leaf;	int err = 0; 	if (WARN_ON_ONCE(!tree->root))		return -EINVAL; 	if (unlikely(fls64(key) > KHO_RADIX_KEY_WIDTH))		return -ERANGE; 	might_sleep(); 	guard(mutex)(&tree->lock); 	/* Go from high levels to low levels */	for (i = KHO_TREE_MAX_DEPTH - 1; i > 0; i--) {		idx = kho_radix_get_table_index(key, i); 		if (node->table[idx]) {			node = phys_to_virt(node->table[idx]);			continue;		} 		/* Next node is empty, create a new node for it */		new_node = kho_radix_alloc_node();		if (!new_node) {			err = -ENOMEM;			goto err_free_nodes;		} 		node->table[idx] = virt_to_phys(new_node); 		/*		 * Capture the node where the new branch starts for cleanup		 * if allocation fails.		 */		if (!anchor_node) {			anchor_node = node;			anchor_idx = idx;		}		intermediate_nodes[i] = new_node; 		node = new_node;	} 	/* Handle the leaf level bitmap (level 0) */	idx = kho_radix_get_bitmap_index(key);	leaf = (struct kho_radix_leaf *)node;	__set_bit(idx, leaf->bitmap); 	return 0; err_free_nodes:	for (i = KHO_TREE_MAX_DEPTH - 1; i > 0; i--) {		if (intermediate_nodes[i])			kho_radix_free_node(intermediate_nodes[i]);	}	if (anchor_node)		anchor_node->table[anchor_idx] = 0; 	return err;}EXPORT_SYMBOL_GPL(kho_radix_add_key); /** * kho_radix_del_key - Removes the key from the radix tree. * @tree: The KHO radix tree. * @key: The key to remove. * * This function traverses the radix tree and clears the bit corresponding to * the @key, effectively removing it from the tree. It does not free the tree's * intermediate nodes, even if they become empty. */void kho_radix_del_key(struct kho_radix_tree *tree, unsigned long key){	struct kho_radix_node *node = tree->root;	struct kho_radix_leaf *leaf;	unsigned int i, idx; 	if (WARN_ON_ONCE(!tree->root))		return; 	/* Keys wider than KHO_RADIX_KEY_WIDTH are not allowed to be added. */	if (unlikely(fls64(key) > KHO_RADIX_KEY_WIDTH))		return; 	might_sleep(); 	guard(mutex)(&tree->lock); 	/* Go from high levels to low levels */	for (i = KHO_TREE_MAX_DEPTH - 1; i > 0; i--) {		idx = kho_radix_get_table_index(key, i); 		/*		 * Attempting to delete a page that has not been preserved,		 * return with a warning.		 */		if (WARN_ON(!node->table[idx]))			return; 		node = phys_to_virt(node->table[idx]);	} 	/* Handle the leaf level bitmap (level 0) */	leaf = (struct kho_radix_leaf *)node;	idx = kho_radix_get_bitmap_index(key);	__clear_bit(idx, leaf->bitmap);}EXPORT_SYMBOL_GPL(kho_radix_del_key); static void __kho_radix_destroy_tree(struct kho_radix_node *root,				     unsigned int level){	unsigned long i; 	if (level == 0) {		kho_radix_free_node(root);		return;	} 	for (i = 0; i < PAGE_SIZE / sizeof(phys_addr_t); i++) {		if (root->table[i])			__kho_radix_destroy_tree(phys_to_virt(root->table[i]),						 level - 1);	} 	kho_radix_free_node(root);} /** * kho_radix_init_tree - initialize the radix tree. * @tree:   the tree to initialize. * @root:   root table of the radix tree. * * Initialize the radix tree with the given root node. If root is %NULL, an * empty root table is allocated. If root is not %NULL, it is the caller's * responsibility to make sure the root is valid and in the correct format. * * Return: 0 on success, -errno on failure. */int kho_radix_init_tree(struct kho_radix_tree *tree, struct kho_radix_node *root){	if (!root)		root = kho_radix_alloc_node();	if (!root)		return -ENOMEM; 	tree->root = root;	mutex_init(&tree->lock);	return 0;}EXPORT_SYMBOL_GPL(kho_radix_init_tree); /** * kho_radix_destroy_tree - Destroy the radix tree * @tree: The radix tree to destroy * * Walk @tree and free all its nodes. */void kho_radix_destroy_tree(struct kho_radix_tree *tree){	if (!tree->root)		return; 	__kho_radix_destroy_tree(tree->root, KHO_TREE_MAX_DEPTH - 1);	tree->root = NULL;}EXPORT_SYMBOL_GPL(kho_radix_destroy_tree); static int kho_radix_walk_leaf(struct kho_radix_leaf *leaf, unsigned long key,			       const struct kho_radix_walk_cb *cb, void *data){	unsigned long *bitmap = (unsigned long *)leaf;	unsigned int i;	int err; 	if (cb->node) {		err = cb->node(virt_to_phys(leaf), data);		if (err)			return err;	} 	if (!cb->leaf)		return 0; 	for_each_set_bit(i, bitmap, PAGE_SIZE * BITS_PER_BYTE) {		err = cb->leaf(key | i, data);		if (err)			return err;	} 	return 0;} static int __kho_radix_walk_tree(struct kho_radix_node *root,				 unsigned int level, unsigned long start,				 const struct kho_radix_walk_cb *cb, void *data){	struct kho_radix_node *node;	struct kho_radix_leaf *leaf;	unsigned long key, i;	unsigned int shift;	int err; 	if (cb->node) {		err = cb->node(virt_to_phys(root), data);		if (err)			return err;	} 	for (i = 0; i < PAGE_SIZE / sizeof(phys_addr_t); i++) {		if (!root->table[i])			continue; 		shift = ((level - 1) * KHO_TABLE_SIZE_LOG2) +			KHO_BITMAP_SIZE_LOG2;		key = start | (i << shift); 		node = phys_to_virt(root->table[i]); 		if (level == 1) {			/*			 * we are at level 1,			 * node is pointing to the level 0 bitmap.			 */			leaf = (struct kho_radix_leaf *)node;			err = kho_radix_walk_leaf(leaf, key, cb, data);		} else {			err  = __kho_radix_walk_tree(node, level - 1,						     key, cb, data);		} 		if (err)			return err;	} 	return 0;} /** * kho_radix_walk_tree - Traverses the radix tree and calls a callback for each key. * @tree: A pointer to the KHO radix tree to walk. * @cb:   Set of callbacks to be invoked during the tree walk. * @data: Opaque data pointer passed to each callback in @cb. * * This function walks the radix tree, searching from the top level down to the * lowest level (level 0), invoking the appropriate callbacks. * * Return: 0 if the walk completed the specified tree, or the non-zero return *         value from the callback that stopped the walk. */int kho_radix_walk_tree(struct kho_radix_tree *tree,			const struct kho_radix_walk_cb *cb, void *data){	if (WARN_ON_ONCE(!tree->root))		return -EINVAL; 	guard(mutex)(&tree->lock); 	return __kho_radix_walk_tree(tree->root, KHO_TREE_MAX_DEPTH - 1, 0, cb,				     data);}EXPORT_SYMBOL_GPL(kho_radix_walk_tree); /* For physically contiguous 0-order pages. */static void kho_init_pages(struct page *page, unsigned long nr_pages){	for (unsigned long i = 0; i < nr_pages; i++) {		set_page_count(page + i, 1);		/* Clear each page's codetag to avoid accounting mismatch. */		clear_page_tag_ref(page + i);	}} static void kho_init_folio(struct page *page, unsigned int order){	unsigned long nr_pages = (1 << order); 	/* Head page gets refcount of 1. */

Lines 1–500 of 2098.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t