Path: blob/master/tools/testing/selftests/arm64/abi/ptrace.c
26289 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2022 ARM Limited.3*/4#include <errno.h>5#include <stdbool.h>6#include <stddef.h>7#include <stdio.h>8#include <stdlib.h>9#include <string.h>10#include <unistd.h>11#include <sys/auxv.h>12#include <sys/prctl.h>13#include <sys/ptrace.h>14#include <sys/types.h>15#include <sys/uio.h>16#include <sys/wait.h>17#include <asm/sigcontext.h>18#include <asm/ptrace.h>1920#include "../../kselftest.h"2122#define EXPECTED_TESTS 112324#define MAX_TPIDRS 22526static bool have_sme(void)27{28return getauxval(AT_HWCAP2) & HWCAP2_SME;29}3031static void test_tpidr(pid_t child)32{33uint64_t read_val[MAX_TPIDRS];34uint64_t write_val[MAX_TPIDRS];35struct iovec read_iov, write_iov;36bool test_tpidr2 = false;37int ret, i;3839read_iov.iov_base = read_val;40write_iov.iov_base = write_val;4142/* Should be able to read a single TPIDR... */43read_iov.iov_len = sizeof(uint64_t);44ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);45ksft_test_result(ret == 0, "read_tpidr_one\n");4647/* ...write a new value.. */48write_iov.iov_len = sizeof(uint64_t);49write_val[0] = read_val[0] + 1;50ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);51ksft_test_result(ret == 0, "write_tpidr_one\n");5253/* ...then read it back */54ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);55ksft_test_result(ret == 0 && write_val[0] == read_val[0],56"verify_tpidr_one\n");5758/* If we have TPIDR2 we should be able to read it */59read_iov.iov_len = sizeof(read_val);60ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);61if (ret == 0) {62/* If we have SME there should be two TPIDRs */63if (read_iov.iov_len >= sizeof(read_val))64test_tpidr2 = true;6566if (have_sme() && test_tpidr2) {67ksft_test_result(test_tpidr2, "count_tpidrs\n");68} else {69ksft_test_result(read_iov.iov_len % sizeof(uint64_t) == 0,70"count_tpidrs\n");71}72} else {73ksft_test_result_fail("count_tpidrs\n");74}7576if (test_tpidr2) {77/* Try to write new values to all known TPIDRs... */78write_iov.iov_len = sizeof(write_val);79for (i = 0; i < MAX_TPIDRS; i++)80write_val[i] = read_val[i] + 1;81ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);8283ksft_test_result(ret == 0 &&84write_iov.iov_len == sizeof(write_val),85"tpidr2_write\n");8687/* ...then read them back */88read_iov.iov_len = sizeof(read_val);89ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);9091if (have_sme()) {92/* Should read back the written value */93ksft_test_result(ret == 0 &&94read_iov.iov_len >= sizeof(read_val) &&95memcmp(read_val, write_val,96sizeof(read_val)) == 0,97"tpidr2_read\n");98} else {99/* TPIDR2 should read as zero */100ksft_test_result(ret == 0 &&101read_iov.iov_len >= sizeof(read_val) &&102read_val[0] == write_val[0] &&103read_val[1] == 0,104"tpidr2_read\n");105}106107/* Writing only TPIDR... */108write_iov.iov_len = sizeof(uint64_t);109memcpy(write_val, read_val, sizeof(read_val));110write_val[0] += 1;111ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);112113if (ret == 0) {114/* ...should leave TPIDR2 untouched */115read_iov.iov_len = sizeof(read_val);116ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS,117&read_iov);118119ksft_test_result(ret == 0 &&120read_iov.iov_len >= sizeof(read_val) &&121memcmp(read_val, write_val,122sizeof(read_val)) == 0,123"write_tpidr_only\n");124} else {125ksft_test_result_fail("write_tpidr_only\n");126}127} else {128ksft_test_result_skip("tpidr2_write\n");129ksft_test_result_skip("tpidr2_read\n");130ksft_test_result_skip("write_tpidr_only\n");131}132}133134static void test_hw_debug(pid_t child, int type, const char *type_name)135{136struct user_hwdebug_state state;137struct iovec iov;138int slots, arch, ret;139140iov.iov_len = sizeof(state);141iov.iov_base = &state;142143/* Should be able to read the values */144ret = ptrace(PTRACE_GETREGSET, child, type, &iov);145ksft_test_result(ret == 0, "read_%s\n", type_name);146147if (ret == 0) {148/* Low 8 bits is the number of slots, next 4 bits the arch */149slots = state.dbg_info & 0xff;150arch = (state.dbg_info >> 8) & 0xf;151152ksft_print_msg("%s version %d with %d slots\n", type_name,153arch, slots);154155/* Zero is not currently architecturally valid */156ksft_test_result(arch, "%s_arch_set\n", type_name);157} else {158ksft_test_result_skip("%s_arch_set\n", type_name);159}160}161162static int do_child(void)163{164if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))165ksft_exit_fail_perror("PTRACE_TRACEME");166167if (raise(SIGSTOP))168ksft_exit_fail_perror("raise(SIGSTOP)");169170return EXIT_SUCCESS;171}172173static int do_parent(pid_t child)174{175int ret = EXIT_FAILURE;176pid_t pid;177int status;178siginfo_t si;179180/* Attach to the child */181while (1) {182int sig;183184pid = wait(&status);185if (pid == -1) {186perror("wait");187goto error;188}189190/*191* This should never happen but it's hard to flag in192* the framework.193*/194if (pid != child)195continue;196197if (WIFEXITED(status) || WIFSIGNALED(status))198ksft_exit_fail_msg("Child died unexpectedly\n");199200if (!WIFSTOPPED(status))201goto error;202203sig = WSTOPSIG(status);204205if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {206if (errno == ESRCH)207goto disappeared;208209if (errno == EINVAL) {210sig = 0; /* bust group-stop */211goto cont;212}213214ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",215strerror(errno));216goto error;217}218219if (sig == SIGSTOP && si.si_code == SI_TKILL &&220si.si_pid == pid)221break;222223cont:224if (ptrace(PTRACE_CONT, pid, NULL, sig)) {225if (errno == ESRCH)226goto disappeared;227228ksft_test_result_fail("PTRACE_CONT: %s\n",229strerror(errno));230goto error;231}232}233234ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);235236test_tpidr(child);237test_hw_debug(child, NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH");238test_hw_debug(child, NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK");239240ret = EXIT_SUCCESS;241242error:243kill(child, SIGKILL);244245disappeared:246return ret;247}248249int main(void)250{251int ret = EXIT_SUCCESS;252pid_t child;253254srandom(getpid());255256ksft_print_header();257258ksft_set_plan(EXPECTED_TESTS);259260child = fork();261if (!child)262return do_child();263264if (do_parent(child))265ret = EXIT_FAILURE;266267ksft_print_cnts();268269return ret;270}271272273