linux/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2016 Google, Inc.
   4 *
   5 * Original Code by Pavel Labath <labath@google.com>
   6 *
   7 * Code modified by Pratyush Anand <panand@redhat.com>
   8 * for testing different byte select for each access size.
   9 */
  10
  11#define _GNU_SOURCE
  12
  13#include <asm/ptrace.h>
  14#include <sys/types.h>
  15#include <sys/wait.h>
  16#include <sys/ptrace.h>
  17#include <sys/param.h>
  18#include <sys/uio.h>
  19#include <stdint.h>
  20#include <stdbool.h>
  21#include <stddef.h>
  22#include <string.h>
  23#include <stdio.h>
  24#include <unistd.h>
  25#include <elf.h>
  26#include <errno.h>
  27#include <signal.h>
  28
  29#include "../kselftest.h"
  30
  31static volatile uint8_t var[96] __attribute__((__aligned__(32)));
  32
  33static void child(int size, int wr)
  34{
  35        volatile uint8_t *addr = &var[32 + wr];
  36
  37        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
  38                ksft_print_msg(
  39                        "ptrace(PTRACE_TRACEME) failed: %s\n",
  40                        strerror(errno));
  41                _exit(1);
  42        }
  43
  44        if (raise(SIGSTOP) != 0) {
  45                ksft_print_msg(
  46                        "raise(SIGSTOP) failed: %s\n", strerror(errno));
  47                _exit(1);
  48        }
  49
  50        if ((uintptr_t) addr % size) {
  51                ksft_print_msg(
  52                         "Wrong address write for the given size: %s\n",
  53                         strerror(errno));
  54                _exit(1);
  55        }
  56
  57        switch (size) {
  58        case 1:
  59                *addr = 47;
  60                break;
  61        case 2:
  62                *(uint16_t *)addr = 47;
  63                break;
  64        case 4:
  65                *(uint32_t *)addr = 47;
  66                break;
  67        case 8:
  68                *(uint64_t *)addr = 47;
  69                break;
  70        case 16:
  71                __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0]));
  72                break;
  73        case 32:
  74                __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0]));
  75                break;
  76        }
  77
  78        _exit(0);
  79}
  80
  81static bool set_watchpoint(pid_t pid, int size, int wp)
  82{
  83        const volatile uint8_t *addr = &var[32 + wp];
  84        const int offset = (uintptr_t)addr % 8;
  85        const unsigned int byte_mask = ((1 << size) - 1) << offset;
  86        const unsigned int type = 2; /* Write */
  87        const unsigned int enable = 1;
  88        const unsigned int control = byte_mask << 5 | type << 3 | enable;
  89        struct user_hwdebug_state dreg_state;
  90        struct iovec iov;
  91
  92        memset(&dreg_state, 0, sizeof(dreg_state));
  93        dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset);
  94        dreg_state.dbg_regs[0].ctrl = control;
  95        iov.iov_base = &dreg_state;
  96        iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) +
  97                                sizeof(dreg_state.dbg_regs[0]);
  98        if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0)
  99                return true;
 100
 101        if (errno == EIO)
 102                ksft_print_msg(
 103                        "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n",
 104                        strerror(errno));
 105
 106        ksft_print_msg(
 107                "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n",
 108                strerror(errno));
 109        return false;
 110}
 111
 112static bool run_test(int wr_size, int wp_size, int wr, int wp)
 113{
 114        int status;
 115        siginfo_t siginfo;
 116        pid_t pid = fork();
 117        pid_t wpid;
 118
 119        if (pid < 0) {
 120                ksft_test_result_fail(
 121                        "fork() failed: %s\n", strerror(errno));
 122                return false;
 123        }
 124        if (pid == 0)
 125                child(wr_size, wr);
 126
 127        wpid = waitpid(pid, &status, __WALL);
 128        if (wpid != pid) {
 129                ksft_print_msg(
 130                        "waitpid() failed: %s\n", strerror(errno));
 131                return false;
 132        }
 133        if (!WIFSTOPPED(status)) {
 134                ksft_print_msg(
 135                        "child did not stop: %s\n", strerror(errno));
 136                return false;
 137        }
 138        if (WSTOPSIG(status) != SIGSTOP) {
 139                ksft_print_msg("child did not stop with SIGSTOP\n");
 140                return false;
 141        }
 142
 143        if (!set_watchpoint(pid, wp_size, wp))
 144                return false;
 145
 146        if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
 147                ksft_print_msg(
 148                        "ptrace(PTRACE_CONT) failed: %s\n",
 149                        strerror(errno));
 150                return false;
 151        }
 152
 153        alarm(3);
 154        wpid = waitpid(pid, &status, __WALL);
 155        if (wpid != pid) {
 156                ksft_print_msg(
 157                        "waitpid() failed: %s\n", strerror(errno));
 158                return false;
 159        }
 160        alarm(0);
 161        if (WIFEXITED(status)) {
 162                ksft_print_msg("child exited prematurely\n");
 163                return false;
 164        }
 165        if (!WIFSTOPPED(status)) {
 166                ksft_print_msg("child did not stop\n");
 167                return false;
 168        }
 169        if (WSTOPSIG(status) != SIGTRAP) {
 170                ksft_print_msg("child did not stop with SIGTRAP\n");
 171                return false;
 172        }
 173        if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {
 174                ksft_print_msg(
 175                        "ptrace(PTRACE_GETSIGINFO): %s\n",
 176                        strerror(errno));
 177                return false;
 178        }
 179        if (siginfo.si_code != TRAP_HWBKPT) {
 180                ksft_print_msg(
 181                        "Unexpected si_code %d\n", siginfo.si_code);
 182                return false;
 183        }
 184
 185        kill(pid, SIGKILL);
 186        wpid = waitpid(pid, &status, 0);
 187        if (wpid != pid) {
 188                ksft_print_msg(
 189                        "waitpid() failed: %s\n", strerror(errno));
 190                return false;
 191        }
 192        return true;
 193}
 194
 195static void sigalrm(int sig)
 196{
 197}
 198
 199int main(int argc, char **argv)
 200{
 201        int opt;
 202        bool succeeded = true;
 203        struct sigaction act;
 204        int wr, wp, size;
 205        bool result;
 206
 207        ksft_print_header();
 208        ksft_set_plan(213);
 209
 210        act.sa_handler = sigalrm;
 211        sigemptyset(&act.sa_mask);
 212        act.sa_flags = 0;
 213        sigaction(SIGALRM, &act, NULL);
 214        for (size = 1; size <= 32; size = size*2) {
 215                for (wr = 0; wr <= 32; wr = wr + size) {
 216                        for (wp = wr - size; wp <= wr + size; wp = wp + size) {
 217                                result = run_test(size, MIN(size, 8), wr, wp);
 218                                if ((result && wr == wp) ||
 219                                    (!result && wr != wp))
 220                                        ksft_test_result_pass(
 221                                                "Test size = %d write offset = %d watchpoint offset = %d\n",
 222                                                size, wr, wp);
 223                                else {
 224                                        ksft_test_result_fail(
 225                                                "Test size = %d write offset = %d watchpoint offset = %d\n",
 226                                                size, wr, wp);
 227                                        succeeded = false;
 228                                }
 229                        }
 230                }
 231        }
 232
 233        for (size = 1; size <= 32; size = size*2) {
 234                if (run_test(size, 8, -size, -8))
 235                        ksft_test_result_pass(
 236                                "Test size = %d write offset = %d watchpoint offset = -8\n",
 237                                size, -size);
 238                else {
 239                        ksft_test_result_fail(
 240                                "Test size = %d write offset = %d watchpoint offset = -8\n",
 241                                size, -size);
 242                        succeeded = false;
 243                }
 244        }
 245
 246        if (succeeded)
 247                ksft_exit_pass();
 248        else
 249                ksft_exit_fail();
 250}
 251