18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Google, Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Original Code by Pavel Labath <labath@google.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Code modified by Pratyush Anand <panand@redhat.com> 88c2ecf20Sopenharmony_ci * for testing different byte select for each access size. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define _GNU_SOURCE 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 148c2ecf20Sopenharmony_ci#include <sys/types.h> 158c2ecf20Sopenharmony_ci#include <sys/wait.h> 168c2ecf20Sopenharmony_ci#include <sys/ptrace.h> 178c2ecf20Sopenharmony_ci#include <sys/param.h> 188c2ecf20Sopenharmony_ci#include <sys/uio.h> 198c2ecf20Sopenharmony_ci#include <stdint.h> 208c2ecf20Sopenharmony_ci#include <stdbool.h> 218c2ecf20Sopenharmony_ci#include <stddef.h> 228c2ecf20Sopenharmony_ci#include <string.h> 238c2ecf20Sopenharmony_ci#include <stdio.h> 248c2ecf20Sopenharmony_ci#include <unistd.h> 258c2ecf20Sopenharmony_ci#include <elf.h> 268c2ecf20Sopenharmony_ci#include <errno.h> 278c2ecf20Sopenharmony_ci#include <signal.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "../kselftest.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic volatile uint8_t var[96] __attribute__((__aligned__(32))); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void child(int size, int wr) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci volatile uint8_t *addr = &var[32 + wr]; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { 388c2ecf20Sopenharmony_ci ksft_print_msg( 398c2ecf20Sopenharmony_ci "ptrace(PTRACE_TRACEME) failed: %s\n", 408c2ecf20Sopenharmony_ci strerror(errno)); 418c2ecf20Sopenharmony_ci _exit(1); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (raise(SIGSTOP) != 0) { 458c2ecf20Sopenharmony_ci ksft_print_msg( 468c2ecf20Sopenharmony_ci "raise(SIGSTOP) failed: %s\n", strerror(errno)); 478c2ecf20Sopenharmony_ci _exit(1); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if ((uintptr_t) addr % size) { 518c2ecf20Sopenharmony_ci ksft_print_msg( 528c2ecf20Sopenharmony_ci "Wrong address write for the given size: %s\n", 538c2ecf20Sopenharmony_ci strerror(errno)); 548c2ecf20Sopenharmony_ci _exit(1); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci switch (size) { 588c2ecf20Sopenharmony_ci case 1: 598c2ecf20Sopenharmony_ci *addr = 47; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case 2: 628c2ecf20Sopenharmony_ci *(uint16_t *)addr = 47; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci case 4: 658c2ecf20Sopenharmony_ci *(uint32_t *)addr = 47; 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case 8: 688c2ecf20Sopenharmony_ci *(uint64_t *)addr = 47; 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci case 16: 718c2ecf20Sopenharmony_ci __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0])); 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case 32: 748c2ecf20Sopenharmony_ci __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0])); 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci _exit(0); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic bool set_watchpoint(pid_t pid, int size, int wp) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci const volatile uint8_t *addr = &var[32 + wp]; 848c2ecf20Sopenharmony_ci const int offset = (uintptr_t)addr % 8; 858c2ecf20Sopenharmony_ci const unsigned int byte_mask = ((1 << size) - 1) << offset; 868c2ecf20Sopenharmony_ci const unsigned int type = 2; /* Write */ 878c2ecf20Sopenharmony_ci const unsigned int enable = 1; 888c2ecf20Sopenharmony_ci const unsigned int control = byte_mask << 5 | type << 3 | enable; 898c2ecf20Sopenharmony_ci struct user_hwdebug_state dreg_state; 908c2ecf20Sopenharmony_ci struct iovec iov; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci memset(&dreg_state, 0, sizeof(dreg_state)); 938c2ecf20Sopenharmony_ci dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset); 948c2ecf20Sopenharmony_ci dreg_state.dbg_regs[0].ctrl = control; 958c2ecf20Sopenharmony_ci iov.iov_base = &dreg_state; 968c2ecf20Sopenharmony_ci iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + 978c2ecf20Sopenharmony_ci sizeof(dreg_state.dbg_regs[0]); 988c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) 998c2ecf20Sopenharmony_ci return true; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (errno == EIO) 1028c2ecf20Sopenharmony_ci ksft_print_msg( 1038c2ecf20Sopenharmony_ci "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", 1048c2ecf20Sopenharmony_ci strerror(errno)); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ksft_print_msg( 1078c2ecf20Sopenharmony_ci "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", 1088c2ecf20Sopenharmony_ci strerror(errno)); 1098c2ecf20Sopenharmony_ci return false; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic bool run_test(int wr_size, int wp_size, int wr, int wp) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int status; 1158c2ecf20Sopenharmony_ci siginfo_t siginfo; 1168c2ecf20Sopenharmony_ci pid_t pid = fork(); 1178c2ecf20Sopenharmony_ci pid_t wpid; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (pid < 0) { 1208c2ecf20Sopenharmony_ci ksft_test_result_fail( 1218c2ecf20Sopenharmony_ci "fork() failed: %s\n", strerror(errno)); 1228c2ecf20Sopenharmony_ci return false; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci if (pid == 0) 1258c2ecf20Sopenharmony_ci child(wr_size, wr); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci wpid = waitpid(pid, &status, __WALL); 1288c2ecf20Sopenharmony_ci if (wpid != pid) { 1298c2ecf20Sopenharmony_ci ksft_print_msg( 1308c2ecf20Sopenharmony_ci "waitpid() failed: %s\n", strerror(errno)); 1318c2ecf20Sopenharmony_ci return false; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (!WIFSTOPPED(status)) { 1348c2ecf20Sopenharmony_ci ksft_print_msg( 1358c2ecf20Sopenharmony_ci "child did not stop: %s\n", strerror(errno)); 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci if (WSTOPSIG(status) != SIGSTOP) { 1398c2ecf20Sopenharmony_ci ksft_print_msg("child did not stop with SIGSTOP\n"); 1408c2ecf20Sopenharmony_ci return false; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!set_watchpoint(pid, wp_size, wp)) 1448c2ecf20Sopenharmony_ci return false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { 1478c2ecf20Sopenharmony_ci ksft_print_msg( 1488c2ecf20Sopenharmony_ci "ptrace(PTRACE_SINGLESTEP) failed: %s\n", 1498c2ecf20Sopenharmony_ci strerror(errno)); 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci alarm(3); 1548c2ecf20Sopenharmony_ci wpid = waitpid(pid, &status, __WALL); 1558c2ecf20Sopenharmony_ci if (wpid != pid) { 1568c2ecf20Sopenharmony_ci ksft_print_msg( 1578c2ecf20Sopenharmony_ci "waitpid() failed: %s\n", strerror(errno)); 1588c2ecf20Sopenharmony_ci return false; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci alarm(0); 1618c2ecf20Sopenharmony_ci if (WIFEXITED(status)) { 1628c2ecf20Sopenharmony_ci ksft_print_msg("child did not single-step\n"); 1638c2ecf20Sopenharmony_ci return false; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci if (!WIFSTOPPED(status)) { 1668c2ecf20Sopenharmony_ci ksft_print_msg("child did not stop\n"); 1678c2ecf20Sopenharmony_ci return false; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci if (WSTOPSIG(status) != SIGTRAP) { 1708c2ecf20Sopenharmony_ci ksft_print_msg("child did not stop with SIGTRAP\n"); 1718c2ecf20Sopenharmony_ci return false; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { 1748c2ecf20Sopenharmony_ci ksft_print_msg( 1758c2ecf20Sopenharmony_ci "ptrace(PTRACE_GETSIGINFO): %s\n", 1768c2ecf20Sopenharmony_ci strerror(errno)); 1778c2ecf20Sopenharmony_ci return false; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci if (siginfo.si_code != TRAP_HWBKPT) { 1808c2ecf20Sopenharmony_ci ksft_print_msg( 1818c2ecf20Sopenharmony_ci "Unexpected si_code %d\n", siginfo.si_code); 1828c2ecf20Sopenharmony_ci return false; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci kill(pid, SIGKILL); 1868c2ecf20Sopenharmony_ci wpid = waitpid(pid, &status, 0); 1878c2ecf20Sopenharmony_ci if (wpid != pid) { 1888c2ecf20Sopenharmony_ci ksft_print_msg( 1898c2ecf20Sopenharmony_ci "waitpid() failed: %s\n", strerror(errno)); 1908c2ecf20Sopenharmony_ci return false; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci return true; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void sigalrm(int sig) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciint main(int argc, char **argv) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci int opt; 2028c2ecf20Sopenharmony_ci bool succeeded = true; 2038c2ecf20Sopenharmony_ci struct sigaction act; 2048c2ecf20Sopenharmony_ci int wr, wp, size; 2058c2ecf20Sopenharmony_ci bool result; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ksft_print_header(); 2088c2ecf20Sopenharmony_ci ksft_set_plan(213); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci act.sa_handler = sigalrm; 2118c2ecf20Sopenharmony_ci sigemptyset(&act.sa_mask); 2128c2ecf20Sopenharmony_ci act.sa_flags = 0; 2138c2ecf20Sopenharmony_ci sigaction(SIGALRM, &act, NULL); 2148c2ecf20Sopenharmony_ci for (size = 1; size <= 32; size = size*2) { 2158c2ecf20Sopenharmony_ci for (wr = 0; wr <= 32; wr = wr + size) { 2168c2ecf20Sopenharmony_ci for (wp = wr - size; wp <= wr + size; wp = wp + size) { 2178c2ecf20Sopenharmony_ci result = run_test(size, MIN(size, 8), wr, wp); 2188c2ecf20Sopenharmony_ci if ((result && wr == wp) || 2198c2ecf20Sopenharmony_ci (!result && wr != wp)) 2208c2ecf20Sopenharmony_ci ksft_test_result_pass( 2218c2ecf20Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = %d\n", 2228c2ecf20Sopenharmony_ci size, wr, wp); 2238c2ecf20Sopenharmony_ci else { 2248c2ecf20Sopenharmony_ci ksft_test_result_fail( 2258c2ecf20Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = %d\n", 2268c2ecf20Sopenharmony_ci size, wr, wp); 2278c2ecf20Sopenharmony_ci succeeded = false; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci for (size = 1; size <= 32; size = size*2) { 2348c2ecf20Sopenharmony_ci if (run_test(size, 8, -size, -8)) 2358c2ecf20Sopenharmony_ci ksft_test_result_pass( 2368c2ecf20Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = -8\n", 2378c2ecf20Sopenharmony_ci size, -size); 2388c2ecf20Sopenharmony_ci else { 2398c2ecf20Sopenharmony_ci ksft_test_result_fail( 2408c2ecf20Sopenharmony_ci "Test size = %d write offset = %d watchpoint offset = -8\n", 2418c2ecf20Sopenharmony_ci size, -size); 2428c2ecf20Sopenharmony_ci succeeded = false; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (succeeded) 2478c2ecf20Sopenharmony_ci ksft_exit_pass(); 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci ksft_exit_fail(); 2508c2ecf20Sopenharmony_ci} 251