2017-10-25 09:37:17 +03:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2005-2017 Andes Technology Corporation
# include <linux/module.h>
# include <linux/elf.h>
# include <linux/vmalloc.h>
# include <linux/moduleloader.h>
2020-06-09 07:32:38 +03:00
# include <linux/pgtable.h>
2017-10-25 09:37:17 +03:00
void * module_alloc ( unsigned long size )
{
return __vmalloc_node_range ( size , 1 , MODULES_VADDR , MODULES_END ,
GFP_KERNEL , PAGE_KERNEL , 0 , NUMA_NO_NODE ,
__builtin_return_address ( 0 ) ) ;
}
void module_free ( struct module * module , void * region )
{
vfree ( region ) ;
}
int module_frob_arch_sections ( Elf_Ehdr * hdr ,
Elf_Shdr * sechdrs ,
char * secstrings , struct module * mod )
{
return 0 ;
}
void do_reloc16 ( unsigned int val , unsigned int * loc , unsigned int val_mask ,
unsigned int val_shift , unsigned int loc_mask ,
unsigned int partial_in_place , unsigned int swap )
{
unsigned int tmp = 0 , tmp2 = 0 ;
__asm__ __volatile__ ( " \t lhi.bi \t %0, [%2], 0 \n "
" \t beqz \t %3, 1f \n "
" \t wsbh \t %0, %1 \n "
" 1: \n " : " =r " ( tmp ) : " 0 " ( tmp ) , " r " ( loc ) , " r " ( swap )
) ;
tmp2 = tmp & loc_mask ;
if ( partial_in_place ) {
2018-07-18 04:54:55 +03:00
tmp & = ( ~ loc_mask ) ;
2017-10-25 09:37:17 +03:00
tmp =
tmp2 | ( ( tmp + ( ( val & val_mask ) > > val_shift ) ) & val_mask ) ;
} else {
tmp = tmp2 | ( ( val & val_mask ) > > val_shift ) ;
}
__asm__ __volatile__ ( " \t beqz \t %3, 2f \n "
" \t wsbh \t %0, %1 \n "
" 2: \n "
" \t shi.bi \t %0, [%2], 0 \n " : " =r " ( tmp ) : " 0 " ( tmp ) ,
" r " ( loc ) , " r " ( swap )
) ;
}
void do_reloc32 ( unsigned int val , unsigned int * loc , unsigned int val_mask ,
unsigned int val_shift , unsigned int loc_mask ,
unsigned int partial_in_place , unsigned int swap )
{
unsigned int tmp = 0 , tmp2 = 0 ;
__asm__ __volatile__ ( " \t lmw.bi \t %0, [%2], %0, 0 \n "
" \t beqz \t %3, 1f \n "
" \t wsbh \t %0, %1 \n "
" \t rotri \t %0, %1, 16 \n "
" 1: \n " : " =r " ( tmp ) : " 0 " ( tmp ) , " r " ( loc ) , " r " ( swap )
) ;
tmp2 = tmp & loc_mask ;
if ( partial_in_place ) {
2018-07-18 04:54:55 +03:00
tmp & = ( ~ loc_mask ) ;
2017-10-25 09:37:17 +03:00
tmp =
tmp2 | ( ( tmp + ( ( val & val_mask ) > > val_shift ) ) & val_mask ) ;
} else {
tmp = tmp2 | ( ( val & val_mask ) > > val_shift ) ;
}
__asm__ __volatile__ ( " \t beqz \t %3, 2f \n "
" \t wsbh \t %0, %1 \n "
" \t rotri \t %0, %1, 16 \n "
" 2: \n "
" \t smw.bi \t %0, [%2], %0, 0 \n " : " =r " ( tmp ) : " 0 " ( tmp ) ,
" r " ( loc ) , " r " ( swap )
) ;
}
static inline int exceed_limit ( int offset , unsigned int val_mask ,
struct module * module , Elf32_Rela * rel ,
unsigned int relindex , unsigned int reloc_order )
{
int abs_off = offset < 0 ? ~ offset : offset ;
if ( abs_off & ( ~ val_mask ) ) {
pr_err ( " \n %s: relocation type %d out of range. \n "
" please rebuild the kernel module with gcc option \" -Wa,-mno-small-text \" . \n " ,
module - > name , ELF32_R_TYPE ( rel - > r_info ) ) ;
pr_err ( " section %d reloc %d offset 0x%x relative 0x%x. \n " ,
relindex , reloc_order , rel - > r_offset , offset ) ;
return true ;
}
return false ;
}
# ifdef __NDS32_EL__
# define NEED_SWAP 1
# else
# define NEED_SWAP 0
# endif
int
apply_relocate_add ( Elf32_Shdr * sechdrs , const char * strtab ,
unsigned int symindex , unsigned int relindex ,
struct module * module )
{
Elf32_Shdr * symsec = sechdrs + symindex ;
Elf32_Shdr * relsec = sechdrs + relindex ;
Elf32_Shdr * dstsec = sechdrs + relsec - > sh_info ;
Elf32_Rela * rel = ( void * ) relsec - > sh_addr ;
unsigned int i ;
for ( i = 0 ; i < relsec - > sh_size / sizeof ( Elf32_Rela ) ; i + + , rel + + ) {
Elf32_Addr * loc ;
Elf32_Sym * sym ;
Elf32_Addr v ;
s32 offset ;
offset = ELF32_R_SYM ( rel - > r_info ) ;
if ( offset < 0
| | offset > ( symsec - > sh_size / sizeof ( Elf32_Sym ) ) ) {
pr_err ( " %s: bad relocation \n " , module - > name ) ;
pr_err ( " section %d reloc %d \n " , relindex , i ) ;
return - ENOEXEC ;
}
sym = ( ( Elf32_Sym * ) symsec - > sh_addr ) + offset ;
if ( rel - > r_offset < 0
| | rel - > r_offset > dstsec - > sh_size - sizeof ( u16 ) ) {
pr_err ( " %s: out of bounds relocation \n " , module - > name ) ;
pr_err ( " section %d reloc %d offset 0x%0x size %d \n " ,
relindex , i , rel - > r_offset , dstsec - > sh_size ) ;
return - ENOEXEC ;
}
loc = ( Elf32_Addr * ) ( dstsec - > sh_addr + rel - > r_offset ) ;
v = sym - > st_value + rel - > r_addend ;
switch ( ELF32_R_TYPE ( rel - > r_info ) ) {
case R_NDS32_NONE :
case R_NDS32_INSN16 :
case R_NDS32_LABEL :
case R_NDS32_LONGCALL1 :
case R_NDS32_LONGCALL2 :
case R_NDS32_LONGCALL3 :
case R_NDS32_LONGCALL4 :
case R_NDS32_LONGJUMP1 :
case R_NDS32_LONGJUMP2 :
case R_NDS32_LONGJUMP3 :
case R_NDS32_9_FIXED_RELA :
case R_NDS32_15_FIXED_RELA :
case R_NDS32_17_FIXED_RELA :
case R_NDS32_25_FIXED_RELA :
case R_NDS32_LOADSTORE :
case R_NDS32_DWARF2_OP1_RELA :
case R_NDS32_DWARF2_OP2_RELA :
case R_NDS32_DWARF2_LEB_RELA :
case R_NDS32_RELA_NOP_MIX . . . R_NDS32_RELA_NOP_MAX :
break ;
case R_NDS32_32_RELA :
do_reloc32 ( v , loc , 0xffffffff , 0 , 0 , 0 , 0 ) ;
break ;
case R_NDS32_HI20_RELA :
do_reloc32 ( v , loc , 0xfffff000 , 12 , 0xfff00000 , 0 ,
NEED_SWAP ) ;
break ;
case R_NDS32_LO12S3_RELA :
do_reloc32 ( v , loc , 0x00000fff , 3 , 0xfffff000 , 0 ,
NEED_SWAP ) ;
break ;
case R_NDS32_LO12S2_RELA :
do_reloc32 ( v , loc , 0x00000fff , 2 , 0xfffff000 , 0 ,
NEED_SWAP ) ;
break ;
case R_NDS32_LO12S1_RELA :
do_reloc32 ( v , loc , 0x00000fff , 1 , 0xfffff000 , 0 ,
NEED_SWAP ) ;
break ;
case R_NDS32_LO12S0_RELA :
case R_NDS32_LO12S0_ORI_RELA :
do_reloc32 ( v , loc , 0x00000fff , 0 , 0xfffff000 , 0 ,
NEED_SWAP ) ;
break ;
case R_NDS32_9_PCREL_RELA :
if ( exceed_limit
( ( v - ( Elf32_Addr ) loc ) , 0x000000ff , module , rel ,
relindex , i ) )
return - ENOEXEC ;
do_reloc16 ( v - ( Elf32_Addr ) loc , loc , 0x000001ff , 1 ,
0xffffff00 , 0 , NEED_SWAP ) ;
break ;
case R_NDS32_15_PCREL_RELA :
if ( exceed_limit
( ( v - ( Elf32_Addr ) loc ) , 0x00003fff , module , rel ,
relindex , i ) )
return - ENOEXEC ;
do_reloc32 ( v - ( Elf32_Addr ) loc , loc , 0x00007fff , 1 ,
0xffffc000 , 0 , NEED_SWAP ) ;
break ;
case R_NDS32_17_PCREL_RELA :
if ( exceed_limit
( ( v - ( Elf32_Addr ) loc ) , 0x0000ffff , module , rel ,
relindex , i ) )
return - ENOEXEC ;
do_reloc32 ( v - ( Elf32_Addr ) loc , loc , 0x0001ffff , 1 ,
0xffff0000 , 0 , NEED_SWAP ) ;
break ;
case R_NDS32_25_PCREL_RELA :
if ( exceed_limit
( ( v - ( Elf32_Addr ) loc ) , 0x00ffffff , module , rel ,
relindex , i ) )
return - ENOEXEC ;
do_reloc32 ( v - ( Elf32_Addr ) loc , loc , 0x01ffffff , 1 ,
0xff000000 , 0 , NEED_SWAP ) ;
break ;
case R_NDS32_WORD_9_PCREL_RELA :
if ( exceed_limit
( ( v - ( Elf32_Addr ) loc ) , 0x000000ff , module , rel ,
relindex , i ) )
return - ENOEXEC ;
do_reloc32 ( v - ( Elf32_Addr ) loc , loc , 0x000001ff , 1 ,
0xffffff00 , 0 , NEED_SWAP ) ;
break ;
case R_NDS32_SDA15S3_RELA :
case R_NDS32_SDA15S2_RELA :
case R_NDS32_SDA15S1_RELA :
case R_NDS32_SDA15S0_RELA :
pr_err ( " %s: unsupported relocation type %d. \n " ,
module - > name , ELF32_R_TYPE ( rel - > r_info ) ) ;
pr_err
( " Small data section access doesn't work in the kernel space; "
" please rebuild the kernel module with gcc option -mcmodel=large. \n " ) ;
pr_err ( " section %d reloc %d offset 0x%x size %d \n " ,
relindex , i , rel - > r_offset , dstsec - > sh_size ) ;
break ;
default :
pr_err ( " %s: unsupported relocation type %d. \n " ,
module - > name , ELF32_R_TYPE ( rel - > r_info ) ) ;
pr_err ( " section %d reloc %d offset 0x%x size %d \n " ,
relindex , i , rel - > r_offset , dstsec - > sh_size ) ;
}
}
return 0 ;
}
int
module_finalize ( const Elf32_Ehdr * hdr , const Elf_Shdr * sechdrs ,
struct module * module )
{
return 0 ;
}
void module_arch_cleanup ( struct module * mod )
{
}