2011-03-16 15:58:30 -04:00
/*
* Jump label s390 support
*
* Copyright IBM Corp . 2011
* Author ( s ) : Jan Glauber < jang @ linux . vnet . ibm . com >
*/
# include <linux/module.h>
# include <linux/uaccess.h>
# include <linux/stop_machine.h>
# include <linux/jump_label.h>
# include <asm/ipl.h>
# ifdef HAVE_JUMP_LABEL
struct insn {
u16 opcode ;
s32 offset ;
} __packed ;
struct insn_args {
2011-09-29 10:58:46 -07:00
struct jump_entry * entry ;
enum jump_label_type type ;
2011-03-16 15:58:30 -04:00
} ;
2015-01-29 13:45:35 +01:00
static void jump_label_make_nop ( struct jump_entry * entry , struct insn * insn )
{
/* brcl 0,0 */
insn - > opcode = 0xc004 ;
insn - > offset = 0 ;
}
static void jump_label_make_branch ( struct jump_entry * entry , struct insn * insn )
{
/* brcl 15,offset */
insn - > opcode = 0xc0f4 ;
insn - > offset = ( entry - > target - entry - > code ) > > 1 ;
}
2015-02-20 08:33:31 +01:00
static void jump_label_bug ( struct jump_entry * entry , struct insn * expected ,
struct insn * new )
2015-01-29 13:45:35 +01:00
{
unsigned char * ipc = ( unsigned char * ) entry - > code ;
2015-02-20 08:33:31 +01:00
unsigned char * ipe = ( unsigned char * ) expected ;
unsigned char * ipn = ( unsigned char * ) new ;
2015-01-29 13:45:35 +01:00
pr_emerg ( " Jump label code mismatch at %pS [%p] \n " , ipc , ipc ) ;
2015-08-27 01:17:46 +06:00
pr_emerg ( " Found: %6ph \n " , ipc ) ;
pr_emerg ( " Expected: %6ph \n " , ipe ) ;
pr_emerg ( " New: %6ph \n " , ipn ) ;
2015-01-29 13:45:35 +01:00
panic ( " Corrupted kernel text " ) ;
}
2015-01-29 14:10:22 +01:00
static struct insn orignop = {
. opcode = 0xc004 ,
. offset = JUMP_LABEL_NOP_OFFSET > > 1 ,
} ;
2011-09-29 10:58:46 -07:00
static void __jump_label_transform ( struct jump_entry * entry ,
2015-01-29 13:45:35 +01:00
enum jump_label_type type ,
int init )
2011-03-16 15:58:30 -04:00
{
2015-01-29 13:45:35 +01:00
struct insn old , new ;
2011-03-16 15:58:30 -04:00
2015-07-24 14:45:44 +02:00
if ( type = = JUMP_LABEL_JMP ) {
2015-01-29 13:45:35 +01:00
jump_label_make_nop ( entry , & old ) ;
jump_label_make_branch ( entry , & new ) ;
2011-03-16 15:58:30 -04:00
} else {
2015-01-29 14:10:22 +01:00
jump_label_make_branch ( entry , & old ) ;
2015-01-29 13:45:35 +01:00
jump_label_make_nop ( entry , & new ) ;
2011-03-16 15:58:30 -04:00
}
2015-01-29 14:10:22 +01:00
if ( init ) {
if ( memcmp ( ( void * ) entry - > code , & orignop , sizeof ( orignop ) ) )
2015-02-20 08:33:31 +01:00
jump_label_bug ( entry , & orignop , & new ) ;
2015-01-29 14:10:22 +01:00
} else {
if ( memcmp ( ( void * ) entry - > code , & old , sizeof ( old ) ) )
2015-02-20 08:33:31 +01:00
jump_label_bug ( entry , & old , & new ) ;
2015-01-29 14:10:22 +01:00
}
2015-03-13 12:55:56 +01:00
s390_kernel_write ( ( void * ) entry - > code , & new , sizeof ( new ) ) ;
2011-09-29 10:58:46 -07:00
}
2011-03-16 15:58:30 -04:00
2011-09-29 10:58:46 -07:00
static int __sm_arch_jump_label_transform ( void * data )
{
struct insn_args * args = data ;
2015-01-29 13:45:35 +01:00
__jump_label_transform ( args - > entry , args - > type , 0 ) ;
2011-09-29 10:58:46 -07:00
return 0 ;
}
void arch_jump_label_transform ( struct jump_entry * entry ,
enum jump_label_type type )
{
struct insn_args args ;
args . entry = entry ;
args . type = type ;
stop_machine ( __sm_arch_jump_label_transform , & args , NULL ) ;
}
void arch_jump_label_transform_static ( struct jump_entry * entry ,
enum jump_label_type type )
{
2015-01-29 13:45:35 +01:00
__jump_label_transform ( entry , type , 1 ) ;
2011-03-16 15:58:30 -04:00
}
# endif