root/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_dbginfo
  2. hwbreak_present
  3. dawr_present
  4. set_breakpoint_addr
  5. set_hwbreakpoint_addr
  6. del_hwbreakpoint_addr
  7. write_var
  8. read_var
  9. trigger_tests
  10. check_success
  11. launch_watchpoints
  12. launch_tests
  13. ptrace_hwbreak
  14. main

   1 // SPDX-License-Identifier: GPL-2.0+
   2 
   3 /*
   4  * Ptrace test for hw breakpoints
   5  *
   6  * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
   7  *
   8  * This test forks and the parent then traces the child doing various
   9  * types of ptrace enabled breakpoints
  10  *
  11  * Copyright (C) 2018 Michael Neuling, IBM Corporation.
  12  */
  13 
  14 #include <sys/ptrace.h>
  15 #include <unistd.h>
  16 #include <stddef.h>
  17 #include <sys/user.h>
  18 #include <stdio.h>
  19 #include <stdlib.h>
  20 #include <signal.h>
  21 #include <sys/types.h>
  22 #include <sys/wait.h>
  23 #include "ptrace.h"
  24 
  25 /* Breakpoint access modes */
  26 enum {
  27         BP_X = 1,
  28         BP_RW = 2,
  29         BP_W = 4,
  30 };
  31 
  32 static pid_t child_pid;
  33 static struct ppc_debug_info dbginfo;
  34 
  35 static void get_dbginfo(void)
  36 {
  37         int ret;
  38 
  39         ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
  40         if (ret) {
  41                 perror("Can't get breakpoint info\n");
  42                 exit(-1);
  43         }
  44 }
  45 
  46 static bool hwbreak_present(void)
  47 {
  48         return (dbginfo.num_data_bps != 0);
  49 }
  50 
  51 static bool dawr_present(void)
  52 {
  53         return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
  54 }
  55 
  56 static void set_breakpoint_addr(void *addr)
  57 {
  58         int ret;
  59 
  60         ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr);
  61         if (ret) {
  62                 perror("Can't set breakpoint addr\n");
  63                 exit(-1);
  64         }
  65 }
  66 
  67 static int set_hwbreakpoint_addr(void *addr, int range)
  68 {
  69         int ret;
  70 
  71         struct ppc_hw_breakpoint info;
  72 
  73         info.version = 1;
  74         info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW;
  75         info.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
  76         if (range > 0)
  77                 info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
  78         info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
  79         info.addr = (__u64)addr;
  80         info.addr2 = (__u64)addr + range;
  81         info.condition_value = 0;
  82 
  83         ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
  84         if (ret < 0) {
  85                 perror("Can't set breakpoint\n");
  86                 exit(-1);
  87         }
  88         return ret;
  89 }
  90 
  91 static int del_hwbreakpoint_addr(int watchpoint_handle)
  92 {
  93         int ret;
  94 
  95         ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle);
  96         if (ret < 0) {
  97                 perror("Can't delete hw breakpoint\n");
  98                 exit(-1);
  99         }
 100         return ret;
 101 }
 102 
 103 #define DAWR_LENGTH_MAX 512
 104 
 105 /* Dummy variables to test read/write accesses */
 106 static unsigned long long
 107         dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
 108         __attribute__((aligned(512)));
 109 static unsigned long long *dummy_var = dummy_array;
 110 
 111 static void write_var(int len)
 112 {
 113         long long *plval;
 114         char *pcval;
 115         short *psval;
 116         int *pival;
 117 
 118         switch (len) {
 119         case 1:
 120                 pcval = (char *)dummy_var;
 121                 *pcval = 0xff;
 122                 break;
 123         case 2:
 124                 psval = (short *)dummy_var;
 125                 *psval = 0xffff;
 126                 break;
 127         case 4:
 128                 pival = (int *)dummy_var;
 129                 *pival = 0xffffffff;
 130                 break;
 131         case 8:
 132                 plval = (long long *)dummy_var;
 133                 *plval = 0xffffffffffffffffLL;
 134                 break;
 135         }
 136 }
 137 
 138 static void read_var(int len)
 139 {
 140         char cval __attribute__((unused));
 141         short sval __attribute__((unused));
 142         int ival __attribute__((unused));
 143         long long lval __attribute__((unused));
 144 
 145         switch (len) {
 146         case 1:
 147                 cval = *(char *)dummy_var;
 148                 break;
 149         case 2:
 150                 sval = *(short *)dummy_var;
 151                 break;
 152         case 4:
 153                 ival = *(int *)dummy_var;
 154                 break;
 155         case 8:
 156                 lval = *(long long *)dummy_var;
 157                 break;
 158         }
 159 }
 160 
 161 /*
 162  * Do the r/w accesses to trigger the breakpoints. And run
 163  * the usual traps.
 164  */
 165 static void trigger_tests(void)
 166 {
 167         int len, ret;
 168 
 169         ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
 170         if (ret) {
 171                 perror("Can't be traced?\n");
 172                 return;
 173         }
 174 
 175         /* Wake up father so that it sets up the first test */
 176         kill(getpid(), SIGUSR1);
 177 
 178         /* Test write watchpoints */
 179         for (len = 1; len <= sizeof(long); len <<= 1)
 180                 write_var(len);
 181 
 182         /* Test read/write watchpoints (on read accesses) */
 183         for (len = 1; len <= sizeof(long); len <<= 1)
 184                 read_var(len);
 185 
 186         /* Test when breakpoint is unset */
 187 
 188         /* Test write watchpoints */
 189         for (len = 1; len <= sizeof(long); len <<= 1)
 190                 write_var(len);
 191 
 192         /* Test read/write watchpoints (on read accesses) */
 193         for (len = 1; len <= sizeof(long); len <<= 1)
 194                 read_var(len);
 195 }
 196 
 197 static void check_success(const char *msg)
 198 {
 199         const char *msg2;
 200         int status;
 201 
 202         /* Wait for the child to SIGTRAP */
 203         wait(&status);
 204 
 205         msg2 = "Failed";
 206 
 207         if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
 208                 msg2 = "Child process hit the breakpoint";
 209         }
 210 
 211         printf("%s Result: [%s]\n", msg, msg2);
 212 }
 213 
 214 static void launch_watchpoints(char *buf, int mode, int len,
 215                                struct ppc_debug_info *dbginfo, bool dawr)
 216 {
 217         const char *mode_str;
 218         unsigned long data = (unsigned long)(dummy_var);
 219         int wh, range;
 220 
 221         data &= ~0x7UL;
 222 
 223         if (mode == BP_W) {
 224                 data |= (1UL << 1);
 225                 mode_str = "write";
 226         } else {
 227                 data |= (1UL << 0);
 228                 data |= (1UL << 1);
 229                 mode_str = "read";
 230         }
 231 
 232         /* Set DABR_TRANSLATION bit */
 233         data |= (1UL << 2);
 234 
 235         /* use PTRACE_SET_DEBUGREG breakpoints */
 236         set_breakpoint_addr((void *)data);
 237         ptrace(PTRACE_CONT, child_pid, NULL, 0);
 238         sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
 239         check_success(buf);
 240         /* Unregister hw brkpoint */
 241         set_breakpoint_addr(NULL);
 242 
 243         data = (data & ~7); /* remove dabr control bits */
 244 
 245         /* use PPC_PTRACE_SETHWDEBUG breakpoint */
 246         if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
 247                 return; /* not supported */
 248         wh = set_hwbreakpoint_addr((void *)data, 0);
 249         ptrace(PTRACE_CONT, child_pid, NULL, 0);
 250         sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
 251         check_success(buf);
 252         /* Unregister hw brkpoint */
 253         del_hwbreakpoint_addr(wh);
 254 
 255         /* try a wider range */
 256         range = 8;
 257         if (dawr)
 258                 range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1));
 259         wh = set_hwbreakpoint_addr((void *)data, range);
 260         ptrace(PTRACE_CONT, child_pid, NULL, 0);
 261         sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
 262         check_success(buf);
 263         /* Unregister hw brkpoint */
 264         del_hwbreakpoint_addr(wh);
 265 }
 266 
 267 /* Set the breakpoints and check the child successfully trigger them */
 268 static int launch_tests(bool dawr)
 269 {
 270         char buf[1024];
 271         int len, i, status;
 272 
 273         struct ppc_debug_info dbginfo;
 274 
 275         i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
 276         if (i) {
 277                 perror("Can't set breakpoint info\n");
 278                 exit(-1);
 279         }
 280         if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
 281                 printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n");
 282 
 283         /* Write watchpoint */
 284         for (len = 1; len <= sizeof(long); len <<= 1)
 285                 launch_watchpoints(buf, BP_W, len, &dbginfo, dawr);
 286 
 287         /* Read-Write watchpoint */
 288         for (len = 1; len <= sizeof(long); len <<= 1)
 289                 launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr);
 290 
 291         ptrace(PTRACE_CONT, child_pid, NULL, 0);
 292 
 293         /*
 294          * Now we have unregistered the breakpoint, access by child
 295          * should not cause SIGTRAP.
 296          */
 297 
 298         wait(&status);
 299 
 300         if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
 301                 printf("FAIL: Child process hit the breakpoint, which is not expected\n");
 302                 ptrace(PTRACE_CONT, child_pid, NULL, 0);
 303                 return TEST_FAIL;
 304         }
 305 
 306         if (WIFEXITED(status))
 307                 printf("Child exited normally\n");
 308 
 309         return TEST_PASS;
 310 }
 311 
 312 static int ptrace_hwbreak(void)
 313 {
 314         pid_t pid;
 315         int ret;
 316         bool dawr;
 317 
 318         pid = fork();
 319         if (!pid) {
 320                 trigger_tests();
 321                 return 0;
 322         }
 323 
 324         wait(NULL);
 325 
 326         child_pid = pid;
 327 
 328         get_dbginfo();
 329         SKIP_IF(!hwbreak_present());
 330         dawr = dawr_present();
 331 
 332         ret = launch_tests(dawr);
 333 
 334         wait(NULL);
 335 
 336         return ret;
 337 }
 338 
 339 int main(int argc, char **argv, char **envp)
 340 {
 341         return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
 342 }

/* [<][>][^][v][top][bottom][index][help] */