162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Google, Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Original Code by Pavel Labath <labath@google.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Code modified by Pratyush Anand <panand@redhat.com>
862306a36Sopenharmony_ci * for testing different byte select for each access size.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define _GNU_SOURCE
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/ptrace.h>
1462306a36Sopenharmony_ci#include <sys/types.h>
1562306a36Sopenharmony_ci#include <sys/wait.h>
1662306a36Sopenharmony_ci#include <sys/ptrace.h>
1762306a36Sopenharmony_ci#include <sys/param.h>
1862306a36Sopenharmony_ci#include <sys/uio.h>
1962306a36Sopenharmony_ci#include <stdint.h>
2062306a36Sopenharmony_ci#include <stdbool.h>
2162306a36Sopenharmony_ci#include <stddef.h>
2262306a36Sopenharmony_ci#include <string.h>
2362306a36Sopenharmony_ci#include <stdio.h>
2462306a36Sopenharmony_ci#include <unistd.h>
2562306a36Sopenharmony_ci#include <elf.h>
2662306a36Sopenharmony_ci#include <errno.h>
2762306a36Sopenharmony_ci#include <signal.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "../kselftest.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic volatile uint8_t var[96] __attribute__((__aligned__(32)));
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic void child(int size, int wr)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	volatile uint8_t *addr = &var[32 + wr];
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
3862306a36Sopenharmony_ci		ksft_print_msg(
3962306a36Sopenharmony_ci			"ptrace(PTRACE_TRACEME) failed: %s\n",
4062306a36Sopenharmony_ci			strerror(errno));
4162306a36Sopenharmony_ci		_exit(1);
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (raise(SIGSTOP) != 0) {
4562306a36Sopenharmony_ci		ksft_print_msg(
4662306a36Sopenharmony_ci			"raise(SIGSTOP) failed: %s\n", strerror(errno));
4762306a36Sopenharmony_ci		_exit(1);
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if ((uintptr_t) addr % size) {
5162306a36Sopenharmony_ci		ksft_print_msg(
5262306a36Sopenharmony_ci			 "Wrong address write for the given size: %s\n",
5362306a36Sopenharmony_ci			 strerror(errno));
5462306a36Sopenharmony_ci		_exit(1);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	switch (size) {
5862306a36Sopenharmony_ci	case 1:
5962306a36Sopenharmony_ci		*addr = 47;
6062306a36Sopenharmony_ci		break;
6162306a36Sopenharmony_ci	case 2:
6262306a36Sopenharmony_ci		*(uint16_t *)addr = 47;
6362306a36Sopenharmony_ci		break;
6462306a36Sopenharmony_ci	case 4:
6562306a36Sopenharmony_ci		*(uint32_t *)addr = 47;
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	case 8:
6862306a36Sopenharmony_ci		*(uint64_t *)addr = 47;
6962306a36Sopenharmony_ci		break;
7062306a36Sopenharmony_ci	case 16:
7162306a36Sopenharmony_ci		__asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0]));
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci	case 32:
7462306a36Sopenharmony_ci		__asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0]));
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	_exit(0);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic bool set_watchpoint(pid_t pid, int size, int wp)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	const volatile uint8_t *addr = &var[32 + wp];
8462306a36Sopenharmony_ci	const int offset = (uintptr_t)addr % 8;
8562306a36Sopenharmony_ci	const unsigned int byte_mask = ((1 << size) - 1) << offset;
8662306a36Sopenharmony_ci	const unsigned int type = 2; /* Write */
8762306a36Sopenharmony_ci	const unsigned int enable = 1;
8862306a36Sopenharmony_ci	const unsigned int control = byte_mask << 5 | type << 3 | enable;
8962306a36Sopenharmony_ci	struct user_hwdebug_state dreg_state;
9062306a36Sopenharmony_ci	struct iovec iov;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	memset(&dreg_state, 0, sizeof(dreg_state));
9362306a36Sopenharmony_ci	dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset);
9462306a36Sopenharmony_ci	dreg_state.dbg_regs[0].ctrl = control;
9562306a36Sopenharmony_ci	iov.iov_base = &dreg_state;
9662306a36Sopenharmony_ci	iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) +
9762306a36Sopenharmony_ci				sizeof(dreg_state.dbg_regs[0]);
9862306a36Sopenharmony_ci	if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0)
9962306a36Sopenharmony_ci		return true;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (errno == EIO)
10262306a36Sopenharmony_ci		ksft_print_msg(
10362306a36Sopenharmony_ci			"ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n",
10462306a36Sopenharmony_ci			strerror(errno));
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ksft_print_msg(
10762306a36Sopenharmony_ci		"ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n",
10862306a36Sopenharmony_ci		strerror(errno));
10962306a36Sopenharmony_ci	return false;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic bool run_test(int wr_size, int wp_size, int wr, int wp)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	int status;
11562306a36Sopenharmony_ci	siginfo_t siginfo;
11662306a36Sopenharmony_ci	pid_t pid = fork();
11762306a36Sopenharmony_ci	pid_t wpid;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (pid < 0) {
12062306a36Sopenharmony_ci		ksft_test_result_fail(
12162306a36Sopenharmony_ci			"fork() failed: %s\n", strerror(errno));
12262306a36Sopenharmony_ci		return false;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	if (pid == 0)
12562306a36Sopenharmony_ci		child(wr_size, wr);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	wpid = waitpid(pid, &status, __WALL);
12862306a36Sopenharmony_ci	if (wpid != pid) {
12962306a36Sopenharmony_ci		ksft_print_msg(
13062306a36Sopenharmony_ci			"waitpid() failed: %s\n", strerror(errno));
13162306a36Sopenharmony_ci		return false;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci	if (!WIFSTOPPED(status)) {
13462306a36Sopenharmony_ci		ksft_print_msg(
13562306a36Sopenharmony_ci			"child did not stop: %s\n", strerror(errno));
13662306a36Sopenharmony_ci		return false;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	if (WSTOPSIG(status) != SIGSTOP) {
13962306a36Sopenharmony_ci		ksft_print_msg("child did not stop with SIGSTOP\n");
14062306a36Sopenharmony_ci		return false;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!set_watchpoint(pid, wp_size, wp))
14462306a36Sopenharmony_ci		return false;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
14762306a36Sopenharmony_ci		ksft_print_msg(
14862306a36Sopenharmony_ci			"ptrace(PTRACE_CONT) failed: %s\n",
14962306a36Sopenharmony_ci			strerror(errno));
15062306a36Sopenharmony_ci		return false;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	alarm(3);
15462306a36Sopenharmony_ci	wpid = waitpid(pid, &status, __WALL);
15562306a36Sopenharmony_ci	if (wpid != pid) {
15662306a36Sopenharmony_ci		ksft_print_msg(
15762306a36Sopenharmony_ci			"waitpid() failed: %s\n", strerror(errno));
15862306a36Sopenharmony_ci		return false;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	alarm(0);
16162306a36Sopenharmony_ci	if (WIFEXITED(status)) {
16262306a36Sopenharmony_ci		ksft_print_msg("child exited prematurely\n");
16362306a36Sopenharmony_ci		return false;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	if (!WIFSTOPPED(status)) {
16662306a36Sopenharmony_ci		ksft_print_msg("child did not stop\n");
16762306a36Sopenharmony_ci		return false;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	if (WSTOPSIG(status) != SIGTRAP) {
17062306a36Sopenharmony_ci		ksft_print_msg("child did not stop with SIGTRAP\n");
17162306a36Sopenharmony_ci		return false;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {
17462306a36Sopenharmony_ci		ksft_print_msg(
17562306a36Sopenharmony_ci			"ptrace(PTRACE_GETSIGINFO): %s\n",
17662306a36Sopenharmony_ci			strerror(errno));
17762306a36Sopenharmony_ci		return false;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	if (siginfo.si_code != TRAP_HWBKPT) {
18062306a36Sopenharmony_ci		ksft_print_msg(
18162306a36Sopenharmony_ci			"Unexpected si_code %d\n", siginfo.si_code);
18262306a36Sopenharmony_ci		return false;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	kill(pid, SIGKILL);
18662306a36Sopenharmony_ci	wpid = waitpid(pid, &status, 0);
18762306a36Sopenharmony_ci	if (wpid != pid) {
18862306a36Sopenharmony_ci		ksft_print_msg(
18962306a36Sopenharmony_ci			"waitpid() failed: %s\n", strerror(errno));
19062306a36Sopenharmony_ci		return false;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	return true;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic void sigalrm(int sig)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciint main(int argc, char **argv)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int opt;
20262306a36Sopenharmony_ci	bool succeeded = true;
20362306a36Sopenharmony_ci	struct sigaction act;
20462306a36Sopenharmony_ci	int wr, wp, size;
20562306a36Sopenharmony_ci	bool result;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	ksft_print_header();
20862306a36Sopenharmony_ci	ksft_set_plan(213);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	act.sa_handler = sigalrm;
21162306a36Sopenharmony_ci	sigemptyset(&act.sa_mask);
21262306a36Sopenharmony_ci	act.sa_flags = 0;
21362306a36Sopenharmony_ci	sigaction(SIGALRM, &act, NULL);
21462306a36Sopenharmony_ci	for (size = 1; size <= 32; size = size*2) {
21562306a36Sopenharmony_ci		for (wr = 0; wr <= 32; wr = wr + size) {
21662306a36Sopenharmony_ci			for (wp = wr - size; wp <= wr + size; wp = wp + size) {
21762306a36Sopenharmony_ci				result = run_test(size, MIN(size, 8), wr, wp);
21862306a36Sopenharmony_ci				if ((result && wr == wp) ||
21962306a36Sopenharmony_ci				    (!result && wr != wp))
22062306a36Sopenharmony_ci					ksft_test_result_pass(
22162306a36Sopenharmony_ci						"Test size = %d write offset = %d watchpoint offset = %d\n",
22262306a36Sopenharmony_ci						size, wr, wp);
22362306a36Sopenharmony_ci				else {
22462306a36Sopenharmony_ci					ksft_test_result_fail(
22562306a36Sopenharmony_ci						"Test size = %d write offset = %d watchpoint offset = %d\n",
22662306a36Sopenharmony_ci						size, wr, wp);
22762306a36Sopenharmony_ci					succeeded = false;
22862306a36Sopenharmony_ci				}
22962306a36Sopenharmony_ci			}
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	for (size = 1; size <= 32; size = size*2) {
23462306a36Sopenharmony_ci		if (run_test(size, 8, -size, -8))
23562306a36Sopenharmony_ci			ksft_test_result_pass(
23662306a36Sopenharmony_ci				"Test size = %d write offset = %d watchpoint offset = -8\n",
23762306a36Sopenharmony_ci				size, -size);
23862306a36Sopenharmony_ci		else {
23962306a36Sopenharmony_ci			ksft_test_result_fail(
24062306a36Sopenharmony_ci				"Test size = %d write offset = %d watchpoint offset = -8\n",
24162306a36Sopenharmony_ci				size, -size);
24262306a36Sopenharmony_ci			succeeded = false;
24362306a36Sopenharmony_ci		}
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (succeeded)
24762306a36Sopenharmony_ci		ksft_exit_pass();
24862306a36Sopenharmony_ci	else
24962306a36Sopenharmony_ci		ksft_exit_fail();
25062306a36Sopenharmony_ci}
251