2016-10-13 10:06:13 +05:30
/*
* User - space Probes ( UProbes ) for sparc
*
* Copyright ( C ) 2013 Oracle Inc .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*
* Authors :
* Jose E . Marchesi < jose . marchesi @ oracle . com >
* Eric Saint Etienne < eric . saint . etienne @ oracle . com >
*/
# include <linux/kernel.h>
# include <linux/highmem.h>
# include <linux/uprobes.h>
# include <linux/uaccess.h>
# include <linux/sched.h> /* For struct task_struct */
# include <linux/kdebug.h>
# include <asm/cacheflush.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2016-10-13 10:06:13 +05:30
/* Compute the address of the breakpoint instruction and return it.
*
* Note that uprobe_get_swbp_addr is defined as a weak symbol in
* kernel / events / uprobe . c .
*/
unsigned long uprobe_get_swbp_addr ( struct pt_regs * regs )
{
return instruction_pointer ( regs ) ;
}
static void copy_to_page ( struct page * page , unsigned long vaddr ,
const void * src , int len )
{
void * kaddr = kmap_atomic ( page ) ;
memcpy ( kaddr + ( vaddr & ~ PAGE_MASK ) , src , len ) ;
kunmap_atomic ( kaddr ) ;
}
/* Fill in the xol area with the probed instruction followed by the
* single - step trap . Some fixups in the copied instruction are
* performed at this point .
*
* Note that uprobe_xol_copy is defined as a weak symbol in
* kernel / events / uprobe . c .
*/
void arch_uprobe_copy_ixol ( struct page * page , unsigned long vaddr ,
void * src , unsigned long len )
{
const u32 stp_insn = UPROBE_STP_INSN ;
u32 insn = * ( u32 * ) src ;
/* Branches annulling their delay slot must be fixed to not do
* so . Clearing the annul bit on these instructions we can be
* sure the single - step breakpoint in the XOL slot will be
* executed .
*/
u32 op = ( insn > > 30 ) & 0x3 ;
u32 op2 = ( insn > > 22 ) & 0x7 ;
if ( op = = 0 & &
( op2 = = 1 | | op2 = = 2 | | op2 = = 3 | | op2 = = 5 | | op2 = = 6 ) & &
( insn & ANNUL_BIT ) = = ANNUL_BIT )
insn & = ~ ANNUL_BIT ;
copy_to_page ( page , vaddr , & insn , len ) ;
copy_to_page ( page , vaddr + len , & stp_insn , 4 ) ;
}
/* Instruction analysis/validity.
*
* This function returns 0 on success or a - ve number on error .
*/
int arch_uprobe_analyze_insn ( struct arch_uprobe * auprobe ,
struct mm_struct * mm , unsigned long addr )
{
/* Any unsupported instruction? Then return -EINVAL */
return 0 ;
}
/* If INSN is a relative control transfer instruction, return the
* corrected branch destination value .
*
* Note that regs - > tpc and regs - > tnpc still hold the values of the
* program counters at the time of the single - step trap due to the
* execution of the UPROBE_STP_INSN at utask - > xol_vaddr + 4.
*
*/
static unsigned long relbranch_fixup ( u32 insn , struct uprobe_task * utask ,
struct pt_regs * regs )
{
/* Branch not taken, no mods necessary. */
if ( regs - > tnpc = = regs - > tpc + 0x4UL )
return utask - > autask . saved_tnpc + 0x4UL ;
/* The three cases are call, branch w/prediction,
* and traditional branch .
*/
if ( ( insn & 0xc0000000 ) = = 0x40000000 | |
( insn & 0xc1c00000 ) = = 0x00400000 | |
( insn & 0xc1c00000 ) = = 0x00800000 ) {
unsigned long real_pc = ( unsigned long ) utask - > vaddr ;
unsigned long ixol_addr = utask - > xol_vaddr ;
/* The instruction did all the work for us
* already , just apply the offset to the correct
* instruction location .
*/
return ( real_pc + ( regs - > tnpc - ixol_addr ) ) ;
}
/* It is jmpl or some other absolute PC modification instruction,
* leave NPC as - is .
*/
return regs - > tnpc ;
}
/* If INSN is an instruction which writes its PC location
* into a destination register , fix that up .
*/
static int retpc_fixup ( struct pt_regs * regs , u32 insn ,
unsigned long real_pc )
{
unsigned long * slot = NULL ;
int rc = 0 ;
/* Simplest case is 'call', which always uses %o7 */
if ( ( insn & 0xc0000000 ) = = 0x40000000 )
slot = & regs - > u_regs [ UREG_I7 ] ;
/* 'jmpl' encodes the register inside of the opcode */
if ( ( insn & 0xc1f80000 ) = = 0x81c00000 ) {
unsigned long rd = ( ( insn > > 25 ) & 0x1f ) ;
if ( rd < = 15 ) {
slot = & regs - > u_regs [ rd ] ;
} else {
unsigned long fp = regs - > u_regs [ UREG_FP ] ;
/* Hard case, it goes onto the stack. */
flushw_all ( ) ;
rd - = 16 ;
if ( test_thread_64bit_stack ( fp ) ) {
unsigned long __user * uslot =
( unsigned long __user * ) ( fp + STACK_BIAS ) + rd ;
rc = __put_user ( real_pc , uslot ) ;
} else {
unsigned int __user * uslot = ( unsigned int
__user * ) fp + rd ;
rc = __put_user ( ( u32 ) real_pc , uslot ) ;
}
}
}
if ( slot ! = NULL )
* slot = real_pc ;
return rc ;
}
/* Single-stepping can be avoided for certain instructions: NOPs and
* instructions that can be emulated . This function determines
* whether the instruction where the uprobe is installed falls in one
* of these cases and emulates it .
*
* This function returns true if the single - stepping can be skipped ,
* false otherwise .
*/
bool arch_uprobe_skip_sstep ( struct arch_uprobe * auprobe , struct pt_regs * regs )
{
/* We currently only emulate NOP instructions.
*/
if ( auprobe - > ixol = = ( 1 < < 24 ) ) {
regs - > tnpc + = 4 ;
regs - > tpc + = 4 ;
return true ;
}
return false ;
}
/* Prepare to execute out of line. At this point
* current - > utask - > xol_vaddr points to an allocated XOL slot properly
* initialized with the original instruction and the single - stepping
* trap instruction .
*
* This function returns 0 on success , any other number on error .
*/
int arch_uprobe_pre_xol ( struct arch_uprobe * auprobe , struct pt_regs * regs )
{
struct uprobe_task * utask = current - > utask ;
struct arch_uprobe_task * autask = & current - > utask - > autask ;
/* Save the current program counters so they can be restored
* later .
*/
autask - > saved_tpc = regs - > tpc ;
autask - > saved_tnpc = regs - > tnpc ;
/* Adjust PC and NPC so the first instruction in the XOL slot
* will be executed by the user task .
*/
instruction_pointer_set ( regs , utask - > xol_vaddr ) ;
return 0 ;
}
/* Prepare to resume execution after the single-step. Called after
* single - stepping . To avoid the SMP problems that can occur when we
* temporarily put back the original opcode to single - step , we
* single - stepped a copy of the instruction .
*
* This function returns 0 on success , any other number on error .
*/
int arch_uprobe_post_xol ( struct arch_uprobe * auprobe , struct pt_regs * regs )
{
struct uprobe_task * utask = current - > utask ;
struct arch_uprobe_task * autask = & utask - > autask ;
u32 insn = auprobe - > ixol ;
int rc = 0 ;
if ( utask - > state = = UTASK_SSTEP_ACK ) {
regs - > tnpc = relbranch_fixup ( insn , utask , regs ) ;
regs - > tpc = autask - > saved_tnpc ;
rc = retpc_fixup ( regs , insn , ( unsigned long ) utask - > vaddr ) ;
} else {
regs - > tnpc = utask - > vaddr + 4 ;
regs - > tpc = autask - > saved_tnpc + 4 ;
}
return rc ;
}
/* Handler for uprobe traps. This is called from the traps table and
* triggers the proper die notification .
*/
asmlinkage void uprobe_trap ( struct pt_regs * regs ,
unsigned long trap_level )
{
BUG_ON ( trap_level ! = 0x173 & & trap_level ! = 0x174 ) ;
/* We are only interested in user-mode code. Uprobe traps
* shall not be present in kernel code .
*/
if ( ! user_mode ( regs ) ) {
local_irq_enable ( ) ;
bad_trap ( regs , trap_level ) ;
return ;
}
/* trap_level == 0x173 --> ta 0x73
* trap_level = = 0x174 - - > ta 0x74
*/
if ( notify_die ( ( trap_level = = 0x173 ) ? DIE_BPT : DIE_SSTEP ,
( trap_level = = 0x173 ) ? " bpt " : " sstep " ,
regs , 0 , trap_level , SIGTRAP ) ! = NOTIFY_STOP )
bad_trap ( regs , trap_level ) ;
}
/* Callback routine for handling die notifications.
*/
int arch_uprobe_exception_notify ( struct notifier_block * self ,
unsigned long val , void * data )
{
int ret = NOTIFY_DONE ;
struct die_args * args = ( struct die_args * ) data ;
/* We are only interested in userspace traps */
if ( args - > regs & & ! user_mode ( args - > regs ) )
return NOTIFY_DONE ;
switch ( val ) {
case DIE_BPT :
if ( uprobe_pre_sstep_notifier ( args - > regs ) )
ret = NOTIFY_STOP ;
break ;
case DIE_SSTEP :
if ( uprobe_post_sstep_notifier ( args - > regs ) )
ret = NOTIFY_STOP ;
default :
break ;
}
return ret ;
}
/* This function gets called when a XOL instruction either gets
* trapped or the thread has a fatal signal , so reset the instruction
* pointer to its probed address .
*/
void arch_uprobe_abort_xol ( struct arch_uprobe * auprobe , struct pt_regs * regs )
{
struct uprobe_task * utask = current - > utask ;
instruction_pointer_set ( regs , utask - > vaddr ) ;
}
/* 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 .
*/
bool arch_uprobe_xol_was_trapped ( struct task_struct * t )
{
return false ;
}
unsigned long
arch_uretprobe_hijack_return_addr ( unsigned long trampoline_vaddr ,
struct pt_regs * regs )
{
unsigned long orig_ret_vaddr = regs - > u_regs [ UREG_I7 ] ;
regs - > u_regs [ UREG_I7 ] = trampoline_vaddr - 8 ;
return orig_ret_vaddr + 8 ;
}