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