Path: blob/master/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2016 Google, Inc.3*4* Original Code by Pavel Labath <[email protected]>5*6* Code modified by Pratyush Anand <[email protected]>7* for testing different byte select for each access size.8*/910#define _GNU_SOURCE1112#include <asm/ptrace.h>13#include <sys/types.h>14#include <sys/wait.h>15#include <sys/ptrace.h>16#include <sys/param.h>17#include <sys/uio.h>18#include <stdint.h>19#include <stdbool.h>20#include <stddef.h>21#include <string.h>22#include <stdio.h>23#include <unistd.h>24#include <elf.h>25#include <errno.h>26#include <signal.h>2728#include "../kselftest.h"2930static volatile uint8_t var[96] __attribute__((__aligned__(32)));3132static void child(int size, int wr)33{34volatile uint8_t *addr = &var[32 + wr];3536if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {37ksft_print_msg(38"ptrace(PTRACE_TRACEME) failed: %s\n",39strerror(errno));40_exit(1);41}4243if (raise(SIGSTOP) != 0) {44ksft_print_msg(45"raise(SIGSTOP) failed: %s\n", strerror(errno));46_exit(1);47}4849if ((uintptr_t) addr % size) {50ksft_print_msg(51"Wrong address write for the given size: %s\n",52strerror(errno));53_exit(1);54}5556switch (size) {57case 1:58*addr = 47;59break;60case 2:61*(uint16_t *)addr = 47;62break;63case 4:64*(uint32_t *)addr = 47;65break;66case 8:67*(uint64_t *)addr = 47;68break;69case 16:70__asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0]));71break;72case 32:73__asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0]));74break;75}7677_exit(0);78}7980static bool set_watchpoint(pid_t pid, int size, int wp)81{82const volatile uint8_t *addr = &var[32 + wp];83const int offset = (uintptr_t)addr % 8;84const unsigned int byte_mask = ((1 << size) - 1) << offset;85const unsigned int type = 2; /* Write */86const unsigned int enable = 1;87const unsigned int control = byte_mask << 5 | type << 3 | enable;88struct user_hwdebug_state dreg_state;89struct iovec iov;9091memset(&dreg_state, 0, sizeof(dreg_state));92dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset);93dreg_state.dbg_regs[0].ctrl = control;94iov.iov_base = &dreg_state;95iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) +96sizeof(dreg_state.dbg_regs[0]);97if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0)98return true;99100if (errno == EIO)101ksft_print_msg(102"ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n",103strerror(errno));104105ksft_print_msg(106"ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n",107strerror(errno));108return false;109}110111static bool run_test(int wr_size, int wp_size, int wr, int wp)112{113int status;114siginfo_t siginfo;115pid_t pid = fork();116pid_t wpid;117118if (pid < 0) {119ksft_test_result_fail(120"fork() failed: %s\n", strerror(errno));121return false;122}123if (pid == 0)124child(wr_size, wr);125126wpid = waitpid(pid, &status, __WALL);127if (wpid != pid) {128ksft_print_msg(129"waitpid() failed: %s\n", strerror(errno));130return false;131}132if (!WIFSTOPPED(status)) {133ksft_print_msg(134"child did not stop: %s\n", strerror(errno));135return false;136}137if (WSTOPSIG(status) != SIGSTOP) {138ksft_print_msg("child did not stop with SIGSTOP\n");139return false;140}141142if (!set_watchpoint(pid, wp_size, wp))143return false;144145if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {146ksft_print_msg(147"ptrace(PTRACE_CONT) failed: %s\n",148strerror(errno));149return false;150}151152alarm(3);153wpid = waitpid(pid, &status, __WALL);154if (wpid != pid) {155ksft_print_msg(156"waitpid() failed: %s\n", strerror(errno));157return false;158}159alarm(0);160if (WIFEXITED(status)) {161ksft_print_msg("child exited prematurely\n");162return false;163}164if (!WIFSTOPPED(status)) {165ksft_print_msg("child did not stop\n");166return false;167}168if (WSTOPSIG(status) != SIGTRAP) {169ksft_print_msg("child did not stop with SIGTRAP\n");170return false;171}172if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {173ksft_print_msg(174"ptrace(PTRACE_GETSIGINFO): %s\n",175strerror(errno));176return false;177}178if (siginfo.si_code != TRAP_HWBKPT) {179ksft_print_msg(180"Unexpected si_code %d\n", siginfo.si_code);181return false;182}183184kill(pid, SIGKILL);185wpid = waitpid(pid, &status, 0);186if (wpid != pid) {187ksft_print_msg(188"waitpid() failed: %s\n", strerror(errno));189return false;190}191return true;192}193194static void sigalrm(int sig)195{196}197198int main(int argc, char **argv)199{200int opt;201bool succeeded = true;202struct sigaction act;203int wr, wp, size;204bool result;205206ksft_print_header();207ksft_set_plan(213);208209act.sa_handler = sigalrm;210sigemptyset(&act.sa_mask);211act.sa_flags = 0;212sigaction(SIGALRM, &act, NULL);213for (size = 1; size <= 32; size = size*2) {214for (wr = 0; wr <= 32; wr = wr + size) {215for (wp = wr - size; wp <= wr + size; wp = wp + size) {216result = run_test(size, MIN(size, 8), wr, wp);217if ((result && wr == wp) ||218(!result && wr != wp))219ksft_test_result_pass(220"Test size = %d write offset = %d watchpoint offset = %d\n",221size, wr, wp);222else {223ksft_test_result_fail(224"Test size = %d write offset = %d watchpoint offset = %d\n",225size, wr, wp);226succeeded = false;227}228}229}230}231232for (size = 1; size <= 32; size = size*2) {233if (run_test(size, 8, -size, -8))234ksft_test_result_pass(235"Test size = %d write offset = %d watchpoint offset = -8\n",236size, -size);237else {238ksft_test_result_fail(239"Test size = %d write offset = %d watchpoint offset = -8\n",240size, -size);241succeeded = false;242}243}244245if (succeeded)246ksft_exit_pass();247else248ksft_exit_fail();249}250251252