new file mode 100644
@@ -0,0 +1,82 @@
+// 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 "ptrace.h"
+#include "../../kselftest.h"
+
+#define EXPECTED_TESTS 6
+#define NUM_TLS_REGS 2
+
+static void test_tpidr(pid_t child)
+{
+ unsigned long read_val[NUM_TLS_REGS];
+ struct iovec read_iov;
+ int ret;
+
+ read_iov.iov_base = read_val;
+
+ /* TLS registers must not be accessible */
+ read_iov.iov_len = 2 * sizeof(unsigned long);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
+ ksft_test_result(ret != 0, "cannot read TLS\n");
+}
+
+static void run_tests(pid_t child)
+{
+ test_tpidr(child);
+ test_user_regs(child);
+}
+
+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)");
+
+ if (raise(SIGSTOP))
+ ksft_exit_fail_perror("raise(SIGSTOP)");
+
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ int ret = EXIT_SUCCESS;
+ pid_t child;
+
+ srandom(getpid());
+
+ ksft_print_header();
+
+ ksft_set_plan(EXPECTED_TESTS);
+
+ child = fork();
+ if (!child)
+ return do_child();
+
+ if (do_parent(child))
+ ret = EXIT_FAILURE;
+
+ ksft_print_cnts();
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../arm64/abi/ptrace.h"
+
+/* Do not pull from asm/ptrace.h since the macro names change for 32-bit */
+#define PSR_MODE32_BIT 0x00000010
+#define PSR_MODE_EL1t 0x00000004
+
+static void test_user_regs(pid_t child)
+{
+ unsigned int read_val[18];
+ struct iovec read_iov;
+ int status;
+ int ret;
+
+ read_iov.iov_base = read_val;
+ read_iov.iov_len = 18 * sizeof(unsigned int);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &read_iov);
+ ksft_test_result(!ret, "read general-purpose registers\n");
+
+ /* Change a random user register */
+ read_val[2] = read_val[2] + 1;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &read_iov);
+ ksft_test_result(!ret, "set user register\n");
+
+ /* 16th register is the CPSR */
+ read_val[16] &= (~PSR_MODE32_BIT);
+
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &read_iov);
+ ksft_test_result(ret, "cannot toggle MODE32 bit\n");
+
+ ret = ptrace(PTRACE_CONT, child, NULL, 0);
+ if (ret) {
+ perror("ptrace");
+ goto error;
+ }
+
+ if (wait(&status) == -1) {
+ perror("wait");
+ goto error;
+ }
+
+ read_val[16] = 0;
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &read_iov);
+ ksft_test_result(!ret, "read general-purpose registers again\n");
+
+ read_val[16] |= PSR_MODE_EL1t;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &read_iov);
+ ksft_test_result(ret, "cannot escalate privilege\n");
+ return;
+
+error:
+ kill(child, SIGKILL);
+}
+
+
For a 32-bit parent debugging a 32-bit child, add tests for reading the TLS registers, and mangling with the mode bits in CPSR. Signed-off-by: Dev Jain <dev.jain@arm.com> --- tools/testing/selftests/arm/abi/ptrace.c | 82 ++++++++++++++++++++++++ tools/testing/selftests/arm/abi/ptrace.h | 57 ++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 tools/testing/selftests/arm/abi/ptrace.c create mode 100644 tools/testing/selftests/arm/abi/ptrace.h