2005-02-21 10:45:09 +00:00
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Copyright ( C ) 2001 Rusty Russell .
* Copyright ( C ) 2003 , 2004 Ralf Baechle ( ralf @ linux - mips . org )
* Copyright ( C ) 2005 Thiemo Seufer
*/
# undef DEBUG
# include <linux/moduleloader.h>
# include <linux/elf.h>
2008-07-23 21:28:13 -07:00
# include <linux/mm.h>
2005-02-21 10:45:09 +00:00
# include <linux/vmalloc.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/kernel.h>
2005-04-16 15:20:36 -07:00
# include <linux/spinlock.h>
2010-12-28 13:26:23 -08:00
# include <linux/jump_label.h>
[MIPS] Load modules to CKSEG0 if CONFIG_BUILD_ELF64=n
This is a patch to load 64-bit modules to CKSEG0 so that can be
compiled with -msym32 option. This makes each module ~10% smaller.
* introduce MODULE_START and MODULE_END
* custom module_alloc()
* PGD for modules
* change XTLB refill handler synthesizer
* enable -msym32 for modules again
(revert ca78b1a5c6a6e70e052d3ea253828e49b5d07c8a)
New XTLB refill handler looks like this:
80000080 dmfc0 k0,C0_BADVADDR
80000084 bltz k0,800000e4 # goto l_module_alloc
80000088 lui k1,0x8046 # %high(pgd_current)
8000008c ld k1,24600(k1) # %low(pgd_current)
80000090 dsrl k0,k0,0x1b # l_vmalloc_done:
80000094 andi k0,k0,0x1ff8
80000098 daddu k1,k1,k0
8000009c dmfc0 k0,C0_BADVADDR
800000a0 ld k1,0(k1)
800000a4 dsrl k0,k0,0x12
800000a8 andi k0,k0,0xff8
800000ac daddu k1,k1,k0
800000b0 dmfc0 k0,C0_XCONTEXT
800000b4 ld k1,0(k1)
800000b8 andi k0,k0,0xff0
800000bc daddu k1,k1,k0
800000c0 ld k0,0(k1)
800000c4 ld k1,8(k1)
800000c8 dsrl k0,k0,0x6
800000cc mtc0 k0,C0_ENTRYLO0
800000d0 dsrl k1,k1,0x6
800000d4 mtc0 k1,C0_ENTRYL01
800000d8 nop
800000dc tlbwr
800000e0 eret
800000e4 dsll k1,k0,0x2 # l_module_alloc:
800000e8 bgez k1,80000008 # goto l_vmalloc
800000ec lui k1,0xc000
800000f0 dsubu k0,k0,k1
800000f4 lui k1,0x8046 # %high(module_pg_dir)
800000f8 beq zero,zero,80000000
800000fc nop
80000000 beq zero,zero,80000090 # goto l_vmalloc_done
80000004 daddiu k1,k1,0x4000
80000008 dsll32 k1,k1,0x0 # l_vmalloc:
8000000c dsubu k0,k0,k1
80000010 beq zero,zero,80000090 # goto l_vmalloc_done
80000014 lui k1,0x8046 # %high(swapper_pg_dir)
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-10-26 00:08:31 +09:00
# include <asm/pgtable.h> /* MODULE_START */
2005-04-16 15:20:36 -07:00
2005-02-21 10:45:09 +00:00
struct mips_hi16 {
struct mips_hi16 * next ;
Elf_Addr * addr ;
Elf_Addr value ;
} ;
2005-04-16 15:20:36 -07:00
static LIST_HEAD ( dbe_list ) ;
static DEFINE_SPINLOCK ( dbe_lock ) ;
2011-06-30 21:22:12 +02:00
# ifdef MODULE_START
2005-02-21 10:45:09 +00:00
void * module_alloc ( unsigned long size )
{
2011-01-13 15:46:02 -08:00
return __vmalloc_node_range ( size , 1 , MODULE_START , MODULE_END ,
GFP_KERNEL , PAGE_KERNEL , - 1 ,
__builtin_return_address ( 0 ) ) ;
2005-02-21 10:45:09 +00:00
}
2011-06-30 21:22:12 +02:00
# endif
2005-02-21 10:45:09 +00:00
2012-08-14 17:13:45 +02:00
int apply_r_mips_none ( struct module * me , u32 * location , Elf_Addr v )
2005-02-21 10:45:09 +00:00
{
return 0 ;
}
static int apply_r_mips_32_rel ( struct module * me , u32 * location , Elf_Addr v )
{
* location + = v ;
return 0 ;
}
static int apply_r_mips_26_rel ( struct module * me , u32 * location , Elf_Addr v )
{
if ( v % 4 ) {
2009-08-03 10:50:19 +01:00
pr_err ( " module %s: dangerous R_MIPS_26 REL relocation \n " ,
me - > name ) ;
2005-02-21 10:45:09 +00:00
return - ENOEXEC ;
}
if ( ( v & 0xf0000000 ) ! = ( ( ( unsigned long ) location + 4 ) & 0xf0000000 ) ) {
printk ( KERN_ERR
" module %s: relocation overflow \n " ,
me - > name ) ;
return - ENOEXEC ;
}
* location = ( * location & ~ 0x03ffffff ) |
2013-01-22 12:59:30 +01:00
( ( * location + ( v > > 2 ) ) & 0x03ffffff ) ;
2005-02-21 10:45:09 +00:00
return 0 ;
}
static int apply_r_mips_hi16_rel ( struct module * me , u32 * location , Elf_Addr v )
{
struct mips_hi16 * n ;
/*
* We cannot relocate this one now because we don ' t know the value of
* the carry we need to add . Save the information , and let LO16 do the
* actual relocation .
*/
n = kmalloc ( sizeof * n , GFP_KERNEL ) ;
if ( ! n )
return - ENOMEM ;
n - > addr = ( Elf_Addr * ) location ;
n - > value = v ;
2012-08-08 16:59:43 +02:00
n - > next = me - > arch . r_mips_hi16_list ;
me - > arch . r_mips_hi16_list = n ;
2005-02-21 10:45:09 +00:00
return 0 ;
}
2012-08-14 00:34:18 +02:00
static void free_relocation_chain ( struct mips_hi16 * l )
{
struct mips_hi16 * next ;
while ( l ) {
next = l - > next ;
kfree ( l ) ;
l = next ;
}
}
2005-02-21 10:45:09 +00:00
static int apply_r_mips_lo16_rel ( struct module * me , u32 * location , Elf_Addr v )
{
unsigned long insnlo = * location ;
2012-08-14 00:34:18 +02:00
struct mips_hi16 * l ;
2005-02-21 10:45:09 +00:00
Elf_Addr val , vallo ;
2013-01-22 12:59:30 +01:00
/* Sign extend the addend we extract from the lo insn. */
2005-02-21 10:45:09 +00:00
vallo = ( ( insnlo & 0xffff ) ^ 0x8000 ) - 0x8000 ;
2012-08-08 16:59:43 +02:00
if ( me - > arch . r_mips_hi16_list ! = NULL ) {
l = me - > arch . r_mips_hi16_list ;
2005-02-21 10:45:09 +00:00
while ( l ! = NULL ) {
2012-08-14 00:34:18 +02:00
struct mips_hi16 * next ;
2005-02-21 10:45:09 +00:00
unsigned long insn ;
/*
* The value for the HI16 had best be the same .
*/
if ( v ! = l - > value )
goto out_danger ;
/*
* Do the HI16 relocation . Note that we actually don ' t
* need to know anything about the LO16 itself , except
* where to find the low 16 bits of the addend needed
* by the LO16 .
*/
insn = * l - > addr ;
val = ( ( insn & 0xffff ) < < 16 ) + vallo ;
val + = v ;
/*
* Account for the sign extension that will happen in
* the low bits .
*/
val = ( ( val > > 16 ) + ( ( val & 0x8000 ) ! = 0 ) ) & 0xffff ;
insn = ( insn & ~ 0xffff ) | val ;
* l - > addr = insn ;
next = l - > next ;
kfree ( l ) ;
l = next ;
}
2012-08-08 16:59:43 +02:00
me - > arch . r_mips_hi16_list = NULL ;
2005-02-21 10:45:09 +00:00
}
/*
2013-01-22 12:59:30 +01:00
* Ok , we ' re done with the HI16 relocs . Now deal with the LO16 .
2005-02-21 10:45:09 +00:00
*/
val = v + vallo ;
insnlo = ( insnlo & ~ 0xffff ) | ( val & 0xffff ) ;
* location = insnlo ;
return 0 ;
out_danger :
2012-08-14 00:34:18 +02:00
free_relocation_chain ( l ) ;
me - > arch . r_mips_hi16_list = NULL ;
2012-08-08 14:57:03 +02:00
2009-08-03 10:50:19 +01:00
pr_err ( " module %s: dangerous R_MIPS_LO16 REL relocation \n " , me - > name ) ;
2005-02-21 10:45:09 +00:00
return - ENOEXEC ;
}
static int ( * reloc_handlers_rel [ ] ) ( struct module * me , u32 * location ,
Elf_Addr v ) = {
[ R_MIPS_NONE ] = apply_r_mips_none ,
[ R_MIPS_32 ] = apply_r_mips_32_rel ,
[ R_MIPS_26 ] = apply_r_mips_26_rel ,
[ R_MIPS_HI16 ] = apply_r_mips_hi16_rel ,
[ R_MIPS_LO16 ] = apply_r_mips_lo16_rel
} ;
int apply_relocate ( Elf_Shdr * sechdrs , const char * strtab ,
unsigned int symindex , unsigned int relsec ,
struct module * me )
{
Elf_Mips_Rel * rel = ( void * ) sechdrs [ relsec ] . sh_addr ;
Elf_Sym * sym ;
u32 * location ;
unsigned int i ;
Elf_Addr v ;
int res ;
pr_debug ( " Applying relocate section %u to %u \n " , relsec ,
sechdrs [ relsec ] . sh_info ) ;
2012-08-08 16:59:43 +02:00
me - > arch . r_mips_hi16_list = NULL ;
2005-02-21 10:45:09 +00:00
for ( i = 0 ; i < sechdrs [ relsec ] . sh_size / sizeof ( * rel ) ; i + + ) {
/* This is where to make the change */
location = ( void * ) sechdrs [ sechdrs [ relsec ] . sh_info ] . sh_addr
+ rel [ i ] . r_offset ;
/* This is the symbol it is referring to */
sym = ( Elf_Sym * ) sechdrs [ symindex ] . sh_addr
+ ELF_MIPS_R_SYM ( rel [ i ] ) ;
2009-07-13 11:14:24 +02:00
if ( IS_ERR_VALUE ( sym - > st_value ) ) {
2006-05-23 00:45:07 +09:00
/* Ignore unresolved weak symbol */
if ( ELF_ST_BIND ( sym - > st_info ) = = STB_WEAK )
continue ;
2005-02-21 10:45:09 +00:00
printk ( KERN_WARNING " %s: Unknown symbol %s \n " ,
me - > name , strtab + sym - > st_name ) ;
return - ENOENT ;
}
v = sym - > st_value ;
res = reloc_handlers_rel [ ELF_MIPS_R_TYPE ( rel [ i ] ) ] ( me , location , v ) ;
if ( res )
return res ;
}
2012-08-14 00:34:18 +02:00
/*
2013-01-22 12:59:30 +01:00
* Normally the hi16 list should be deallocated at this point . A
2012-08-14 00:34:18 +02:00
* malformed binary however could contain a series of R_MIPS_HI16
* relocations not followed by a R_MIPS_LO16 relocation . In that
* case , free up the list and return an error .
*/
if ( me - > arch . r_mips_hi16_list ) {
free_relocation_chain ( me - > arch . r_mips_hi16_list ) ;
me - > arch . r_mips_hi16_list = NULL ;
return - ENOEXEC ;
}
2005-02-21 10:45:09 +00:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
/* Given an address, look for it in the module exception tables. */
const struct exception_table_entry * search_module_dbetables ( unsigned long addr )
{
unsigned long flags ;
const struct exception_table_entry * e = NULL ;
struct mod_arch_specific * dbe ;
spin_lock_irqsave ( & dbe_lock , flags ) ;
list_for_each_entry ( dbe , & dbe_list , dbe_list ) {
e = search_extable ( dbe - > dbe_start , dbe - > dbe_end - 1 , addr ) ;
if ( e )
break ;
}
spin_unlock_irqrestore ( & dbe_lock , flags ) ;
/* Now, if we found one, we are running inside it now, hence
2013-01-22 12:59:30 +01:00
we cannot unload the module , hence no refcnt needed . */
2005-04-16 15:20:36 -07:00
return e ;
}
2007-10-19 23:10:43 +02:00
/* Put in dbe list if necessary. */
2005-04-16 15:20:36 -07:00
int module_finalize ( const Elf_Ehdr * hdr ,
const Elf_Shdr * sechdrs ,
struct module * me )
{
const Elf_Shdr * s ;
char * secstrings = ( void * ) hdr + sechdrs [ hdr - > e_shstrndx ] . sh_offset ;
2010-12-28 13:26:23 -08:00
/* Make jump label nops. */
jump_label_apply_nops ( me ) ;
2005-04-16 15:20:36 -07:00
INIT_LIST_HEAD ( & me - > arch . dbe_list ) ;
for ( s = sechdrs ; s < sechdrs + hdr - > e_shnum ; s + + ) {
if ( strcmp ( " __dbe_table " , secstrings + s - > sh_name ) ! = 0 )
continue ;
me - > arch . dbe_start = ( void * ) s - > sh_addr ;
me - > arch . dbe_end = ( void * ) s - > sh_addr + s - > sh_size ;
spin_lock_irq ( & dbe_lock ) ;
list_add ( & me - > arch . dbe_list , & dbe_list ) ;
spin_unlock_irq ( & dbe_lock ) ;
}
return 0 ;
}
void module_arch_cleanup ( struct module * mod )
{
spin_lock_irq ( & dbe_lock ) ;
list_del ( & mod - > arch . dbe_list ) ;
spin_unlock_irq ( & dbe_lock ) ;
}