diff options
author | Mike Rapoport (IBM) <rppt@kernel.org> | 2024-05-05 19:06:19 +0300 |
---|---|---|
committer | Luis Chamberlain <mcgrof@kernel.org> | 2024-05-14 00:31:43 -0700 |
commit | f6bec26c0a7364d3506a3e12dab7c228ef32bd65 (patch) | |
tree | 4686bc944b0452c523664ad70241e3f0a115d12b /mm | |
parent | 12af2b83d0b17ec8b379b721dd4a8fbcd5d791f3 (diff) |
mm/execmem, arch: convert simple overrides of module_alloc to execmem
Several architectures override module_alloc() only to define address
range for code allocations different than VMALLOC address space.
Provide a generic implementation in execmem that uses the parameters for
address space ranges, required alignment and page protections provided
by architectures.
The architectures must fill execmem_info structure and implement
execmem_arch_setup() that returns a pointer to that structure. This way the
execmem initialization won't be called from every architecture, but rather
from a central place, namely a core_initcall() in execmem.
The execmem provides execmem_alloc() API that wraps __vmalloc_node_range()
with the parameters defined by the architectures. If an architecture does
not implement execmem_arch_setup(), execmem_alloc() will fall back to
module_alloc().
Signed-off-by: Mike Rapoport (IBM) <rppt@kernel.org>
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/execmem.c | 67 | ||||
-rw-r--r-- | mm/mm_init.c | 2 |
2 files changed, 66 insertions, 3 deletions
diff --git a/mm/execmem.c b/mm/execmem.c index 480adc69b20d..80e61c1e7319 100644 --- a/mm/execmem.c +++ b/mm/execmem.c @@ -11,14 +11,30 @@ #include <linux/execmem.h> #include <linux/moduleloader.h> -static void *__execmem_alloc(size_t size) +static struct execmem_info *execmem_info __ro_after_init; + +static void *__execmem_alloc(struct execmem_range *range, size_t size) { - return module_alloc(size); + unsigned long start = range->start; + unsigned long end = range->end; + unsigned int align = range->alignment; + pgprot_t pgprot = range->pgprot; + + return __vmalloc_node_range(size, align, start, end, + GFP_KERNEL, pgprot, VM_FLUSH_RESET_PERMS, + NUMA_NO_NODE, __builtin_return_address(0)); } void *execmem_alloc(enum execmem_type type, size_t size) { - return __execmem_alloc(size); + struct execmem_range *range; + + if (!execmem_info) + return module_alloc(size); + + range = &execmem_info->ranges[type]; + + return __execmem_alloc(range, size); } void execmem_free(void *ptr) @@ -30,3 +46,48 @@ void execmem_free(void *ptr) WARN_ON(in_interrupt()); vfree(ptr); } + +static bool execmem_validate(struct execmem_info *info) +{ + struct execmem_range *r = &info->ranges[EXECMEM_DEFAULT]; + + if (!r->alignment || !r->start || !r->end || !pgprot_val(r->pgprot)) { + pr_crit("Invalid parameters for execmem allocator, module loading will fail"); + return false; + } + + return true; +} + +static void execmem_init_missing(struct execmem_info *info) +{ + struct execmem_range *default_range = &info->ranges[EXECMEM_DEFAULT]; + + for (int i = EXECMEM_DEFAULT + 1; i < EXECMEM_TYPE_MAX; i++) { + struct execmem_range *r = &info->ranges[i]; + + if (!r->start) { + r->pgprot = default_range->pgprot; + r->alignment = default_range->alignment; + r->start = default_range->start; + r->end = default_range->end; + } + } +} + +struct execmem_info * __weak execmem_arch_setup(void) +{ + return NULL; +} + +void __init execmem_init(void) +{ + struct execmem_info *info = execmem_arch_setup(); + + if (!info || !execmem_validate(info)) + return; + + execmem_init_missing(info); + + execmem_info = info; +} diff --git a/mm/mm_init.c b/mm/mm_init.c index 549e76af8f82..b6a1fcf6e13a 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -27,6 +27,7 @@ #include <linux/swap.h> #include <linux/cma.h> #include <linux/crash_dump.h> +#include <linux/execmem.h> #include "internal.h" #include "slab.h" #include "shuffle.h" @@ -2793,4 +2794,5 @@ void __init mm_core_init(void) pti_init(); kmsan_init_runtime(); mm_cache_init(); + execmem_init(); } |