Move the shared KHO radix tree key encoding and indexing helpers, vmalloc flag conversion helpers, and FDT validation and lookup helpers from kexec_handover.c into kexec_handover_internal.h as static inline functions.
The radix tree and vmalloc flag helpers are small bit-manipulation routines, and the FDT helpers are small wrappers with a single call site per translation unit, so it is natural to inline them in the internal header. This is going to be used by the upcoming KHO read-only mode to walk preserved KHO radix trees, vmalloc preservation chains, and FDT subtrees without duplicating the implementation. Signed-off-by: Pasha Tatashin <[email protected]> --- kernel/liveupdate/kexec_handover.c | 115 +---------------- kernel/liveupdate/kexec_handover_internal.h | 134 ++++++++++++++++++++ 2 files changed, 136 insertions(+), 113 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index ce5983abf181..f427cdd9a969 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -113,29 +113,6 @@ 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. @@ -161,20 +138,6 @@ static phys_addr_t kho_decode_radix_key(unsigned long key, unsigned int *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; @@ -634,21 +597,6 @@ static int __init kho_preserved_memory_reserve(unsigned long key, void *data) return 0; } -/* Returns physical address of the preserved memory map from FDT */ -static phys_addr_t __init kho_get_mem_map_phys(const void *fdt) -{ - const void *mem_ptr; - int len; - - 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 map\n"); - return 0; - } - - return get_unaligned((const u64 *)mem_ptr); -} - static void __init *kho_get_mem_map(const void *fdt) { phys_addr_t phys = kho_get_mem_map_phys(fdt); @@ -1287,13 +1235,6 @@ void kho_unpreserve_pages(struct page *page, unsigned long nr_pages) } EXPORT_SYMBOL_GPL(kho_unpreserve_pages); -/* vmalloc flags KHO supports */ -#define KHO_VMALLOC_SUPPORTED_FLAGS (VM_ALLOC | VM_ALLOW_HUGE_VMAP) - -/* KHO internal flags for vmalloc preservations */ -#define KHO_VMALLOC_ALLOC 0x0001 -#define KHO_VMALLOC_HUGE_VMAP 0x0002 - static unsigned short vmalloc_flags_to_kho(unsigned int vm_flags) { unsigned short kho_flags = 0; @@ -1306,18 +1247,6 @@ static unsigned short vmalloc_flags_to_kho(unsigned int vm_flags) return kho_flags; } -static unsigned int kho_flags_to_vmalloc(unsigned short kho_flags) -{ - unsigned int vm_flags = 0; - - if (kho_flags & KHO_VMALLOC_ALLOC) - vm_flags |= VM_ALLOC; - if (kho_flags & KHO_VMALLOC_HUGE_VMAP) - vm_flags |= VM_ALLOW_HUGE_VMAP; - - return vm_flags; -} - static struct kho_vmalloc_chunk *new_vmalloc_chunk(struct kho_vmalloc_chunk *cur) { struct kho_vmalloc_chunk *chunk; @@ -1660,37 +1589,7 @@ EXPORT_SYMBOL_GPL(is_kho_boot); */ int kho_retrieve_subtree(const char *name, phys_addr_t *phys, size_t *size) { - const void *fdt = kho_get_fdt(); - const u64 *val; - int offset, len; - - if (!fdt) - return -ENOENT; - - if (!phys) - return -EINVAL; - - offset = fdt_subnode_offset(fdt, 0, name); - if (offset < 0) - return -ENOENT; - - val = fdt_getprop(fdt, offset, KHO_SUB_TREE_PROP_NAME, &len); - if (!val || len != sizeof(*val)) - return -EINVAL; - - *phys = (phys_addr_t)*val; - - val = fdt_getprop(fdt, offset, KHO_SUB_TREE_SIZE_PROP_NAME, &len); - if (!val || len != sizeof(*val)) { - pr_warn("broken KHO subnode '%s': missing or invalid blob-size property\n", - name); - return -EINVAL; - } - - if (size) - *size = (size_t)*val; - - return 0; + return __kho_retrieve_subtree(kho_get_fdt(), name, phys, size); } EXPORT_SYMBOL_GPL(kho_retrieve_subtree); @@ -1981,18 +1880,8 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, pr_warn("setup: failed to memremap FDT (0x%llx)\n", fdt_phys); goto report; } - err = fdt_check_header(fdt); - if (err) { - pr_warn("setup: handover FDT (0x%llx) is invalid: %d\n", - fdt_phys, err); + if (kho_check_fdt(fdt, fdt_phys)) goto unmap_fdt; - } - err = fdt_node_check_compatible(fdt, 0, KHO_FDT_COMPATIBLE); - if (err) { - pr_warn("setup: handover FDT (0x%llx) is incompatible with '%s': %d\n", - fdt_phys, KHO_FDT_COMPATIBLE, err); - goto unmap_fdt; - } mem_map_phys = kho_get_mem_map_phys(fdt); if (!mem_map_phys) diff --git a/kernel/liveupdate/kexec_handover_internal.h b/kernel/liveupdate/kexec_handover_internal.h index 38181076f603..094b4917a0f1 100644 --- a/kernel/liveupdate/kexec_handover_internal.h +++ b/kernel/liveupdate/kexec_handover_internal.h @@ -3,8 +3,142 @@ #define LINUX_KEXEC_HANDOVER_INTERNAL_H #include <linux/kexec_handover.h> +#include <linux/kho/abi/kexec_handover.h> +#include <linux/libfdt.h> #include <linux/list.h> +#include <linux/mm.h> +#include <linux/printk.h> #include <linux/types.h> +#include <linux/unaligned.h> +#include <linux/vmalloc.h> + +/* vmalloc flags KHO supports */ +#define KHO_VMALLOC_SUPPORTED_FLAGS (VM_ALLOC | VM_ALLOW_HUGE_VMAP) + +/* KHO internal flags for vmalloc preservations */ +#define KHO_VMALLOC_ALLOC 0x0001 +#define KHO_VMALLOC_HUGE_VMAP 0x0002 + +static inline unsigned int kho_flags_to_vmalloc(unsigned short kho_flags) +{ + unsigned int vm_flags = 0; + + if (kho_flags & KHO_VMALLOC_ALLOC) + vm_flags |= VM_ALLOC; + if (kho_flags & KHO_VMALLOC_HUGE_VMAP) + vm_flags |= VM_ALLOW_HUGE_VMAP; + + return vm_flags; +} + +/** + * 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 inline 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; +} + +static inline unsigned long kho_radix_get_bitmap_index(unsigned long key) +{ + return key % (1 << KHO_BITMAP_SIZE_LOG2); +} + +static inline 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 inline int kho_check_fdt(const void *fdt, phys_addr_t fdt_phys) +{ + int err; + + err = fdt_check_header(fdt); + if (err) { + pr_warn("handover FDT (0x%llx) is invalid: %d\n", + (unsigned long long)fdt_phys, err); + return err; + } + + err = fdt_node_check_compatible(fdt, 0, KHO_FDT_COMPATIBLE); + if (err) { + pr_warn("handover FDT (0x%llx) is incompatible with '%s': %d\n", + (unsigned long long)fdt_phys, KHO_FDT_COMPATIBLE, err); + return err; + } + + return 0; +} + +/* Returns physical address of the preserved memory map from FDT */ +static inline phys_addr_t kho_get_mem_map_phys(const void *fdt) +{ + const void *mem_ptr; + int len; + + 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 map\n"); + return 0; + } + + return get_unaligned((const u64 *)mem_ptr); +} + +static inline int __kho_retrieve_subtree(const void *fdt, const char *name, + phys_addr_t *phys, size_t *size) +{ + const u64 *val; + int offset, len; + + if (!fdt) + return -ENOENT; + + if (!phys) + return -EINVAL; + + offset = fdt_subnode_offset(fdt, 0, name); + if (offset < 0) + return -ENOENT; + + val = fdt_getprop(fdt, offset, KHO_SUB_TREE_PROP_NAME, &len); + if (!val || len != sizeof(*val)) + return -EINVAL; + + *phys = (phys_addr_t)get_unaligned(val); + + val = fdt_getprop(fdt, offset, KHO_SUB_TREE_SIZE_PROP_NAME, &len); + if (!val || len != sizeof(*val)) { + pr_warn("broken KHO subnode '%s': missing or invalid blob-size property\n", + name); + return -EINVAL; + } + + if (size) + *size = (size_t)get_unaligned(val); + + return 0; +} #ifdef CONFIG_KEXEC_HANDOVER_DEBUGFS #include <linux/debugfs.h> -- 2.56.0.176.g3575e65682-goog

