162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2022 ARM Limited.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <errno.h>
662306a36Sopenharmony_ci#include <stdbool.h>
762306a36Sopenharmony_ci#include <stddef.h>
862306a36Sopenharmony_ci#include <stdio.h>
962306a36Sopenharmony_ci#include <stdlib.h>
1062306a36Sopenharmony_ci#include <string.h>
1162306a36Sopenharmony_ci#include <unistd.h>
1262306a36Sopenharmony_ci#include <sys/auxv.h>
1362306a36Sopenharmony_ci#include <sys/prctl.h>
1462306a36Sopenharmony_ci#include <sys/ptrace.h>
1562306a36Sopenharmony_ci#include <sys/types.h>
1662306a36Sopenharmony_ci#include <sys/uio.h>
1762306a36Sopenharmony_ci#include <sys/wait.h>
1862306a36Sopenharmony_ci#include <asm/sigcontext.h>
1962306a36Sopenharmony_ci#include <asm/ptrace.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "../../kselftest.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define EXPECTED_TESTS 11
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define MAX_TPIDRS 2
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic bool have_sme(void)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	return getauxval(AT_HWCAP2) & HWCAP2_SME;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void test_tpidr(pid_t child)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	uint64_t read_val[MAX_TPIDRS];
3562306a36Sopenharmony_ci	uint64_t write_val[MAX_TPIDRS];
3662306a36Sopenharmony_ci	struct iovec read_iov, write_iov;
3762306a36Sopenharmony_ci	bool test_tpidr2 = false;
3862306a36Sopenharmony_ci	int ret, i;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	read_iov.iov_base = read_val;
4162306a36Sopenharmony_ci	write_iov.iov_base = write_val;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Should be able to read a single TPIDR... */
4462306a36Sopenharmony_ci	read_iov.iov_len = sizeof(uint64_t);
4562306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
4662306a36Sopenharmony_ci	ksft_test_result(ret == 0, "read_tpidr_one\n");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* ...write a new value.. */
4962306a36Sopenharmony_ci	write_iov.iov_len = sizeof(uint64_t);
5062306a36Sopenharmony_ci	write_val[0] = read_val[0]++;
5162306a36Sopenharmony_ci	ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
5262306a36Sopenharmony_ci	ksft_test_result(ret == 0, "write_tpidr_one\n");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* ...then read it back */
5562306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
5662306a36Sopenharmony_ci	ksft_test_result(ret == 0 && write_val[0] == read_val[0],
5762306a36Sopenharmony_ci			 "verify_tpidr_one\n");
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* If we have TPIDR2 we should be able to read it */
6062306a36Sopenharmony_ci	read_iov.iov_len = sizeof(read_val);
6162306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
6262306a36Sopenharmony_ci	if (ret == 0) {
6362306a36Sopenharmony_ci		/* If we have SME there should be two TPIDRs */
6462306a36Sopenharmony_ci		if (read_iov.iov_len >= sizeof(read_val))
6562306a36Sopenharmony_ci			test_tpidr2 = true;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		if (have_sme() && test_tpidr2) {
6862306a36Sopenharmony_ci			ksft_test_result(test_tpidr2, "count_tpidrs\n");
6962306a36Sopenharmony_ci		} else {
7062306a36Sopenharmony_ci			ksft_test_result(read_iov.iov_len % sizeof(uint64_t) == 0,
7162306a36Sopenharmony_ci					 "count_tpidrs\n");
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		ksft_test_result_fail("count_tpidrs\n");
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (test_tpidr2) {
7862306a36Sopenharmony_ci		/* Try to write new values to all known TPIDRs... */
7962306a36Sopenharmony_ci		write_iov.iov_len = sizeof(write_val);
8062306a36Sopenharmony_ci		for (i = 0; i < MAX_TPIDRS; i++)
8162306a36Sopenharmony_ci			write_val[i] = read_val[i] + 1;
8262306a36Sopenharmony_ci		ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		ksft_test_result(ret == 0 &&
8562306a36Sopenharmony_ci				 write_iov.iov_len == sizeof(write_val),
8662306a36Sopenharmony_ci				 "tpidr2_write\n");
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		/* ...then read them back */
8962306a36Sopenharmony_ci		read_iov.iov_len = sizeof(read_val);
9062306a36Sopenharmony_ci		ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		if (have_sme()) {
9362306a36Sopenharmony_ci			/* Should read back the written value */
9462306a36Sopenharmony_ci			ksft_test_result(ret == 0 &&
9562306a36Sopenharmony_ci					 read_iov.iov_len >= sizeof(read_val) &&
9662306a36Sopenharmony_ci					 memcmp(read_val, write_val,
9762306a36Sopenharmony_ci						sizeof(read_val)) == 0,
9862306a36Sopenharmony_ci					 "tpidr2_read\n");
9962306a36Sopenharmony_ci		} else {
10062306a36Sopenharmony_ci			/* TPIDR2 should read as zero */
10162306a36Sopenharmony_ci			ksft_test_result(ret == 0 &&
10262306a36Sopenharmony_ci					 read_iov.iov_len >= sizeof(read_val) &&
10362306a36Sopenharmony_ci					 read_val[0] == write_val[0] &&
10462306a36Sopenharmony_ci					 read_val[1] == 0,
10562306a36Sopenharmony_ci					 "tpidr2_read\n");
10662306a36Sopenharmony_ci		}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		/* Writing only TPIDR... */
10962306a36Sopenharmony_ci		write_iov.iov_len = sizeof(uint64_t);
11062306a36Sopenharmony_ci		memcpy(write_val, read_val, sizeof(read_val));
11162306a36Sopenharmony_ci		write_val[0] += 1;
11262306a36Sopenharmony_ci		ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (ret == 0) {
11562306a36Sopenharmony_ci			/* ...should leave TPIDR2 untouched */
11662306a36Sopenharmony_ci			read_iov.iov_len = sizeof(read_val);
11762306a36Sopenharmony_ci			ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS,
11862306a36Sopenharmony_ci				     &read_iov);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci			ksft_test_result(ret == 0 &&
12162306a36Sopenharmony_ci					 read_iov.iov_len >= sizeof(read_val) &&
12262306a36Sopenharmony_ci					 memcmp(read_val, write_val,
12362306a36Sopenharmony_ci						sizeof(read_val)) == 0,
12462306a36Sopenharmony_ci					 "write_tpidr_only\n");
12562306a36Sopenharmony_ci		} else {
12662306a36Sopenharmony_ci			ksft_test_result_fail("write_tpidr_only\n");
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	} else {
12962306a36Sopenharmony_ci		ksft_test_result_skip("tpidr2_write\n");
13062306a36Sopenharmony_ci		ksft_test_result_skip("tpidr2_read\n");
13162306a36Sopenharmony_ci		ksft_test_result_skip("write_tpidr_only\n");
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void test_hw_debug(pid_t child, int type, const char *type_name)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct user_hwdebug_state state;
13862306a36Sopenharmony_ci	struct iovec iov;
13962306a36Sopenharmony_ci	int slots, arch, ret;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	iov.iov_len = sizeof(state);
14262306a36Sopenharmony_ci	iov.iov_base = &state;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Should be able to read the values */
14562306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
14662306a36Sopenharmony_ci	ksft_test_result(ret == 0, "read_%s\n", type_name);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (ret == 0) {
14962306a36Sopenharmony_ci		/* Low 8 bits is the number of slots, next 4 bits the arch */
15062306a36Sopenharmony_ci		slots = state.dbg_info & 0xff;
15162306a36Sopenharmony_ci		arch = (state.dbg_info >> 8) & 0xf;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		ksft_print_msg("%s version %d with %d slots\n", type_name,
15462306a36Sopenharmony_ci			       arch, slots);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/* Zero is not currently architecturally valid */
15762306a36Sopenharmony_ci		ksft_test_result(arch, "%s_arch_set\n", type_name);
15862306a36Sopenharmony_ci	} else {
15962306a36Sopenharmony_ci		ksft_test_result_skip("%s_arch_set\n");
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int do_child(void)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
16662306a36Sopenharmony_ci		ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (raise(SIGSTOP))
16962306a36Sopenharmony_ci		ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return EXIT_SUCCESS;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int do_parent(pid_t child)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int ret = EXIT_FAILURE;
17762306a36Sopenharmony_ci	pid_t pid;
17862306a36Sopenharmony_ci	int status;
17962306a36Sopenharmony_ci	siginfo_t si;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Attach to the child */
18262306a36Sopenharmony_ci	while (1) {
18362306a36Sopenharmony_ci		int sig;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		pid = wait(&status);
18662306a36Sopenharmony_ci		if (pid == -1) {
18762306a36Sopenharmony_ci			perror("wait");
18862306a36Sopenharmony_ci			goto error;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		/*
19262306a36Sopenharmony_ci		 * This should never happen but it's hard to flag in
19362306a36Sopenharmony_ci		 * the framework.
19462306a36Sopenharmony_ci		 */
19562306a36Sopenharmony_ci		if (pid != child)
19662306a36Sopenharmony_ci			continue;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		if (WIFEXITED(status) || WIFSIGNALED(status))
19962306a36Sopenharmony_ci			ksft_exit_fail_msg("Child died unexpectedly\n");
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		if (!WIFSTOPPED(status))
20262306a36Sopenharmony_ci			goto error;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		sig = WSTOPSIG(status);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
20762306a36Sopenharmony_ci			if (errno == ESRCH)
20862306a36Sopenharmony_ci				goto disappeared;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			if (errno == EINVAL) {
21162306a36Sopenharmony_ci				sig = 0; /* bust group-stop */
21262306a36Sopenharmony_ci				goto cont;
21362306a36Sopenharmony_ci			}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci			ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
21662306a36Sopenharmony_ci					      strerror(errno));
21762306a36Sopenharmony_ci			goto error;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		if (sig == SIGSTOP && si.si_code == SI_TKILL &&
22162306a36Sopenharmony_ci		    si.si_pid == pid)
22262306a36Sopenharmony_ci			break;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	cont:
22562306a36Sopenharmony_ci		if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
22662306a36Sopenharmony_ci			if (errno == ESRCH)
22762306a36Sopenharmony_ci				goto disappeared;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci			ksft_test_result_fail("PTRACE_CONT: %s\n",
23062306a36Sopenharmony_ci					      strerror(errno));
23162306a36Sopenharmony_ci			goto error;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	test_tpidr(child);
23862306a36Sopenharmony_ci	test_hw_debug(child, NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH");
23962306a36Sopenharmony_ci	test_hw_debug(child, NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK");
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ret = EXIT_SUCCESS;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cierror:
24462306a36Sopenharmony_ci	kill(child, SIGKILL);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cidisappeared:
24762306a36Sopenharmony_ci	return ret;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciint main(void)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	int ret = EXIT_SUCCESS;
25362306a36Sopenharmony_ci	pid_t child;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	srandom(getpid());
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ksft_print_header();
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	ksft_set_plan(EXPECTED_TESTS);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	child = fork();
26262306a36Sopenharmony_ci	if (!child)
26362306a36Sopenharmony_ci		return do_child();
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (do_parent(child))
26662306a36Sopenharmony_ci		ret = EXIT_FAILURE;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ksft_print_cnts();
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return ret;
27162306a36Sopenharmony_ci}
272