xref: /linux-6.15/mm/execmem.c (revision f6bec26c)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2002 Richard Henderson
4  * Copyright (C) 2001 Rusty Russell, 2002, 2010 Rusty Russell IBM.
5  * Copyright (C) 2023 Luis Chamberlain <[email protected]>
6  * Copyright (C) 2024 Mike Rapoport IBM.
7  */
8 
9 #include <linux/mm.h>
10 #include <linux/vmalloc.h>
11 #include <linux/execmem.h>
12 #include <linux/moduleloader.h>
13 
14 static struct execmem_info *execmem_info __ro_after_init;
15 
16 static void *__execmem_alloc(struct execmem_range *range, size_t size)
17 {
18 	unsigned long start = range->start;
19 	unsigned long end = range->end;
20 	unsigned int align = range->alignment;
21 	pgprot_t pgprot = range->pgprot;
22 
23 	return __vmalloc_node_range(size, align, start, end,
24 				    GFP_KERNEL, pgprot, VM_FLUSH_RESET_PERMS,
25 				    NUMA_NO_NODE, __builtin_return_address(0));
26 }
27 
28 void *execmem_alloc(enum execmem_type type, size_t size)
29 {
30 	struct execmem_range *range;
31 
32 	if (!execmem_info)
33 		return module_alloc(size);
34 
35 	range = &execmem_info->ranges[type];
36 
37 	return __execmem_alloc(range, size);
38 }
39 
40 void execmem_free(void *ptr)
41 {
42 	/*
43 	 * This memory may be RO, and freeing RO memory in an interrupt is not
44 	 * supported by vmalloc.
45 	 */
46 	WARN_ON(in_interrupt());
47 	vfree(ptr);
48 }
49 
50 static bool execmem_validate(struct execmem_info *info)
51 {
52 	struct execmem_range *r = &info->ranges[EXECMEM_DEFAULT];
53 
54 	if (!r->alignment || !r->start || !r->end || !pgprot_val(r->pgprot)) {
55 		pr_crit("Invalid parameters for execmem allocator, module loading will fail");
56 		return false;
57 	}
58 
59 	return true;
60 }
61 
62 static void execmem_init_missing(struct execmem_info *info)
63 {
64 	struct execmem_range *default_range = &info->ranges[EXECMEM_DEFAULT];
65 
66 	for (int i = EXECMEM_DEFAULT + 1; i < EXECMEM_TYPE_MAX; i++) {
67 		struct execmem_range *r = &info->ranges[i];
68 
69 		if (!r->start) {
70 			r->pgprot = default_range->pgprot;
71 			r->alignment = default_range->alignment;
72 			r->start = default_range->start;
73 			r->end = default_range->end;
74 		}
75 	}
76 }
77 
78 struct execmem_info * __weak execmem_arch_setup(void)
79 {
80 	return NULL;
81 }
82 
83 void __init execmem_init(void)
84 {
85 	struct execmem_info *info = execmem_arch_setup();
86 
87 	if (!info || !execmem_validate(info))
88 		return;
89 
90 	execmem_init_missing(info);
91 
92 	execmem_info = info;
93 }
94