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#define SPRN_PVR        0x11F
  26#define PVR_8xx         0x00500000
  27
  28bool is_8xx;
  29
  30/*
  31 * Use volatile on all global var so that compiler doesn't
  32 * optimise their load/stores. Otherwise selftest can fail.
  33 */
  34static volatile __u64 glvar;
  35
  36#define DAWR_MAX_LEN 512
  37static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
  38
  39#define A_LEN 6
  40#define B_LEN 6
  41struct gstruct {
  42        __u8 a[A_LEN]; /* double word aligned */
  43        __u8 b[B_LEN]; /* double word unaligned */
  44};
  45static volatile struct gstruct gstruct __attribute__((aligned(512)));
  46
  47
  48static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
  49{
  50        if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
  51                perror("Can't get breakpoint info");
  52                exit(-1);
  53        }
  54}
  55
  56static bool dawr_present(struct ppc_debug_info *dbginfo)
  57{
  58        return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
  59}
  60
  61static void write_var(int len)
  62{
  63        __u8 *pcvar;
  64        __u16 *psvar;
  65        __u32 *pivar;
  66        __u64 *plvar;
  67
  68        switch (len) {
  69        case 1:
  70                pcvar = (__u8 *)&glvar;
  71                *pcvar = 0xff;
  72                break;
  73        case 2:
  74                psvar = (__u16 *)&glvar;
  75                *psvar = 0xffff;
  76                break;
  77        case 4:
  78                pivar = (__u32 *)&glvar;
  79                *pivar = 0xffffffff;
  80                break;
  81        case 8:
  82                plvar = (__u64 *)&glvar;
  83                *plvar = 0xffffffffffffffffLL;
  84                break;
  85        }
  86}
  87
  88static void read_var(int len)
  89{
  90        __u8 cvar __attribute__((unused));
  91        __u16 svar __attribute__((unused));
  92        __u32 ivar __attribute__((unused));
  93        __u64 lvar __attribute__((unused));
  94
  95        switch (len) {
  96        case 1:
  97                cvar = (__u8)glvar;
  98                break;
  99        case 2:
 100                svar = (__u16)glvar;
 101                break;
 102        case 4:
 103                ivar = (__u32)glvar;
 104                break;
 105        case 8:
 106                lvar = (__u64)glvar;
 107                break;
 108        }
 109}
 110
 111static void test_workload(void)
 112{
 113        __u8 cvar __attribute__((unused));
 114        __u32 ivar __attribute__((unused));
 115        int len = 0;
 116
 117        if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
 118                perror("Child can't be traced?");
 119                exit(-1);
 120        }
 121
 122        /* Wake up father so that it sets up the first test */
 123        kill(getpid(), SIGUSR1);
 124
 125        /* PTRACE_SET_DEBUGREG, WO test */
 126        for (len = 1; len <= sizeof(glvar); len <<= 1)
 127                write_var(len);
 128
 129        /* PTRACE_SET_DEBUGREG, RO test */
 130        for (len = 1; len <= sizeof(glvar); len <<= 1)
 131                read_var(len);
 132
 133        /* PTRACE_SET_DEBUGREG, RW test */
 134        for (len = 1; len <= sizeof(glvar); len <<= 1) {
 135                if (rand() % 2)
 136                        read_var(len);
 137                else
 138                        write_var(len);
 139        }
 140
 141        /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
 142        write_var(1);
 143
 144        /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
 145        read_var(1);
 146
 147        /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
 148        if (rand() % 2)
 149                write_var(1);
 150        else
 151                read_var(1);
 152
 153        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
 154        gstruct.a[rand() % A_LEN] = 'a';
 155
 156        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
 157        cvar = gstruct.a[rand() % A_LEN];
 158
 159        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
 160        if (rand() % 2)
 161                gstruct.a[rand() % A_LEN] = 'a';
 162        else
 163                cvar = gstruct.a[rand() % A_LEN];
 164
 165        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
 166        gstruct.b[rand() % B_LEN] = 'b';
 167
 168        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
 169        cvar = gstruct.b[rand() % B_LEN];
 170
 171        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
 172        if (rand() % 2)
 173                gstruct.b[rand() % B_LEN] = 'b';
 174        else
 175                cvar = gstruct.b[rand() % B_LEN];
 176
 177        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
 178        if (rand() % 2)
 179                *((int *)(gstruct.a + 4)) = 10;
 180        else
 181                ivar = *((int *)(gstruct.a + 4));
 182
 183        /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
 184        if (rand() % 2)
 185                big_var[rand() % DAWR_MAX_LEN] = 'a';
 186        else
 187                cvar = big_var[rand() % DAWR_MAX_LEN];
 188}
 189
 190static void check_success(pid_t child_pid, const char *name, const char *type,
 191                          unsigned long saddr, int len)
 192{
 193        int status;
 194        siginfo_t siginfo;
 195        unsigned long eaddr = (saddr + len - 1) | 0x7;
 196
 197        saddr &= ~0x7;
 198
 199        /* Wait for the child to SIGTRAP */
 200        wait(&status);
 201
 202        ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
 203
 204        if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
 205            (unsigned long)siginfo.si_addr < saddr ||
 206            (unsigned long)siginfo.si_addr > eaddr) {
 207                printf("%s, %s, len: %d: Fail\n", name, type, len);
 208                exit(-1);
 209        }
 210
 211        printf("%s, %s, len: %d: Ok\n", name, type, len);
 212
 213        if (!is_8xx) {
 214                /*
 215                 * For ptrace registered watchpoint, signal is generated
 216                 * before executing load/store. Singlestep the instruction
 217                 * and then continue the test.
 218                 */
 219                ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
 220                wait(NULL);
 221        }
 222}
 223
 224static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
 225{
 226        if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
 227                perror("PTRACE_SET_DEBUGREG failed");
 228                exit(-1);
 229        }
 230}
 231
 232static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
 233{
 234        int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
 235
 236        if (wh <= 0) {
 237                perror("PPC_PTRACE_SETHWDEBUG failed");
 238                exit(-1);
 239        }
 240        return wh;
 241}
 242
 243static void ptrace_delhwdebug(pid_t child_pid, int wh)
 244{
 245        if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
 246                perror("PPC_PTRACE_DELHWDEBUG failed");
 247                exit(-1);
 248        }
 249}
 250
 251#define DABR_READ_SHIFT         0
 252#define DABR_WRITE_SHIFT        1
 253#define DABR_TRANSLATION_SHIFT  2
 254
 255static int test_set_debugreg(pid_t child_pid)
 256{
 257        unsigned long wp_addr = (unsigned long)&glvar;
 258        char *name = "PTRACE_SET_DEBUGREG";
 259        int len;
 260
 261        /* PTRACE_SET_DEBUGREG, WO test*/
 262        wp_addr &= ~0x7UL;
 263        wp_addr |= (1UL << DABR_WRITE_SHIFT);
 264        wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
 265        for (len = 1; len <= sizeof(glvar); len <<= 1) {
 266                ptrace_set_debugreg(child_pid, wp_addr);
 267                ptrace(PTRACE_CONT, child_pid, NULL, 0);
 268                check_success(child_pid, name, "WO", wp_addr, len);
 269        }
 270
 271        /* PTRACE_SET_DEBUGREG, RO test */
 272        wp_addr &= ~0x7UL;
 273        wp_addr |= (1UL << DABR_READ_SHIFT);
 274        wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
 275        for (len = 1; len <= sizeof(glvar); len <<= 1) {
 276                ptrace_set_debugreg(child_pid, wp_addr);
 277                ptrace(PTRACE_CONT, child_pid, NULL, 0);
 278                check_success(child_pid, name, "RO", wp_addr, len);
 279        }
 280
 281        /* PTRACE_SET_DEBUGREG, RW test */
 282        wp_addr &= ~0x7UL;
 283        wp_addr |= (1Ul << DABR_READ_SHIFT);
 284        wp_addr |= (1UL << DABR_WRITE_SHIFT);
 285        wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
 286        for (len = 1; len <= sizeof(glvar); len <<= 1) {
 287                ptrace_set_debugreg(child_pid, wp_addr);
 288                ptrace(PTRACE_CONT, child_pid, NULL, 0);
 289                check_success(child_pid, name, "RW", wp_addr, len);
 290        }
 291
 292        ptrace_set_debugreg(child_pid, 0);
 293        return 0;
 294}
 295
 296static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
 297                                  unsigned long addr, int len)
 298{
 299        info->version = 1;
 300        info->trigger_type = type;
 301        info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
 302        info->addr = (__u64)addr;
 303        info->addr2 = (__u64)addr + len;
 304        info->condition_value = 0;
 305        if (!len)
 306                info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
 307        else
 308                info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
 309}
 310
 311static void test_sethwdebug_exact(pid_t child_pid)
 312{
 313        struct ppc_hw_breakpoint info;
 314        unsigned long wp_addr = (unsigned long)&glvar;
 315        char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
 316        int len = 1; /* hardcoded in kernel */
 317        int wh;
 318
 319        /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
 320        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
 321        wh = ptrace_sethwdebug(child_pid, &info);
 322        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 323        check_success(child_pid, name, "WO", wp_addr, len);
 324        ptrace_delhwdebug(child_pid, wh);
 325
 326        /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
 327        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
 328        wh = ptrace_sethwdebug(child_pid, &info);
 329        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 330        check_success(child_pid, name, "RO", wp_addr, len);
 331        ptrace_delhwdebug(child_pid, wh);
 332
 333        /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
 334        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
 335        wh = ptrace_sethwdebug(child_pid, &info);
 336        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 337        check_success(child_pid, name, "RW", wp_addr, len);
 338        ptrace_delhwdebug(child_pid, wh);
 339}
 340
 341static void test_sethwdebug_range_aligned(pid_t child_pid)
 342{
 343        struct ppc_hw_breakpoint info;
 344        unsigned long wp_addr;
 345        char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
 346        int len;
 347        int wh;
 348
 349        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
 350        wp_addr = (unsigned long)&gstruct.a;
 351        len = A_LEN;
 352        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
 353        wh = ptrace_sethwdebug(child_pid, &info);
 354        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 355        check_success(child_pid, name, "WO", wp_addr, len);
 356        ptrace_delhwdebug(child_pid, wh);
 357
 358        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
 359        wp_addr = (unsigned long)&gstruct.a;
 360        len = A_LEN;
 361        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
 362        wh = ptrace_sethwdebug(child_pid, &info);
 363        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 364        check_success(child_pid, name, "RO", wp_addr, len);
 365        ptrace_delhwdebug(child_pid, wh);
 366
 367        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
 368        wp_addr = (unsigned long)&gstruct.a;
 369        len = A_LEN;
 370        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
 371        wh = ptrace_sethwdebug(child_pid, &info);
 372        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 373        check_success(child_pid, name, "RW", wp_addr, len);
 374        ptrace_delhwdebug(child_pid, wh);
 375}
 376
 377static void test_sethwdebug_range_unaligned(pid_t child_pid)
 378{
 379        struct ppc_hw_breakpoint info;
 380        unsigned long wp_addr;
 381        char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
 382        int len;
 383        int wh;
 384
 385        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
 386        wp_addr = (unsigned long)&gstruct.b;
 387        len = B_LEN;
 388        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
 389        wh = ptrace_sethwdebug(child_pid, &info);
 390        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 391        check_success(child_pid, name, "WO", wp_addr, len);
 392        ptrace_delhwdebug(child_pid, wh);
 393
 394        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
 395        wp_addr = (unsigned long)&gstruct.b;
 396        len = B_LEN;
 397        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
 398        wh = ptrace_sethwdebug(child_pid, &info);
 399        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 400        check_success(child_pid, name, "RO", wp_addr, len);
 401        ptrace_delhwdebug(child_pid, wh);
 402
 403        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
 404        wp_addr = (unsigned long)&gstruct.b;
 405        len = B_LEN;
 406        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
 407        wh = ptrace_sethwdebug(child_pid, &info);
 408        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 409        check_success(child_pid, name, "RW", wp_addr, len);
 410        ptrace_delhwdebug(child_pid, wh);
 411
 412}
 413
 414static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
 415{
 416        struct ppc_hw_breakpoint info;
 417        unsigned long wp_addr;
 418        char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
 419        int len;
 420        int wh;
 421
 422        /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
 423        wp_addr = (unsigned long)&gstruct.b;
 424        len = B_LEN;
 425        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
 426        wh = ptrace_sethwdebug(child_pid, &info);
 427        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 428        check_success(child_pid, name, "RW", wp_addr, len);
 429        ptrace_delhwdebug(child_pid, wh);
 430}
 431
 432static void test_sethwdebug_dawr_max_range(pid_t child_pid)
 433{
 434        struct ppc_hw_breakpoint info;
 435        unsigned long wp_addr;
 436        char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
 437        int len;
 438        int wh;
 439
 440        /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
 441        wp_addr = (unsigned long)big_var;
 442        len = DAWR_MAX_LEN;
 443        get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
 444        wh = ptrace_sethwdebug(child_pid, &info);
 445        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 446        check_success(child_pid, name, "RW", wp_addr, len);
 447        ptrace_delhwdebug(child_pid, wh);
 448}
 449
 450/* Set the breakpoints and check the child successfully trigger them */
 451static void
 452run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
 453{
 454        test_set_debugreg(child_pid);
 455        if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
 456                test_sethwdebug_exact(child_pid);
 457
 458                if (!is_8xx)
 459                        test_sethwdebug_range_aligned(child_pid);
 460                if (dawr && !is_8xx) {
 461                        test_sethwdebug_range_unaligned(child_pid);
 462                        test_sethwdebug_range_unaligned_dar(child_pid);
 463                        test_sethwdebug_dawr_max_range(child_pid);
 464                }
 465        }
 466}
 467
 468static int ptrace_hwbreak(void)
 469{
 470        pid_t child_pid;
 471        struct ppc_debug_info dbginfo;
 472        bool dawr;
 473
 474        child_pid = fork();
 475        if (!child_pid) {
 476                test_workload();
 477                return 0;
 478        }
 479
 480        wait(NULL);
 481
 482        get_dbginfo(child_pid, &dbginfo);
 483        SKIP_IF(dbginfo.num_data_bps == 0);
 484
 485        dawr = dawr_present(&dbginfo);
 486        run_tests(child_pid, &dbginfo, dawr);
 487
 488        /* Let the child exit first. */
 489        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 490        wait(NULL);
 491
 492        /*
 493         * Testcases exits immediately with -1 on any failure. If
 494         * it has reached here, it means all tests were successful.
 495         */
 496        return TEST_PASS;
 497}
 498
 499int main(int argc, char **argv, char **envp)
 500{
 501        int pvr = 0;
 502        asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
 503        if (pvr == PVR_8xx)
 504                is_8xx = true;
 505
 506        return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
 507}
 508