Commit | Line | Data |
---|---|---|
12af2b83 MRI |
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 <mcgrof@kernel.org> | |
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 | ||
f6bec26c MRI |
14 | static struct execmem_info *execmem_info __ro_after_init; |
15 | ||
16 | static void *__execmem_alloc(struct execmem_range *range, size_t size) | |
12af2b83 | 17 | { |
f6bec26c MRI |
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)); | |
12af2b83 MRI |
26 | } |
27 | ||
28 | void *execmem_alloc(enum execmem_type type, size_t size) | |
29 | { | |
f6bec26c MRI |
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); | |
12af2b83 MRI |
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 | } | |
f6bec26c MRI |
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 | } |