2010-09-17 11:09:22 -04:00
/*
* jump label x86 support
*
* Copyright ( C ) 2009 Jason Baron < jbaron @ redhat . com >
*
*/
# include <linux/jump_label.h>
# include <linux/memory.h>
# include <linux/uaccess.h>
# include <linux/module.h>
# include <linux/list.h>
# include <linux/jhash.h>
# include <linux/cpu.h>
# include <asm/kprobes.h>
# include <asm/alternative.h>
# ifdef HAVE_JUMP_LABEL
union jump_code_union {
char code [ JUMP_LABEL_NOP_SIZE ] ;
struct {
char jump ;
int offset ;
} __attribute__ ( ( packed ) ) ;
} ;
2012-02-01 09:59:24 -05:00
static void bug_at ( unsigned char * ip , int line )
{
/*
* The location is not an op that we were expecting .
* Something went wrong . Crash the box , as something could be
* corrupting the kernel .
*/
pr_warning ( " Unexpected op at %pS [%p] (%02x %02x %02x %02x %02x) %s:%d \n " ,
ip , ip , ip [ 0 ] , ip [ 1 ] , ip [ 2 ] , ip [ 3 ] , ip [ 4 ] , __FILE__ , line ) ;
BUG ( ) ;
}
2011-09-29 11:11:09 -07:00
static void __jump_label_transform ( struct jump_entry * entry ,
enum jump_label_type type ,
2012-01-26 18:38:07 -05:00
void * ( * poker ) ( void * , const void * , size_t ) ,
int init )
2010-09-17 11:09:22 -04:00
{
union jump_code_union code ;
2012-01-26 18:38:07 -05:00
const unsigned char * ideal_nop = ideal_nops [ NOP_ATOMIC5 ] ;
2010-09-17 11:09:22 -04:00
if ( type = = JUMP_LABEL_ENABLE ) {
2012-01-26 18:38:07 -05:00
/*
* We are enabling this jump label . If it is not a nop
* then something must have gone wrong .
*/
2012-02-01 09:59:24 -05:00
if ( unlikely ( memcmp ( ( void * ) entry - > code , ideal_nop , 5 ) ! = 0 ) )
bug_at ( ( void * ) entry - > code , __LINE__ ) ;
2012-01-26 18:38:07 -05:00
2010-09-17 11:09:22 -04:00
code . jump = 0xe9 ;
code . offset = entry - > target -
( entry - > code + JUMP_LABEL_NOP_SIZE ) ;
2012-01-26 18:38:07 -05:00
} else {
/*
* We are disabling this jump label . If it is not what
* we think it is , then something must have gone wrong .
* If this is the first initialization call , then we
* are converting the default nop to the ideal nop .
*/
if ( init ) {
const unsigned char default_nop [ ] = { STATIC_KEY_INIT_NOP } ;
2012-02-01 09:59:24 -05:00
if ( unlikely ( memcmp ( ( void * ) entry - > code , default_nop , 5 ) ! = 0 ) )
bug_at ( ( void * ) entry - > code , __LINE__ ) ;
2012-01-26 18:38:07 -05:00
} else {
code . jump = 0xe9 ;
code . offset = entry - > target -
( entry - > code + JUMP_LABEL_NOP_SIZE ) ;
2012-02-01 09:59:24 -05:00
if ( unlikely ( memcmp ( ( void * ) entry - > code , & code , 5 ) ! = 0 ) )
bug_at ( ( void * ) entry - > code , __LINE__ ) ;
2012-01-26 18:38:07 -05:00
}
2011-04-18 15:19:51 -07:00
memcpy ( & code , ideal_nops [ NOP_ATOMIC5 ] , JUMP_LABEL_NOP_SIZE ) ;
2012-01-26 18:38:07 -05:00
}
2011-09-29 11:11:09 -07:00
( * poker ) ( ( void * ) entry - > code , & code , JUMP_LABEL_NOP_SIZE ) ;
}
void arch_jump_label_transform ( struct jump_entry * entry ,
enum jump_label_type type )
{
2010-09-17 11:09:22 -04:00
get_online_cpus ( ) ;
mutex_lock ( & text_mutex ) ;
2012-01-26 18:38:07 -05:00
__jump_label_transform ( entry , type , text_poke_smp , 0 ) ;
2010-09-17 11:09:22 -04:00
mutex_unlock ( & text_mutex ) ;
put_online_cpus ( ) ;
}
2012-01-26 18:16:15 -05:00
static enum {
JL_STATE_START ,
JL_STATE_NO_UPDATE ,
JL_STATE_UPDATE ,
} jlstate __initdata_or_module = JL_STATE_START ;
2011-12-06 17:27:29 +01:00
__init_or_module void arch_jump_label_transform_static ( struct jump_entry * entry ,
2011-09-29 11:11:09 -07:00
enum jump_label_type type )
{
2012-01-26 18:16:15 -05:00
/*
* This function is called at boot up and when modules are
* first loaded . Check if the default nop , the one that is
* inserted at compile time , is the ideal nop . If it is , then
* we do not need to update the nop , and we can leave it as is .
* If it is not , then we need to update the nop to the ideal nop .
*/
if ( jlstate = = JL_STATE_START ) {
const unsigned char default_nop [ ] = { STATIC_KEY_INIT_NOP } ;
const unsigned char * ideal_nop = ideal_nops [ NOP_ATOMIC5 ] ;
if ( memcmp ( ideal_nop , default_nop , 5 ) ! = 0 )
jlstate = JL_STATE_UPDATE ;
else
jlstate = JL_STATE_NO_UPDATE ;
}
if ( jlstate = = JL_STATE_UPDATE )
2012-01-26 18:38:07 -05:00
__jump_label_transform ( entry , type , text_poke_early , 1 ) ;
2011-09-29 11:11:09 -07:00
}
2010-09-17 11:09:22 -04:00
# endif