@@ -18,15 +18,22 @@
#include <asm/sigcontext.h>
#include <asm/ptrace.h>
+#include "ptrace.h"
#include "../../kselftest.h"
#define EXPECTED_TESTS 11
#define MAX_TPIDRS 2
-static bool have_sme(void)
+static int do_child(void)
{
- return getauxval(AT_HWCAP2) & HWCAP2_SME;
+ if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
+ ksft_exit_fail_perror("PTRACE_TRACEME");
+
+ if (raise(SIGSTOP))
+ ksft_exit_fail_perror("raise(SIGSTOP)");
+
+ return EXIT_SUCCESS;
}
static void test_tpidr(pid_t child)
@@ -132,119 +139,11 @@ static void test_tpidr(pid_t child)
}
}
-static void test_hw_debug(pid_t child, int type, const char *type_name)
-{
- struct user_hwdebug_state state;
- struct iovec iov;
- int slots, arch, ret;
-
- iov.iov_len = sizeof(state);
- iov.iov_base = &state;
-
- /* Should be able to read the values */
- ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
- ksft_test_result(ret == 0, "read_%s\n", type_name);
-
- if (ret == 0) {
- /* Low 8 bits is the number of slots, next 4 bits the arch */
- slots = state.dbg_info & 0xff;
- arch = (state.dbg_info >> 8) & 0xf;
-
- ksft_print_msg("%s version %d with %d slots\n", type_name,
- arch, slots);
-
- /* Zero is not currently architecturally valid */
- ksft_test_result(arch, "%s_arch_set\n", type_name);
- } else {
- ksft_test_result_skip("%s_arch_set\n", type_name);
- }
-}
-
-static int do_child(void)
-{
- if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
- ksft_exit_fail_perror("PTRACE_TRACEME");
-
- if (raise(SIGSTOP))
- ksft_exit_fail_perror("raise(SIGSTOP)");
-
- return EXIT_SUCCESS;
-}
-
-static int do_parent(pid_t child)
+static void run_tests(pid_t child)
{
- int ret = EXIT_FAILURE;
- pid_t pid;
- int status;
- siginfo_t si;
-
- /* Attach to the child */
- while (1) {
- int sig;
-
- pid = wait(&status);
- if (pid == -1) {
- perror("wait");
- goto error;
- }
-
- /*
- * This should never happen but it's hard to flag in
- * the framework.
- */
- if (pid != child)
- continue;
-
- if (WIFEXITED(status) || WIFSIGNALED(status))
- ksft_exit_fail_msg("Child died unexpectedly\n");
-
- if (!WIFSTOPPED(status))
- goto error;
-
- sig = WSTOPSIG(status);
-
- if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
- if (errno == ESRCH)
- goto disappeared;
-
- if (errno == EINVAL) {
- sig = 0; /* bust group-stop */
- goto cont;
- }
-
- ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
- strerror(errno));
- goto error;
- }
-
- if (sig == SIGSTOP && si.si_code == SI_TKILL &&
- si.si_pid == pid)
- break;
-
- cont:
- if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
- if (errno == ESRCH)
- goto disappeared;
-
- ksft_test_result_fail("PTRACE_CONT: %s\n",
- strerror(errno));
- goto error;
- }
- }
-
- ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
-
test_tpidr(child);
test_hw_debug(child, NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH");
test_hw_debug(child, NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK");
-
- ret = EXIT_SUCCESS;
-
-error:
- kill(child, SIGKILL);
-
-disappeared:
- return ret;
}
int main(void)
new file mode 100644
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 ARM Limited.
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/auxv.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <asm/sigcontext.h>
+#include <asm/ptrace.h>
+
+#include "../../kselftest.h"
+
+static void run_tests(pid_t child);
+
+static int do_child(void);
+
+#ifdef __aarch64__
+static bool have_sme(void)
+{
+ return getauxval(AT_HWCAP2) & HWCAP2_SME;
+}
+
+static void test_hw_debug(pid_t child, int type, const char *type_name)
+{
+ struct user_hwdebug_state state;
+ struct iovec iov;
+ int slots, arch, ret;
+
+ iov.iov_len = sizeof(state);
+ iov.iov_base = &state;
+
+ /* Should be able to read the values */
+ ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
+ ksft_test_result(ret == 0, "read_%s\n", type_name);
+
+ if (ret == 0) {
+ /* Low 8 bits is the number of slots, next 4 bits the arch */
+ slots = state.dbg_info & 0xff;
+ arch = (state.dbg_info >> 8) & 0xf;
+
+ ksft_print_msg("%s version %d with %d slots\n", type_name,
+ arch, slots);
+
+ /* Zero is not currently architecturally valid */
+ ksft_test_result(arch, "%s_arch_set\n", type_name);
+ } else {
+ ksft_test_result_skip("%s_arch_set\n", type_name);
+ }
+}
+#endif
+
+static int do_parent(pid_t child)
+{
+ int ret = EXIT_FAILURE;
+ pid_t pid;
+ int status;
+ siginfo_t si;
+
+ /* Attach to the child */
+ while (1) {
+ int sig;
+
+ pid = wait(&status);
+ if (pid == -1) {
+ perror("wait");
+ goto error;
+ }
+
+ /*
+ * This should never happen but it's hard to flag in
+ * the framework.
+ */
+ if (pid != child)
+ continue;
+
+ if (WIFEXITED(status) || WIFSIGNALED(status))
+ ksft_exit_fail_msg("Child died unexpectedly\n");
+
+ if (!WIFSTOPPED(status))
+ goto error;
+
+ sig = WSTOPSIG(status);
+
+ if (sig == SIGTRAP)
+ ksft_print_msg("Child received SIGTRAP\n");
+
+ if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
+ if (errno == ESRCH)
+ goto disappeared;
+
+ if (errno == EINVAL)
+ goto cont;
+
+ ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
+ strerror(errno));
+ goto error;
+ }
+
+ if (sig == SIGSTOP && si.si_code == SI_TKILL &&
+ si.si_pid == pid)
+ break;
+
+cont:
+ /* bust group-stop */
+ if (ptrace(PTRACE_CONT, pid, NULL, 0)) {
+ if (errno == ESRCH)
+ goto disappeared;
+
+ ksft_test_result_fail("PTRACE_CONT: %s\n",
+ strerror(errno));
+ goto error;
+ }
+ }
+
+ ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
+
+ ret = EXIT_SUCCESS;
+ run_tests(child);
+
+error:
+ kill(child, SIGKILL);
+
+disappeared:
+ return ret;
+}
Split some of the common code to be used by selftests/arm, into ptrace.h. Signed-off-by: Dev Jain <dev.jain@arm.com> --- tools/testing/selftests/arm64/abi/ptrace.c | 121 ++---------------- tools/testing/selftests/arm64/abi/ptrace.h | 135 +++++++++++++++++++++ 2 files changed, 145 insertions(+), 111 deletions(-) create mode 100644 tools/testing/selftests/arm64/abi/ptrace.h