2018-03-12 14:45:48 +01:00
/*
* Powerpc needs __SANE_USERSPACE_TYPES__ before < linux / types . h > to select
* ' int - ll64 . h ' and avoid compile warnings when printing __u64 with % llu .
*/
# define __SANE_USERSPACE_TYPES__
# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <sys/ioctl.h>
# include <time.h>
# include <fcntl.h>
# include <signal.h>
# include <sys/mman.h>
# include <linux/compiler.h>
# include <linux/hw_breakpoint.h>
# include <sys/ioctl.h>
# include "tests.h"
# include "debug.h"
# include "perf.h"
# include "cloexec.h"
volatile long the_var ;
static noinline int test_function ( void )
{
return 0 ;
}
static int __event ( bool is_x , void * addr , struct perf_event_attr * attr )
{
int fd ;
memset ( attr , 0 , sizeof ( struct perf_event_attr ) ) ;
attr - > type = PERF_TYPE_BREAKPOINT ;
attr - > size = sizeof ( struct perf_event_attr ) ;
attr - > config = 0 ;
attr - > bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W ;
attr - > bp_addr = ( unsigned long ) addr ;
attr - > bp_len = sizeof ( long ) ;
attr - > sample_period = 1 ;
attr - > sample_type = PERF_SAMPLE_IP ;
attr - > exclude_kernel = 1 ;
attr - > exclude_hv = 1 ;
fd = sys_perf_event_open ( attr , - 1 , 0 , - 1 ,
perf_event_open_cloexec_flag ( ) ) ;
if ( fd < 0 ) {
pr_debug ( " failed opening event %llx \n " , attr - > config ) ;
return TEST_FAIL ;
}
return fd ;
}
static int wp_event ( void * addr , struct perf_event_attr * attr )
{
return __event ( false , addr , attr ) ;
}
static int bp_event ( void * addr , struct perf_event_attr * attr )
{
return __event ( true , addr , attr ) ;
}
static int bp_accounting ( int wp_cnt , int share )
{
struct perf_event_attr attr , attr_mod , attr_new ;
int i , fd [ wp_cnt ] , fd_wp , ret ;
for ( i = 0 ; i < wp_cnt ; i + + ) {
fd [ i ] = wp_event ( ( void * ) & the_var , & attr ) ;
TEST_ASSERT_VAL ( " failed to create wp \n " , fd [ i ] ! = - 1 ) ;
pr_debug ( " wp %d created \n " , i ) ;
}
attr_mod = attr ;
attr_mod . bp_type = HW_BREAKPOINT_X ;
attr_mod . bp_addr = ( unsigned long ) test_function ;
ret = ioctl ( fd [ 0 ] , PERF_EVENT_IOC_MODIFY_ATTRIBUTES , & attr_mod ) ;
TEST_ASSERT_VAL ( " failed to modify wp \n " , ret = = 0 ) ;
pr_debug ( " wp 0 modified to bp \n " ) ;
if ( ! share ) {
fd_wp = wp_event ( ( void * ) & the_var , & attr_new ) ;
TEST_ASSERT_VAL ( " failed to create max wp \n " , fd_wp ! = - 1 ) ;
pr_debug ( " wp max created \n " ) ;
}
for ( i = 0 ; i < wp_cnt ; i + + )
close ( fd [ i ] ) ;
return 0 ;
}
static int detect_cnt ( bool is_x )
{
struct perf_event_attr attr ;
2018-03-19 10:51:00 -03:00
void * addr = is_x ? ( void * ) test_function : ( void * ) & the_var ;
2018-03-12 14:45:48 +01:00
int fd [ 100 ] , cnt = 0 , i ;
while ( 1 ) {
if ( cnt = = 100 ) {
pr_debug ( " way too many debug registers, fix the test \n " ) ;
return 0 ;
}
2018-03-14 17:33:54 +00:00
fd [ cnt ] = __event ( is_x , addr , & attr ) ;
2018-03-12 14:45:48 +01:00
2018-03-14 17:33:54 +00:00
if ( fd [ cnt ] < 0 )
break ;
2018-03-12 14:45:48 +01:00
cnt + + ;
}
for ( i = 0 ; i < cnt ; i + + )
close ( fd [ i ] ) ;
return cnt ;
}
static int detect_ioctl ( void )
{
struct perf_event_attr attr ;
int fd , ret = 1 ;
fd = wp_event ( ( void * ) & the_var , & attr ) ;
if ( fd > 0 ) {
ret = ioctl ( fd , PERF_EVENT_IOC_MODIFY_ATTRIBUTES , & attr ) ;
close ( fd ) ;
}
return ret ? 0 : 1 ;
}
static int detect_share ( int wp_cnt , int bp_cnt )
{
struct perf_event_attr attr ;
int i , fd [ wp_cnt + bp_cnt ] , ret ;
for ( i = 0 ; i < wp_cnt ; i + + ) {
fd [ i ] = wp_event ( ( void * ) & the_var , & attr ) ;
TEST_ASSERT_VAL ( " failed to create wp \n " , fd [ i ] ! = - 1 ) ;
}
for ( ; i < ( bp_cnt + wp_cnt ) ; i + + ) {
fd [ i ] = bp_event ( ( void * ) test_function , & attr ) ;
if ( fd [ i ] = = - 1 )
break ;
}
ret = i ! = ( bp_cnt + wp_cnt ) ;
while ( i - - )
close ( fd [ i ] ) ;
return ret ;
}
/*
* This test does following :
* - detects the number of watch / break - points ,
* skip test if any is missing
* - detects PERF_EVENT_IOC_MODIFY_ATTRIBUTES ioctl ,
* skip test if it ' s missing
* - detects if watchpoints and breakpoints share
* same slots
* - create all possible watchpoints on cpu 0
* - change one of it to breakpoint
* - in case wp and bp do not share slots ,
* we create another watchpoint to ensure
* the slot accounting is correct
*/
int test__bp_accounting ( struct test * test __maybe_unused , int subtest __maybe_unused )
{
int has_ioctl = detect_ioctl ( ) ;
int wp_cnt = detect_cnt ( false ) ;
int bp_cnt = detect_cnt ( true ) ;
int share = detect_share ( wp_cnt , bp_cnt ) ;
pr_debug ( " watchpoints count %d, breakpoints count %d, has_ioctl %d, share %d \n " ,
wp_cnt , bp_cnt , has_ioctl , share ) ;
if ( ! wp_cnt | | ! bp_cnt | | ! has_ioctl )
return TEST_SKIP ;
return bp_accounting ( wp_cnt , share ) ;
}