linux/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c
<<
>>
Prefs
   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 */
  26enum {
  27        BP_X = 1,
  28        BP_RW = 2,
  29        BP_W = 4,
  30};
  31
  32static pid_t child_pid;
  33static struct ppc_debug_info dbginfo;
  34
  35static 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
  46static bool hwbreak_present(void)
  47{
  48        return (dbginfo.num_data_bps != 0);
  49}
  50
  51static bool dawr_present(void)
  52{
  53        return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
  54}
  55
  56static 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
  67static 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
  91static 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 */
 106static unsigned long long
 107        dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
 108        __attribute__((aligned(512)));
 109static unsigned long long *dummy_var = dummy_array;
 110
 111static 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
 138static 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 */
 165static 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
 197static 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
 214static 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 */
 268static 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
 312static 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
 339int main(int argc, char **argv, char **envp)
 340{
 341        return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
 342}
 343