123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- * Copyright (C) 2016 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Original Code by Pavel Labath <labath@google.com>
- *
- * Code modified by Pratyush Anand <panand@redhat.com>
- * for testing different byte select for each access size.
- *
- */
- #define _GNU_SOURCE
- #include <asm/ptrace.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/ptrace.h>
- #include <sys/param.h>
- #include <sys/uio.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <stddef.h>
- #include <string.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <elf.h>
- #include <errno.h>
- #include <signal.h>
- #include "../kselftest.h"
- static volatile uint8_t var[96] __attribute__((__aligned__(32)));
- static void child(int size, int wr)
- {
- volatile uint8_t *addr = &var[32 + wr];
- if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
- ksft_print_msg(
- "ptrace(PTRACE_TRACEME) failed: %s\n",
- strerror(errno));
- _exit(1);
- }
- if (raise(SIGSTOP) != 0) {
- ksft_print_msg(
- "raise(SIGSTOP) failed: %s\n", strerror(errno));
- _exit(1);
- }
- if ((uintptr_t) addr % size) {
- ksft_print_msg(
- "Wrong address write for the given size: %s\n",
- strerror(errno));
- _exit(1);
- }
- switch (size) {
- case 1:
- *addr = 47;
- break;
- case 2:
- *(uint16_t *)addr = 47;
- break;
- case 4:
- *(uint32_t *)addr = 47;
- break;
- case 8:
- *(uint64_t *)addr = 47;
- break;
- case 16:
- __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0]));
- break;
- case 32:
- __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0]));
- break;
- }
- _exit(0);
- }
- static bool set_watchpoint(pid_t pid, int size, int wp)
- {
- const volatile uint8_t *addr = &var[32 + wp];
- const int offset = (uintptr_t)addr % 8;
- const unsigned int byte_mask = ((1 << size) - 1) << offset;
- const unsigned int type = 2; /* Write */
- const unsigned int enable = 1;
- const unsigned int control = byte_mask << 5 | type << 3 | enable;
- struct user_hwdebug_state dreg_state;
- struct iovec iov;
- memset(&dreg_state, 0, sizeof(dreg_state));
- dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset);
- dreg_state.dbg_regs[0].ctrl = control;
- iov.iov_base = &dreg_state;
- iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) +
- sizeof(dreg_state.dbg_regs[0]);
- if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0)
- return true;
- if (errno == EIO)
- ksft_print_msg(
- "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n",
- strerror(errno));
- ksft_print_msg(
- "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n",
- strerror(errno));
- return false;
- }
- static bool run_test(int wr_size, int wp_size, int wr, int wp)
- {
- int status;
- siginfo_t siginfo;
- pid_t pid = fork();
- pid_t wpid;
- if (pid < 0) {
- ksft_test_result_fail(
- "fork() failed: %s\n", strerror(errno));
- return false;
- }
- if (pid == 0)
- child(wr_size, wr);
- wpid = waitpid(pid, &status, __WALL);
- if (wpid != pid) {
- ksft_print_msg(
- "waitpid() failed: %s\n", strerror(errno));
- return false;
- }
- if (!WIFSTOPPED(status)) {
- ksft_print_msg(
- "child did not stop: %s\n", strerror(errno));
- return false;
- }
- if (WSTOPSIG(status) != SIGSTOP) {
- ksft_print_msg("child did not stop with SIGSTOP\n");
- return false;
- }
- if (!set_watchpoint(pid, wp_size, wp))
- return false;
- if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
- ksft_print_msg(
- "ptrace(PTRACE_SINGLESTEP) failed: %s\n",
- strerror(errno));
- return false;
- }
- alarm(3);
- wpid = waitpid(pid, &status, __WALL);
- if (wpid != pid) {
- ksft_print_msg(
- "waitpid() failed: %s\n", strerror(errno));
- return false;
- }
- alarm(0);
- if (WIFEXITED(status)) {
- ksft_print_msg("child did not single-step\n");
- return false;
- }
- if (!WIFSTOPPED(status)) {
- ksft_print_msg("child did not stop\n");
- return false;
- }
- if (WSTOPSIG(status) != SIGTRAP) {
- ksft_print_msg("child did not stop with SIGTRAP\n");
- return false;
- }
- if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {
- ksft_print_msg(
- "ptrace(PTRACE_GETSIGINFO): %s\n",
- strerror(errno));
- return false;
- }
- if (siginfo.si_code != TRAP_HWBKPT) {
- ksft_print_msg(
- "Unexpected si_code %d\n", siginfo.si_code);
- return false;
- }
- kill(pid, SIGKILL);
- wpid = waitpid(pid, &status, 0);
- if (wpid != pid) {
- ksft_print_msg(
- "waitpid() failed: %s\n", strerror(errno));
- return false;
- }
- return true;
- }
- static void sigalrm(int sig)
- {
- }
- int main(int argc, char **argv)
- {
- int opt;
- bool succeeded = true;
- struct sigaction act;
- int wr, wp, size;
- bool result;
- ksft_print_header();
- act.sa_handler = sigalrm;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGALRM, &act, NULL);
- for (size = 1; size <= 32; size = size*2) {
- for (wr = 0; wr <= 32; wr = wr + size) {
- for (wp = wr - size; wp <= wr + size; wp = wp + size) {
- result = run_test(size, MIN(size, 8), wr, wp);
- if ((result && wr == wp) ||
- (!result && wr != wp))
- ksft_test_result_pass(
- "Test size = %d write offset = %d watchpoint offset = %d\n",
- size, wr, wp);
- else {
- ksft_test_result_fail(
- "Test size = %d write offset = %d watchpoint offset = %d\n",
- size, wr, wp);
- succeeded = false;
- }
- }
- }
- }
- for (size = 1; size <= 32; size = size*2) {
- if (run_test(size, 8, -size, -8))
- ksft_test_result_pass(
- "Test size = %d write offset = %d watchpoint offset = -8\n",
- size, -size);
- else {
- ksft_test_result_fail(
- "Test size = %d write offset = %d watchpoint offset = -8\n",
- size, -size);
- succeeded = false;
- }
- }
- if (succeeded)
- ksft_exit_pass();
- else
- ksft_exit_fail();
- }
|