Add riscv specific selftest for hardhardware breakpoints.
These tests are based on:
tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
Signed-off-by: Jesse Taube <jesse@rivosinc.com>
---
The selftest fails as register_user_hw_breakpoint seemingly does not
call arch_install_hw_breakpoint. The test also seems to fail on arm64
in the same way when I tested it.
RFC -> V1:
- New commit
---
tools/perf/tests/tests.h | 3 +-
tools/testing/selftests/riscv/Makefile | 2 +-
.../selftests/riscv/breakpoints/.gitignore | 1 +
.../selftests/riscv/breakpoints/Makefile | 12 +
.../riscv/breakpoints/breakpoint_test.c | 246 ++++++++++++++++++
5 files changed, 262 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/riscv/breakpoints/.gitignore
create mode 100644 tools/testing/selftests/riscv/breakpoints/Makefile
create mode 100644 tools/testing/selftests/riscv/breakpoints/breakpoint_test.c
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 8aea344536b8..5ff35304c11a 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -183,7 +183,8 @@ DECLARE_SUITE(util);
* Just disable the test for these architectures until these issues are
* resolved.
*/
-#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) || defined(__aarch64__)
+#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) || defined(__aarch64__) || \
+ defined(__riscv)
#define BP_SIGNAL_IS_SUPPORTED 0
#else
#define BP_SIGNAL_IS_SUPPORTED 1
diff --git a/tools/testing/selftests/riscv/Makefile b/tools/testing/selftests/riscv/Makefile
index 099b8c1f46f8..96aba246cb3e 100644
--- a/tools/testing/selftests/riscv/Makefile
+++ b/tools/testing/selftests/riscv/Makefile
@@ -5,7 +5,7 @@
ARCH ?= $(shell uname -m 2>/dev/null || echo not)
ifneq (,$(filter $(ARCH),riscv))
-RISCV_SUBTARGETS ?= abi hwprobe mm sigreturn vector
+RISCV_SUBTARGETS ?= abi hwprobe mm sigreturn vector breakpoints
else
RISCV_SUBTARGETS :=
endif
diff --git a/tools/testing/selftests/riscv/breakpoints/.gitignore b/tools/testing/selftests/riscv/breakpoints/.gitignore
new file mode 100644
index 000000000000..9b3193d06608
--- /dev/null
+++ b/tools/testing/selftests/riscv/breakpoints/.gitignore
@@ -0,0 +1 @@
+breakpoint_test
diff --git a/tools/testing/selftests/riscv/breakpoints/Makefile b/tools/testing/selftests/riscv/breakpoints/Makefile
new file mode 100644
index 000000000000..91e1c02c0073
--- /dev/null
+++ b/tools/testing/selftests/riscv/breakpoints/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 ARM Limited
+# Originally tools/testing/arm64/abi/Makefile
+
+CFLAGS += -I$(top_srcdir)/tools/include
+
+TEST_GEN_PROGS := breakpoint_test
+
+include ../../lib.mk
+
+$(OUTPUT)/breakpoint_test: breakpoint_test.c
+ $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
diff --git a/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c b/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c
new file mode 100644
index 000000000000..faeecc72da12
--- /dev/null
+++ b/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * Original Code by Pavel Labath <labath@google.com>
+ *
+ * Code modified by Pratyush Anand <panand@redhat.com>
+ * for testing different byte select for each access size.
+ * Originally tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
+ */
+
+#define _GNU_SOURCE
+
+#include <asm/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "../../kselftest.h"
+
+#define MAX_BP_SIZE 8
+
+static volatile uint8_t var[3*MAX_BP_SIZE] __attribute__((__aligned__(MAX_BP_SIZE)));
+
+static void child(int size, int wr)
+{
+ volatile uint8_t *addr = &var[MAX_BP_SIZE + wr];
+
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
+ ksft_print_msg(
+ "ptrace(PTRACE_TRACEME) failed: %s\n",
+ strerror(errno));
+ _exit(1);
+ }
+
+ if (raise(SIGSTOP) != 0) {
+ ksft_print_msg(
+ "raise(SIGSTOP) failed: %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ if ((uintptr_t) addr % size) {
+ ksft_print_msg(
+ "Wrong address write for the given size: %s\n",
+ strerror(errno));
+ _exit(1);
+ }
+
+ switch (size) {
+ case 1:
+ *addr = 47;
+ break;
+ case 2:
+ *(uint16_t *)addr = 47;
+ break;
+ case 4:
+ *(uint32_t *)addr = 47;
+ break;
+ case 8:
+ *(uint64_t *)addr = 47;
+ break;
+ }
+
+ _exit(0);
+}
+
+static bool set_watchpoint(pid_t pid, int size, int wp)
+{
+ const volatile uint8_t *addr = &var[MAX_BP_SIZE + wp];
+ const int offset = (uintptr_t)addr % 8;
+ const unsigned int type = 2; /* Write */
+ const unsigned int enable = 1;
+ struct __riscv_hwdebug_state debug_state;
+ struct iovec iov;
+
+ memset(&debug_state, 0, sizeof(debug_state));
+ debug_state.addr = (uintptr_t)(addr - offset);
+ debug_state.len = size;
+ debug_state.ctrl = enable;
+ debug_state.type = type;
+ iov.iov_base = &debug_state;
+ iov.iov_len = sizeof(debug_state);
+ if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_HW_BREAK, &iov) == 0)
+ return true;
+
+ if (errno == EIO)
+ ksft_print_msg(
+ "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_BREAK) not supported on this hardware: %s\n",
+ strerror(errno));
+
+ ksft_print_msg(
+ "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_BREAK) failed: %s\n",
+ strerror(errno));
+ return false;
+}
+
+static bool run_test(int wr_size, int wp_size, int wr, int wp)
+{
+ int status;
+ siginfo_t siginfo;
+ pid_t pid = fork();
+ pid_t wpid;
+
+ if (pid < 0) {
+ ksft_test_result_fail(
+ "fork() failed: %s\n", strerror(errno));
+ return false;
+ }
+ if (pid == 0)
+ child(wr_size, wr);
+
+ wpid = waitpid(pid, &status, __WALL);
+ if (wpid != pid) {
+ ksft_print_msg(
+ "waitpid() failed: %s\n", strerror(errno));
+ return false;
+ }
+ if (!WIFSTOPPED(status)) {
+ ksft_print_msg(
+ "child did not stop: %s\n", strerror(errno));
+ return false;
+ }
+ if (WSTOPSIG(status) != SIGSTOP) {
+ ksft_print_msg("child did not stop with SIGSTOP\n");
+ return false;
+ }
+
+ if (!set_watchpoint(pid, wp_size, wp))
+ return false;
+
+ if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
+ ksft_print_msg(
+ "ptrace(PTRACE_CONT) failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+
+ alarm(3);
+ wpid = waitpid(pid, &status, __WALL);
+ if (wpid != pid) {
+ ksft_print_msg(
+ "waitpid() failed: %s\n", strerror(errno));
+ return false;
+ }
+ alarm(0);
+ if (WIFEXITED(status)) {
+ ksft_print_msg("child exited prematurely\n");
+ return false;
+ }
+ if (!WIFSTOPPED(status)) {
+ ksft_print_msg("child did not stop\n");
+ return false;
+ }
+ if (WSTOPSIG(status) != SIGTRAP) {
+ ksft_print_msg("child did not stop with SIGTRAP\n");
+ return false;
+ }
+ if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {
+ ksft_print_msg(
+ "ptrace(PTRACE_GETSIGINFO): %s\n",
+ strerror(errno));
+ return false;
+ }
+ if (siginfo.si_code != TRAP_HWBKPT) {
+ ksft_print_msg(
+ "Unexpected si_code %d\n", siginfo.si_code);
+ return false;
+ }
+
+ kill(pid, SIGKILL);
+ wpid = waitpid(pid, &status, 0);
+ if (wpid != pid) {
+ ksft_print_msg(
+ "waitpid() failed: %s\n", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+static void sigalrm(int sig)
+{
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ bool succeeded = true;
+ struct sigaction act;
+ int wr, wp, size;
+ bool result;
+
+ ksft_print_header();
+ ksft_set_plan(213);
+
+ act.sa_handler = sigalrm;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, NULL);
+ for (size = 1; size <= MAX_BP_SIZE; size = size*2) {
+ for (wr = 0; wr <= MAX_BP_SIZE; wr = wr + size) {
+ for (wp = wr - size; wp <= wr + size; wp = wp + size) {
+ result = run_test(size, MIN(size, 8), wr, wp);
+ if ((result && wr == wp) ||
+ (!result && wr != wp))
+ ksft_test_result_pass(
+ "Test size = %d write offset = %d watchpoint offset = %d\n",
+ size, wr, wp);
+ else {
+ ksft_test_result_fail(
+ "Test size = %d write offset = %d watchpoint offset = %d\n",
+ size, wr, wp);
+ succeeded = false;
+ }
+ }
+ }
+ }
+
+ for (size = 1; size <= MAX_BP_SIZE; size = size*2) {
+ if (run_test(size, 8, -size, -8))
+ ksft_test_result_pass(
+ "Test size = %d write offset = %d watchpoint offset = -8\n",
+ size, -size);
+ else {
+ ksft_test_result_fail(
+ "Test size = %d write offset = %d watchpoint offset = -8\n",
+ size, -size);
+ succeeded = false;
+ }
+ }
+
+ if (succeeded)
+ ksft_exit_pass();
+ else
+ ksft_exit_fail();
+}
--
2.43.0
Hi Jesse, We had a pretty huge cc list on the thread, I've trimmed it right back. Feel free to add some people back but keep it focused. On Wed, 6 Aug 2025 at 05:42, Jesse Taube <jesse@rivosinc.com> wrote: > > Add riscv specific selftest for hardhardware breakpoints. nit: double hardware > These tests are based on: > tools/testing/selftests/breakpoints/breakpoint_test_arm64.c The sefltest didn't build for me. There's a few suggested fixes below. > Signed-off-by: Jesse Taube <jesse@rivosinc.com> > --- > The selftest fails as register_user_hw_breakpoint seemingly does not > call arch_install_hw_breakpoint. The test also seems to fail on arm64 > in the same way when I tested it. Is this still a problem with your patchset? Do you test in qemu? Can you share your version, command line, etc? > --- /dev/null > +++ b/tools/testing/selftests/riscv/breakpoints/Makefile > @@ -0,0 +1,12 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (C) 2021 ARM Limited > +# Originally tools/testing/arm64/abi/Makefile CFLAGS += $(KHDR_INCLUDES) This adds -isystem to include local headers for the up to date ptrace.h and elf.h definitions. $ make headers $ make -C tools/testing/selftests CROSS_COMPILE=riscv64-linux-gnu- ARCH=riscv TARGETS=riscv/breakpoints make: Entering directory 'tools/testing/selftests' riscv64-linux-gnu-gcc -static -otools/testing/selftests/riscv/breakpoints/breakpoint_test -isystem usr/include -Itools/testing/selftests/../../../tools/include -D_GNU_SOURCE= breakpoint_test.c > + > +CFLAGS += -I$(top_srcdir)/tools/include > + > +TEST_GEN_PROGS := breakpoint_test > + > +include ../../lib.mk > + > +$(OUTPUT)/breakpoint_test: breakpoint_test.c > + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ > diff --git a/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c b/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c > new file mode 100644 > index 000000000000..faeecc72da12 > --- /dev/null > +++ b/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c > @@ -0,0 +1,246 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2016 Google, Inc. > + * > + * Original Code by Pavel Labath <labath@google.com> > + * > + * Code modified by Pratyush Anand <panand@redhat.com> > + * for testing different byte select for each access size. > + * Originally tools/testing/selftests/breakpoints/breakpoint_test_arm64.c > + */ > + > +#define _GNU_SOURCE > + > +#include <asm/ptrace.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <sys/ptrace.h> > +#include <sys/param.h> > +#include <sys/uio.h> > +#include <stdint.h> > +#include <stdbool.h> > +#include <stddef.h> > +#include <string.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <elf.h> This is the wrong elf.h, we want the one with NT_RISCV_HW_BREAK. -#include <elf.h> +#include <linux/elf.h> > +#include <errno.h> > +#include <signal.h> > + > +#include "../../kselftest.h" > + > +#define MAX_BP_SIZE 8 > +
On Thu, Aug 14, 2025 at 10:42 PM Joel Stanley <joel@jms.id.au> wrote: > > Hi Jesse, > > We had a pretty huge cc list on the thread, I've trimmed it right > back. Feel free to add some people back but keep it focused. > > On Wed, 6 Aug 2025 at 05:42, Jesse Taube <jesse@rivosinc.com> wrote: > > > > Add riscv specific selftest for hardhardware breakpoints. > > nit: double hardware > > > These tests are based on: > > tools/testing/selftests/breakpoints/breakpoint_test_arm64.c > > The sefltest didn't build for me. There's a few suggested fixes below. > > > Signed-off-by: Jesse Taube <jesse@rivosinc.com> > > --- > > The selftest fails as register_user_hw_breakpoint seemingly does not > > call arch_install_hw_breakpoint. The test also seems to fail on arm64 > > in the same way when I tested it. > > Is this still a problem with your patchset? Yes > > Do you test in qemu? Can you share your version, command line, etc? Yes. I used buildroot's `qemu_aarch64_virt_defconfig` config and set `BR2_PACKAGE_LINUX_TOOLS_SELFTESTS` and tested with the example command from the boards readme. > > > --- /dev/null > > +++ b/tools/testing/selftests/riscv/breakpoints/Makefile > > @@ -0,0 +1,12 @@ > > +# SPDX-License-Identifier: GPL-2.0 > > +# Copyright (C) 2021 ARM Limited > > +# Originally tools/testing/arm64/abi/Makefile > > CFLAGS += $(KHDR_INCLUDES) > > This adds -isystem to include local headers for the up to date > ptrace.h and elf.h definitions. > > $ make headers oh that's why it wasn't building! Thanks, Jesse Taube > $ make -C tools/testing/selftests CROSS_COMPILE=riscv64-linux-gnu- > ARCH=riscv TARGETS=riscv/breakpoints > make: Entering directory 'tools/testing/selftests' > riscv64-linux-gnu-gcc -static > -otools/testing/selftests/riscv/breakpoints/breakpoint_test > -isystem usr/include -Itools/testing/selftests/../../../tools/include > -D_GNU_SOURCE= breakpoint_test.c > > > + > > +CFLAGS += -I$(top_srcdir)/tools/include > > + > > +TEST_GEN_PROGS := breakpoint_test > > + > > +include ../../lib.mk > > + > > +$(OUTPUT)/breakpoint_test: breakpoint_test.c > > + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ > > diff --git a/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c b/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c > > new file mode 100644 > > index 000000000000..faeecc72da12 > > --- /dev/null > > +++ b/tools/testing/selftests/riscv/breakpoints/breakpoint_test.c > > @@ -0,0 +1,246 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright (C) 2016 Google, Inc. > > + * > > + * Original Code by Pavel Labath <labath@google.com> > > + * > > + * Code modified by Pratyush Anand <panand@redhat.com> > > + * for testing different byte select for each access size. > > + * Originally tools/testing/selftests/breakpoints/breakpoint_test_arm64.c > > + */ > > + > > +#define _GNU_SOURCE > > + > > +#include <asm/ptrace.h> > > +#include <sys/types.h> > > +#include <sys/wait.h> > > +#include <sys/ptrace.h> > > +#include <sys/param.h> > > +#include <sys/uio.h> > > +#include <stdint.h> > > +#include <stdbool.h> > > +#include <stddef.h> > > +#include <string.h> > > +#include <stdio.h> > > +#include <unistd.h> > > +#include <elf.h> > > This is the wrong elf.h, we want the one with NT_RISCV_HW_BREAK. > > -#include <elf.h> > +#include <linux/elf.h> > > > > +#include <errno.h> > > +#include <signal.h> > > + > > +#include "../../kselftest.h" > > + > > +#define MAX_BP_SIZE 8 > > +
© 2016 - 2025 Red Hat, Inc.