2008-01-30 13:31:41 +01:00
# include <linux/module.h>
# include <linux/spinlock.h>
2012-04-20 17:12:48 -07:00
# include <linux/sort.h>
2008-01-30 13:31:41 +01:00
# include <asm/uaccess.h>
2012-04-20 17:12:48 -07:00
static inline unsigned long
ex_insn_addr ( const struct exception_table_entry * x )
{
return ( unsigned long ) & x - > insn + x - > insn ;
}
static inline unsigned long
ex_fixup_addr ( const struct exception_table_entry * x )
{
return ( unsigned long ) & x - > fixup + x - > fixup ;
}
2008-01-30 13:31:41 +01:00
int fixup_exception ( struct pt_regs * regs )
{
const struct exception_table_entry * fixup ;
2012-04-20 17:12:48 -07:00
unsigned long new_ip ;
2008-01-30 13:31:41 +01:00
# ifdef CONFIG_PNPBIOS
if ( unlikely ( SEGMENT_IS_PNP_CODE ( regs - > cs ) ) ) {
extern u32 pnp_bios_fault_eip , pnp_bios_fault_esp ;
extern u32 pnp_bios_is_utter_crap ;
pnp_bios_is_utter_crap = 1 ;
printk ( KERN_CRIT " PNPBIOS fault.. attempting recovery. \n " ) ;
__asm__ volatile (
" movl %0, %%esp \n \t "
" jmp *%1 \n \t "
: : " g " ( pnp_bios_fault_esp ) , " g " ( pnp_bios_fault_eip ) ) ;
panic ( " do_trap: can't hit this " ) ;
}
# endif
fixup = search_exception_tables ( regs - > ip ) ;
if ( fixup ) {
2012-04-20 17:12:48 -07:00
new_ip = ex_fixup_addr ( fixup ) ;
if ( fixup - > fixup - fixup - > insn > = 0x7ffffff0 - 4 ) {
/* Special hack for uaccess_err */
2011-11-07 16:33:40 -08:00
current_thread_info ( ) - > uaccess_err = 1 ;
2012-04-20 17:12:48 -07:00
new_ip - = 0x7ffffff0 ;
2009-01-23 15:49:41 -08:00
}
2012-04-20 17:12:48 -07:00
regs - > ip = new_ip ;
2008-01-30 13:31:41 +01:00
return 1 ;
}
return 0 ;
}
2012-04-19 15:24:20 -07:00
/* Restricted version used during very early boot */
int __init early_fixup_exception ( unsigned long * ip )
{
const struct exception_table_entry * fixup ;
2012-04-20 17:12:48 -07:00
unsigned long new_ip ;
2012-04-19 15:24:20 -07:00
fixup = search_exception_tables ( * ip ) ;
if ( fixup ) {
2012-04-20 17:12:48 -07:00
new_ip = ex_fixup_addr ( fixup ) ;
if ( fixup - > fixup - fixup - > insn > = 0x7ffffff0 - 4 ) {
/* uaccess handling not supported during early boot */
return 0 ;
}
2012-04-19 15:24:20 -07:00
2012-04-20 17:12:48 -07:00
* ip = new_ip ;
2012-04-19 15:24:20 -07:00
return 1 ;
}
return 0 ;
}
2012-04-20 17:12:48 -07:00
/*
* Search one exception table for an entry corresponding to the
* given instruction address , and return the address of the entry ,
* or NULL if none is found .
* We use a binary search , and thus we assume that the table is
* already sorted .
*/
const struct exception_table_entry *
search_extable ( const struct exception_table_entry * first ,
const struct exception_table_entry * last ,
unsigned long value )
{
while ( first < = last ) {
const struct exception_table_entry * mid ;
unsigned long addr ;
mid = ( ( last - first ) > > 1 ) + first ;
addr = ex_insn_addr ( mid ) ;
if ( addr < value )
first = mid + 1 ;
else if ( addr > value )
last = mid - 1 ;
else
return mid ;
}
return NULL ;
}
/*
* The exception table needs to be sorted so that the binary
* search that we use to find entries in it works properly .
* This is used both for the kernel exception table and for
* the exception tables of modules that get loaded .
*
*/
static int cmp_ex ( const void * a , const void * b )
{
const struct exception_table_entry * x = a , * y = b ;
/*
* This value will always end up fittin in an int , because on
* both i386 and x86 - 64 the kernel symbol - reachable address
* space is < 2 GiB .
*
* This compare is only valid after normalization .
*/
return x - > insn - y - > insn ;
}
void sort_extable ( struct exception_table_entry * start ,
struct exception_table_entry * finish )
{
struct exception_table_entry * p ;
int i ;
/* Convert all entries to being relative to the start of the section */
i = 0 ;
for ( p = start ; p < finish ; p + + ) {
p - > insn + = i ;
i + = 4 ;
p - > fixup + = i ;
i + = 4 ;
}
sort ( start , finish - start , sizeof ( struct exception_table_entry ) ,
cmp_ex , NULL ) ;
/* Denormalize all entries */
i = 0 ;
for ( p = start ; p < finish ; p + + ) {
p - > insn - = i ;
i + = 4 ;
p - > fixup - = i ;
i + = 4 ;
}
}
# ifdef CONFIG_MODULES
/*
* If the exception table is sorted , any referring to the module init
* will be at the beginning or the end .
*/
void trim_init_extable ( struct module * m )
{
/*trim the beginning*/
while ( m - > num_exentries & &
within_module_init ( ex_insn_addr ( & m - > extable [ 0 ] ) , m ) ) {
m - > extable + + ;
m - > num_exentries - - ;
}
/*trim the end*/
while ( m - > num_exentries & &
within_module_init ( ex_insn_addr ( & m - > extable [ m - > num_exentries - 1 ] ) , m ) )
m - > num_exentries - - ;
}
# endif /* CONFIG_MODULES */