2019-05-29 07:17:56 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2016-11-14 19:32:46 +05:30
/*
* Copyright ( C ) 2016 Google , Inc .
*
* Original Code by Pavel Labath < labath @ google . com >
*
* Code modified by Pratyush Anand < panand @ redhat . com >
* for testing different byte select for each access size .
*/
# define _GNU_SOURCE
2017-10-23 13:12:22 +05:30
# include <asm/ptrace.h>
2016-11-14 19:32:46 +05:30
# include <sys/types.h>
# include <sys/wait.h>
# include <sys/ptrace.h>
# include <sys/param.h>
# include <sys/uio.h>
# include <stdint.h>
# include <stdbool.h>
# include <stddef.h>
# include <string.h>
# include <stdio.h>
# include <unistd.h>
# include <elf.h>
# include <errno.h>
# include <signal.h>
# include "../kselftest.h"
static volatile uint8_t var [ 96 ] __attribute__ ( ( __aligned__ ( 32 ) ) ) ;
static void child ( int size , int wr )
{
volatile uint8_t * addr = & var [ 32 + wr ] ;
if ( ptrace ( PTRACE_TRACEME , 0 , NULL , NULL ) ! = 0 ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" ptrace(PTRACE_TRACEME) failed: %s \n " ,
strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
_exit ( 1 ) ;
}
if ( raise ( SIGSTOP ) ! = 0 ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" raise(SIGSTOP) failed: %s \n " , strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
_exit ( 1 ) ;
}
if ( ( uintptr_t ) addr % size ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" Wrong address write for the given size: %s \n " ,
strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
_exit ( 1 ) ;
}
2017-06-29 15:52:42 -06:00
2016-11-14 19:32:46 +05:30
switch ( size ) {
case 1 :
* addr = 47 ;
break ;
case 2 :
* ( uint16_t * ) addr = 47 ;
break ;
case 4 :
* ( uint32_t * ) addr = 47 ;
break ;
case 8 :
* ( uint64_t * ) addr = 47 ;
break ;
case 16 :
__asm__ volatile ( " stp x29, x30, %0 " : " =m " ( addr [ 0 ] ) ) ;
break ;
case 32 :
__asm__ volatile ( " stp q29, q30, %0 " : " =m " ( addr [ 0 ] ) ) ;
break ;
}
_exit ( 0 ) ;
}
static bool set_watchpoint ( pid_t pid , int size , int wp )
{
const volatile uint8_t * addr = & var [ 32 + wp ] ;
const int offset = ( uintptr_t ) addr % 8 ;
const unsigned int byte_mask = ( ( 1 < < size ) - 1 ) < < offset ;
const unsigned int type = 2 ; /* Write */
const unsigned int enable = 1 ;
const unsigned int control = byte_mask < < 5 | type < < 3 | enable ;
struct user_hwdebug_state dreg_state ;
struct iovec iov ;
memset ( & dreg_state , 0 , sizeof ( dreg_state ) ) ;
dreg_state . dbg_regs [ 0 ] . addr = ( uintptr_t ) ( addr - offset ) ;
dreg_state . dbg_regs [ 0 ] . ctrl = control ;
iov . iov_base = & dreg_state ;
iov . iov_len = offsetof ( struct user_hwdebug_state , dbg_regs ) +
sizeof ( dreg_state . dbg_regs [ 0 ] ) ;
if ( ptrace ( PTRACE_SETREGSET , pid , NT_ARM_HW_WATCH , & iov ) = = 0 )
return true ;
2017-06-29 15:52:42 -06:00
if ( errno = = EIO )
ksft_print_msg (
" ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s \n " ,
strerror ( errno ) ) ;
ksft_print_msg (
" ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s \n " ,
strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
2019-10-31 21:23:00 +09:00
static bool run_test ( int wr_size , int wp_size , int wr , int wp )
2016-11-14 19:32:46 +05:30
{
int status ;
siginfo_t siginfo ;
pid_t pid = fork ( ) ;
pid_t wpid ;
if ( pid < 0 ) {
2017-06-29 15:52:42 -06:00
ksft_test_result_fail (
" fork() failed: %s \n " , strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( pid = = 0 )
child ( wr_size , wr ) ;
wpid = waitpid ( pid , & status , __WALL ) ;
if ( wpid ! = pid ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" waitpid() failed: %s \n " , strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( ! WIFSTOPPED ( status ) ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" child did not stop: %s \n " , strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( WSTOPSIG ( status ) ! = SIGSTOP ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg ( " child did not stop with SIGSTOP \n " ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( ! set_watchpoint ( pid , wp_size , wp ) )
return false ;
if ( ptrace ( PTRACE_CONT , pid , NULL , NULL ) < 0 ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
2021-02-05 14:33:59 +08:00
" ptrace(PTRACE_CONT) failed: %s \n " ,
2017-06-29 15:52:42 -06:00
strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
alarm ( 3 ) ;
wpid = waitpid ( pid , & status , __WALL ) ;
if ( wpid ! = pid ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" waitpid() failed: %s \n " , strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
alarm ( 0 ) ;
if ( WIFEXITED ( status ) ) {
2021-02-05 14:33:59 +08:00
ksft_print_msg ( " child exited prematurely \n " ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( ! WIFSTOPPED ( status ) ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg ( " child did not stop \n " ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( WSTOPSIG ( status ) ! = SIGTRAP ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg ( " child did not stop with SIGTRAP \n " ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( ptrace ( PTRACE_GETSIGINFO , pid , NULL , & siginfo ) ! = 0 ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" ptrace(PTRACE_GETSIGINFO): %s \n " ,
strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
if ( siginfo . si_code ! = TRAP_HWBKPT ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" Unexpected si_code %d \n " , siginfo . si_code ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
kill ( pid , SIGKILL ) ;
wpid = waitpid ( pid , & status , 0 ) ;
if ( wpid ! = pid ) {
2017-06-29 15:52:42 -06:00
ksft_print_msg (
" waitpid() failed: %s \n " , strerror ( errno ) ) ;
2016-11-14 19:32:46 +05:30
return false ;
}
return true ;
}
static void sigalrm ( int sig )
{
}
int main ( int argc , char * * argv )
{
int opt ;
bool succeeded = true ;
struct sigaction act ;
int wr , wp , size ;
bool result ;
2017-06-29 15:52:42 -06:00
ksft_print_header ( ) ;
2019-04-24 16:12:37 -07:00
ksft_set_plan ( 213 ) ;
2017-06-29 15:52:42 -06:00
2016-11-14 19:32:46 +05:30
act . sa_handler = sigalrm ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
sigaction ( SIGALRM , & act , NULL ) ;
for ( size = 1 ; size < = 32 ; size = size * 2 ) {
for ( wr = 0 ; wr < = 32 ; wr = wr + size ) {
for ( wp = wr - size ; wp < = wr + size ; wp = wp + size ) {
result = run_test ( size , MIN ( size , 8 ) , wr , wp ) ;
2017-06-29 15:52:42 -06:00
if ( ( result & & wr = = wp ) | |
( ! result & & wr ! = wp ) )
ksft_test_result_pass (
" Test size = %d write offset = %d watchpoint offset = %d \n " ,
size , wr , wp ) ;
else {
ksft_test_result_fail (
" Test size = %d write offset = %d watchpoint offset = %d \n " ,
size , wr , wp ) ;
2016-11-14 19:32:46 +05:30
succeeded = false ;
}
}
}
}
for ( size = 1 ; size < = 32 ; size = size * 2 ) {
2017-06-29 15:52:42 -06:00
if ( run_test ( size , 8 , - size , - 8 ) )
ksft_test_result_pass (
" Test size = %d write offset = %d watchpoint offset = -8 \n " ,
size , - size ) ;
else {
ksft_test_result_fail (
" Test size = %d write offset = %d watchpoint offset = -8 \n " ,
size , - size ) ;
2016-11-14 19:32:46 +05:30
succeeded = false ;
}
}
if ( succeeded )
ksft_exit_pass ( ) ;
else
ksft_exit_fail ( ) ;
}