123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Ptrace test for hw breakpoints
- *
- * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
- *
- * This test forks and the parent then traces the child doing various
- * types of ptrace enabled breakpoints
- *
- * Copyright (C) 2018 Michael Neuling, IBM Corporation.
- */
- #include <sys/ptrace.h>
- #include <unistd.h>
- #include <stddef.h>
- #include <sys/user.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/syscall.h>
- #include <linux/limits.h>
- #include "ptrace.h"
- #define SPRN_PVR 0x11F
- #define PVR_8xx 0x00500000
- bool is_8xx;
- /*
- * Use volatile on all global var so that compiler doesn't
- * optimise their load/stores. Otherwise selftest can fail.
- */
- static volatile __u64 glvar;
- #define DAWR_MAX_LEN 512
- static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
- #define A_LEN 6
- #define B_LEN 6
- struct gstruct {
- __u8 a[A_LEN]; /* double word aligned */
- __u8 b[B_LEN]; /* double word unaligned */
- };
- static volatile struct gstruct gstruct __attribute__((aligned(512)));
- static volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
- static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
- {
- if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
- perror("Can't get breakpoint info");
- exit(-1);
- }
- }
- static bool dawr_present(struct ppc_debug_info *dbginfo)
- {
- return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
- }
- static void write_var(int len)
- {
- __u8 *pcvar;
- __u16 *psvar;
- __u32 *pivar;
- __u64 *plvar;
- switch (len) {
- case 1:
- pcvar = (__u8 *)&glvar;
- *pcvar = 0xff;
- break;
- case 2:
- psvar = (__u16 *)&glvar;
- *psvar = 0xffff;
- break;
- case 4:
- pivar = (__u32 *)&glvar;
- *pivar = 0xffffffff;
- break;
- case 8:
- plvar = (__u64 *)&glvar;
- *plvar = 0xffffffffffffffffLL;
- break;
- }
- }
- static void read_var(int len)
- {
- __u8 cvar __attribute__((unused));
- __u16 svar __attribute__((unused));
- __u32 ivar __attribute__((unused));
- __u64 lvar __attribute__((unused));
- switch (len) {
- case 1:
- cvar = (__u8)glvar;
- break;
- case 2:
- svar = (__u16)glvar;
- break;
- case 4:
- ivar = (__u32)glvar;
- break;
- case 8:
- lvar = (__u64)glvar;
- break;
- }
- }
- static void test_workload(void)
- {
- __u8 cvar __attribute__((unused));
- __u32 ivar __attribute__((unused));
- int len = 0;
- if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
- perror("Child can't be traced?");
- exit(-1);
- }
- /* Wake up father so that it sets up the first test */
- kill(getpid(), SIGUSR1);
- /* PTRACE_SET_DEBUGREG, WO test */
- for (len = 1; len <= sizeof(glvar); len <<= 1)
- write_var(len);
- /* PTRACE_SET_DEBUGREG, RO test */
- for (len = 1; len <= sizeof(glvar); len <<= 1)
- read_var(len);
- /* PTRACE_SET_DEBUGREG, RW test */
- for (len = 1; len <= sizeof(glvar); len <<= 1) {
- if (rand() % 2)
- read_var(len);
- else
- write_var(len);
- }
- /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
- syscall(__NR_getcwd, &cwd, PATH_MAX);
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
- write_var(1);
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
- read_var(1);
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
- if (rand() % 2)
- write_var(1);
- else
- read_var(1);
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
- syscall(__NR_getcwd, &cwd, PATH_MAX);
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
- gstruct.a[rand() % A_LEN] = 'a';
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
- cvar = gstruct.a[rand() % A_LEN];
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
- if (rand() % 2)
- gstruct.a[rand() % A_LEN] = 'a';
- else
- cvar = gstruct.a[rand() % A_LEN];
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
- gstruct.b[rand() % B_LEN] = 'b';
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
- cvar = gstruct.b[rand() % B_LEN];
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
- if (rand() % 2)
- gstruct.b[rand() % B_LEN] = 'b';
- else
- cvar = gstruct.b[rand() % B_LEN];
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
- if (rand() % 2)
- *((int *)(gstruct.a + 4)) = 10;
- else
- ivar = *((int *)(gstruct.a + 4));
- /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
- if (rand() % 2)
- big_var[rand() % DAWR_MAX_LEN] = 'a';
- else
- cvar = big_var[rand() % DAWR_MAX_LEN];
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
- gstruct.a[rand() % A_LEN] = 'a';
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
- cvar = gstruct.b[rand() % B_LEN];
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
- gstruct.a[rand() % A_LEN] = 'a';
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
- cvar = gstruct.a[rand() % A_LEN];
- }
- static void check_success(pid_t child_pid, const char *name, const char *type,
- unsigned long saddr, int len)
- {
- int status;
- siginfo_t siginfo;
- unsigned long eaddr = (saddr + len - 1) | 0x7;
- saddr &= ~0x7;
- /* Wait for the child to SIGTRAP */
- wait(&status);
- ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
- if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
- (unsigned long)siginfo.si_addr < saddr ||
- (unsigned long)siginfo.si_addr > eaddr) {
- printf("%s, %s, len: %d: Fail\n", name, type, len);
- exit(-1);
- }
- printf("%s, %s, len: %d: Ok\n", name, type, len);
- if (!is_8xx) {
- /*
- * For ptrace registered watchpoint, signal is generated
- * before executing load/store. Singlestep the instruction
- * and then continue the test.
- */
- ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
- wait(NULL);
- }
- }
- static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
- {
- if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
- perror("PTRACE_SET_DEBUGREG failed");
- exit(-1);
- }
- }
- static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
- {
- int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
- if (wh <= 0) {
- perror("PPC_PTRACE_SETHWDEBUG failed");
- exit(-1);
- }
- return wh;
- }
- static void ptrace_delhwdebug(pid_t child_pid, int wh)
- {
- if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
- perror("PPC_PTRACE_DELHWDEBUG failed");
- exit(-1);
- }
- }
- #define DABR_READ_SHIFT 0
- #define DABR_WRITE_SHIFT 1
- #define DABR_TRANSLATION_SHIFT 2
- static int test_set_debugreg(pid_t child_pid)
- {
- unsigned long wp_addr = (unsigned long)&glvar;
- char *name = "PTRACE_SET_DEBUGREG";
- int len;
- /* PTRACE_SET_DEBUGREG, WO test*/
- wp_addr &= ~0x7UL;
- wp_addr |= (1UL << DABR_WRITE_SHIFT);
- wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
- for (len = 1; len <= sizeof(glvar); len <<= 1) {
- ptrace_set_debugreg(child_pid, wp_addr);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "WO", wp_addr, len);
- }
- /* PTRACE_SET_DEBUGREG, RO test */
- wp_addr &= ~0x7UL;
- wp_addr |= (1UL << DABR_READ_SHIFT);
- wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
- for (len = 1; len <= sizeof(glvar); len <<= 1) {
- ptrace_set_debugreg(child_pid, wp_addr);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RO", wp_addr, len);
- }
- /* PTRACE_SET_DEBUGREG, RW test */
- wp_addr &= ~0x7UL;
- wp_addr |= (1Ul << DABR_READ_SHIFT);
- wp_addr |= (1UL << DABR_WRITE_SHIFT);
- wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
- for (len = 1; len <= sizeof(glvar); len <<= 1) {
- ptrace_set_debugreg(child_pid, wp_addr);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RW", wp_addr, len);
- }
- ptrace_set_debugreg(child_pid, 0);
- return 0;
- }
- static int test_set_debugreg_kernel_userspace(pid_t child_pid)
- {
- unsigned long wp_addr = (unsigned long)cwd;
- char *name = "PTRACE_SET_DEBUGREG";
- /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
- wp_addr &= ~0x7UL;
- wp_addr |= (1Ul << DABR_READ_SHIFT);
- wp_addr |= (1UL << DABR_WRITE_SHIFT);
- wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
- ptrace_set_debugreg(child_pid, wp_addr);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8);
- ptrace_set_debugreg(child_pid, 0);
- return 0;
- }
- static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
- unsigned long addr, int len)
- {
- info->version = 1;
- info->trigger_type = type;
- info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
- info->addr = (__u64)addr;
- info->addr2 = (__u64)addr + len;
- info->condition_value = 0;
- if (!len)
- info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
- else
- info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
- }
- static void test_sethwdebug_exact(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info;
- unsigned long wp_addr = (unsigned long)&glvar;
- char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
- int len = 1; /* hardcoded in kernel */
- int wh;
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "WO", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RO", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RW", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- }
- static void test_sethwdebug_exact_kernel_userspace(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info;
- unsigned long wp_addr = (unsigned long)&cwd;
- char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
- int len = 1; /* hardcoded in kernel */
- int wh;
- /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- }
- static void test_sethwdebug_range_aligned(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info;
- unsigned long wp_addr;
- char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
- int len;
- int wh;
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
- wp_addr = (unsigned long)&gstruct.a;
- len = A_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "WO", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
- wp_addr = (unsigned long)&gstruct.a;
- len = A_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RO", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
- wp_addr = (unsigned long)&gstruct.a;
- len = A_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RW", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- }
- static void test_multi_sethwdebug_range(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info1, info2;
- unsigned long wp_addr1, wp_addr2;
- char *name1 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED";
- char *name2 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED";
- int len1, len2;
- int wh1, wh2;
- wp_addr1 = (unsigned long)&gstruct.a;
- wp_addr2 = (unsigned long)&gstruct.b;
- len1 = A_LEN;
- len2 = B_LEN;
- get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
- get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
- wh1 = ptrace_sethwdebug(child_pid, &info1);
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
- wh2 = ptrace_sethwdebug(child_pid, &info2);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name1, "WO", wp_addr1, len1);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name2, "RO", wp_addr2, len2);
- ptrace_delhwdebug(child_pid, wh1);
- ptrace_delhwdebug(child_pid, wh2);
- }
- static void test_multi_sethwdebug_range_dawr_overlap(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info1, info2;
- unsigned long wp_addr1, wp_addr2;
- char *name = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap";
- int len1, len2;
- int wh1, wh2;
- wp_addr1 = (unsigned long)&gstruct.a;
- wp_addr2 = (unsigned long)&gstruct.a;
- len1 = A_LEN;
- len2 = A_LEN;
- get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
- get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
- wh1 = ptrace_sethwdebug(child_pid, &info1);
- /* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
- wh2 = ptrace_sethwdebug(child_pid, &info2);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "WO", wp_addr1, len1);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RO", wp_addr2, len2);
- ptrace_delhwdebug(child_pid, wh1);
- ptrace_delhwdebug(child_pid, wh2);
- }
- static void test_sethwdebug_range_unaligned(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info;
- unsigned long wp_addr;
- char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
- int len;
- int wh;
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
- wp_addr = (unsigned long)&gstruct.b;
- len = B_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "WO", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
- wp_addr = (unsigned long)&gstruct.b;
- len = B_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RO", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
- wp_addr = (unsigned long)&gstruct.b;
- len = B_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RW", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- }
- static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info;
- unsigned long wp_addr;
- char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
- int len;
- int wh;
- /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
- wp_addr = (unsigned long)&gstruct.b;
- len = B_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RW", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- }
- static void test_sethwdebug_dawr_max_range(pid_t child_pid)
- {
- struct ppc_hw_breakpoint info;
- unsigned long wp_addr;
- char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
- int len;
- int wh;
- /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
- wp_addr = (unsigned long)big_var;
- len = DAWR_MAX_LEN;
- get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
- wh = ptrace_sethwdebug(child_pid, &info);
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- check_success(child_pid, name, "RW", wp_addr, len);
- ptrace_delhwdebug(child_pid, wh);
- }
- /* Set the breakpoints and check the child successfully trigger them */
- static void
- run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
- {
- test_set_debugreg(child_pid);
- test_set_debugreg_kernel_userspace(child_pid);
- test_sethwdebug_exact(child_pid);
- test_sethwdebug_exact_kernel_userspace(child_pid);
- if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
- test_sethwdebug_range_aligned(child_pid);
- if (dawr || is_8xx) {
- test_sethwdebug_range_unaligned(child_pid);
- test_sethwdebug_range_unaligned_dar(child_pid);
- test_sethwdebug_dawr_max_range(child_pid);
- if (dbginfo->num_data_bps > 1) {
- test_multi_sethwdebug_range(child_pid);
- test_multi_sethwdebug_range_dawr_overlap(child_pid);
- }
- }
- }
- }
- static int ptrace_hwbreak(void)
- {
- pid_t child_pid;
- struct ppc_debug_info dbginfo;
- bool dawr;
- child_pid = fork();
- if (!child_pid) {
- test_workload();
- return 0;
- }
- wait(NULL);
- get_dbginfo(child_pid, &dbginfo);
- SKIP_IF(dbginfo.num_data_bps == 0);
- dawr = dawr_present(&dbginfo);
- run_tests(child_pid, &dbginfo, dawr);
- /* Let the child exit first. */
- ptrace(PTRACE_CONT, child_pid, NULL, 0);
- wait(NULL);
- /*
- * Testcases exits immediately with -1 on any failure. If
- * it has reached here, it means all tests were successful.
- */
- return TEST_PASS;
- }
- int main(int argc, char **argv, char **envp)
- {
- int pvr = 0;
- asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
- if (pvr == PVR_8xx)
- is_8xx = true;
- return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
- }
|