2015-07-29 22:44:53 +02:00
# include <linux/highmem.h>
# include <linux/kdebug.h>
# include <linux/types.h>
# include <linux/notifier.h>
# include <linux/sched.h>
# include <linux/uprobes.h>
# include <asm/branch.h>
# include <asm/cpu-features.h>
# include <asm/ptrace.h>
2016-09-30 11:33:45 +02:00
# include "probes-common.h"
2015-07-29 22:44:53 +02:00
static inline int insn_has_delay_slot ( const union mips_instruction insn )
{
2016-09-30 11:33:45 +02:00
return __insn_has_delay_slot ( insn ) ;
2015-07-29 22:44:53 +02:00
}
/**
* arch_uprobe_analyze_insn - instruction analysis including validity and fixups .
* @ mm : the probed address space .
* @ arch_uprobe : the probepoint information .
* @ addr : virtual address at which to install the probepoint
* Return 0 on success or a - ve number on error .
*/
int arch_uprobe_analyze_insn ( struct arch_uprobe * aup ,
struct mm_struct * mm , unsigned long addr )
{
union mips_instruction inst ;
/*
* For the time being this also blocks attempts to use uprobes with
* MIPS16 and microMIPS .
*/
if ( addr & 0x03 )
return - EINVAL ;
inst . word = aup - > insn [ 0 ] ;
2016-09-30 11:33:46 +02:00
if ( __insn_is_compact_branch ( inst ) ) {
pr_notice ( " Uprobes for compact branches are not supported \n " ) ;
return - EINVAL ;
}
2015-07-29 22:44:53 +02:00
aup - > ixol [ 0 ] = aup - > insn [ insn_has_delay_slot ( inst ) ] ;
aup - > ixol [ 1 ] = UPROBE_BRK_UPROBE_XOL ; /* NOP */
return 0 ;
}
/**
* is_trap_insn - check if the instruction is a trap variant
* @ insn : instruction to be checked .
* Returns true if @ insn is a trap variant .
*
* This definition overrides the weak definition in kernel / events / uprobes . c .
* and is needed for the case where an architecture has multiple trap
* instructions ( like PowerPC or MIPS ) . We treat BREAK just like the more
* modern conditional trap instructions .
*/
bool is_trap_insn ( uprobe_opcode_t * insn )
{
union mips_instruction inst ;
inst . word = * insn ;
switch ( inst . i_format . opcode ) {
case spec_op :
switch ( inst . r_format . func ) {
case break_op :
case teq_op :
case tge_op :
case tgeu_op :
case tlt_op :
case tltu_op :
case tne_op :
return 1 ;
}
break ;
case bcond_op : /* Yes, really ... */
switch ( inst . u_format . rt ) {
case teqi_op :
case tgei_op :
case tgeiu_op :
case tlti_op :
case tltiu_op :
case tnei_op :
return 1 ;
}
break ;
}
return 0 ;
}
# define UPROBE_TRAP_NR ULONG_MAX
/*
* arch_uprobe_pre_xol - prepare to execute out of line .
* @ auprobe : the probepoint information .
* @ regs : reflects the saved user state of current task .
*/
int arch_uprobe_pre_xol ( struct arch_uprobe * aup , struct pt_regs * regs )
{
struct uprobe_task * utask = current - > utask ;
/*
* Now find the EPC where to resume after the breakpoint has been
* dealt with . This may require emulation of a branch .
*/
aup - > resume_epc = regs - > cp0_epc + 4 ;
if ( insn_has_delay_slot ( ( union mips_instruction ) aup - > insn [ 0 ] ) ) {
unsigned long epc ;
epc = regs - > cp0_epc ;
2016-09-22 15:38:33 +02:00
__compute_return_epc_for_insn ( regs ,
( union mips_instruction ) aup - > insn [ 0 ] ) ;
2015-07-29 22:44:53 +02:00
aup - > resume_epc = regs - > cp0_epc ;
}
utask - > autask . saved_trap_nr = current - > thread . trap_nr ;
current - > thread . trap_nr = UPROBE_TRAP_NR ;
regs - > cp0_epc = current - > utask - > xol_vaddr ;
return 0 ;
}
int arch_uprobe_post_xol ( struct arch_uprobe * aup , struct pt_regs * regs )
{
struct uprobe_task * utask = current - > utask ;
current - > thread . trap_nr = utask - > autask . saved_trap_nr ;
regs - > cp0_epc = aup - > resume_epc ;
return 0 ;
}
/*
* If xol insn itself traps and generates a signal ( Say ,
* SIGILL / SIGSEGV / etc ) , then detect the case where a singlestepped
* instruction jumps back to its own address . It is assumed that anything
* like do_page_fault / do_trap / etc sets thread . trap_nr ! = - 1.
*
* arch_uprobe_pre_xol / arch_uprobe_post_xol save / restore thread . trap_nr ,
* arch_uprobe_xol_was_trapped ( ) simply checks that - > trap_nr is not equal to
* UPROBE_TRAP_NR = = - 1 set by arch_uprobe_pre_xol ( ) .
*/
bool arch_uprobe_xol_was_trapped ( struct task_struct * tsk )
{
if ( tsk - > thread . trap_nr ! = UPROBE_TRAP_NR )
return true ;
return false ;
}
int arch_uprobe_exception_notify ( struct notifier_block * self ,
unsigned long val , void * data )
{
struct die_args * args = data ;
struct pt_regs * regs = args - > regs ;
/* regs == NULL is a kernel bug */
if ( WARN_ON ( ! regs ) )
return NOTIFY_DONE ;
/* We are only interested in userspace traps */
if ( ! user_mode ( regs ) )
return NOTIFY_DONE ;
switch ( val ) {
2016-08-11 09:02:30 +02:00
case DIE_UPROBE :
2015-07-29 22:44:53 +02:00
if ( uprobe_pre_sstep_notifier ( regs ) )
return NOTIFY_STOP ;
break ;
case DIE_UPROBE_XOL :
if ( uprobe_post_sstep_notifier ( regs ) )
return NOTIFY_STOP ;
default :
break ;
}
return 0 ;
}
/*
* This function gets called when XOL instruction either gets trapped or
* the thread has a fatal signal . Reset the instruction pointer to its
* probed address for the potential restart or for post mortem analysis .
*/
void arch_uprobe_abort_xol ( struct arch_uprobe * aup ,
struct pt_regs * regs )
{
struct uprobe_task * utask = current - > utask ;
instruction_pointer_set ( regs , utask - > vaddr ) ;
}
unsigned long arch_uretprobe_hijack_return_addr (
unsigned long trampoline_vaddr , struct pt_regs * regs )
{
unsigned long ra ;
ra = regs - > regs [ 31 ] ;
/* Replace the return address with the trampoline address */
2016-09-22 15:38:31 +02:00
regs - > regs [ 31 ] = trampoline_vaddr ;
2015-07-29 22:44:53 +02:00
return ra ;
}
/**
* set_swbp - store breakpoint at a given address .
* @ auprobe : arch specific probepoint information .
* @ mm : the probed process address space .
* @ vaddr : the virtual address to insert the opcode .
*
* For mm @ mm , store the breakpoint instruction at @ vaddr .
* Return 0 ( success ) or a negative errno .
*
* This version overrides the weak version in kernel / events / uprobes . c .
* It is required to handle MIPS16 and microMIPS .
*/
int __weak set_swbp ( struct arch_uprobe * auprobe , struct mm_struct * mm ,
unsigned long vaddr )
{
return uprobe_write_opcode ( mm , vaddr , UPROBE_SWBP_INSN ) ;
}
void __weak arch_uprobe_copy_ixol ( struct page * page , unsigned long vaddr ,
void * src , unsigned long len )
{
2016-09-01 17:30:13 +01:00
unsigned long kaddr , kstart ;
2015-07-29 22:44:53 +02:00
/* Initialize the slot */
2016-09-01 17:30:13 +01:00
kaddr = ( unsigned long ) kmap_atomic ( page ) ;
kstart = kaddr + ( vaddr & ~ PAGE_MASK ) ;
memcpy ( ( void * ) kstart , src , len ) ;
flush_icache_range ( kstart , kstart + len ) ;
kunmap_atomic ( ( void * ) kaddr ) ;
2015-07-29 22:44:53 +02:00
}
/**
* uprobe_get_swbp_addr - compute address of swbp given post - swbp regs
* @ regs : Reflects the saved state of the task after it has hit a breakpoint
* instruction .
* Return the address of the breakpoint instruction .
*
* This overrides the weak version in kernel / events / uprobes . c .
*/
unsigned long uprobe_get_swbp_addr ( struct pt_regs * regs )
{
return instruction_pointer ( regs ) ;
}
/*
* See if the instruction can be emulated .
* Returns true if instruction was emulated , false otherwise .
*
* For now we always emulate so this function just returns 0.
*/
bool arch_uprobe_skip_sstep ( struct arch_uprobe * auprobe , struct pt_regs * regs )
{
return 0 ;
}