2024-05-05 19:06:18 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2002 Richard Henderson
* Copyright ( C ) 2001 Rusty Russell , 2002 , 2010 Rusty Russell IBM .
* Copyright ( C ) 2023 Luis Chamberlain < mcgrof @ kernel . org >
* Copyright ( C ) 2024 Mike Rapoport IBM .
*/
# include <linux/mm.h>
# include <linux/vmalloc.h>
# include <linux/execmem.h>
# include <linux/moduleloader.h>
2024-05-05 19:06:19 +03:00
static struct execmem_info * execmem_info __ro_after_init ;
2024-05-05 19:06:20 +03:00
static struct execmem_info default_execmem_info __ro_after_init ;
2024-05-05 19:06:19 +03:00
static void * __execmem_alloc ( struct execmem_range * range , size_t size )
2024-05-05 19:06:18 +03:00
{
2024-05-05 19:06:20 +03:00
bool kasan = range - > flags & EXECMEM_KASAN_SHADOW ;
unsigned long vm_flags = VM_FLUSH_RESET_PERMS ;
gfp_t gfp_flags = GFP_KERNEL | __GFP_NOWARN ;
2024-05-05 19:06:19 +03:00
unsigned long start = range - > start ;
unsigned long end = range - > end ;
unsigned int align = range - > alignment ;
pgprot_t pgprot = range - > pgprot ;
2024-05-05 19:06:20 +03:00
void * p ;
if ( kasan )
vm_flags | = VM_DEFER_KMEMLEAK ;
p = __vmalloc_node_range ( size , align , start , end , gfp_flags ,
pgprot , vm_flags , NUMA_NO_NODE ,
__builtin_return_address ( 0 ) ) ;
if ( ! p & & range - > fallback_start ) {
start = range - > fallback_start ;
end = range - > fallback_end ;
p = __vmalloc_node_range ( size , align , start , end , gfp_flags ,
pgprot , vm_flags , NUMA_NO_NODE ,
__builtin_return_address ( 0 ) ) ;
}
if ( ! p ) {
pr_warn_ratelimited ( " execmem: unable to allocate memory \n " ) ;
return NULL ;
}
if ( kasan & & ( kasan_alloc_module_shadow ( p , size , GFP_KERNEL ) < 0 ) ) {
vfree ( p ) ;
return NULL ;
}
2024-05-05 19:06:19 +03:00
2024-05-05 19:06:20 +03:00
return kasan_reset_tag ( p ) ;
2024-05-05 19:06:18 +03:00
}
void * execmem_alloc ( enum execmem_type type , size_t size )
{
2024-05-05 19:06:20 +03:00
struct execmem_range * range = & execmem_info - > ranges [ type ] ;
2024-05-05 19:06:19 +03:00
return __execmem_alloc ( range , size ) ;
2024-05-05 19:06:18 +03:00
}
void execmem_free ( void * ptr )
{
/*
* This memory may be RO , and freeing RO memory in an interrupt is not
* supported by vmalloc .
*/
WARN_ON ( in_interrupt ( ) ) ;
vfree ( ptr ) ;
}
2024-05-05 19:06:19 +03:00
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 ) {
2024-05-05 19:06:20 +03:00
if ( i = = EXECMEM_MODULE_DATA )
r - > pgprot = PAGE_KERNEL ;
else
r - > pgprot = default_range - > pgprot ;
2024-05-05 19:06:19 +03:00
r - > alignment = default_range - > alignment ;
r - > start = default_range - > start ;
r - > end = default_range - > end ;
2024-05-05 19:06:20 +03:00
r - > flags = default_range - > flags ;
r - > fallback_start = default_range - > fallback_start ;
r - > fallback_end = default_range - > fallback_end ;
2024-05-05 19:06:19 +03:00
}
}
}
struct execmem_info * __weak execmem_arch_setup ( void )
{
return NULL ;
}
2024-05-05 19:06:20 +03:00
static void __init __execmem_init ( void )
2024-05-05 19:06:19 +03:00
{
struct execmem_info * info = execmem_arch_setup ( ) ;
2024-05-05 19:06:20 +03:00
if ( ! info ) {
info = execmem_info = & default_execmem_info ;
info - > ranges [ EXECMEM_DEFAULT ] . start = VMALLOC_START ;
info - > ranges [ EXECMEM_DEFAULT ] . end = VMALLOC_END ;
info - > ranges [ EXECMEM_DEFAULT ] . pgprot = PAGE_KERNEL_EXEC ;
info - > ranges [ EXECMEM_DEFAULT ] . alignment = 1 ;
}
if ( ! execmem_validate ( info ) )
2024-05-05 19:06:19 +03:00
return ;
execmem_init_missing ( info ) ;
execmem_info = info ;
}
2024-05-05 19:06:20 +03:00
# ifdef CONFIG_ARCH_WANTS_EXECMEM_LATE
static int __init execmem_late_init ( void )
{
__execmem_init ( ) ;
return 0 ;
}
core_initcall ( execmem_late_init ) ;
# else
void __init execmem_init ( void )
{
__execmem_init ( ) ;
}
# endif