162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Google, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Original Code by Pavel Labath <labath@google.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Code modified by Pratyush Anand <panand@redhat.com> 862306a36Sopenharmony_ci * for testing different byte select for each access size. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define _GNU_SOURCE 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/ptrace.h> 1462306a36Sopenharmony_ci#include <sys/types.h> 1562306a36Sopenharmony_ci#include <sys/wait.h> 1662306a36Sopenharmony_ci#include <sys/ptrace.h> 1762306a36Sopenharmony_ci#include <sys/param.h> 1862306a36Sopenharmony_ci#include <sys/uio.h> 1962306a36Sopenharmony_ci#include <stdint.h> 2062306a36Sopenharmony_ci#include <stdbool.h> 2162306a36Sopenharmony_ci#include <stddef.h> 2262306a36Sopenharmony_ci#include <string.h> 2362306a36Sopenharmony_ci#include <stdio.h> 2462306a36Sopenharmony_ci#include <unistd.h> 2562306a36Sopenharmony_ci#include <elf.h> 2662306a36Sopenharmony_ci#include <errno.h> 2762306a36Sopenharmony_ci#include <signal.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "../kselftest.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic volatile uint8_t var[96] __attribute__((__aligned__(32))); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void child(int size, int wr) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci volatile uint8_t *addr = &var[32 + wr]; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { 3862306a36Sopenharmony_ci ksft_print_msg( 3962306a36Sopenharmony_ci "ptrace(PTRACE_TRACEME) failed: %s\n", 4062306a36Sopenharmony_ci strerror(errno)); 4162306a36Sopenharmony_ci _exit(1); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (raise(SIGSTOP) != 0) { 4562306a36Sopenharmony_ci ksft_print_msg( 4662306a36Sopenharmony_ci "raise(SIGSTOP) failed: %s\n", strerror(errno)); 4762306a36Sopenharmony_ci _exit(1); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if ((uintptr_t) addr % size) { 5162306a36Sopenharmony_ci ksft_print_msg( 5262306a36Sopenharmony_ci "Wrong address write for the given size: %s\n", 5362306a36Sopenharmony_ci strerror(errno)); 5462306a36Sopenharmony_ci _exit(1); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci switch (size) { 5862306a36Sopenharmony_ci case 1: 5962306a36Sopenharmony_ci *addr = 47; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci case 2: 6262306a36Sopenharmony_ci *(uint16_t *)addr = 47; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci case 4: 6562306a36Sopenharmony_ci *(uint32_t *)addr = 47; 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci case 8: 6862306a36Sopenharmony_ci *(uint64_t *)addr = 47; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci case 16: 7162306a36Sopenharmony_ci __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0])); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci case 32: 7462306a36Sopenharmony_ci __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0])); 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci _exit(0); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic bool set_watchpoint(pid_t pid, int size, int wp) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci const volatile uint8_t *addr = &var[32 + wp]; 8462306a36Sopenharmony_ci const int offset = (uintptr_t)addr % 8; 8562306a36Sopenharmony_ci const unsigned int byte_mask = ((1 << size) - 1) << offset; 8662306a36Sopenharmony_ci const unsigned int type = 2; /* Write */ 8762306a36Sopenharmony_ci const unsigned int enable = 1; 8862306a36Sopenharmony_ci const unsigned int control = byte_mask << 5 | type << 3 | enable; 8962306a36Sopenharmony_ci struct user_hwdebug_state dreg_state; 9062306a36Sopenharmony_ci struct iovec iov; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci memset(&dreg_state, 0, sizeof(dreg_state)); 9362306a36Sopenharmony_ci dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset); 9462306a36Sopenharmony_ci dreg_state.dbg_regs[0].ctrl = control; 9562306a36Sopenharmony_ci iov.iov_base = &dreg_state; 9662306a36Sopenharmony_ci iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + 9762306a36Sopenharmony_ci sizeof(dreg_state.dbg_regs[0]); 9862306a36Sopenharmony_ci if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) 9962306a36Sopenharmony_ci return true; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (errno == EIO) 10262306a36Sopenharmony_ci ksft_print_msg( 10362306a36Sopenharmony_ci "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", 10462306a36Sopenharmony_ci strerror(errno)); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ksft_print_msg( 10762306a36Sopenharmony_ci "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", 10862306a36Sopenharmony_ci strerror(errno)); 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic bool run_test(int wr_size, int wp_size, int wr, int wp) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int status; 11562306a36Sopenharmony_ci siginfo_t siginfo; 11662306a36Sopenharmony_ci pid_t pid = fork(); 11762306a36Sopenharmony_ci pid_t wpid; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (pid < 0) { 12062306a36Sopenharmony_ci ksft_test_result_fail( 12162306a36Sopenharmony_ci "fork() failed: %s\n", strerror(errno)); 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci if (pid == 0) 12562306a36Sopenharmony_ci child(wr_size, wr); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci wpid = waitpid(pid, &status, __WALL); 12862306a36Sopenharmony_ci if (wpid != pid) { 12962306a36Sopenharmony_ci ksft_print_msg( 13062306a36Sopenharmony_ci "waitpid() failed: %s\n", strerror(errno)); 13162306a36Sopenharmony_ci return false; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci if (!WIFSTOPPED(status)) { 13462306a36Sopenharmony_ci ksft_print_msg( 13562306a36Sopenharmony_ci "child did not stop: %s\n", strerror(errno)); 13662306a36Sopenharmony_ci return false; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci if (WSTOPSIG(status) != SIGSTOP) { 13962306a36Sopenharmony_ci ksft_print_msg("child did not stop with SIGSTOP\n"); 14062306a36Sopenharmony_ci return false; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!set_watchpoint(pid, wp_size, wp)) 14462306a36Sopenharmony_ci return false; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { 14762306a36Sopenharmony_ci ksft_print_msg( 14862306a36Sopenharmony_ci "ptrace(PTRACE_CONT) failed: %s\n", 14962306a36Sopenharmony_ci strerror(errno)); 15062306a36Sopenharmony_ci return false; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci alarm(3); 15462306a36Sopenharmony_ci wpid = waitpid(pid, &status, __WALL); 15562306a36Sopenharmony_ci if (wpid != pid) { 15662306a36Sopenharmony_ci ksft_print_msg( 15762306a36Sopenharmony_ci "waitpid() failed: %s\n", strerror(errno)); 15862306a36Sopenharmony_ci return false; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci alarm(0); 16162306a36Sopenharmony_ci if (WIFEXITED(status)) { 16262306a36Sopenharmony_ci ksft_print_msg("child exited prematurely\n"); 16362306a36Sopenharmony_ci return false; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci if (!WIFSTOPPED(status)) { 16662306a36Sopenharmony_ci ksft_print_msg("child did not stop\n"); 16762306a36Sopenharmony_ci return false; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci if (WSTOPSIG(status) != SIGTRAP) { 17062306a36Sopenharmony_ci ksft_print_msg("child did not stop with SIGTRAP\n"); 17162306a36Sopenharmony_ci return false; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { 17462306a36Sopenharmony_ci ksft_print_msg( 17562306a36Sopenharmony_ci "ptrace(PTRACE_GETSIGINFO): %s\n", 17662306a36Sopenharmony_ci strerror(errno)); 17762306a36Sopenharmony_ci return false; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci if (siginfo.si_code != TRAP_HWBKPT) { 18062306a36Sopenharmony_ci ksft_print_msg( 18162306a36Sopenharmony_ci "Unexpected si_code %d\n", siginfo.si_code); 18262306a36Sopenharmony_ci return false; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci kill(pid, SIGKILL); 18662306a36Sopenharmony_ci wpid = waitpid(pid, &status, 0); 18762306a36Sopenharmony_ci if (wpid != pid) { 18862306a36Sopenharmony_ci ksft_print_msg( 18962306a36Sopenharmony_ci "waitpid() failed: %s\n", strerror(errno)); 19062306a36Sopenharmony_ci return false; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci return true; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void sigalrm(int sig) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint main(int argc, char **argv) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int opt; 20262306a36Sopenharmony_ci bool succeeded = true; 20362306a36Sopenharmony_ci struct sigaction act; 20462306a36Sopenharmony_ci int wr, wp, size; 20562306a36Sopenharmony_ci bool result; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ksft_print_header(); 20862306a36Sopenharmony_ci ksft_set_plan(213); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci act.sa_handler = sigalrm; 21162306a36Sopenharmony_ci sigemptyset(&act.sa_mask); 21262306a36Sopenharmony_ci act.sa_flags = 0; 21362306a36Sopenharmony_ci sigaction(SIGALRM, &act, NULL); 21462306a36Sopenharmony_ci for (size = 1; size <= 32; size = size*2) { 21562306a36Sopenharmony_ci for (wr = 0; wr <= 32; wr = wr + size) { 21662306a36Sopenharmony_ci for (wp = wr - size; wp <= wr + size; wp = wp + size) { 21762306a36Sopenharmony_ci result = run_test(size, MIN(size, 8), wr, wp); 21862306a36Sopenharmony_ci if ((result && wr == wp) || 21962306a36Sopenharmony_ci (!result && wr != wp)) 22062306a36Sopenharmony_ci ksft_test_result_pass( 22162306a36Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = %d\n", 22262306a36Sopenharmony_ci size, wr, wp); 22362306a36Sopenharmony_ci else { 22462306a36Sopenharmony_ci ksft_test_result_fail( 22562306a36Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = %d\n", 22662306a36Sopenharmony_ci size, wr, wp); 22762306a36Sopenharmony_ci succeeded = false; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (size = 1; size <= 32; size = size*2) { 23462306a36Sopenharmony_ci if (run_test(size, 8, -size, -8)) 23562306a36Sopenharmony_ci ksft_test_result_pass( 23662306a36Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = -8\n", 23762306a36Sopenharmony_ci size, -size); 23862306a36Sopenharmony_ci else { 23962306a36Sopenharmony_ci ksft_test_result_fail( 24062306a36Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = -8\n", 24162306a36Sopenharmony_ci size, -size); 24262306a36Sopenharmony_ci succeeded = false; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (succeeded) 24762306a36Sopenharmony_ci ksft_exit_pass(); 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci ksft_exit_fail(); 25062306a36Sopenharmony_ci} 251