brintos

brintos / linux-shallow public Read only

0
0
Text · 3.5 KiB · 0c4b36b Raw
144 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2002 Richard Henderson4 * 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 14static struct execmem_info *execmem_info __ro_after_init;15static struct execmem_info default_execmem_info __ro_after_init;16 17static void *__execmem_alloc(struct execmem_range *range, size_t size)18{19	bool kasan = range->flags & EXECMEM_KASAN_SHADOW;20	unsigned long vm_flags  = VM_FLUSH_RESET_PERMS;21	gfp_t gfp_flags = GFP_KERNEL | __GFP_NOWARN;22	unsigned long start = range->start;23	unsigned long end = range->end;24	unsigned int align = range->alignment;25	pgprot_t pgprot = range->pgprot;26	void *p;27 28	if (kasan)29		vm_flags |= VM_DEFER_KMEMLEAK;30 31	p = __vmalloc_node_range(size, align, start, end, gfp_flags,32				 pgprot, vm_flags, NUMA_NO_NODE,33				 __builtin_return_address(0));34	if (!p && range->fallback_start) {35		start = range->fallback_start;36		end = range->fallback_end;37		p = __vmalloc_node_range(size, align, start, end, gfp_flags,38					 pgprot, vm_flags, NUMA_NO_NODE,39					 __builtin_return_address(0));40	}41 42	if (!p) {43		pr_warn_ratelimited("execmem: unable to allocate memory\n");44		return NULL;45	}46 47	if (kasan && (kasan_alloc_module_shadow(p, size, GFP_KERNEL) < 0)) {48		vfree(p);49		return NULL;50	}51 52	return kasan_reset_tag(p);53}54 55void *execmem_alloc(enum execmem_type type, size_t size)56{57	struct execmem_range *range = &execmem_info->ranges[type];58 59	return __execmem_alloc(range, size);60}61 62void execmem_free(void *ptr)63{64	/*65	 * This memory may be RO, and freeing RO memory in an interrupt is not66	 * supported by vmalloc.67	 */68	WARN_ON(in_interrupt());69	vfree(ptr);70}71 72static bool execmem_validate(struct execmem_info *info)73{74	struct execmem_range *r = &info->ranges[EXECMEM_DEFAULT];75 76	if (!r->alignment || !r->start || !r->end || !pgprot_val(r->pgprot)) {77		pr_crit("Invalid parameters for execmem allocator, module loading will fail");78		return false;79	}80 81	return true;82}83 84static void execmem_init_missing(struct execmem_info *info)85{86	struct execmem_range *default_range = &info->ranges[EXECMEM_DEFAULT];87 88	for (int i = EXECMEM_DEFAULT + 1; i < EXECMEM_TYPE_MAX; i++) {89		struct execmem_range *r = &info->ranges[i];90 91		if (!r->start) {92			if (i == EXECMEM_MODULE_DATA)93				r->pgprot = PAGE_KERNEL;94			else95				r->pgprot = default_range->pgprot;96			r->alignment = default_range->alignment;97			r->start = default_range->start;98			r->end = default_range->end;99			r->flags = default_range->flags;100			r->fallback_start = default_range->fallback_start;101			r->fallback_end = default_range->fallback_end;102		}103	}104}105 106struct execmem_info * __weak execmem_arch_setup(void)107{108	return NULL;109}110 111static void __init __execmem_init(void)112{113	struct execmem_info *info = execmem_arch_setup();114 115	if (!info) {116		info = execmem_info = &default_execmem_info;117		info->ranges[EXECMEM_DEFAULT].start = VMALLOC_START;118		info->ranges[EXECMEM_DEFAULT].end = VMALLOC_END;119		info->ranges[EXECMEM_DEFAULT].pgprot = PAGE_KERNEL_EXEC;120		info->ranges[EXECMEM_DEFAULT].alignment = 1;121	}122 123	if (!execmem_validate(info))124		return;125 126	execmem_init_missing(info);127 128	execmem_info = info;129}130 131#ifdef CONFIG_ARCH_WANTS_EXECMEM_LATE132static int __init execmem_late_init(void)133{134	__execmem_init();135	return 0;136}137core_initcall(execmem_late_init);138#else139void __init execmem_init(void)140{141	__execmem_init();142}143#endif144