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