linux/tools/testing/selftests/breakpoints/breakpoint_test.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
   3 *
   4 * Licensed under the terms of the GNU GPL License version 2
   5 *
   6 * Selftests for breakpoints (and more generally the do_debug() path) in x86.
   7 */
   8
   9
  10#include <sys/ptrace.h>
  11#include <unistd.h>
  12#include <stddef.h>
  13#include <sys/user.h>
  14#include <stdio.h>
  15#include <stdlib.h>
  16#include <signal.h>
  17#include <sys/types.h>
  18#include <sys/wait.h>
  19
  20#include "../kselftest.h"
  21
  22
  23/* Breakpoint access modes */
  24enum {
  25        BP_X = 1,
  26        BP_RW = 2,
  27        BP_W = 4,
  28};
  29
  30static pid_t child_pid;
  31
  32/*
  33 * Ensures the child and parent are always "talking" about
  34 * the same test sequence. (ie: that we haven't forgotten
  35 * to call check_trapped() somewhere).
  36 */
  37static int nr_tests;
  38
  39static void set_breakpoint_addr(void *addr, int n)
  40{
  41        int ret;
  42
  43        ret = ptrace(PTRACE_POKEUSER, child_pid,
  44                     offsetof(struct user, u_debugreg[n]), addr);
  45        if (ret) {
  46                perror("Can't set breakpoint addr\n");
  47                ksft_exit_fail();
  48        }
  49}
  50
  51static void toggle_breakpoint(int n, int type, int len,
  52                              int local, int global, int set)
  53{
  54        int ret;
  55
  56        int xtype, xlen;
  57        unsigned long vdr7, dr7;
  58
  59        switch (type) {
  60        case BP_X:
  61                xtype = 0;
  62                break;
  63        case BP_W:
  64                xtype = 1;
  65                break;
  66        case BP_RW:
  67                xtype = 3;
  68                break;
  69        }
  70
  71        switch (len) {
  72        case 1:
  73                xlen = 0;
  74                break;
  75        case 2:
  76                xlen = 4;
  77                break;
  78        case 4:
  79                xlen = 0xc;
  80                break;
  81        case 8:
  82                xlen = 8;
  83                break;
  84        }
  85
  86        dr7 = ptrace(PTRACE_PEEKUSER, child_pid,
  87                     offsetof(struct user, u_debugreg[7]), 0);
  88
  89        vdr7 = (xlen | xtype) << 16;
  90        vdr7 <<= 4 * n;
  91
  92        if (local) {
  93                vdr7 |= 1 << (2 * n);
  94                vdr7 |= 1 << 8;
  95        }
  96        if (global) {
  97                vdr7 |= 2 << (2 * n);
  98                vdr7 |= 1 << 9;
  99        }
 100
 101        if (set)
 102                dr7 |= vdr7;
 103        else
 104                dr7 &= ~vdr7;
 105
 106        ret = ptrace(PTRACE_POKEUSER, child_pid,
 107                     offsetof(struct user, u_debugreg[7]), dr7);
 108        if (ret) {
 109                perror("Can't set dr7");
 110                ksft_exit_fail();
 111        }
 112}
 113
 114/* Dummy variables to test read/write accesses */
 115static unsigned long long dummy_var[4];
 116
 117/* Dummy functions to test execution accesses */
 118static void dummy_func(void) { }
 119static void dummy_func1(void) { }
 120static void dummy_func2(void) { }
 121static void dummy_func3(void) { }
 122
 123static void (*dummy_funcs[])(void) = {
 124        dummy_func,
 125        dummy_func1,
 126        dummy_func2,
 127        dummy_func3,
 128};
 129
 130static int trapped;
 131
 132static void check_trapped(void)
 133{
 134        /*
 135         * If we haven't trapped, wake up the parent
 136         * so that it notices the failure.
 137         */
 138        if (!trapped)
 139                kill(getpid(), SIGUSR1);
 140        trapped = 0;
 141
 142        nr_tests++;
 143}
 144
 145static void write_var(int len)
 146{
 147        char *pcval; short *psval; int *pival; long long *plval;
 148        int i;
 149
 150        for (i = 0; i < 4; i++) {
 151                switch (len) {
 152                case 1:
 153                        pcval = (char *)&dummy_var[i];
 154                        *pcval = 0xff;
 155                        break;
 156                case 2:
 157                        psval = (short *)&dummy_var[i];
 158                        *psval = 0xffff;
 159                        break;
 160                case 4:
 161                        pival = (int *)&dummy_var[i];
 162                        *pival = 0xffffffff;
 163                        break;
 164                case 8:
 165                        plval = (long long *)&dummy_var[i];
 166                        *plval = 0xffffffffffffffffLL;
 167                        break;
 168                }
 169                check_trapped();
 170        }
 171}
 172
 173static void read_var(int len)
 174{
 175        char cval; short sval; int ival; long long lval;
 176        int i;
 177
 178        for (i = 0; i < 4; i++) {
 179                switch (len) {
 180                case 1:
 181                        cval = *(char *)&dummy_var[i];
 182                        break;
 183                case 2:
 184                        sval = *(short *)&dummy_var[i];
 185                        break;
 186                case 4:
 187                        ival = *(int *)&dummy_var[i];
 188                        break;
 189                case 8:
 190                        lval = *(long long *)&dummy_var[i];
 191                        break;
 192                }
 193                check_trapped();
 194        }
 195}
 196
 197/*
 198 * Do the r/w/x accesses to trigger the breakpoints. And run
 199 * the usual traps.
 200 */
 201static void trigger_tests(void)
 202{
 203        int len, local, global, i;
 204        char val;
 205        int ret;
 206
 207        ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
 208        if (ret) {
 209                perror("Can't be traced?\n");
 210                return;
 211        }
 212
 213        /* Wake up father so that it sets up the first test */
 214        kill(getpid(), SIGUSR1);
 215
 216        /* Test instruction breakpoints */
 217        for (local = 0; local < 2; local++) {
 218                for (global = 0; global < 2; global++) {
 219                        if (!local && !global)
 220                                continue;
 221
 222                        for (i = 0; i < 4; i++) {
 223                                dummy_funcs[i]();
 224                                check_trapped();
 225                        }
 226                }
 227        }
 228
 229        /* Test write watchpoints */
 230        for (len = 1; len <= sizeof(long); len <<= 1) {
 231                for (local = 0; local < 2; local++) {
 232                        for (global = 0; global < 2; global++) {
 233                                if (!local && !global)
 234                                        continue;
 235                                write_var(len);
 236                        }
 237                }
 238        }
 239
 240        /* Test read/write watchpoints (on read accesses) */
 241        for (len = 1; len <= sizeof(long); len <<= 1) {
 242                for (local = 0; local < 2; local++) {
 243                        for (global = 0; global < 2; global++) {
 244                                if (!local && !global)
 245                                        continue;
 246                                read_var(len);
 247                        }
 248                }
 249        }
 250
 251        /* Icebp trap */
 252        asm(".byte 0xf1\n");
 253        check_trapped();
 254
 255        /* Int 3 trap */
 256        asm("int $3\n");
 257        check_trapped();
 258
 259        kill(getpid(), SIGUSR1);
 260}
 261
 262static void check_success(const char *msg)
 263{
 264        const char *msg2;
 265        int child_nr_tests;
 266        int status;
 267
 268        /* Wait for the child to SIGTRAP */
 269        wait(&status);
 270
 271        msg2 = "Failed";
 272
 273        if (WSTOPSIG(status) == SIGTRAP) {
 274                child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,
 275                                        &nr_tests, 0);
 276                if (child_nr_tests == nr_tests)
 277                        msg2 = "Ok";
 278                if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) {
 279                        perror("Can't poke\n");
 280                        ksft_exit_fail();
 281                }
 282        }
 283
 284        nr_tests++;
 285
 286        printf("%s [%s]\n", msg, msg2);
 287}
 288
 289static void launch_instruction_breakpoints(char *buf, int local, int global)
 290{
 291        int i;
 292
 293        for (i = 0; i < 4; i++) {
 294                set_breakpoint_addr(dummy_funcs[i], i);
 295                toggle_breakpoint(i, BP_X, 1, local, global, 1);
 296                ptrace(PTRACE_CONT, child_pid, NULL, 0);
 297                sprintf(buf, "Test breakpoint %d with local: %d global: %d",
 298                        i, local, global);
 299                check_success(buf);
 300                toggle_breakpoint(i, BP_X, 1, local, global, 0);
 301        }
 302}
 303
 304static void launch_watchpoints(char *buf, int mode, int len,
 305                               int local, int global)
 306{
 307        const char *mode_str;
 308        int i;
 309
 310        if (mode == BP_W)
 311                mode_str = "write";
 312        else
 313                mode_str = "read";
 314
 315        for (i = 0; i < 4; i++) {
 316                set_breakpoint_addr(&dummy_var[i], i);
 317                toggle_breakpoint(i, mode, len, local, global, 1);
 318                ptrace(PTRACE_CONT, child_pid, NULL, 0);
 319                sprintf(buf, "Test %s watchpoint %d with len: %d local: "
 320                        "%d global: %d", mode_str, i, len, local, global);
 321                check_success(buf);
 322                toggle_breakpoint(i, mode, len, local, global, 0);
 323        }
 324}
 325
 326/* Set the breakpoints and check the child successfully trigger them */
 327static void launch_tests(void)
 328{
 329        char buf[1024];
 330        int len, local, global, i;
 331
 332        /* Instruction breakpoints */
 333        for (local = 0; local < 2; local++) {
 334                for (global = 0; global < 2; global++) {
 335                        if (!local && !global)
 336                                continue;
 337                        launch_instruction_breakpoints(buf, local, global);
 338                }
 339        }
 340
 341        /* Write watchpoint */
 342        for (len = 1; len <= sizeof(long); len <<= 1) {
 343                for (local = 0; local < 2; local++) {
 344                        for (global = 0; global < 2; global++) {
 345                                if (!local && !global)
 346                                        continue;
 347                                launch_watchpoints(buf, BP_W, len,
 348                                                   local, global);
 349                        }
 350                }
 351        }
 352
 353        /* Read-Write watchpoint */
 354        for (len = 1; len <= sizeof(long); len <<= 1) {
 355                for (local = 0; local < 2; local++) {
 356                        for (global = 0; global < 2; global++) {
 357                                if (!local && !global)
 358                                        continue;
 359                                launch_watchpoints(buf, BP_RW, len,
 360                                                   local, global);
 361                        }
 362                }
 363        }
 364
 365        /* Icebp traps */
 366        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 367        check_success("Test icebp");
 368
 369        /* Int 3 traps */
 370        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 371        check_success("Test int 3 trap");
 372
 373        ptrace(PTRACE_CONT, child_pid, NULL, 0);
 374}
 375
 376int main(int argc, char **argv)
 377{
 378        pid_t pid;
 379        int ret;
 380
 381        pid = fork();
 382        if (!pid) {
 383                trigger_tests();
 384                return 0;
 385        }
 386
 387        child_pid = pid;
 388
 389        wait(NULL);
 390
 391        launch_tests();
 392
 393        wait(NULL);
 394
 395        return ksft_exit_pass();
 396}
 397