2018-04-25 15:18:03 +03:00
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Traceprobe fetch helper inlines
*/
static nokprobe_inline void
fetch_store_raw ( unsigned long val , struct fetch_insn * code , void * buf )
{
switch ( code - > size ) {
case 1 :
* ( u8 * ) buf = ( u8 ) val ;
break ;
case 2 :
* ( u16 * ) buf = ( u16 ) val ;
break ;
case 4 :
* ( u32 * ) buf = ( u32 ) val ;
break ;
case 8 :
//TBD: 32bit signed
* ( u64 * ) buf = ( u64 ) val ;
break ;
default :
* ( unsigned long * ) buf = val ;
}
}
static nokprobe_inline void
fetch_apply_bitfield ( struct fetch_insn * code , void * buf )
{
switch ( code - > basesize ) {
case 1 :
* ( u8 * ) buf < < = code - > lshift ;
* ( u8 * ) buf > > = code - > rshift ;
break ;
case 2 :
* ( u16 * ) buf < < = code - > lshift ;
* ( u16 * ) buf > > = code - > rshift ;
break ;
case 4 :
* ( u32 * ) buf < < = code - > lshift ;
* ( u32 * ) buf > > = code - > rshift ;
break ;
case 8 :
* ( u64 * ) buf < < = code - > lshift ;
* ( u64 * ) buf > > = code - > rshift ;
break ;
}
}
2018-04-25 15:19:01 +03:00
/*
2018-04-25 15:19:59 +03:00
* These functions must be defined for each callsite .
2018-04-25 15:19:01 +03:00
* Return consumed dynamic data size ( > = 0 ) , or error ( < 0 ) .
* If dest is NULL , don ' t store result and return required dynamic data size .
*/
2018-04-25 15:18:03 +03:00
static int
process_fetch_insn ( struct fetch_insn * code , struct pt_regs * regs ,
2018-04-25 15:19:01 +03:00
void * dest , void * base ) ;
2018-04-25 15:19:59 +03:00
static nokprobe_inline int fetch_store_strlen ( unsigned long addr ) ;
static nokprobe_inline int
fetch_store_string ( unsigned long addr , void * dest , void * base ) ;
static nokprobe_inline int
probe_mem_read ( void * dest , void * src , size_t size ) ;
/* From the 2nd stage, routine is same */
static nokprobe_inline int
process_fetch_insn_bottom ( struct fetch_insn * code , unsigned long val ,
void * dest , void * base )
{
2018-04-25 15:21:55 +03:00
struct fetch_insn * s3 = NULL ;
int total = 0 , ret = 0 , i = 0 ;
u32 loc = 0 ;
unsigned long lval = val ;
2018-04-25 15:19:59 +03:00
2018-04-25 15:21:55 +03:00
stage2 :
2018-04-25 15:19:59 +03:00
/* 2nd stage: dereference memory if needed */
while ( code - > op = = FETCH_OP_DEREF ) {
2018-04-25 15:21:55 +03:00
lval = val ;
2018-04-25 15:19:59 +03:00
ret = probe_mem_read ( & val , ( void * ) val + code - > offset ,
sizeof ( val ) ) ;
if ( ret )
return ret ;
code + + ;
}
2018-04-25 15:21:55 +03:00
s3 = code ;
stage3 :
2018-04-25 15:19:59 +03:00
/* 3rd stage: store value to buffer */
if ( unlikely ( ! dest ) ) {
2018-04-25 15:21:55 +03:00
if ( code - > op = = FETCH_OP_ST_STRING ) {
2019-05-07 16:55:52 +03:00
ret = fetch_store_strlen ( val + code - > offset ) ;
2018-04-25 15:21:55 +03:00
code + + ;
goto array ;
} else
2018-04-25 15:19:59 +03:00
return - EILSEQ ;
}
switch ( code - > op ) {
case FETCH_OP_ST_RAW :
fetch_store_raw ( val , code , dest ) ;
break ;
case FETCH_OP_ST_MEM :
probe_mem_read ( dest , ( void * ) val + code - > offset , code - > size ) ;
break ;
case FETCH_OP_ST_STRING :
2018-04-25 15:21:55 +03:00
loc = * ( u32 * ) dest ;
2018-04-25 15:19:59 +03:00
ret = fetch_store_string ( val + code - > offset , dest , base ) ;
break ;
default :
return - EILSEQ ;
}
code + + ;
/* 4th stage: modify stored value if needed */
if ( code - > op = = FETCH_OP_MOD_BF ) {
fetch_apply_bitfield ( code , dest ) ;
code + + ;
}
2018-04-25 15:21:55 +03:00
array :
/* the last stage: Loop on array */
if ( code - > op = = FETCH_OP_LP_ARRAY ) {
total + = ret ;
if ( + + i < code - > param ) {
code = s3 ;
if ( s3 - > op ! = FETCH_OP_ST_STRING ) {
dest + = s3 - > size ;
val + = s3 - > size ;
goto stage3 ;
}
code - - ;
val = lval + sizeof ( char * ) ;
if ( dest ) {
dest + = sizeof ( u32 ) ;
* ( u32 * ) dest = update_data_loc ( loc , ret ) ;
}
goto stage2 ;
}
code + + ;
ret = total ;
}
2018-04-25 15:19:59 +03:00
return code - > op = = FETCH_OP_END ? ret : - EILSEQ ;
}
2018-04-25 15:18:03 +03:00
/* Sum up total data length for dynamic arraies (strings) */
static nokprobe_inline int
__get_data_size ( struct trace_probe * tp , struct pt_regs * regs )
{
struct probe_arg * arg ;
2018-04-25 15:19:01 +03:00
int i , len , ret = 0 ;
2018-04-25 15:18:03 +03:00
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
arg = tp - > args + i ;
if ( unlikely ( arg - > dynamic ) ) {
2018-04-25 15:19:01 +03:00
len = process_fetch_insn ( arg - > code , regs , NULL , NULL ) ;
if ( len > 0 )
ret + = len ;
2018-04-25 15:18:03 +03:00
}
}
return ret ;
}
/* Store the value of each argument */
static nokprobe_inline void
2018-04-25 15:19:01 +03:00
store_trace_args ( void * data , struct trace_probe * tp , struct pt_regs * regs ,
int header_size , int maxlen )
2018-04-25 15:18:03 +03:00
{
struct probe_arg * arg ;
2018-04-25 15:19:01 +03:00
void * base = data - header_size ;
void * dyndata = data + tp - > size ;
u32 * dl ; /* Data location */
int ret , i ;
2018-04-25 15:18:03 +03:00
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
arg = tp - > args + i ;
2018-04-25 15:19:01 +03:00
dl = data + arg - > offset ;
/* Point the dynamic data area if needed */
if ( unlikely ( arg - > dynamic ) )
* dl = make_data_loc ( maxlen , dyndata - base ) ;
ret = process_fetch_insn ( arg - > code , regs , dl , base ) ;
2019-02-06 22:00:13 +03:00
if ( unlikely ( ret < 0 & & arg - > dynamic ) ) {
2018-04-25 15:19:01 +03:00
* dl = make_data_loc ( 0 , dyndata - base ) ;
2019-02-06 22:00:13 +03:00
} else {
2018-04-25 15:19:01 +03:00
dyndata + = ret ;
2019-02-06 22:00:13 +03:00
maxlen - = ret ;
}
2018-04-25 15:18:03 +03:00
}
}
static inline int
print_probe_args ( struct trace_seq * s , struct probe_arg * args , int nr_args ,
u8 * data , void * field )
{
2018-04-25 15:21:55 +03:00
void * p ;
int i , j ;
2018-04-25 15:18:03 +03:00
for ( i = 0 ; i < nr_args ; i + + ) {
2018-04-25 15:21:55 +03:00
struct probe_arg * a = args + i ;
trace_seq_printf ( s , " %s= " , a - > name ) ;
if ( likely ( ! a - > count ) ) {
if ( ! a - > type - > print ( s , data + a - > offset , field ) )
return - ENOMEM ;
continue ;
}
trace_seq_putc ( s , ' { ' ) ;
p = data + a - > offset ;
for ( j = 0 ; j < a - > count ; j + + ) {
if ( ! a - > type - > print ( s , p , field ) )
return - ENOMEM ;
trace_seq_putc ( s , j = = a - > count - 1 ? ' } ' : ' , ' ) ;
p + = a - > type - > size ;
}
2018-04-25 15:18:03 +03:00
}
return 0 ;
}