18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Ptrace test for hw breakpoints 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on tools/testing/selftests/breakpoints/breakpoint_test.c 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This test forks and the parent then traces the child doing various 98c2ecf20Sopenharmony_ci * types of ptrace enabled breakpoints 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2018 Michael Neuling, IBM Corporation. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <sys/ptrace.h> 158c2ecf20Sopenharmony_ci#include <unistd.h> 168c2ecf20Sopenharmony_ci#include <stddef.h> 178c2ecf20Sopenharmony_ci#include <sys/user.h> 188c2ecf20Sopenharmony_ci#include <stdio.h> 198c2ecf20Sopenharmony_ci#include <stdlib.h> 208c2ecf20Sopenharmony_ci#include <signal.h> 218c2ecf20Sopenharmony_ci#include <sys/types.h> 228c2ecf20Sopenharmony_ci#include <sys/wait.h> 238c2ecf20Sopenharmony_ci#include <sys/syscall.h> 248c2ecf20Sopenharmony_ci#include <linux/limits.h> 258c2ecf20Sopenharmony_ci#include "ptrace.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define SPRN_PVR 0x11F 288c2ecf20Sopenharmony_ci#define PVR_8xx 0x00500000 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cibool is_8xx; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Use volatile on all global var so that compiler doesn't 348c2ecf20Sopenharmony_ci * optimise their load/stores. Otherwise selftest can fail. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic volatile __u64 glvar; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define DAWR_MAX_LEN 512 398c2ecf20Sopenharmony_cistatic volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512))); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define A_LEN 6 428c2ecf20Sopenharmony_ci#define B_LEN 6 438c2ecf20Sopenharmony_cistruct gstruct { 448c2ecf20Sopenharmony_ci __u8 a[A_LEN]; /* double word aligned */ 458c2ecf20Sopenharmony_ci __u8 b[B_LEN]; /* double word unaligned */ 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_cistatic volatile struct gstruct gstruct __attribute__((aligned(512))); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic volatile char cwd[PATH_MAX] __attribute__((aligned(8))); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) { 548c2ecf20Sopenharmony_ci perror("Can't get breakpoint info"); 558c2ecf20Sopenharmony_ci exit(-1); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool dawr_present(struct ppc_debug_info *dbginfo) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void write_var(int len) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci __u8 *pcvar; 678c2ecf20Sopenharmony_ci __u16 *psvar; 688c2ecf20Sopenharmony_ci __u32 *pivar; 698c2ecf20Sopenharmony_ci __u64 *plvar; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci switch (len) { 728c2ecf20Sopenharmony_ci case 1: 738c2ecf20Sopenharmony_ci pcvar = (__u8 *)&glvar; 748c2ecf20Sopenharmony_ci *pcvar = 0xff; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci case 2: 778c2ecf20Sopenharmony_ci psvar = (__u16 *)&glvar; 788c2ecf20Sopenharmony_ci *psvar = 0xffff; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci case 4: 818c2ecf20Sopenharmony_ci pivar = (__u32 *)&glvar; 828c2ecf20Sopenharmony_ci *pivar = 0xffffffff; 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci case 8: 858c2ecf20Sopenharmony_ci plvar = (__u64 *)&glvar; 868c2ecf20Sopenharmony_ci *plvar = 0xffffffffffffffffLL; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void read_var(int len) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci __u8 cvar __attribute__((unused)); 948c2ecf20Sopenharmony_ci __u16 svar __attribute__((unused)); 958c2ecf20Sopenharmony_ci __u32 ivar __attribute__((unused)); 968c2ecf20Sopenharmony_ci __u64 lvar __attribute__((unused)); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci switch (len) { 998c2ecf20Sopenharmony_ci case 1: 1008c2ecf20Sopenharmony_ci cvar = (__u8)glvar; 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci case 2: 1038c2ecf20Sopenharmony_ci svar = (__u16)glvar; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case 4: 1068c2ecf20Sopenharmony_ci ivar = (__u32)glvar; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci case 8: 1098c2ecf20Sopenharmony_ci lvar = (__u64)glvar; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void test_workload(void) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci __u8 cvar __attribute__((unused)); 1178c2ecf20Sopenharmony_ci __u32 ivar __attribute__((unused)); 1188c2ecf20Sopenharmony_ci int len = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) { 1218c2ecf20Sopenharmony_ci perror("Child can't be traced?"); 1228c2ecf20Sopenharmony_ci exit(-1); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Wake up father so that it sets up the first test */ 1268c2ecf20Sopenharmony_ci kill(getpid(), SIGUSR1); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, WO test */ 1298c2ecf20Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) 1308c2ecf20Sopenharmony_ci write_var(len); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RO test */ 1338c2ecf20Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) 1348c2ecf20Sopenharmony_ci read_var(len); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RW test */ 1378c2ecf20Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 1388c2ecf20Sopenharmony_ci if (rand() % 2) 1398c2ecf20Sopenharmony_ci read_var(len); 1408c2ecf20Sopenharmony_ci else 1418c2ecf20Sopenharmony_ci write_var(len); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */ 1458c2ecf20Sopenharmony_ci syscall(__NR_getcwd, &cwd, PATH_MAX); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ 1488c2ecf20Sopenharmony_ci write_var(1); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ 1518c2ecf20Sopenharmony_ci read_var(1); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ 1548c2ecf20Sopenharmony_ci if (rand() % 2) 1558c2ecf20Sopenharmony_ci write_var(1); 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci read_var(1); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */ 1608c2ecf20Sopenharmony_ci syscall(__NR_getcwd, &cwd, PATH_MAX); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ 1638c2ecf20Sopenharmony_ci gstruct.a[rand() % A_LEN] = 'a'; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ 1668c2ecf20Sopenharmony_ci cvar = gstruct.a[rand() % A_LEN]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ 1698c2ecf20Sopenharmony_ci if (rand() % 2) 1708c2ecf20Sopenharmony_ci gstruct.a[rand() % A_LEN] = 'a'; 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci cvar = gstruct.a[rand() % A_LEN]; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ 1758c2ecf20Sopenharmony_ci gstruct.b[rand() % B_LEN] = 'b'; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ 1788c2ecf20Sopenharmony_ci cvar = gstruct.b[rand() % B_LEN]; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ 1818c2ecf20Sopenharmony_ci if (rand() % 2) 1828c2ecf20Sopenharmony_ci gstruct.b[rand() % B_LEN] = 'b'; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci cvar = gstruct.b[rand() % B_LEN]; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ 1878c2ecf20Sopenharmony_ci if (rand() % 2) 1888c2ecf20Sopenharmony_ci *((int *)(gstruct.a + 4)) = 10; 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci ivar = *((int *)(gstruct.a + 4)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */ 1938c2ecf20Sopenharmony_ci if (rand() % 2) 1948c2ecf20Sopenharmony_ci big_var[rand() % DAWR_MAX_LEN] = 'a'; 1958c2ecf20Sopenharmony_ci else 1968c2ecf20Sopenharmony_ci cvar = big_var[rand() % DAWR_MAX_LEN]; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void check_success(pid_t child_pid, const char *name, const char *type, 2008c2ecf20Sopenharmony_ci unsigned long saddr, int len) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int status; 2038c2ecf20Sopenharmony_ci siginfo_t siginfo; 2048c2ecf20Sopenharmony_ci unsigned long eaddr = (saddr + len - 1) | 0x7; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci saddr &= ~0x7; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Wait for the child to SIGTRAP */ 2098c2ecf20Sopenharmony_ci wait(&status); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP || 2148c2ecf20Sopenharmony_ci (unsigned long)siginfo.si_addr < saddr || 2158c2ecf20Sopenharmony_ci (unsigned long)siginfo.si_addr > eaddr) { 2168c2ecf20Sopenharmony_ci printf("%s, %s, len: %d: Fail\n", name, type, len); 2178c2ecf20Sopenharmony_ci exit(-1); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci printf("%s, %s, len: %d: Ok\n", name, type, len); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (!is_8xx) { 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * For ptrace registered watchpoint, signal is generated 2258c2ecf20Sopenharmony_ci * before executing load/store. Singlestep the instruction 2268c2ecf20Sopenharmony_ci * and then continue the test. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0); 2298c2ecf20Sopenharmony_ci wait(NULL); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) { 2368c2ecf20Sopenharmony_ci perror("PTRACE_SET_DEBUGREG failed"); 2378c2ecf20Sopenharmony_ci exit(-1); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (wh <= 0) { 2468c2ecf20Sopenharmony_ci perror("PPC_PTRACE_SETHWDEBUG failed"); 2478c2ecf20Sopenharmony_ci exit(-1); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci return wh; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void ptrace_delhwdebug(pid_t child_pid, int wh) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) { 2558c2ecf20Sopenharmony_ci perror("PPC_PTRACE_DELHWDEBUG failed"); 2568c2ecf20Sopenharmony_ci exit(-1); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#define DABR_READ_SHIFT 0 2618c2ecf20Sopenharmony_ci#define DABR_WRITE_SHIFT 1 2628c2ecf20Sopenharmony_ci#define DABR_TRANSLATION_SHIFT 2 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int test_set_debugreg(pid_t child_pid) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci unsigned long wp_addr = (unsigned long)&glvar; 2678c2ecf20Sopenharmony_ci char *name = "PTRACE_SET_DEBUGREG"; 2688c2ecf20Sopenharmony_ci int len; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, WO test*/ 2718c2ecf20Sopenharmony_ci wp_addr &= ~0x7UL; 2728c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_WRITE_SHIFT); 2738c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 2748c2ecf20Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 2758c2ecf20Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 2768c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 2778c2ecf20Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RO test */ 2818c2ecf20Sopenharmony_ci wp_addr &= ~0x7UL; 2828c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_READ_SHIFT); 2838c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 2848c2ecf20Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 2858c2ecf20Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 2868c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 2878c2ecf20Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RW test */ 2918c2ecf20Sopenharmony_ci wp_addr &= ~0x7UL; 2928c2ecf20Sopenharmony_ci wp_addr |= (1Ul << DABR_READ_SHIFT); 2938c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_WRITE_SHIFT); 2948c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 2958c2ecf20Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 2968c2ecf20Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 2978c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 2988c2ecf20Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ptrace_set_debugreg(child_pid, 0); 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int test_set_debugreg_kernel_userspace(pid_t child_pid) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci unsigned long wp_addr = (unsigned long)cwd; 3088c2ecf20Sopenharmony_ci char *name = "PTRACE_SET_DEBUGREG"; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */ 3118c2ecf20Sopenharmony_ci wp_addr &= ~0x7UL; 3128c2ecf20Sopenharmony_ci wp_addr |= (1Ul << DABR_READ_SHIFT); 3138c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_WRITE_SHIFT); 3148c2ecf20Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 3158c2ecf20Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 3168c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 3178c2ecf20Sopenharmony_ci check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ptrace_set_debugreg(child_pid, 0); 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type, 3248c2ecf20Sopenharmony_ci unsigned long addr, int len) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci info->version = 1; 3278c2ecf20Sopenharmony_ci info->trigger_type = type; 3288c2ecf20Sopenharmony_ci info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; 3298c2ecf20Sopenharmony_ci info->addr = (__u64)addr; 3308c2ecf20Sopenharmony_ci info->addr2 = (__u64)addr + len; 3318c2ecf20Sopenharmony_ci info->condition_value = 0; 3328c2ecf20Sopenharmony_ci if (!len) 3338c2ecf20Sopenharmony_ci info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void test_sethwdebug_exact(pid_t child_pid) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct ppc_hw_breakpoint info; 3418c2ecf20Sopenharmony_ci unsigned long wp_addr = (unsigned long)&glvar; 3428c2ecf20Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; 3438c2ecf20Sopenharmony_ci int len = 1; /* hardcoded in kernel */ 3448c2ecf20Sopenharmony_ci int wh; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ 3478c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); 3488c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 3498c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 3508c2ecf20Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 3518c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ 3548c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0); 3558c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 3568c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 3578c2ecf20Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 3588c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ 3618c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0); 3628c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 3638c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 3648c2ecf20Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 3658c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void test_sethwdebug_exact_kernel_userspace(pid_t child_pid) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct ppc_hw_breakpoint info; 3718c2ecf20Sopenharmony_ci unsigned long wp_addr = (unsigned long)&cwd; 3728c2ecf20Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; 3738c2ecf20Sopenharmony_ci int len = 1; /* hardcoded in kernel */ 3748c2ecf20Sopenharmony_ci int wh; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */ 3778c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); 3788c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 3798c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 3808c2ecf20Sopenharmony_ci check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len); 3818c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void test_sethwdebug_range_aligned(pid_t child_pid) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct ppc_hw_breakpoint info; 3878c2ecf20Sopenharmony_ci unsigned long wp_addr; 3888c2ecf20Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED"; 3898c2ecf20Sopenharmony_ci int len; 3908c2ecf20Sopenharmony_ci int wh; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ 3938c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.a; 3948c2ecf20Sopenharmony_ci len = A_LEN; 3958c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); 3968c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 3978c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 3988c2ecf20Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 3998c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ 4028c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.a; 4038c2ecf20Sopenharmony_ci len = A_LEN; 4048c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); 4058c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4068c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4078c2ecf20Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 4088c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ 4118c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.a; 4128c2ecf20Sopenharmony_ci len = A_LEN; 4138c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); 4148c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4158c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4168c2ecf20Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 4178c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void test_sethwdebug_range_unaligned(pid_t child_pid) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct ppc_hw_breakpoint info; 4238c2ecf20Sopenharmony_ci unsigned long wp_addr; 4248c2ecf20Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED"; 4258c2ecf20Sopenharmony_ci int len; 4268c2ecf20Sopenharmony_ci int wh; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ 4298c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 4308c2ecf20Sopenharmony_ci len = B_LEN; 4318c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); 4328c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4338c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4348c2ecf20Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 4358c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ 4388c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 4398c2ecf20Sopenharmony_ci len = B_LEN; 4408c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); 4418c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4428c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4438c2ecf20Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 4448c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ 4478c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 4488c2ecf20Sopenharmony_ci len = B_LEN; 4498c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); 4508c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4518c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4528c2ecf20Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 4538c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void test_sethwdebug_range_unaligned_dar(pid_t child_pid) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct ppc_hw_breakpoint info; 4608c2ecf20Sopenharmony_ci unsigned long wp_addr; 4618c2ecf20Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE"; 4628c2ecf20Sopenharmony_ci int len; 4638c2ecf20Sopenharmony_ci int wh; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ 4668c2ecf20Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 4678c2ecf20Sopenharmony_ci len = B_LEN; 4688c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); 4698c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4708c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4718c2ecf20Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 4728c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void test_sethwdebug_dawr_max_range(pid_t child_pid) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct ppc_hw_breakpoint info; 4788c2ecf20Sopenharmony_ci unsigned long wp_addr; 4798c2ecf20Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN"; 4808c2ecf20Sopenharmony_ci int len; 4818c2ecf20Sopenharmony_ci int wh; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */ 4848c2ecf20Sopenharmony_ci wp_addr = (unsigned long)big_var; 4858c2ecf20Sopenharmony_ci len = DAWR_MAX_LEN; 4868c2ecf20Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); 4878c2ecf20Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 4888c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 4898c2ecf20Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 4908c2ecf20Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* Set the breakpoints and check the child successfully trigger them */ 4948c2ecf20Sopenharmony_cistatic void 4958c2ecf20Sopenharmony_cirun_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci test_set_debugreg(child_pid); 4988c2ecf20Sopenharmony_ci test_set_debugreg_kernel_userspace(child_pid); 4998c2ecf20Sopenharmony_ci test_sethwdebug_exact(child_pid); 5008c2ecf20Sopenharmony_ci test_sethwdebug_exact_kernel_userspace(child_pid); 5018c2ecf20Sopenharmony_ci if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) { 5028c2ecf20Sopenharmony_ci test_sethwdebug_range_aligned(child_pid); 5038c2ecf20Sopenharmony_ci if (dawr || is_8xx) { 5048c2ecf20Sopenharmony_ci test_sethwdebug_range_unaligned(child_pid); 5058c2ecf20Sopenharmony_ci test_sethwdebug_range_unaligned_dar(child_pid); 5068c2ecf20Sopenharmony_ci test_sethwdebug_dawr_max_range(child_pid); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int ptrace_hwbreak(void) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci pid_t child_pid; 5148c2ecf20Sopenharmony_ci struct ppc_debug_info dbginfo; 5158c2ecf20Sopenharmony_ci bool dawr; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci child_pid = fork(); 5188c2ecf20Sopenharmony_ci if (!child_pid) { 5198c2ecf20Sopenharmony_ci test_workload(); 5208c2ecf20Sopenharmony_ci return 0; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci wait(NULL); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci get_dbginfo(child_pid, &dbginfo); 5268c2ecf20Sopenharmony_ci SKIP_IF(dbginfo.num_data_bps == 0); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci dawr = dawr_present(&dbginfo); 5298c2ecf20Sopenharmony_ci run_tests(child_pid, &dbginfo, dawr); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Let the child exit first. */ 5328c2ecf20Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 5338c2ecf20Sopenharmony_ci wait(NULL); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* 5368c2ecf20Sopenharmony_ci * Testcases exits immediately with -1 on any failure. If 5378c2ecf20Sopenharmony_ci * it has reached here, it means all tests were successful. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci return TEST_PASS; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciint main(int argc, char **argv, char **envp) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci int pvr = 0; 5458c2ecf20Sopenharmony_ci asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR)); 5468c2ecf20Sopenharmony_ci if (pvr == PVR_8xx) 5478c2ecf20Sopenharmony_ci is_8xx = true; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return test_harness(ptrace_hwbreak, "ptrace-hwbreak"); 5508c2ecf20Sopenharmony_ci} 551