aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/include/asm/hw_breakpoint.h6
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c153
-rw-r--r--arch/arm64/kernel/ptrace.c7
-rw-r--r--include/uapi/linux/hw_breakpoint.h4
-rw-r--r--tools/include/uapi/linux/hw_breakpoint.h4
-rw-r--r--tools/testing/selftests/breakpoints/Makefile5
-rw-r--r--tools/testing/selftests/breakpoints/breakpoint_test_arm64.c236
7 files changed, 372 insertions, 43 deletions
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index 9510ace570e2..b6b167ac082b 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -77,7 +77,11 @@ static inline void decode_ctrl_reg(u32 reg,
77/* Lengths */ 77/* Lengths */
78#define ARM_BREAKPOINT_LEN_1 0x1 78#define ARM_BREAKPOINT_LEN_1 0x1
79#define ARM_BREAKPOINT_LEN_2 0x3 79#define ARM_BREAKPOINT_LEN_2 0x3
80#define ARM_BREAKPOINT_LEN_3 0x7
80#define ARM_BREAKPOINT_LEN_4 0xf 81#define ARM_BREAKPOINT_LEN_4 0xf
82#define ARM_BREAKPOINT_LEN_5 0x1f
83#define ARM_BREAKPOINT_LEN_6 0x3f
84#define ARM_BREAKPOINT_LEN_7 0x7f
81#define ARM_BREAKPOINT_LEN_8 0xff 85#define ARM_BREAKPOINT_LEN_8 0xff
82 86
83/* Kernel stepping */ 87/* Kernel stepping */
@@ -119,7 +123,7 @@ struct perf_event;
119struct pmu; 123struct pmu;
120 124
121extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, 125extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
122 int *gen_len, int *gen_type); 126 int *gen_len, int *gen_type, int *offset);
123extern int arch_check_bp_in_kernelspace(struct perf_event *bp); 127extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
124extern int arch_validate_hwbkpt_settings(struct perf_event *bp); 128extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
125extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, 129extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 948b73148d56..1b3c747fedda 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -317,9 +317,21 @@ static int get_hbp_len(u8 hbp_len)
317 case ARM_BREAKPOINT_LEN_2: 317 case ARM_BREAKPOINT_LEN_2:
318 len_in_bytes = 2; 318 len_in_bytes = 2;
319 break; 319 break;
320 case ARM_BREAKPOINT_LEN_3:
321 len_in_bytes = 3;
322 break;
320 case ARM_BREAKPOINT_LEN_4: 323 case ARM_BREAKPOINT_LEN_4:
321 len_in_bytes = 4; 324 len_in_bytes = 4;
322 break; 325 break;
326 case ARM_BREAKPOINT_LEN_5:
327 len_in_bytes = 5;
328 break;
329 case ARM_BREAKPOINT_LEN_6:
330 len_in_bytes = 6;
331 break;
332 case ARM_BREAKPOINT_LEN_7:
333 len_in_bytes = 7;
334 break;
323 case ARM_BREAKPOINT_LEN_8: 335 case ARM_BREAKPOINT_LEN_8:
324 len_in_bytes = 8; 336 len_in_bytes = 8;
325 break; 337 break;
@@ -349,7 +361,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
349 * to generic breakpoint descriptions. 361 * to generic breakpoint descriptions.
350 */ 362 */
351int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, 363int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
352 int *gen_len, int *gen_type) 364 int *gen_len, int *gen_type, int *offset)
353{ 365{
354 /* Type */ 366 /* Type */
355 switch (ctrl.type) { 367 switch (ctrl.type) {
@@ -369,17 +381,33 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
369 return -EINVAL; 381 return -EINVAL;
370 } 382 }
371 383
384 if (!ctrl.len)
385 return -EINVAL;
386 *offset = __ffs(ctrl.len);
387
372 /* Len */ 388 /* Len */
373 switch (ctrl.len) { 389 switch (ctrl.len >> *offset) {
374 case ARM_BREAKPOINT_LEN_1: 390 case ARM_BREAKPOINT_LEN_1:
375 *gen_len = HW_BREAKPOINT_LEN_1; 391 *gen_len = HW_BREAKPOINT_LEN_1;
376 break; 392 break;
377 case ARM_BREAKPOINT_LEN_2: 393 case ARM_BREAKPOINT_LEN_2:
378 *gen_len = HW_BREAKPOINT_LEN_2; 394 *gen_len = HW_BREAKPOINT_LEN_2;
379 break; 395 break;
396 case ARM_BREAKPOINT_LEN_3:
397 *gen_len = HW_BREAKPOINT_LEN_3;
398 break;
380 case ARM_BREAKPOINT_LEN_4: 399 case ARM_BREAKPOINT_LEN_4:
381 *gen_len = HW_BREAKPOINT_LEN_4; 400 *gen_len = HW_BREAKPOINT_LEN_4;
382 break; 401 break;
402 case ARM_BREAKPOINT_LEN_5:
403 *gen_len = HW_BREAKPOINT_LEN_5;
404 break;
405 case ARM_BREAKPOINT_LEN_6:
406 *gen_len = HW_BREAKPOINT_LEN_6;
407 break;
408 case ARM_BREAKPOINT_LEN_7:
409 *gen_len = HW_BREAKPOINT_LEN_7;
410 break;
383 case ARM_BREAKPOINT_LEN_8: 411 case ARM_BREAKPOINT_LEN_8:
384 *gen_len = HW_BREAKPOINT_LEN_8; 412 *gen_len = HW_BREAKPOINT_LEN_8;
385 break; 413 break;
@@ -423,9 +451,21 @@ static int arch_build_bp_info(struct perf_event *bp)
423 case HW_BREAKPOINT_LEN_2: 451 case HW_BREAKPOINT_LEN_2:
424 info->ctrl.len = ARM_BREAKPOINT_LEN_2; 452 info->ctrl.len = ARM_BREAKPOINT_LEN_2;
425 break; 453 break;
454 case HW_BREAKPOINT_LEN_3:
455 info->ctrl.len = ARM_BREAKPOINT_LEN_3;
456 break;
426 case HW_BREAKPOINT_LEN_4: 457 case HW_BREAKPOINT_LEN_4:
427 info->ctrl.len = ARM_BREAKPOINT_LEN_4; 458 info->ctrl.len = ARM_BREAKPOINT_LEN_4;
428 break; 459 break;
460 case HW_BREAKPOINT_LEN_5:
461 info->ctrl.len = ARM_BREAKPOINT_LEN_5;
462 break;
463 case HW_BREAKPOINT_LEN_6:
464 info->ctrl.len = ARM_BREAKPOINT_LEN_6;
465 break;
466 case HW_BREAKPOINT_LEN_7:
467 info->ctrl.len = ARM_BREAKPOINT_LEN_7;
468 break;
429 case HW_BREAKPOINT_LEN_8: 469 case HW_BREAKPOINT_LEN_8:
430 info->ctrl.len = ARM_BREAKPOINT_LEN_8; 470 info->ctrl.len = ARM_BREAKPOINT_LEN_8;
431 break; 471 break;
@@ -517,18 +557,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
517 default: 557 default:
518 return -EINVAL; 558 return -EINVAL;
519 } 559 }
520
521 info->address &= ~alignment_mask;
522 info->ctrl.len <<= offset;
523 } else { 560 } else {
524 if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) 561 if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
525 alignment_mask = 0x3; 562 alignment_mask = 0x3;
526 else 563 else
527 alignment_mask = 0x7; 564 alignment_mask = 0x7;
528 if (info->address & alignment_mask) 565 offset = info->address & alignment_mask;
529 return -EINVAL;
530 } 566 }
531 567
568 info->address &= ~alignment_mask;
569 info->ctrl.len <<= offset;
570
532 /* 571 /*
533 * Disallow per-task kernel breakpoints since these would 572 * Disallow per-task kernel breakpoints since these would
534 * complicate the stepping code. 573 * complicate the stepping code.
@@ -661,12 +700,47 @@ unlock:
661} 700}
662NOKPROBE_SYMBOL(breakpoint_handler); 701NOKPROBE_SYMBOL(breakpoint_handler);
663 702
703/*
704 * Arm64 hardware does not always report a watchpoint hit address that matches
705 * one of the watchpoints set. It can also report an address "near" the
706 * watchpoint if a single instruction access both watched and unwatched
707 * addresses. There is no straight-forward way, short of disassembling the
708 * offending instruction, to map that address back to the watchpoint. This
709 * function computes the distance of the memory access from the watchpoint as a
710 * heuristic for the likelyhood that a given access triggered the watchpoint.
711 *
712 * See Section D2.10.5 "Determining the memory location that caused a Watchpoint
713 * exception" of ARMv8 Architecture Reference Manual for details.
714 *
715 * The function returns the distance of the address from the bytes watched by
716 * the watchpoint. In case of an exact match, it returns 0.
717 */
718static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
719 struct arch_hw_breakpoint_ctrl *ctrl)
720{
721 u64 wp_low, wp_high;
722 u32 lens, lene;
723
724 lens = __ffs(ctrl->len);
725 lene = __fls(ctrl->len);
726
727 wp_low = val + lens;
728 wp_high = val + lene;
729 if (addr < wp_low)
730 return wp_low - addr;
731 else if (addr > wp_high)
732 return addr - wp_high;
733 else
734 return 0;
735}
736
664static int watchpoint_handler(unsigned long addr, unsigned int esr, 737static int watchpoint_handler(unsigned long addr, unsigned int esr,
665 struct pt_regs *regs) 738 struct pt_regs *regs)
666{ 739{
667 int i, step = 0, *kernel_step, access; 740 int i, step = 0, *kernel_step, access, closest_match = 0;
741 u64 min_dist = -1, dist;
668 u32 ctrl_reg; 742 u32 ctrl_reg;
669 u64 val, alignment_mask; 743 u64 val;
670 struct perf_event *wp, **slots; 744 struct perf_event *wp, **slots;
671 struct debug_info *debug_info; 745 struct debug_info *debug_info;
672 struct arch_hw_breakpoint *info; 746 struct arch_hw_breakpoint *info;
@@ -675,35 +749,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
675 slots = this_cpu_ptr(wp_on_reg); 749 slots = this_cpu_ptr(wp_on_reg);
676 debug_info = &current->thread.debug; 750 debug_info = &current->thread.debug;
677 751
752 /*
753 * Find all watchpoints that match the reported address. If no exact
754 * match is found. Attribute the hit to the closest watchpoint.
755 */
756 rcu_read_lock();
678 for (i = 0; i < core_num_wrps; ++i) { 757 for (i = 0; i < core_num_wrps; ++i) {
679 rcu_read_lock();
680
681 wp = slots[i]; 758 wp = slots[i];
682
683 if (wp == NULL) 759 if (wp == NULL)
684 goto unlock; 760 continue;
685
686 info = counter_arch_bp(wp);
687 /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
688 if (is_compat_task()) {
689 if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
690 alignment_mask = 0x7;
691 else
692 alignment_mask = 0x3;
693 } else {
694 alignment_mask = 0x7;
695 }
696
697 /* Check if the watchpoint value matches. */
698 val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
699 if (val != (addr & ~alignment_mask))
700 goto unlock;
701
702 /* Possible match, check the byte address select to confirm. */
703 ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
704 decode_ctrl_reg(ctrl_reg, &ctrl);
705 if (!((1 << (addr & alignment_mask)) & ctrl.len))
706 goto unlock;
707 761
708 /* 762 /*
709 * Check that the access type matches. 763 * Check that the access type matches.
@@ -712,18 +766,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
712 access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : 766 access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
713 HW_BREAKPOINT_R; 767 HW_BREAKPOINT_R;
714 if (!(access & hw_breakpoint_type(wp))) 768 if (!(access & hw_breakpoint_type(wp)))
715 goto unlock; 769 continue;
716 770
771 /* Check if the watchpoint value and byte select match. */
772 val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
773 ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
774 decode_ctrl_reg(ctrl_reg, &ctrl);
775 dist = get_distance_from_watchpoint(addr, val, &ctrl);
776 if (dist < min_dist) {
777 min_dist = dist;
778 closest_match = i;
779 }
780 /* Is this an exact match? */
781 if (dist != 0)
782 continue;
783
784 info = counter_arch_bp(wp);
717 info->trigger = addr; 785 info->trigger = addr;
718 perf_bp_event(wp, regs); 786 perf_bp_event(wp, regs);
719 787
720 /* Do we need to handle the stepping? */ 788 /* Do we need to handle the stepping? */
721 if (is_default_overflow_handler(wp)) 789 if (is_default_overflow_handler(wp))
722 step = 1; 790 step = 1;
791 }
792 if (min_dist > 0 && min_dist != -1) {
793 /* No exact match found. */
794 wp = slots[closest_match];
795 info = counter_arch_bp(wp);
796 info->trigger = addr;
797 perf_bp_event(wp, regs);
723 798
724unlock: 799 /* Do we need to handle the stepping? */
725 rcu_read_unlock(); 800 if (is_default_overflow_handler(wp))
801 step = 1;
726 } 802 }
803 rcu_read_unlock();
727 804
728 if (!step) 805 if (!step)
729 return 0; 806 return 0;
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index e0c81da60f76..fc35e06ccaac 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -327,13 +327,13 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
327 struct arch_hw_breakpoint_ctrl ctrl, 327 struct arch_hw_breakpoint_ctrl ctrl,
328 struct perf_event_attr *attr) 328 struct perf_event_attr *attr)
329{ 329{
330 int err, len, type, disabled = !ctrl.enabled; 330 int err, len, type, offset, disabled = !ctrl.enabled;
331 331
332 attr->disabled = disabled; 332 attr->disabled = disabled;
333 if (disabled) 333 if (disabled)
334 return 0; 334 return 0;
335 335
336 err = arch_bp_generic_fields(ctrl, &len, &type); 336 err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
337 if (err) 337 if (err)
338 return err; 338 return err;
339 339
@@ -352,6 +352,7 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
352 352
353 attr->bp_len = len; 353 attr->bp_len = len;
354 attr->bp_type = type; 354 attr->bp_type = type;
355 attr->bp_addr += offset;
355 356
356 return 0; 357 return 0;
357} 358}
@@ -404,7 +405,7 @@ static int ptrace_hbp_get_addr(unsigned int note_type,
404 if (IS_ERR(bp)) 405 if (IS_ERR(bp))
405 return PTR_ERR(bp); 406 return PTR_ERR(bp);
406 407
407 *addr = bp ? bp->attr.bp_addr : 0; 408 *addr = bp ? counter_arch_bp(bp)->address : 0;
408 return 0; 409 return 0;
409} 410}
410 411
diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h
index b04000a2296a..2b65efd19a46 100644
--- a/include/uapi/linux/hw_breakpoint.h
+++ b/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
4enum { 4enum {
5 HW_BREAKPOINT_LEN_1 = 1, 5 HW_BREAKPOINT_LEN_1 = 1,
6 HW_BREAKPOINT_LEN_2 = 2, 6 HW_BREAKPOINT_LEN_2 = 2,
7 HW_BREAKPOINT_LEN_3 = 3,
7 HW_BREAKPOINT_LEN_4 = 4, 8 HW_BREAKPOINT_LEN_4 = 4,
9 HW_BREAKPOINT_LEN_5 = 5,
10 HW_BREAKPOINT_LEN_6 = 6,
11 HW_BREAKPOINT_LEN_7 = 7,
8 HW_BREAKPOINT_LEN_8 = 8, 12 HW_BREAKPOINT_LEN_8 = 8,
9}; 13};
10 14
diff --git a/tools/include/uapi/linux/hw_breakpoint.h b/tools/include/uapi/linux/hw_breakpoint.h
index b04000a2296a..2b65efd19a46 100644
--- a/tools/include/uapi/linux/hw_breakpoint.h
+++ b/tools/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
4enum { 4enum {
5 HW_BREAKPOINT_LEN_1 = 1, 5 HW_BREAKPOINT_LEN_1 = 1,
6 HW_BREAKPOINT_LEN_2 = 2, 6 HW_BREAKPOINT_LEN_2 = 2,
7 HW_BREAKPOINT_LEN_3 = 3,
7 HW_BREAKPOINT_LEN_4 = 4, 8 HW_BREAKPOINT_LEN_4 = 4,
9 HW_BREAKPOINT_LEN_5 = 5,
10 HW_BREAKPOINT_LEN_6 = 6,
11 HW_BREAKPOINT_LEN_7 = 7,
8 HW_BREAKPOINT_LEN_8 = 8, 12 HW_BREAKPOINT_LEN_8 = 8,
9}; 13};
10 14
diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile
index 74e533fd4bc5..61b79e8df1f4 100644
--- a/tools/testing/selftests/breakpoints/Makefile
+++ b/tools/testing/selftests/breakpoints/Makefile
@@ -5,6 +5,9 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
5ifeq ($(ARCH),x86) 5ifeq ($(ARCH),x86)
6TEST_PROGS := breakpoint_test 6TEST_PROGS := breakpoint_test
7endif 7endif
8ifeq ($(ARCH),aarch64)
9TEST_PROGS := breakpoint_test_arm64
10endif
8 11
9TEST_PROGS += step_after_suspend_test 12TEST_PROGS += step_after_suspend_test
10 13
@@ -13,4 +16,4 @@ all: $(TEST_PROGS)
13include ../lib.mk 16include ../lib.mk
14 17
15clean: 18clean:
16 rm -fr breakpoint_test step_after_suspend_test 19 rm -fr breakpoint_test breakpoint_test_arm64 step_after_suspend_test
diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
new file mode 100644
index 000000000000..3897e996541e
--- /dev/null
+++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
@@ -0,0 +1,236 @@
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}