162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Ptrace test for hw breakpoints 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on tools/testing/selftests/breakpoints/breakpoint_test.c 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This test forks and the parent then traces the child doing various 962306a36Sopenharmony_ci * types of ptrace enabled breakpoints 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (C) 2018 Michael Neuling, IBM Corporation. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <sys/ptrace.h> 1562306a36Sopenharmony_ci#include <unistd.h> 1662306a36Sopenharmony_ci#include <stddef.h> 1762306a36Sopenharmony_ci#include <sys/user.h> 1862306a36Sopenharmony_ci#include <stdio.h> 1962306a36Sopenharmony_ci#include <stdlib.h> 2062306a36Sopenharmony_ci#include <signal.h> 2162306a36Sopenharmony_ci#include <sys/types.h> 2262306a36Sopenharmony_ci#include <sys/wait.h> 2362306a36Sopenharmony_ci#include <sys/syscall.h> 2462306a36Sopenharmony_ci#include <linux/limits.h> 2562306a36Sopenharmony_ci#include "ptrace.h" 2662306a36Sopenharmony_ci#include "reg.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define SPRN_PVR 0x11F 2962306a36Sopenharmony_ci#define PVR_8xx 0x00500000 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cibool is_8xx; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Use volatile on all global var so that compiler doesn't 3562306a36Sopenharmony_ci * optimise their load/stores. Otherwise selftest can fail. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic volatile __u64 glvar; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define DAWR_MAX_LEN 512 4062306a36Sopenharmony_cistatic volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512))); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define A_LEN 6 4362306a36Sopenharmony_ci#define B_LEN 6 4462306a36Sopenharmony_cistruct gstruct { 4562306a36Sopenharmony_ci __u8 a[A_LEN]; /* double word aligned */ 4662306a36Sopenharmony_ci __u8 b[B_LEN]; /* double word unaligned */ 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_cistatic volatile struct gstruct gstruct __attribute__((aligned(512))); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic volatile char cwd[PATH_MAX] __attribute__((aligned(8))); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) { 5562306a36Sopenharmony_ci perror("Can't get breakpoint info"); 5662306a36Sopenharmony_ci exit(-1); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic bool dawr_present(struct ppc_debug_info *dbginfo) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void write_var(int len) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci volatile __u8 *pcvar; 6862306a36Sopenharmony_ci volatile __u16 *psvar; 6962306a36Sopenharmony_ci volatile __u32 *pivar; 7062306a36Sopenharmony_ci volatile __u64 *plvar; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci switch (len) { 7362306a36Sopenharmony_ci case 1: 7462306a36Sopenharmony_ci pcvar = (volatile __u8 *)&glvar; 7562306a36Sopenharmony_ci *pcvar = 0xff; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case 2: 7862306a36Sopenharmony_ci psvar = (volatile __u16 *)&glvar; 7962306a36Sopenharmony_ci *psvar = 0xffff; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case 4: 8262306a36Sopenharmony_ci pivar = (volatile __u32 *)&glvar; 8362306a36Sopenharmony_ci *pivar = 0xffffffff; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case 8: 8662306a36Sopenharmony_ci plvar = (volatile __u64 *)&glvar; 8762306a36Sopenharmony_ci *plvar = 0xffffffffffffffffLL; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void read_var(int len) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci __u8 cvar __attribute__((unused)); 9562306a36Sopenharmony_ci __u16 svar __attribute__((unused)); 9662306a36Sopenharmony_ci __u32 ivar __attribute__((unused)); 9762306a36Sopenharmony_ci __u64 lvar __attribute__((unused)); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci switch (len) { 10062306a36Sopenharmony_ci case 1: 10162306a36Sopenharmony_ci cvar = (volatile __u8)glvar; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case 2: 10462306a36Sopenharmony_ci svar = (volatile __u16)glvar; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci case 4: 10762306a36Sopenharmony_ci ivar = (volatile __u32)glvar; 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci case 8: 11062306a36Sopenharmony_ci lvar = (volatile __u64)glvar; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void test_workload(void) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci __u8 cvar __attribute__((unused)); 11862306a36Sopenharmony_ci __u32 ivar __attribute__((unused)); 11962306a36Sopenharmony_ci int len = 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) { 12262306a36Sopenharmony_ci perror("Child can't be traced?"); 12362306a36Sopenharmony_ci exit(-1); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Wake up father so that it sets up the first test */ 12762306a36Sopenharmony_ci kill(getpid(), SIGUSR1); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, WO test */ 13062306a36Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) 13162306a36Sopenharmony_ci write_var(len); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RO test */ 13462306a36Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) 13562306a36Sopenharmony_ci read_var(len); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RW test */ 13862306a36Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 13962306a36Sopenharmony_ci if (rand() % 2) 14062306a36Sopenharmony_ci read_var(len); 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci write_var(len); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */ 14662306a36Sopenharmony_ci syscall(__NR_getcwd, &cwd, PATH_MAX); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ 14962306a36Sopenharmony_ci write_var(1); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ 15262306a36Sopenharmony_ci read_var(1); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ 15562306a36Sopenharmony_ci if (rand() % 2) 15662306a36Sopenharmony_ci write_var(1); 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci read_var(1); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */ 16162306a36Sopenharmony_ci syscall(__NR_getcwd, &cwd, PATH_MAX); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ 16462306a36Sopenharmony_ci gstruct.a[rand() % A_LEN] = 'a'; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ 16762306a36Sopenharmony_ci cvar = gstruct.a[rand() % A_LEN]; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ 17062306a36Sopenharmony_ci if (rand() % 2) 17162306a36Sopenharmony_ci gstruct.a[rand() % A_LEN] = 'a'; 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci cvar = gstruct.a[rand() % A_LEN]; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ 17662306a36Sopenharmony_ci gstruct.b[rand() % B_LEN] = 'b'; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ 17962306a36Sopenharmony_ci cvar = gstruct.b[rand() % B_LEN]; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ 18262306a36Sopenharmony_ci if (rand() % 2) 18362306a36Sopenharmony_ci gstruct.b[rand() % B_LEN] = 'b'; 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci cvar = gstruct.b[rand() % B_LEN]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ 18862306a36Sopenharmony_ci if (rand() % 2) 18962306a36Sopenharmony_ci *((int *)(gstruct.a + 4)) = 10; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci ivar = *((int *)(gstruct.a + 4)); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */ 19462306a36Sopenharmony_ci if (rand() % 2) 19562306a36Sopenharmony_ci big_var[rand() % DAWR_MAX_LEN] = 'a'; 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci cvar = big_var[rand() % DAWR_MAX_LEN]; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */ 20062306a36Sopenharmony_ci gstruct.a[rand() % A_LEN] = 'a'; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */ 20362306a36Sopenharmony_ci cvar = gstruct.b[rand() % B_LEN]; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */ 20662306a36Sopenharmony_ci gstruct.a[rand() % A_LEN] = 'a'; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */ 20962306a36Sopenharmony_ci cvar = gstruct.a[rand() % A_LEN]; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void check_success(pid_t child_pid, const char *name, const char *type, 21362306a36Sopenharmony_ci unsigned long saddr, int len) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int status; 21662306a36Sopenharmony_ci siginfo_t siginfo; 21762306a36Sopenharmony_ci unsigned long eaddr = (saddr + len - 1) | 0x7; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci saddr &= ~0x7; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Wait for the child to SIGTRAP */ 22262306a36Sopenharmony_ci wait(&status); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP || 22762306a36Sopenharmony_ci (unsigned long)siginfo.si_addr < saddr || 22862306a36Sopenharmony_ci (unsigned long)siginfo.si_addr > eaddr) { 22962306a36Sopenharmony_ci printf("%s, %s, len: %d: Fail\n", name, type, len); 23062306a36Sopenharmony_ci exit(-1); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci printf("%s, %s, len: %d: Ok\n", name, type, len); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!is_8xx) { 23662306a36Sopenharmony_ci /* 23762306a36Sopenharmony_ci * For ptrace registered watchpoint, signal is generated 23862306a36Sopenharmony_ci * before executing load/store. Singlestep the instruction 23962306a36Sopenharmony_ci * and then continue the test. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0); 24262306a36Sopenharmony_ci wait(NULL); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) { 24962306a36Sopenharmony_ci perror("PTRACE_SET_DEBUGREG failed"); 25062306a36Sopenharmony_ci exit(-1); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (wh <= 0) { 25962306a36Sopenharmony_ci perror("PPC_PTRACE_SETHWDEBUG failed"); 26062306a36Sopenharmony_ci exit(-1); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci return wh; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void ptrace_delhwdebug(pid_t child_pid, int wh) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) { 26862306a36Sopenharmony_ci perror("PPC_PTRACE_DELHWDEBUG failed"); 26962306a36Sopenharmony_ci exit(-1); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci#define DABR_READ_SHIFT 0 27462306a36Sopenharmony_ci#define DABR_WRITE_SHIFT 1 27562306a36Sopenharmony_ci#define DABR_TRANSLATION_SHIFT 2 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int test_set_debugreg(pid_t child_pid) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci unsigned long wp_addr = (unsigned long)&glvar; 28062306a36Sopenharmony_ci char *name = "PTRACE_SET_DEBUGREG"; 28162306a36Sopenharmony_ci int len; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, WO test*/ 28462306a36Sopenharmony_ci wp_addr &= ~0x7UL; 28562306a36Sopenharmony_ci wp_addr |= (1UL << DABR_WRITE_SHIFT); 28662306a36Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 28762306a36Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 28862306a36Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 28962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 29062306a36Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RO test */ 29462306a36Sopenharmony_ci wp_addr &= ~0x7UL; 29562306a36Sopenharmony_ci wp_addr |= (1UL << DABR_READ_SHIFT); 29662306a36Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 29762306a36Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 29862306a36Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 29962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 30062306a36Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, RW test */ 30462306a36Sopenharmony_ci wp_addr &= ~0x7UL; 30562306a36Sopenharmony_ci wp_addr |= (1Ul << DABR_READ_SHIFT); 30662306a36Sopenharmony_ci wp_addr |= (1UL << DABR_WRITE_SHIFT); 30762306a36Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 30862306a36Sopenharmony_ci for (len = 1; len <= sizeof(glvar); len <<= 1) { 30962306a36Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 31062306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 31162306a36Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ptrace_set_debugreg(child_pid, 0); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int test_set_debugreg_kernel_userspace(pid_t child_pid) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci unsigned long wp_addr = (unsigned long)cwd; 32162306a36Sopenharmony_ci char *name = "PTRACE_SET_DEBUGREG"; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */ 32462306a36Sopenharmony_ci wp_addr &= ~0x7UL; 32562306a36Sopenharmony_ci wp_addr |= (1Ul << DABR_READ_SHIFT); 32662306a36Sopenharmony_ci wp_addr |= (1UL << DABR_WRITE_SHIFT); 32762306a36Sopenharmony_ci wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); 32862306a36Sopenharmony_ci ptrace_set_debugreg(child_pid, wp_addr); 32962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 33062306a36Sopenharmony_ci check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci ptrace_set_debugreg(child_pid, 0); 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type, 33762306a36Sopenharmony_ci unsigned long addr, int len) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci info->version = 1; 34062306a36Sopenharmony_ci info->trigger_type = type; 34162306a36Sopenharmony_ci info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; 34262306a36Sopenharmony_ci info->addr = (__u64)addr; 34362306a36Sopenharmony_ci info->addr2 = (__u64)addr + len; 34462306a36Sopenharmony_ci info->condition_value = 0; 34562306a36Sopenharmony_ci if (!len) 34662306a36Sopenharmony_ci info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void test_sethwdebug_exact(pid_t child_pid) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct ppc_hw_breakpoint info; 35462306a36Sopenharmony_ci unsigned long wp_addr = (unsigned long)&glvar; 35562306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; 35662306a36Sopenharmony_ci int len = 1; /* hardcoded in kernel */ 35762306a36Sopenharmony_ci int wh; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ 36062306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); 36162306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 36262306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 36362306a36Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 36462306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ 36762306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0); 36862306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 36962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 37062306a36Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 37162306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ 37462306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0); 37562306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 37662306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 37762306a36Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 37862306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void test_sethwdebug_exact_kernel_userspace(pid_t child_pid) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct ppc_hw_breakpoint info; 38462306a36Sopenharmony_ci unsigned long wp_addr = (unsigned long)&cwd; 38562306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; 38662306a36Sopenharmony_ci int len = 1; /* hardcoded in kernel */ 38762306a36Sopenharmony_ci int wh; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */ 39062306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); 39162306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 39262306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 39362306a36Sopenharmony_ci check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len); 39462306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void test_sethwdebug_range_aligned(pid_t child_pid) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct ppc_hw_breakpoint info; 40062306a36Sopenharmony_ci unsigned long wp_addr; 40162306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED"; 40262306a36Sopenharmony_ci int len; 40362306a36Sopenharmony_ci int wh; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ 40662306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.a; 40762306a36Sopenharmony_ci len = A_LEN; 40862306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); 40962306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 41062306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 41162306a36Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 41262306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ 41562306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.a; 41662306a36Sopenharmony_ci len = A_LEN; 41762306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); 41862306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 41962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 42062306a36Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 42162306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ 42462306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.a; 42562306a36Sopenharmony_ci len = A_LEN; 42662306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); 42762306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 42862306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 42962306a36Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 43062306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void test_multi_sethwdebug_range(pid_t child_pid) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct ppc_hw_breakpoint info1, info2; 43662306a36Sopenharmony_ci unsigned long wp_addr1, wp_addr2; 43762306a36Sopenharmony_ci char *name1 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED"; 43862306a36Sopenharmony_ci char *name2 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED"; 43962306a36Sopenharmony_ci int len1, len2; 44062306a36Sopenharmony_ci int wh1, wh2; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci wp_addr1 = (unsigned long)&gstruct.a; 44362306a36Sopenharmony_ci wp_addr2 = (unsigned long)&gstruct.b; 44462306a36Sopenharmony_ci len1 = A_LEN; 44562306a36Sopenharmony_ci len2 = B_LEN; 44662306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1); 44762306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */ 45062306a36Sopenharmony_ci wh1 = ptrace_sethwdebug(child_pid, &info1); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */ 45362306a36Sopenharmony_ci wh2 = ptrace_sethwdebug(child_pid, &info2); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 45662306a36Sopenharmony_ci check_success(child_pid, name1, "WO", wp_addr1, len1); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 45962306a36Sopenharmony_ci check_success(child_pid, name2, "RO", wp_addr2, len2); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh1); 46262306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh2); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void test_multi_sethwdebug_range_dawr_overlap(pid_t child_pid) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct ppc_hw_breakpoint info1, info2; 46862306a36Sopenharmony_ci unsigned long wp_addr1, wp_addr2; 46962306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap"; 47062306a36Sopenharmony_ci int len1, len2; 47162306a36Sopenharmony_ci int wh1, wh2; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci wp_addr1 = (unsigned long)&gstruct.a; 47462306a36Sopenharmony_ci wp_addr2 = (unsigned long)&gstruct.a; 47562306a36Sopenharmony_ci len1 = A_LEN; 47662306a36Sopenharmony_ci len2 = A_LEN; 47762306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1); 47862306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */ 48162306a36Sopenharmony_ci wh1 = ptrace_sethwdebug(child_pid, &info1); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */ 48462306a36Sopenharmony_ci wh2 = ptrace_sethwdebug(child_pid, &info2); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 48762306a36Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr1, len1); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 49062306a36Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr2, len2); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh1); 49362306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh2); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void test_sethwdebug_range_unaligned(pid_t child_pid) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct ppc_hw_breakpoint info; 49962306a36Sopenharmony_ci unsigned long wp_addr; 50062306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED"; 50162306a36Sopenharmony_ci int len; 50262306a36Sopenharmony_ci int wh; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ 50562306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 50662306a36Sopenharmony_ci len = B_LEN; 50762306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); 50862306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 50962306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 51062306a36Sopenharmony_ci check_success(child_pid, name, "WO", wp_addr, len); 51162306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ 51462306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 51562306a36Sopenharmony_ci len = B_LEN; 51662306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); 51762306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 51862306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 51962306a36Sopenharmony_ci check_success(child_pid, name, "RO", wp_addr, len); 52062306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ 52362306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 52462306a36Sopenharmony_ci len = B_LEN; 52562306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); 52662306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 52762306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 52862306a36Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 52962306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void test_sethwdebug_range_unaligned_dar(pid_t child_pid) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct ppc_hw_breakpoint info; 53662306a36Sopenharmony_ci unsigned long wp_addr; 53762306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE"; 53862306a36Sopenharmony_ci int len; 53962306a36Sopenharmony_ci int wh; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ 54262306a36Sopenharmony_ci wp_addr = (unsigned long)&gstruct.b; 54362306a36Sopenharmony_ci len = B_LEN; 54462306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); 54562306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 54662306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 54762306a36Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 54862306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void test_sethwdebug_dawr_max_range(pid_t child_pid) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct ppc_hw_breakpoint info; 55462306a36Sopenharmony_ci unsigned long wp_addr; 55562306a36Sopenharmony_ci char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN"; 55662306a36Sopenharmony_ci int len; 55762306a36Sopenharmony_ci int wh; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */ 56062306a36Sopenharmony_ci wp_addr = (unsigned long)big_var; 56162306a36Sopenharmony_ci len = DAWR_MAX_LEN; 56262306a36Sopenharmony_ci get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); 56362306a36Sopenharmony_ci wh = ptrace_sethwdebug(child_pid, &info); 56462306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 56562306a36Sopenharmony_ci check_success(child_pid, name, "RW", wp_addr, len); 56662306a36Sopenharmony_ci ptrace_delhwdebug(child_pid, wh); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/* Set the breakpoints and check the child successfully trigger them */ 57062306a36Sopenharmony_cistatic void 57162306a36Sopenharmony_cirun_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci test_set_debugreg(child_pid); 57462306a36Sopenharmony_ci test_set_debugreg_kernel_userspace(child_pid); 57562306a36Sopenharmony_ci test_sethwdebug_exact(child_pid); 57662306a36Sopenharmony_ci test_sethwdebug_exact_kernel_userspace(child_pid); 57762306a36Sopenharmony_ci if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) { 57862306a36Sopenharmony_ci test_sethwdebug_range_aligned(child_pid); 57962306a36Sopenharmony_ci if (dawr || is_8xx) { 58062306a36Sopenharmony_ci test_sethwdebug_range_unaligned(child_pid); 58162306a36Sopenharmony_ci test_sethwdebug_range_unaligned_dar(child_pid); 58262306a36Sopenharmony_ci test_sethwdebug_dawr_max_range(child_pid); 58362306a36Sopenharmony_ci if (dbginfo->num_data_bps > 1) { 58462306a36Sopenharmony_ci test_multi_sethwdebug_range(child_pid); 58562306a36Sopenharmony_ci test_multi_sethwdebug_range_dawr_overlap(child_pid); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int ptrace_hwbreak(void) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci pid_t child_pid; 59462306a36Sopenharmony_ci struct ppc_debug_info dbginfo; 59562306a36Sopenharmony_ci bool dawr; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci child_pid = fork(); 59862306a36Sopenharmony_ci if (!child_pid) { 59962306a36Sopenharmony_ci test_workload(); 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci wait(NULL); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci get_dbginfo(child_pid, &dbginfo); 60662306a36Sopenharmony_ci SKIP_IF_MSG(dbginfo.num_data_bps == 0, "No data breakpoints present"); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci dawr = dawr_present(&dbginfo); 60962306a36Sopenharmony_ci run_tests(child_pid, &dbginfo, dawr); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Let the child exit first. */ 61262306a36Sopenharmony_ci ptrace(PTRACE_CONT, child_pid, NULL, 0); 61362306a36Sopenharmony_ci wait(NULL); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * Testcases exits immediately with -1 on any failure. If 61762306a36Sopenharmony_ci * it has reached here, it means all tests were successful. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci return TEST_PASS; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ciint main(int argc, char **argv, char **envp) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci is_8xx = mfspr(SPRN_PVR) == PVR_8xx; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return test_harness(ptrace_hwbreak, "ptrace-hwbreak"); 62762306a36Sopenharmony_ci} 628