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