brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · b51d21f Raw
272 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2022 ARM Limited.4 */5#include <errno.h>6#include <stdbool.h>7#include <stddef.h>8#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <unistd.h>12#include <sys/auxv.h>13#include <sys/prctl.h>14#include <sys/ptrace.h>15#include <sys/types.h>16#include <sys/uio.h>17#include <sys/wait.h>18#include <asm/sigcontext.h>19#include <asm/ptrace.h>20 21#include "../../kselftest.h"22 23#define EXPECTED_TESTS 1124 25#define MAX_TPIDRS 226 27static bool have_sme(void)28{29	return getauxval(AT_HWCAP2) & HWCAP2_SME;30}31 32static void test_tpidr(pid_t child)33{34	uint64_t read_val[MAX_TPIDRS];35	uint64_t write_val[MAX_TPIDRS];36	struct iovec read_iov, write_iov;37	bool test_tpidr2 = false;38	int ret, i;39 40	read_iov.iov_base = read_val;41	write_iov.iov_base = write_val;42 43	/* Should be able to read a single TPIDR... */44	read_iov.iov_len = sizeof(uint64_t);45	ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);46	ksft_test_result(ret == 0, "read_tpidr_one\n");47 48	/* ...write a new value.. */49	write_iov.iov_len = sizeof(uint64_t);50	write_val[0] = read_val[0] + 1;51	ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);52	ksft_test_result(ret == 0, "write_tpidr_one\n");53 54	/* ...then read it back */55	ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);56	ksft_test_result(ret == 0 && write_val[0] == read_val[0],57			 "verify_tpidr_one\n");58 59	/* If we have TPIDR2 we should be able to read it */60	read_iov.iov_len = sizeof(read_val);61	ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);62	if (ret == 0) {63		/* If we have SME there should be two TPIDRs */64		if (read_iov.iov_len >= sizeof(read_val))65			test_tpidr2 = true;66 67		if (have_sme() && test_tpidr2) {68			ksft_test_result(test_tpidr2, "count_tpidrs\n");69		} else {70			ksft_test_result(read_iov.iov_len % sizeof(uint64_t) == 0,71					 "count_tpidrs\n");72		}73	} else {74		ksft_test_result_fail("count_tpidrs\n");75	}76 77	if (test_tpidr2) {78		/* Try to write new values to all known TPIDRs... */79		write_iov.iov_len = sizeof(write_val);80		for (i = 0; i < MAX_TPIDRS; i++)81			write_val[i] = read_val[i] + 1;82		ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);83 84		ksft_test_result(ret == 0 &&85				 write_iov.iov_len == sizeof(write_val),86				 "tpidr2_write\n");87 88		/* ...then read them back */89		read_iov.iov_len = sizeof(read_val);90		ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);91 92		if (have_sme()) {93			/* Should read back the written value */94			ksft_test_result(ret == 0 &&95					 read_iov.iov_len >= sizeof(read_val) &&96					 memcmp(read_val, write_val,97						sizeof(read_val)) == 0,98					 "tpidr2_read\n");99		} else {100			/* TPIDR2 should read as zero */101			ksft_test_result(ret == 0 &&102					 read_iov.iov_len >= sizeof(read_val) &&103					 read_val[0] == write_val[0] &&104					 read_val[1] == 0,105					 "tpidr2_read\n");106		}107 108		/* Writing only TPIDR... */109		write_iov.iov_len = sizeof(uint64_t);110		memcpy(write_val, read_val, sizeof(read_val));111		write_val[0] += 1;112		ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);113 114		if (ret == 0) {115			/* ...should leave TPIDR2 untouched */116			read_iov.iov_len = sizeof(read_val);117			ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS,118				     &read_iov);119 120			ksft_test_result(ret == 0 &&121					 read_iov.iov_len >= sizeof(read_val) &&122					 memcmp(read_val, write_val,123						sizeof(read_val)) == 0,124					 "write_tpidr_only\n");125		} else {126			ksft_test_result_fail("write_tpidr_only\n");127		}128	} else {129		ksft_test_result_skip("tpidr2_write\n");130		ksft_test_result_skip("tpidr2_read\n");131		ksft_test_result_skip("write_tpidr_only\n");132	}133}134 135static void test_hw_debug(pid_t child, int type, const char *type_name)136{137	struct user_hwdebug_state state;138	struct iovec iov;139	int slots, arch, ret;140 141	iov.iov_len = sizeof(state);142	iov.iov_base = &state;143 144	/* Should be able to read the values */145	ret = ptrace(PTRACE_GETREGSET, child, type, &iov);146	ksft_test_result(ret == 0, "read_%s\n", type_name);147 148	if (ret == 0) {149		/* Low 8 bits is the number of slots, next 4 bits the arch */150		slots = state.dbg_info & 0xff;151		arch = (state.dbg_info >> 8) & 0xf;152 153		ksft_print_msg("%s version %d with %d slots\n", type_name,154			       arch, slots);155 156		/* Zero is not currently architecturally valid */157		ksft_test_result(arch, "%s_arch_set\n", type_name);158	} else {159		ksft_test_result_skip("%s_arch_set\n", type_name);160	}161}162 163static int do_child(void)164{165	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))166		ksft_exit_fail_perror("PTRACE_TRACEME");167 168	if (raise(SIGSTOP))169		ksft_exit_fail_perror("raise(SIGSTOP)");170 171	return EXIT_SUCCESS;172}173 174static int do_parent(pid_t child)175{176	int ret = EXIT_FAILURE;177	pid_t pid;178	int status;179	siginfo_t si;180 181	/* Attach to the child */182	while (1) {183		int sig;184 185		pid = wait(&status);186		if (pid == -1) {187			perror("wait");188			goto error;189		}190 191		/*192		 * This should never happen but it's hard to flag in193		 * the framework.194		 */195		if (pid != child)196			continue;197 198		if (WIFEXITED(status) || WIFSIGNALED(status))199			ksft_exit_fail_msg("Child died unexpectedly\n");200 201		if (!WIFSTOPPED(status))202			goto error;203 204		sig = WSTOPSIG(status);205 206		if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {207			if (errno == ESRCH)208				goto disappeared;209 210			if (errno == EINVAL) {211				sig = 0; /* bust group-stop */212				goto cont;213			}214 215			ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",216					      strerror(errno));217			goto error;218		}219 220		if (sig == SIGSTOP && si.si_code == SI_TKILL &&221		    si.si_pid == pid)222			break;223 224	cont:225		if (ptrace(PTRACE_CONT, pid, NULL, sig)) {226			if (errno == ESRCH)227				goto disappeared;228 229			ksft_test_result_fail("PTRACE_CONT: %s\n",230					      strerror(errno));231			goto error;232		}233	}234 235	ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);236 237	test_tpidr(child);238	test_hw_debug(child, NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH");239	test_hw_debug(child, NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK");240 241	ret = EXIT_SUCCESS;242 243error:244	kill(child, SIGKILL);245 246disappeared:247	return ret;248}249 250int main(void)251{252	int ret = EXIT_SUCCESS;253	pid_t child;254 255	srandom(getpid());256 257	ksft_print_header();258 259	ksft_set_plan(EXPECTED_TESTS);260 261	child = fork();262	if (!child)263		return do_child();264 265	if (do_parent(child))266		ret = EXIT_FAILURE;267 268	ksft_print_cnts();269 270	return ret;271}272