* [PATCH v1 0/2] riscv: Introduce support for hardware break/watchpoints
@ 2025-07-10 12:52 ` Himanshu Chauhan
0 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-07-10 12:52 UTC (permalink / raw)
To: paul.walmsley, palmer, aou, linux-riscv, linux-kernel; +Cc: Himanshu Chauhan
This patchset adds support of hardware breakpoints and watchpoints in RISC-V
architecture. The framework is built on top of perf subsystem and SBI debug
trigger extension.
Currently following features are not supported and are in works:
- Ptrace support
- Single stepping
- Virtualization of debug triggers
The SBI debug trigger extension proposal can be found in Chapter-19 of SBI specification:
https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v3.0-rc8/riscv-sbi.pdf
The Sdtrig ISA is part of RISC-V debug specification which can be found at:
https://github.com/riscv/riscv-debug-spec
Himanshu Chauhan (2):
riscv: Add SBI debug trigger extension and function ids
riscv: Introduce support for hardware break/watchpoints
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/hw_breakpoint.h | 320 ++++++++++++
arch/riscv/include/asm/kdebug.h | 3 +-
arch/riscv/include/asm/sbi.h | 29 ++
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/hw_breakpoint.c | 657 +++++++++++++++++++++++++
arch/riscv/kernel/traps.c | 6 +
7 files changed, 1016 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/include/asm/hw_breakpoint.h
create mode 100644 arch/riscv/kernel/hw_breakpoint.c
--
2.45.2
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 0/2] riscv: Introduce support for hardware break/watchpoints
@ 2025-07-10 12:52 ` Himanshu Chauhan
0 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-07-10 12:52 UTC (permalink / raw)
To: paul.walmsley, palmer, aou, linux-riscv, linux-kernel; +Cc: Himanshu Chauhan
This patchset adds support of hardware breakpoints and watchpoints in RISC-V
architecture. The framework is built on top of perf subsystem and SBI debug
trigger extension.
Currently following features are not supported and are in works:
- Ptrace support
- Single stepping
- Virtualization of debug triggers
The SBI debug trigger extension proposal can be found in Chapter-19 of SBI specification:
https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v3.0-rc8/riscv-sbi.pdf
The Sdtrig ISA is part of RISC-V debug specification which can be found at:
https://github.com/riscv/riscv-debug-spec
Himanshu Chauhan (2):
riscv: Add SBI debug trigger extension and function ids
riscv: Introduce support for hardware break/watchpoints
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/hw_breakpoint.h | 320 ++++++++++++
arch/riscv/include/asm/kdebug.h | 3 +-
arch/riscv/include/asm/sbi.h | 29 ++
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/hw_breakpoint.c | 657 +++++++++++++++++++++++++
arch/riscv/kernel/traps.c | 6 +
7 files changed, 1016 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/include/asm/hw_breakpoint.h
create mode 100644 arch/riscv/kernel/hw_breakpoint.c
--
2.45.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 1/2] riscv: Add SBI debug trigger extension and function ids
2025-07-10 12:52 ` Himanshu Chauhan
@ 2025-07-10 12:52 ` Himanshu Chauhan
-1 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-07-10 12:52 UTC (permalink / raw)
To: paul.walmsley, palmer, aou, linux-riscv, linux-kernel; +Cc: Himanshu Chauhan
Debug trigger extension is an SBI extension to support native debugging
in S-mode and VS-mode. This patch adds the extension and the function
IDs defined by the extension.
Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
---
arch/riscv/include/asm/sbi.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 341e74238aa0..d1d906bc5365 100644
--- a/arch/riscv/include/asm/sbi.h
+++ b/arch/riscv/include/asm/sbi.h
@@ -36,6 +36,7 @@ enum sbi_ext_id {
SBI_EXT_STA = 0x535441,
SBI_EXT_NACL = 0x4E41434C,
SBI_EXT_FWFT = 0x46574654,
+ SBI_EXT_DBTR = 0x44425452,
/* Experimentals extensions must lie within this range */
SBI_EXT_EXPERIMENTAL_START = 0x08000000,
@@ -430,6 +431,34 @@ enum sbi_fwft_feature_t {
#define SBI_FWFT_SET_FLAG_LOCK BIT(0)
+/* SBI debug triggers function IDs */
+enum sbi_ext_dbtr_fid {
+ SBI_EXT_DBTR_NUM_TRIGGERS = 0,
+ SBI_EXT_DBTR_SETUP_SHMEM,
+ SBI_EXT_DBTR_TRIG_READ,
+ SBI_EXT_DBTR_TRIG_INSTALL,
+ SBI_EXT_DBTR_TRIG_UPDATE,
+ SBI_EXT_DBTR_TRIG_UNINSTALL,
+ SBI_EXT_DBTR_TRIG_ENABLE,
+ SBI_EXT_DBTR_TRIG_DISABLE,
+};
+
+struct sbi_dbtr_data_msg {
+ unsigned long tstate;
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+struct sbi_dbtr_id_msg {
+ unsigned long idx;
+};
+
+union sbi_dbtr_shmem_entry {
+ struct sbi_dbtr_data_msg data;
+ struct sbi_dbtr_id_msg id;
+};
+
/* SBI spec version fields */
#define SBI_SPEC_VERSION_DEFAULT 0x1
#define SBI_SPEC_VERSION_MAJOR_SHIFT 24
--
2.45.2
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 1/2] riscv: Add SBI debug trigger extension and function ids
@ 2025-07-10 12:52 ` Himanshu Chauhan
0 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-07-10 12:52 UTC (permalink / raw)
To: paul.walmsley, palmer, aou, linux-riscv, linux-kernel; +Cc: Himanshu Chauhan
Debug trigger extension is an SBI extension to support native debugging
in S-mode and VS-mode. This patch adds the extension and the function
IDs defined by the extension.
Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
---
arch/riscv/include/asm/sbi.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 341e74238aa0..d1d906bc5365 100644
--- a/arch/riscv/include/asm/sbi.h
+++ b/arch/riscv/include/asm/sbi.h
@@ -36,6 +36,7 @@ enum sbi_ext_id {
SBI_EXT_STA = 0x535441,
SBI_EXT_NACL = 0x4E41434C,
SBI_EXT_FWFT = 0x46574654,
+ SBI_EXT_DBTR = 0x44425452,
/* Experimentals extensions must lie within this range */
SBI_EXT_EXPERIMENTAL_START = 0x08000000,
@@ -430,6 +431,34 @@ enum sbi_fwft_feature_t {
#define SBI_FWFT_SET_FLAG_LOCK BIT(0)
+/* SBI debug triggers function IDs */
+enum sbi_ext_dbtr_fid {
+ SBI_EXT_DBTR_NUM_TRIGGERS = 0,
+ SBI_EXT_DBTR_SETUP_SHMEM,
+ SBI_EXT_DBTR_TRIG_READ,
+ SBI_EXT_DBTR_TRIG_INSTALL,
+ SBI_EXT_DBTR_TRIG_UPDATE,
+ SBI_EXT_DBTR_TRIG_UNINSTALL,
+ SBI_EXT_DBTR_TRIG_ENABLE,
+ SBI_EXT_DBTR_TRIG_DISABLE,
+};
+
+struct sbi_dbtr_data_msg {
+ unsigned long tstate;
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+struct sbi_dbtr_id_msg {
+ unsigned long idx;
+};
+
+union sbi_dbtr_shmem_entry {
+ struct sbi_dbtr_data_msg data;
+ struct sbi_dbtr_id_msg id;
+};
+
/* SBI spec version fields */
#define SBI_SPEC_VERSION_DEFAULT 0x1
#define SBI_SPEC_VERSION_MAJOR_SHIFT 24
--
2.45.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
2025-07-10 12:52 ` Himanshu Chauhan
@ 2025-07-10 12:52 ` Himanshu Chauhan
-1 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-07-10 12:52 UTC (permalink / raw)
To: paul.walmsley, palmer, aou, linux-riscv, linux-kernel; +Cc: Himanshu Chauhan
RISC-V hardware breakpoint framework is built on top of perf subsystem
and uses SBI debug trigger extension to
install/uninstall/update/enable/disable hardware triggers as specified
in Sdtrig ISA extension.
Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
---
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/hw_breakpoint.h | 320 ++++++++++++
arch/riscv/include/asm/kdebug.h | 3 +-
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/hw_breakpoint.c | 657 +++++++++++++++++++++++++
arch/riscv/kernel/traps.c | 6 +
6 files changed, 987 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/include/asm/hw_breakpoint.h
create mode 100644 arch/riscv/kernel/hw_breakpoint.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d71ea0f4466f..741c8765724e 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -169,6 +169,7 @@ config RISCV
select HAVE_FUNCTION_ERROR_INJECTION
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_VDSO if MMU && 64BIT
+ select HAVE_HW_BREAKPOINT if PERF_EVENTS
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_KERNEL_BZIP2 if !XIP_KERNEL && !EFI_ZBOOT
select HAVE_KERNEL_GZIP if !XIP_KERNEL && !EFI_ZBOOT
diff --git a/arch/riscv/include/asm/hw_breakpoint.h b/arch/riscv/include/asm/hw_breakpoint.h
new file mode 100644
index 000000000000..a8fa96ce3ee3
--- /dev/null
+++ b/arch/riscv/include/asm/hw_breakpoint.h
@@ -0,0 +1,320 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __RISCV_HW_BREAKPOINT_H
+#define __RISCV_HW_BREAKPOINT_H
+
+struct task_struct;
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+
+#include <uapi/linux/hw_breakpoint.h>
+
+#if __riscv_xlen == 64
+#define cpu_to_le cpu_to_le64
+#define le_to_cpu le64_to_cpu
+#elif __riscv_xlen == 32
+#define cpu_to_le cpu_to_le32
+#define le_to_cpu le32_to_cpu
+#else
+#error "Unexpected __riscv_xlen"
+#endif
+
+#define RV_DBTR_BIT(_prefix, _name) \
+ RV_DBTR_##_prefix##_##_name##_BIT
+
+#define RV_DBTR_BIT_MASK(_prefix, _name) \
+ RV_DBTR_##_prefix##_name##_BIT_MASK
+
+#define RV_DBTR_BIT_MASK_VAL(_prefix, _name, _width) \
+ (((1UL << _width) - 1) << RV_DBTR_BIT(_prefix, _name))
+
+#define CLEAR_DBTR_BIT(_target, _prefix, _bit_name) \
+ __clear_bit(RV_DBTR_BIT(_prefix, _bit_name), &_target)
+
+#define SET_DBTR_BIT(_target, _prefix, _bit_name) \
+ __set_bit(RV_DBTR_BIT(_prefix, _bit_name), &_target)
+
+enum {
+ RV_DBTR_BP = 0,
+ RV_DBTR_WP = 1,
+};
+
+enum {
+ RV_DBTR_TRIG_NONE = 0,
+ RV_DBTR_TRIG_LEGACY,
+ RV_DBTR_TRIG_MCONTROL,
+ RV_DBTR_TRIG_ICOUNT,
+ RV_DBTR_TRIG_ITRIGGER,
+ RV_DBTR_TRIG_ETRIGGER,
+ RV_DBTR_TRIG_MCONTROL6,
+};
+
+/* Trigger Data 1 */
+enum {
+ RV_DBTR_BIT(TDATA1, DATA) = 0,
+#if __riscv_xlen == 64
+ RV_DBTR_BIT(TDATA1, DMODE) = 59,
+ RV_DBTR_BIT(TDATA1, TYPE) = 60,
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT(TDATA1, DMODE) = 27,
+ RV_DBTR_BIT(TDATA1, TYPE) = 28,
+#else
+ #error "Unknown __riscv_xlen"
+#endif
+};
+
+enum {
+#if __riscv_xlen == 64
+ RV_DBTR_BIT_MASK(TDATA1, DATA) = RV_DBTR_BIT_MASK_VAL(TDATA1, DATA, 59),
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT_MASK(TDATA1, DATA) = RV_DBTR_BIT_MASK_VAL(TDATA1, DATA, 27),
+#else
+ #error "Unknown __riscv_xlen"
+#endif
+ RV_DBTR_BIT_MASK(TDAT1, DMODE) = RV_DBTR_BIT_MASK_VAL(TDATA1, DMODE, 1),
+ RV_DBTR_BIT_MASK(TDATA1, TYPE) = RV_DBTR_BIT_MASK_VAL(TDATA1, TYPE, 4),
+};
+
+/* MC - Match Control Type Register */
+enum {
+ RV_DBTR_BIT(MC, LOAD) = 0,
+ RV_DBTR_BIT(MC, STORE) = 1,
+ RV_DBTR_BIT(MC, EXEC) = 2,
+ RV_DBTR_BIT(MC, U) = 3,
+ RV_DBTR_BIT(MC, S) = 4,
+ RV_DBTR_BIT(MC, RES2) = 5,
+ RV_DBTR_BIT(MC, M) = 6,
+ RV_DBTR_BIT(MC, MATCH) = 7,
+ RV_DBTR_BIT(MC, CHAIN) = 11,
+ RV_DBTR_BIT(MC, ACTION) = 12,
+ RV_DBTR_BIT(MC, SIZELO) = 16,
+ RV_DBTR_BIT(MC, TIMING) = 18,
+ RV_DBTR_BIT(MC, SELECT) = 19,
+ RV_DBTR_BIT(MC, HIT) = 20,
+#if __riscv_xlen >= 64
+ RV_DBTR_BIT(MC, SIZEHI) = 21,
+#endif
+#if __riscv_xlen == 64
+ RV_DBTR_BIT(MC, MASKMAX) = 53,
+ RV_DBTR_BIT(MC, DMODE) = 59,
+ RV_DBTR_BIT(MC, TYPE) = 60,
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT(MC, MASKMAX) = 21,
+ RV_DBTR_BIT(MC, DMODE) = 27,
+ RV_DBTR_BIT(MC, TYPE) = 28,
+#else
+ #error "Unknown riscv xlen"
+#endif
+};
+
+enum {
+ RV_DBTR_BIT_MASK(MC, LOAD) = RV_DBTR_BIT_MASK_VAL(MC, LOAD, 1),
+ RV_DBTR_BIT_MASK(MC, STORE) = RV_DBTR_BIT_MASK_VAL(MC, STORE, 1),
+ RV_DBTR_BIT_MASK(MC, EXEC) = RV_DBTR_BIT_MASK_VAL(MC, EXEC, 1),
+ RV_DBTR_BIT_MASK(MC, U) = RV_DBTR_BIT_MASK_VAL(MC, U, 1),
+ RV_DBTR_BIT_MASK(MC, S) = RV_DBTR_BIT_MASK_VAL(MC, S, 1),
+ RV_DBTR_BIT_MASK(MC, RES2) = RV_DBTR_BIT_MASK_VAL(MC, RES2, 1),
+ RV_DBTR_BIT_MASK(MC, M) = RV_DBTR_BIT_MASK_VAL(MC, M, 1),
+ RV_DBTR_BIT_MASK(MC, MATCH) = RV_DBTR_BIT_MASK_VAL(MC, MATCH, 4),
+ RV_DBTR_BIT_MASK(MC, CHAIN) = RV_DBTR_BIT_MASK_VAL(MC, CHAIN, 1),
+ RV_DBTR_BIT_MASK(MC, ACTION) = RV_DBTR_BIT_MASK_VAL(MC, ACTION, 4),
+ RV_DBTR_BIT_MASK(MC, SIZELO) = RV_DBTR_BIT_MASK_VAL(MC, SIZELO, 2),
+ RV_DBTR_BIT_MASK(MC, TIMING) = RV_DBTR_BIT_MASK_VAL(MC, TIMING, 1),
+ RV_DBTR_BIT_MASK(MC, SELECT) = RV_DBTR_BIT_MASK_VAL(MC, SELECT, 1),
+ RV_DBTR_BIT_MASK(MC, HIT) = RV_DBTR_BIT_MASK_VAL(MC, HIT, 1),
+#if __riscv_xlen >= 64
+ RV_DBTR_BIT_MASK(MC, SIZEHI) = RV_DBTR_BIT_MASK_VAL(MC, SIZEHI, 2),
+#endif
+ RV_DBTR_BIT_MASK(MC, MASKMAX) = RV_DBTR_BIT_MASK_VAL(MC, MASKMAX, 6),
+ RV_DBTR_BIT_MASK(MC, DMODE) = RV_DBTR_BIT_MASK_VAL(MC, DMODE, 1),
+ RV_DBTR_BIT_MASK(MC, TYPE) = RV_DBTR_BIT_MASK_VAL(MC, TYPE, 4),
+};
+
+/* MC6 - Match Control 6 Type Register */
+enum {
+ RV_DBTR_BIT(MC6, LOAD) = 0,
+ RV_DBTR_BIT(MC6, STORE) = 1,
+ RV_DBTR_BIT(MC6, EXEC) = 2,
+ RV_DBTR_BIT(MC6, U) = 3,
+ RV_DBTR_BIT(MC6, S) = 4,
+ RV_DBTR_BIT(MC6, RES2) = 5,
+ RV_DBTR_BIT(MC6, M) = 6,
+ RV_DBTR_BIT(MC6, MATCH) = 7,
+ RV_DBTR_BIT(MC6, CHAIN) = 11,
+ RV_DBTR_BIT(MC6, ACTION) = 12,
+ RV_DBTR_BIT(MC6, SIZE) = 16,
+ RV_DBTR_BIT(MC6, TIMING) = 20,
+ RV_DBTR_BIT(MC6, SELECT) = 21,
+ RV_DBTR_BIT(MC6, HIT) = 22,
+ RV_DBTR_BIT(MC6, VU) = 23,
+ RV_DBTR_BIT(MC6, VS) = 24,
+#if __riscv_xlen == 64
+ RV_DBTR_BIT(MC6, DMODE) = 59,
+ RV_DBTR_BIT(MC6, TYPE) = 60,
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT(MC6, DMODE) = 27,
+ RV_DBTR_BIT(MC6, TYPE) = 28,
+#else
+ #error "Unknown riscv xlen"
+#endif
+};
+
+enum {
+ RV_DBTR_BIT_MASK(MC6, LOAD) = RV_DBTR_BIT_MASK_VAL(MC6, LOAD, 1),
+ RV_DBTR_BIT_MASK(MC6, STORE) = RV_DBTR_BIT_MASK_VAL(MC6, STORE, 1),
+ RV_DBTR_BIT_MASK(MC6, EXEC) = RV_DBTR_BIT_MASK_VAL(MC6, EXEC, 1),
+ RV_DBTR_BIT_MASK(MC6, U) = RV_DBTR_BIT_MASK_VAL(MC6, U, 1),
+ RV_DBTR_BIT_MASK(MC6, S) = RV_DBTR_BIT_MASK_VAL(MC6, S, 1),
+ RV_DBTR_BIT_MASK(MC6, RES2) = RV_DBTR_BIT_MASK_VAL(MC6, RES2, 1),
+ RV_DBTR_BIT_MASK(MC6, M) = RV_DBTR_BIT_MASK_VAL(MC6, M, 1),
+ RV_DBTR_BIT_MASK(MC6, MATCH) = RV_DBTR_BIT_MASK_VAL(MC6, MATCH, 4),
+ RV_DBTR_BIT_MASK(MC6, CHAIN) = RV_DBTR_BIT_MASK_VAL(MC6, CHAIN, 1),
+ RV_DBTR_BIT_MASK(MC6, ACTION) = RV_DBTR_BIT_MASK_VAL(MC6, ACTION, 4),
+ RV_DBTR_BIT_MASK(MC6, SIZE) = RV_DBTR_BIT_MASK_VAL(MC6, SIZE, 4),
+ RV_DBTR_BIT_MASK(MC6, TIMING) = RV_DBTR_BIT_MASK_VAL(MC6, TIMING, 1),
+ RV_DBTR_BIT_MASK(MC6, SELECT) = RV_DBTR_BIT_MASK_VAL(MC6, SELECT, 1),
+ RV_DBTR_BIT_MASK(MC6, HIT) = RV_DBTR_BIT_MASK_VAL(MC6, HIT, 1),
+ RV_DBTR_BIT_MASK(MC6, VU) = RV_DBTR_BIT_MASK_VAL(MC6, VU, 1),
+ RV_DBTR_BIT_MASK(MC6, VS) = RV_DBTR_BIT_MASK_VAL(MC6, VS, 1),
+#if __riscv_xlen == 64
+ RV_DBTR_BIT_MASK(MC6, DMODE) = RV_DBTR_BIT_MASK_VAL(MC6, DMODE, 1),
+ RV_DBTR_BIT_MASK(MC6, TYPE) = RV_DBTR_BIT_MASK_VAL(MC6, TYPE, 4),
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT_MASK(MC6, DMODE) = RV_DBTR_BIT_MASK_VAL(MC6, DMODE, 1),
+ RV_DBTR_BIT_MASK(MC6, TYPE) = RV_DBTR_BIT_MASK_VAL(MC6, TYPE, 4),
+#else
+ #error "Unknown riscv xlen"
+#endif
+};
+
+#define RV_DBTR_SET_TDATA1_TYPE(_t1, _type) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(TDATA1, TYPE); \
+ _t1 |= (((unsigned long)_type \
+ << RV_DBTR_BIT(TDATA1, TYPE)) \
+ & RV_DBTR_BIT_MASK(TDATA1, TYPE)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC_TYPE(_t1, _type) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC, TYPE); \
+ _t1 |= (((unsigned long)_type \
+ << RV_DBTR_BIT(MC, TYPE)) \
+ & RV_DBTR_BIT_MASK(MC, TYPE)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC6_TYPE(_t1, _type) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC6, TYPE); \
+ _t1 |= (((unsigned long)_type \
+ << RV_DBTR_BIT(MC6, TYPE)) \
+ & RV_DBTR_BIT_MASK(MC6, TYPE)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC_EXEC(_t1) \
+ SET_DBTR_BIT(_t1, MC, EXEC)
+
+#define RV_DBTR_SET_MC_LOAD(_t1) \
+ SET_DBTR_BIT(_t1, MC, LOAD)
+
+#define RV_DBTR_SET_MC_STORE(_t1) \
+ SET_DBTR_BIT(_t1, MC, STORE)
+
+#define RV_DBTR_SET_MC_SIZELO(_t1, _val) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC, SIZELO); \
+ _t1 |= ((_val << RV_DBTR_BIT(MC, SIZELO)) \
+ & RV_DBTR_BIT_MASK(MC, SIZELO)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC_SIZEHI(_t1, _val) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC, SIZEHI); \
+ _t1 |= ((_val << RV_DBTR_BIT(MC, SIZEHI)) \
+ & RV_DBTR_BIT_MASK(MC, SIZEHI)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC6_EXEC(_t1) \
+ SET_DBTR_BIT(_t1, MC6, EXEC)
+
+#define RV_DBTR_SET_MC6_LOAD(_t1) \
+ SET_DBTR_BIT(_t1, MC6, LOAD)
+
+#define RV_DBTR_SET_MC6_STORE(_t1) \
+ SET_DBTR_BIT(_t1, MC6, STORE)
+
+#define RV_DBTR_SET_MC6_SIZE(_t1, _val) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC6, SIZE); \
+ _t1 |= ((_val << RV_DBTR_BIT(MC6, SIZE)) \
+ & RV_DBTR_BIT_MASK(MC6, SIZE)); \
+ } while (0)
+
+struct arch_hw_breakpoint {
+ unsigned long address;
+ unsigned long len;
+ unsigned int type;
+
+ /* Trigger configuration data */
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+/* Maximum number of hardware breakpoints supported */
+#define HW_BP_NUM_MAX 32
+
+struct perf_event_attr;
+struct notifier_block;
+struct perf_event;
+struct pt_regs;
+
+int hw_breakpoint_slots(int type);
+int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);
+int hw_breakpoint_arch_parse(struct perf_event *bp,
+ const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw);
+int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
+ unsigned long val, void *data);
+
+void arch_enable_hw_breakpoint(struct perf_event *bp);
+void arch_update_hw_breakpoint(struct perf_event *bp);
+void arch_disable_hw_breakpoint(struct perf_event *bp);
+int arch_install_hw_breakpoint(struct perf_event *bp);
+void arch_uninstall_hw_breakpoint(struct perf_event *bp);
+void hw_breakpoint_pmu_read(struct perf_event *bp);
+void clear_ptrace_hw_breakpoint(struct task_struct *tsk);
+void flush_ptrace_hw_breakpoint(struct task_struct *tsk);
+
+#else
+
+int hw_breakpoint_slots(int type)
+{
+ return 0;
+}
+
+static inline void clear_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+}
+
+static inline void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+}
+
+void arch_enable_hw_breakpoint(struct perf_event *bp)
+{
+}
+
+void arch_update_hw_breakpoint(struct perf_event *bp)
+{
+}
+
+void arch_disable_hw_breakpoint(struct perf_event *bp)
+{
+}
+
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+#endif /* __RISCV_HW_BREAKPOINT_H */
diff --git a/arch/riscv/include/asm/kdebug.h b/arch/riscv/include/asm/kdebug.h
index 85ac00411f6e..53e989781aa1 100644
--- a/arch/riscv/include/asm/kdebug.h
+++ b/arch/riscv/include/asm/kdebug.h
@@ -6,7 +6,8 @@
enum die_val {
DIE_UNUSED,
DIE_TRAP,
- DIE_OOPS
+ DIE_OOPS,
+ DIE_DEBUG
};
#endif
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index c7b542573407..39a3489bd9e2 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
+obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_RISCV_SBI) += sbi.o sbi_ecall.o
ifeq ($(CONFIG_RISCV_SBI), y)
obj-$(CONFIG_SMP) += sbi-ipi.o
diff --git a/arch/riscv/kernel/hw_breakpoint.c b/arch/riscv/kernel/hw_breakpoint.c
new file mode 100644
index 000000000000..3a43d1e59de8
--- /dev/null
+++ b/arch/riscv/kernel/hw_breakpoint.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/hw_breakpoint.h>
+#include <linux/perf_event.h>
+#include <linux/spinlock.h>
+#include <linux/percpu.h>
+#include <linux/kdebug.h>
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/cpuhotplug.h>
+
+#include <asm/sbi.h>
+
+/* Registered per-cpu bp/wp */
+static DEFINE_PER_CPU(struct perf_event *, pcpu_hw_bp_events[HW_BP_NUM_MAX]);
+static DEFINE_PER_CPU(unsigned long, ecall_lock_flags);
+static DEFINE_PER_CPU(raw_spinlock_t, ecall_lock);
+
+/* Per-cpu shared memory between S and M mode */
+static union sbi_dbtr_shmem_entry __percpu *sbi_dbtr_shmem;
+
+/* number of debug triggers on this cpu . */
+static int dbtr_total_num __ro_after_init;
+static int dbtr_type __ro_after_init;
+static int dbtr_init __ro_after_init;
+
+#if __riscv_xlen == 64
+#define MEM_HI(_m) 0
+#define MEM_LO(_m) ((u64)_m)
+#elif __riscv_xlen == 32
+#define MEM_HI(_m) ((u64)_m >> 32)
+#define MEM_LO(_m) ((u64)_m & 0xFFFFFFFFUL)
+#else
+#error "Unknown __riscv_xlen"
+#endif
+
+static int arch_smp_setup_sbi_shmem(unsigned int cpu)
+{
+ union sbi_dbtr_shmem_entry *dbtr_shmem;
+ unsigned long shmem_pa;
+ struct sbiret ret;
+ int rc = 0;
+
+ dbtr_shmem = per_cpu_ptr(sbi_dbtr_shmem, cpu);
+ if (!dbtr_shmem) {
+ pr_err("Invalid per-cpu shared memory for debug triggers\n");
+ return -ENODEV;
+ }
+
+ shmem_pa = __pa(dbtr_shmem);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
+ MEM_LO(shmem_pa), MEM_HI(shmem_pa), 0, 0, 0, 0);
+
+ if (ret.error) {
+ switch (ret.error) {
+ case SBI_ERR_DENIED:
+ pr_warn("%s: Access denied for shared memory at %lx\n",
+ __func__, shmem_pa);
+ rc = -EPERM;
+ break;
+
+ case SBI_ERR_INVALID_PARAM:
+ case SBI_ERR_INVALID_ADDRESS:
+ pr_warn("%s: Invalid address parameter (%lu)\n",
+ __func__, ret.error);
+ rc = -EINVAL;
+ break;
+
+ case SBI_ERR_ALREADY_AVAILABLE:
+ pr_warn("%s: Shared memory is already set\n",
+ __func__);
+ rc = -EADDRINUSE;
+ break;
+
+ case SBI_ERR_FAILURE:
+ pr_err("%s: Internal sdtrig state error\n",
+ __func__);
+ rc = -ENXIO;
+ break;
+
+ default:
+ pr_warn("%s: Unknown error %lu\n", __func__, ret.error);
+ rc = -ENXIO;
+ break;
+ }
+ }
+
+ pr_info("CPU %d: HW Breakpoint shared memory registered.\n", cpu);
+
+ return rc;
+}
+
+static int arch_smp_teardown_sbi_shmem(unsigned int cpu)
+{
+ struct sbiret ret;
+
+ /* Disable shared memory */
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
+ -1UL, -1UL, 0, 0, 0, 0);
+
+ if (ret.error) {
+ switch (ret.error) {
+ case SBI_ERR_DENIED:
+ pr_err("%s: Access denied for shared memory.\n",
+ __func__);
+ break;
+
+ case SBI_ERR_INVALID_PARAM:
+ case SBI_ERR_INVALID_ADDRESS:
+ pr_err("%s: Invalid address parameter (%lu)\n",
+ __func__, ret.error);
+ break;
+
+ case SBI_ERR_ALREADY_AVAILABLE:
+ pr_err("%s: Shared memory is already set\n",
+ __func__);
+ break;
+ case SBI_ERR_FAILURE:
+ pr_err("%s: Internal sdtrig state error\n",
+ __func__);
+ break;
+ default:
+ pr_err("%s: Unknown error %lu\n", __func__, ret.error);
+ break;
+ }
+ }
+
+ pr_warn("CPU %d: HW Breakpoint shared memory disabled.\n", cpu);
+
+ return 0;
+}
+
+static void init_sbi_dbtr(void)
+{
+ unsigned long tdata1;
+ struct sbiret ret;
+
+ if (sbi_probe_extension(SBI_EXT_DBTR) <= 0) {
+ pr_warn("%s: SBI_EXT_DBTR is not supported\n", __func__);
+ dbtr_total_num = 0;
+ goto done;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
+ 0, 0, 0, 0, 0, 0);
+ if (ret.error) {
+ pr_warn("%s: Failed to detect triggers\n", __func__);
+ dbtr_total_num = 0;
+ goto done;
+ }
+
+ tdata1 = 0;
+ RV_DBTR_SET_TDATA1_TYPE(tdata1, RV_DBTR_TRIG_MCONTROL6);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
+ tdata1, 0, 0, 0, 0, 0);
+ if (ret.error) {
+ pr_warn("%s: failed to detect mcontrol6 triggers\n", __func__);
+ } else if (!ret.value) {
+ pr_warn("%s: type 6 triggers not available\n", __func__);
+ } else {
+ dbtr_total_num = ret.value;
+ dbtr_type = RV_DBTR_TRIG_MCONTROL6;
+ pr_warn("%s: mcontrol6 trigger available.\n", __func__);
+ goto done;
+ }
+
+ /* fallback to type 2 triggers if type 6 is not available */
+
+ tdata1 = 0;
+ RV_DBTR_SET_TDATA1_TYPE(tdata1, RV_DBTR_TRIG_MCONTROL);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
+ tdata1, 0, 0, 0, 0, 0);
+ if (ret.error) {
+ pr_warn("%s: failed to detect mcontrol triggers\n", __func__);
+ } else if (!ret.value) {
+ pr_warn("%s: type 2 triggers not available\n", __func__);
+ } else {
+ dbtr_total_num = ret.value;
+ dbtr_type = RV_DBTR_TRIG_MCONTROL;
+ goto done;
+ }
+
+done:
+ dbtr_init = 1;
+}
+
+int hw_breakpoint_slots(int type)
+{
+ /*
+ * We can be called early, so don't rely on
+ * static variables being initialised.
+ */
+
+ if (!dbtr_init)
+ init_sbi_dbtr();
+
+ return dbtr_total_num;
+}
+
+int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw)
+{
+ unsigned int len;
+ unsigned long va;
+
+ va = hw->address;
+ len = hw->len;
+
+ return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
+}
+
+static int rv_init_mcontrol_trigger(const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw)
+{
+ switch (attr->bp_type) {
+ case HW_BREAKPOINT_X:
+ hw->type = RV_DBTR_BP;
+ RV_DBTR_SET_MC_EXEC(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_R:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC_LOAD(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_W:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC_STORE(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_RW:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC_LOAD(hw->tdata1);
+ RV_DBTR_SET_MC_STORE(hw->tdata1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (attr->bp_len) {
+ case HW_BREAKPOINT_LEN_1:
+ hw->len = 1;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 1);
+ break;
+ case HW_BREAKPOINT_LEN_2:
+ hw->len = 2;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 2);
+ break;
+ case HW_BREAKPOINT_LEN_4:
+ hw->len = 4;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 3);
+ break;
+#if __riscv_xlen >= 64
+ case HW_BREAKPOINT_LEN_8:
+ hw->len = 8;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 1);
+ RV_DBTR_SET_MC_SIZEHI(hw->tdata1, 1);
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ RV_DBTR_SET_MC_TYPE(hw->tdata1, RV_DBTR_TRIG_MCONTROL);
+
+ CLEAR_DBTR_BIT(hw->tdata1, MC, DMODE);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, TIMING);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, SELECT);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, ACTION);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, CHAIN);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, MATCH);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, M);
+
+ SET_DBTR_BIT(hw->tdata1, MC, S);
+ SET_DBTR_BIT(hw->tdata1, MC, U);
+
+ return 0;
+}
+
+static int rv_init_mcontrol6_trigger(const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw)
+{
+ switch (attr->bp_type) {
+ case HW_BREAKPOINT_X:
+ hw->type = RV_DBTR_BP;
+ RV_DBTR_SET_MC6_EXEC(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_R:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC6_LOAD(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_W:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC6_STORE(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_RW:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC6_STORE(hw->tdata1);
+ RV_DBTR_SET_MC6_LOAD(hw->tdata1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (attr->bp_len) {
+ case HW_BREAKPOINT_LEN_1:
+ hw->len = 1;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 1);
+ break;
+ case HW_BREAKPOINT_LEN_2:
+ hw->len = 2;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 2);
+ break;
+ case HW_BREAKPOINT_LEN_4:
+ hw->len = 4;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 3);
+ break;
+ case HW_BREAKPOINT_LEN_8:
+ hw->len = 8;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ RV_DBTR_SET_MC6_TYPE(hw->tdata1, RV_DBTR_TRIG_MCONTROL6);
+
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, DMODE);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, TIMING);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, SELECT);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, ACTION);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, CHAIN);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, MATCH);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, M);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, VS);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, VU);
+
+ SET_DBTR_BIT(hw->tdata1, MC6, S);
+ SET_DBTR_BIT(hw->tdata1, MC6, U);
+
+ return 0;
+}
+
+int hw_breakpoint_arch_parse(struct perf_event *bp,
+ const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw)
+{
+ int ret;
+
+ /* Breakpoint address */
+ hw->address = attr->bp_addr;
+ hw->tdata2 = attr->bp_addr;
+ hw->tdata3 = 0x0;
+
+ switch (dbtr_type) {
+ case RV_DBTR_TRIG_MCONTROL:
+ ret = rv_init_mcontrol_trigger(attr, hw);
+ break;
+ case RV_DBTR_TRIG_MCONTROL6:
+ ret = rv_init_mcontrol6_trigger(attr, hw);
+ break;
+ default:
+ pr_warn("unsupported trigger type\n");
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * HW Breakpoint/watchpoint handler
+ */
+static int hw_breakpoint_handler(struct die_args *args)
+{
+ int ret = NOTIFY_DONE;
+ struct arch_hw_breakpoint *bp;
+ struct perf_event *event;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ event = this_cpu_read(pcpu_hw_bp_events[i]);
+ if (!event)
+ continue;
+
+ bp = counter_arch_bp(event);
+ switch (bp->type) {
+ /* Breakpoint */
+ case RV_DBTR_BP:
+ if (bp->address == args->regs->epc) {
+ perf_bp_event(event, args->regs);
+ ret = NOTIFY_STOP;
+ }
+ break;
+
+ /* Watchpoint */
+ case RV_DBTR_WP:
+ if (bp->address == csr_read(CSR_STVAL)) {
+ perf_bp_event(event, args->regs);
+ ret = NOTIFY_STOP;
+ }
+ break;
+
+ default:
+ pr_warn("%s: Unknown type: %u\n", __func__, bp->type);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
+ unsigned long val, void *data)
+{
+ if (val != DIE_DEBUG)
+ return NOTIFY_DONE;
+
+ return hw_breakpoint_handler(data);
+}
+
+/* atomic: counter->ctx->lock is held */
+int arch_install_hw_breakpoint(struct perf_event *event)
+{
+ struct arch_hw_breakpoint *bp = counter_arch_bp(event);
+ union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
+ struct sbi_dbtr_data_msg *xmit;
+ struct sbi_dbtr_id_msg *recv;
+ struct perf_event **slot;
+ unsigned long idx;
+ struct sbiret ret;
+ int err = 0;
+
+ raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+
+ xmit = &shmem->data;
+ recv = &shmem->id;
+ xmit->tdata1 = cpu_to_le(bp->tdata1);
+ xmit->tdata2 = cpu_to_le(bp->tdata2);
+ xmit->tdata3 = cpu_to_le(bp->tdata3);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_INSTALL,
+ 1, 0, 0, 0, 0, 0);
+
+ if (ret.error) {
+ pr_warn("%s: failed to install trigger\n", __func__);
+ err = -EIO;
+ goto done;
+ }
+
+ idx = le_to_cpu(recv->idx);
+ if (idx >= dbtr_total_num) {
+ pr_warn("%s: invalid trigger index %lu\n", __func__, idx);
+ err = -EINVAL;
+ goto done;
+ }
+
+ slot = this_cpu_ptr(&pcpu_hw_bp_events[idx]);
+ if (*slot) {
+ pr_warn("%s: slot %lu is in use\n", __func__, idx);
+ err = -EBUSY;
+ goto done;
+ }
+
+ pr_debug("Trigger %lu installed at index 0x%lx\n", bp->tdata2, idx);
+
+ /* Save the event - to be looked up in handler */
+ *slot = event;
+
+done:
+ raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+ return err;
+}
+
+/* atomic: counter->ctx->lock is held */
+void arch_uninstall_hw_breakpoint(struct perf_event *event)
+{
+ struct sbiret ret;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ struct perf_event **slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event) {
+ *slot = NULL;
+ break;
+ }
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UNINSTALL,
+ i, 1, 0, 0, 0, 0);
+ if (ret.error)
+ pr_warn("%s: Failed to uninstall trigger %d.\n", __func__, i);
+}
+
+void arch_enable_hw_breakpoint(struct perf_event *event)
+{
+ struct sbiret ret;
+ int i;
+ struct perf_event **slot;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event)
+ break;
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_ENABLE,
+ i, 1, 0, 0, 0, 0);
+
+ if (ret.error) {
+ pr_warn("%s: Failed to install trigger %d\n", __func__, i);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(arch_enable_hw_breakpoint);
+
+void arch_update_hw_breakpoint(struct perf_event *event)
+{
+ struct arch_hw_breakpoint *bp = counter_arch_bp(event);
+ union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
+ struct sbi_dbtr_data_msg *xmit;
+ struct perf_event **slot;
+ struct sbiret ret;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event)
+ break;
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+
+ xmit = &shmem->data;
+ xmit->tdata1 = cpu_to_le(bp->tdata1);
+ xmit->tdata2 = cpu_to_le(bp->tdata2);
+ xmit->tdata3 = cpu_to_le(bp->tdata3);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE,
+ i, 1, 0, 0, 0, 0);
+ if (ret.error)
+ pr_warn("%s: Failed to update trigger %d.\n", __func__, i);
+
+ raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+}
+EXPORT_SYMBOL_GPL(arch_update_hw_breakpoint);
+
+void arch_disable_hw_breakpoint(struct perf_event *event)
+{
+ struct sbiret ret;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ struct perf_event **slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event)
+ break;
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE,
+ i, 1, 0, 0, 0, 0);
+
+ if (ret.error) {
+ pr_warn("%s: Failed to uninstall trigger %d.\n", __func__, i);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(arch_disable_hw_breakpoint);
+
+void hw_breakpoint_pmu_read(struct perf_event *bp)
+{
+ /* TODO */
+}
+
+void clear_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+ /* TODO */
+}
+
+void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+ /* TODO */
+}
+
+static int __init arch_hw_breakpoint_init(void)
+{
+ unsigned int cpu;
+ int rc = 0;
+
+ for_each_possible_cpu(cpu)
+ raw_spin_lock_init(&per_cpu(ecall_lock, cpu));
+
+ if (!dbtr_init)
+ init_sbi_dbtr();
+
+ if (dbtr_total_num) {
+ pr_info("%s: total number of type %d triggers: %u\n",
+ __func__, dbtr_type, dbtr_total_num);
+ } else {
+ pr_info("%s: No hardware triggers available\n", __func__);
+ goto out;
+ }
+
+ /* Allocate per-cpu shared memory */
+ sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
+ PAGE_SIZE);
+
+ if (!sbi_dbtr_shmem) {
+ pr_warn("%s: Failed to allocate shared memory.\n", __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Hotplug handler to register/unregister shared memory with SBI */
+ rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "riscv/hw_breakpoint:prepare",
+ arch_smp_setup_sbi_shmem,
+ arch_smp_teardown_sbi_shmem);
+
+ if (rc < 0) {
+ pr_warn("%s: Failed to setup CPU hotplug state\n", __func__);
+ free_percpu(sbi_dbtr_shmem);
+ return rc;
+ }
+ out:
+ return rc;
+}
+arch_initcall(arch_hw_breakpoint_init);
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 9c83848797a7..cc8ef120ff41 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -288,6 +288,12 @@ void handle_break(struct pt_regs *regs)
if (probe_breakpoint_handler(regs))
return;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ if (notify_die(DIE_DEBUG, "EBREAK", regs, 0, regs->cause, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
+#endif
+
current->thread.bad_cause = regs->cause;
if (user_mode(regs))
--
2.45.2
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
@ 2025-07-10 12:52 ` Himanshu Chauhan
0 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-07-10 12:52 UTC (permalink / raw)
To: paul.walmsley, palmer, aou, linux-riscv, linux-kernel; +Cc: Himanshu Chauhan
RISC-V hardware breakpoint framework is built on top of perf subsystem
and uses SBI debug trigger extension to
install/uninstall/update/enable/disable hardware triggers as specified
in Sdtrig ISA extension.
Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
---
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/hw_breakpoint.h | 320 ++++++++++++
arch/riscv/include/asm/kdebug.h | 3 +-
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/hw_breakpoint.c | 657 +++++++++++++++++++++++++
arch/riscv/kernel/traps.c | 6 +
6 files changed, 987 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/include/asm/hw_breakpoint.h
create mode 100644 arch/riscv/kernel/hw_breakpoint.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d71ea0f4466f..741c8765724e 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -169,6 +169,7 @@ config RISCV
select HAVE_FUNCTION_ERROR_INJECTION
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_VDSO if MMU && 64BIT
+ select HAVE_HW_BREAKPOINT if PERF_EVENTS
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_KERNEL_BZIP2 if !XIP_KERNEL && !EFI_ZBOOT
select HAVE_KERNEL_GZIP if !XIP_KERNEL && !EFI_ZBOOT
diff --git a/arch/riscv/include/asm/hw_breakpoint.h b/arch/riscv/include/asm/hw_breakpoint.h
new file mode 100644
index 000000000000..a8fa96ce3ee3
--- /dev/null
+++ b/arch/riscv/include/asm/hw_breakpoint.h
@@ -0,0 +1,320 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __RISCV_HW_BREAKPOINT_H
+#define __RISCV_HW_BREAKPOINT_H
+
+struct task_struct;
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+
+#include <uapi/linux/hw_breakpoint.h>
+
+#if __riscv_xlen == 64
+#define cpu_to_le cpu_to_le64
+#define le_to_cpu le64_to_cpu
+#elif __riscv_xlen == 32
+#define cpu_to_le cpu_to_le32
+#define le_to_cpu le32_to_cpu
+#else
+#error "Unexpected __riscv_xlen"
+#endif
+
+#define RV_DBTR_BIT(_prefix, _name) \
+ RV_DBTR_##_prefix##_##_name##_BIT
+
+#define RV_DBTR_BIT_MASK(_prefix, _name) \
+ RV_DBTR_##_prefix##_name##_BIT_MASK
+
+#define RV_DBTR_BIT_MASK_VAL(_prefix, _name, _width) \
+ (((1UL << _width) - 1) << RV_DBTR_BIT(_prefix, _name))
+
+#define CLEAR_DBTR_BIT(_target, _prefix, _bit_name) \
+ __clear_bit(RV_DBTR_BIT(_prefix, _bit_name), &_target)
+
+#define SET_DBTR_BIT(_target, _prefix, _bit_name) \
+ __set_bit(RV_DBTR_BIT(_prefix, _bit_name), &_target)
+
+enum {
+ RV_DBTR_BP = 0,
+ RV_DBTR_WP = 1,
+};
+
+enum {
+ RV_DBTR_TRIG_NONE = 0,
+ RV_DBTR_TRIG_LEGACY,
+ RV_DBTR_TRIG_MCONTROL,
+ RV_DBTR_TRIG_ICOUNT,
+ RV_DBTR_TRIG_ITRIGGER,
+ RV_DBTR_TRIG_ETRIGGER,
+ RV_DBTR_TRIG_MCONTROL6,
+};
+
+/* Trigger Data 1 */
+enum {
+ RV_DBTR_BIT(TDATA1, DATA) = 0,
+#if __riscv_xlen == 64
+ RV_DBTR_BIT(TDATA1, DMODE) = 59,
+ RV_DBTR_BIT(TDATA1, TYPE) = 60,
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT(TDATA1, DMODE) = 27,
+ RV_DBTR_BIT(TDATA1, TYPE) = 28,
+#else
+ #error "Unknown __riscv_xlen"
+#endif
+};
+
+enum {
+#if __riscv_xlen == 64
+ RV_DBTR_BIT_MASK(TDATA1, DATA) = RV_DBTR_BIT_MASK_VAL(TDATA1, DATA, 59),
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT_MASK(TDATA1, DATA) = RV_DBTR_BIT_MASK_VAL(TDATA1, DATA, 27),
+#else
+ #error "Unknown __riscv_xlen"
+#endif
+ RV_DBTR_BIT_MASK(TDAT1, DMODE) = RV_DBTR_BIT_MASK_VAL(TDATA1, DMODE, 1),
+ RV_DBTR_BIT_MASK(TDATA1, TYPE) = RV_DBTR_BIT_MASK_VAL(TDATA1, TYPE, 4),
+};
+
+/* MC - Match Control Type Register */
+enum {
+ RV_DBTR_BIT(MC, LOAD) = 0,
+ RV_DBTR_BIT(MC, STORE) = 1,
+ RV_DBTR_BIT(MC, EXEC) = 2,
+ RV_DBTR_BIT(MC, U) = 3,
+ RV_DBTR_BIT(MC, S) = 4,
+ RV_DBTR_BIT(MC, RES2) = 5,
+ RV_DBTR_BIT(MC, M) = 6,
+ RV_DBTR_BIT(MC, MATCH) = 7,
+ RV_DBTR_BIT(MC, CHAIN) = 11,
+ RV_DBTR_BIT(MC, ACTION) = 12,
+ RV_DBTR_BIT(MC, SIZELO) = 16,
+ RV_DBTR_BIT(MC, TIMING) = 18,
+ RV_DBTR_BIT(MC, SELECT) = 19,
+ RV_DBTR_BIT(MC, HIT) = 20,
+#if __riscv_xlen >= 64
+ RV_DBTR_BIT(MC, SIZEHI) = 21,
+#endif
+#if __riscv_xlen == 64
+ RV_DBTR_BIT(MC, MASKMAX) = 53,
+ RV_DBTR_BIT(MC, DMODE) = 59,
+ RV_DBTR_BIT(MC, TYPE) = 60,
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT(MC, MASKMAX) = 21,
+ RV_DBTR_BIT(MC, DMODE) = 27,
+ RV_DBTR_BIT(MC, TYPE) = 28,
+#else
+ #error "Unknown riscv xlen"
+#endif
+};
+
+enum {
+ RV_DBTR_BIT_MASK(MC, LOAD) = RV_DBTR_BIT_MASK_VAL(MC, LOAD, 1),
+ RV_DBTR_BIT_MASK(MC, STORE) = RV_DBTR_BIT_MASK_VAL(MC, STORE, 1),
+ RV_DBTR_BIT_MASK(MC, EXEC) = RV_DBTR_BIT_MASK_VAL(MC, EXEC, 1),
+ RV_DBTR_BIT_MASK(MC, U) = RV_DBTR_BIT_MASK_VAL(MC, U, 1),
+ RV_DBTR_BIT_MASK(MC, S) = RV_DBTR_BIT_MASK_VAL(MC, S, 1),
+ RV_DBTR_BIT_MASK(MC, RES2) = RV_DBTR_BIT_MASK_VAL(MC, RES2, 1),
+ RV_DBTR_BIT_MASK(MC, M) = RV_DBTR_BIT_MASK_VAL(MC, M, 1),
+ RV_DBTR_BIT_MASK(MC, MATCH) = RV_DBTR_BIT_MASK_VAL(MC, MATCH, 4),
+ RV_DBTR_BIT_MASK(MC, CHAIN) = RV_DBTR_BIT_MASK_VAL(MC, CHAIN, 1),
+ RV_DBTR_BIT_MASK(MC, ACTION) = RV_DBTR_BIT_MASK_VAL(MC, ACTION, 4),
+ RV_DBTR_BIT_MASK(MC, SIZELO) = RV_DBTR_BIT_MASK_VAL(MC, SIZELO, 2),
+ RV_DBTR_BIT_MASK(MC, TIMING) = RV_DBTR_BIT_MASK_VAL(MC, TIMING, 1),
+ RV_DBTR_BIT_MASK(MC, SELECT) = RV_DBTR_BIT_MASK_VAL(MC, SELECT, 1),
+ RV_DBTR_BIT_MASK(MC, HIT) = RV_DBTR_BIT_MASK_VAL(MC, HIT, 1),
+#if __riscv_xlen >= 64
+ RV_DBTR_BIT_MASK(MC, SIZEHI) = RV_DBTR_BIT_MASK_VAL(MC, SIZEHI, 2),
+#endif
+ RV_DBTR_BIT_MASK(MC, MASKMAX) = RV_DBTR_BIT_MASK_VAL(MC, MASKMAX, 6),
+ RV_DBTR_BIT_MASK(MC, DMODE) = RV_DBTR_BIT_MASK_VAL(MC, DMODE, 1),
+ RV_DBTR_BIT_MASK(MC, TYPE) = RV_DBTR_BIT_MASK_VAL(MC, TYPE, 4),
+};
+
+/* MC6 - Match Control 6 Type Register */
+enum {
+ RV_DBTR_BIT(MC6, LOAD) = 0,
+ RV_DBTR_BIT(MC6, STORE) = 1,
+ RV_DBTR_BIT(MC6, EXEC) = 2,
+ RV_DBTR_BIT(MC6, U) = 3,
+ RV_DBTR_BIT(MC6, S) = 4,
+ RV_DBTR_BIT(MC6, RES2) = 5,
+ RV_DBTR_BIT(MC6, M) = 6,
+ RV_DBTR_BIT(MC6, MATCH) = 7,
+ RV_DBTR_BIT(MC6, CHAIN) = 11,
+ RV_DBTR_BIT(MC6, ACTION) = 12,
+ RV_DBTR_BIT(MC6, SIZE) = 16,
+ RV_DBTR_BIT(MC6, TIMING) = 20,
+ RV_DBTR_BIT(MC6, SELECT) = 21,
+ RV_DBTR_BIT(MC6, HIT) = 22,
+ RV_DBTR_BIT(MC6, VU) = 23,
+ RV_DBTR_BIT(MC6, VS) = 24,
+#if __riscv_xlen == 64
+ RV_DBTR_BIT(MC6, DMODE) = 59,
+ RV_DBTR_BIT(MC6, TYPE) = 60,
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT(MC6, DMODE) = 27,
+ RV_DBTR_BIT(MC6, TYPE) = 28,
+#else
+ #error "Unknown riscv xlen"
+#endif
+};
+
+enum {
+ RV_DBTR_BIT_MASK(MC6, LOAD) = RV_DBTR_BIT_MASK_VAL(MC6, LOAD, 1),
+ RV_DBTR_BIT_MASK(MC6, STORE) = RV_DBTR_BIT_MASK_VAL(MC6, STORE, 1),
+ RV_DBTR_BIT_MASK(MC6, EXEC) = RV_DBTR_BIT_MASK_VAL(MC6, EXEC, 1),
+ RV_DBTR_BIT_MASK(MC6, U) = RV_DBTR_BIT_MASK_VAL(MC6, U, 1),
+ RV_DBTR_BIT_MASK(MC6, S) = RV_DBTR_BIT_MASK_VAL(MC6, S, 1),
+ RV_DBTR_BIT_MASK(MC6, RES2) = RV_DBTR_BIT_MASK_VAL(MC6, RES2, 1),
+ RV_DBTR_BIT_MASK(MC6, M) = RV_DBTR_BIT_MASK_VAL(MC6, M, 1),
+ RV_DBTR_BIT_MASK(MC6, MATCH) = RV_DBTR_BIT_MASK_VAL(MC6, MATCH, 4),
+ RV_DBTR_BIT_MASK(MC6, CHAIN) = RV_DBTR_BIT_MASK_VAL(MC6, CHAIN, 1),
+ RV_DBTR_BIT_MASK(MC6, ACTION) = RV_DBTR_BIT_MASK_VAL(MC6, ACTION, 4),
+ RV_DBTR_BIT_MASK(MC6, SIZE) = RV_DBTR_BIT_MASK_VAL(MC6, SIZE, 4),
+ RV_DBTR_BIT_MASK(MC6, TIMING) = RV_DBTR_BIT_MASK_VAL(MC6, TIMING, 1),
+ RV_DBTR_BIT_MASK(MC6, SELECT) = RV_DBTR_BIT_MASK_VAL(MC6, SELECT, 1),
+ RV_DBTR_BIT_MASK(MC6, HIT) = RV_DBTR_BIT_MASK_VAL(MC6, HIT, 1),
+ RV_DBTR_BIT_MASK(MC6, VU) = RV_DBTR_BIT_MASK_VAL(MC6, VU, 1),
+ RV_DBTR_BIT_MASK(MC6, VS) = RV_DBTR_BIT_MASK_VAL(MC6, VS, 1),
+#if __riscv_xlen == 64
+ RV_DBTR_BIT_MASK(MC6, DMODE) = RV_DBTR_BIT_MASK_VAL(MC6, DMODE, 1),
+ RV_DBTR_BIT_MASK(MC6, TYPE) = RV_DBTR_BIT_MASK_VAL(MC6, TYPE, 4),
+#elif __riscv_xlen == 32
+ RV_DBTR_BIT_MASK(MC6, DMODE) = RV_DBTR_BIT_MASK_VAL(MC6, DMODE, 1),
+ RV_DBTR_BIT_MASK(MC6, TYPE) = RV_DBTR_BIT_MASK_VAL(MC6, TYPE, 4),
+#else
+ #error "Unknown riscv xlen"
+#endif
+};
+
+#define RV_DBTR_SET_TDATA1_TYPE(_t1, _type) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(TDATA1, TYPE); \
+ _t1 |= (((unsigned long)_type \
+ << RV_DBTR_BIT(TDATA1, TYPE)) \
+ & RV_DBTR_BIT_MASK(TDATA1, TYPE)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC_TYPE(_t1, _type) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC, TYPE); \
+ _t1 |= (((unsigned long)_type \
+ << RV_DBTR_BIT(MC, TYPE)) \
+ & RV_DBTR_BIT_MASK(MC, TYPE)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC6_TYPE(_t1, _type) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC6, TYPE); \
+ _t1 |= (((unsigned long)_type \
+ << RV_DBTR_BIT(MC6, TYPE)) \
+ & RV_DBTR_BIT_MASK(MC6, TYPE)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC_EXEC(_t1) \
+ SET_DBTR_BIT(_t1, MC, EXEC)
+
+#define RV_DBTR_SET_MC_LOAD(_t1) \
+ SET_DBTR_BIT(_t1, MC, LOAD)
+
+#define RV_DBTR_SET_MC_STORE(_t1) \
+ SET_DBTR_BIT(_t1, MC, STORE)
+
+#define RV_DBTR_SET_MC_SIZELO(_t1, _val) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC, SIZELO); \
+ _t1 |= ((_val << RV_DBTR_BIT(MC, SIZELO)) \
+ & RV_DBTR_BIT_MASK(MC, SIZELO)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC_SIZEHI(_t1, _val) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC, SIZEHI); \
+ _t1 |= ((_val << RV_DBTR_BIT(MC, SIZEHI)) \
+ & RV_DBTR_BIT_MASK(MC, SIZEHI)); \
+ } while (0)
+
+#define RV_DBTR_SET_MC6_EXEC(_t1) \
+ SET_DBTR_BIT(_t1, MC6, EXEC)
+
+#define RV_DBTR_SET_MC6_LOAD(_t1) \
+ SET_DBTR_BIT(_t1, MC6, LOAD)
+
+#define RV_DBTR_SET_MC6_STORE(_t1) \
+ SET_DBTR_BIT(_t1, MC6, STORE)
+
+#define RV_DBTR_SET_MC6_SIZE(_t1, _val) \
+ do { \
+ _t1 &= ~RV_DBTR_BIT_MASK(MC6, SIZE); \
+ _t1 |= ((_val << RV_DBTR_BIT(MC6, SIZE)) \
+ & RV_DBTR_BIT_MASK(MC6, SIZE)); \
+ } while (0)
+
+struct arch_hw_breakpoint {
+ unsigned long address;
+ unsigned long len;
+ unsigned int type;
+
+ /* Trigger configuration data */
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+/* Maximum number of hardware breakpoints supported */
+#define HW_BP_NUM_MAX 32
+
+struct perf_event_attr;
+struct notifier_block;
+struct perf_event;
+struct pt_regs;
+
+int hw_breakpoint_slots(int type);
+int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);
+int hw_breakpoint_arch_parse(struct perf_event *bp,
+ const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw);
+int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
+ unsigned long val, void *data);
+
+void arch_enable_hw_breakpoint(struct perf_event *bp);
+void arch_update_hw_breakpoint(struct perf_event *bp);
+void arch_disable_hw_breakpoint(struct perf_event *bp);
+int arch_install_hw_breakpoint(struct perf_event *bp);
+void arch_uninstall_hw_breakpoint(struct perf_event *bp);
+void hw_breakpoint_pmu_read(struct perf_event *bp);
+void clear_ptrace_hw_breakpoint(struct task_struct *tsk);
+void flush_ptrace_hw_breakpoint(struct task_struct *tsk);
+
+#else
+
+int hw_breakpoint_slots(int type)
+{
+ return 0;
+}
+
+static inline void clear_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+}
+
+static inline void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+}
+
+void arch_enable_hw_breakpoint(struct perf_event *bp)
+{
+}
+
+void arch_update_hw_breakpoint(struct perf_event *bp)
+{
+}
+
+void arch_disable_hw_breakpoint(struct perf_event *bp)
+{
+}
+
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+#endif /* __RISCV_HW_BREAKPOINT_H */
diff --git a/arch/riscv/include/asm/kdebug.h b/arch/riscv/include/asm/kdebug.h
index 85ac00411f6e..53e989781aa1 100644
--- a/arch/riscv/include/asm/kdebug.h
+++ b/arch/riscv/include/asm/kdebug.h
@@ -6,7 +6,8 @@
enum die_val {
DIE_UNUSED,
DIE_TRAP,
- DIE_OOPS
+ DIE_OOPS,
+ DIE_DEBUG
};
#endif
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index c7b542573407..39a3489bd9e2 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
+obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_RISCV_SBI) += sbi.o sbi_ecall.o
ifeq ($(CONFIG_RISCV_SBI), y)
obj-$(CONFIG_SMP) += sbi-ipi.o
diff --git a/arch/riscv/kernel/hw_breakpoint.c b/arch/riscv/kernel/hw_breakpoint.c
new file mode 100644
index 000000000000..3a43d1e59de8
--- /dev/null
+++ b/arch/riscv/kernel/hw_breakpoint.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/hw_breakpoint.h>
+#include <linux/perf_event.h>
+#include <linux/spinlock.h>
+#include <linux/percpu.h>
+#include <linux/kdebug.h>
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/cpuhotplug.h>
+
+#include <asm/sbi.h>
+
+/* Registered per-cpu bp/wp */
+static DEFINE_PER_CPU(struct perf_event *, pcpu_hw_bp_events[HW_BP_NUM_MAX]);
+static DEFINE_PER_CPU(unsigned long, ecall_lock_flags);
+static DEFINE_PER_CPU(raw_spinlock_t, ecall_lock);
+
+/* Per-cpu shared memory between S and M mode */
+static union sbi_dbtr_shmem_entry __percpu *sbi_dbtr_shmem;
+
+/* number of debug triggers on this cpu . */
+static int dbtr_total_num __ro_after_init;
+static int dbtr_type __ro_after_init;
+static int dbtr_init __ro_after_init;
+
+#if __riscv_xlen == 64
+#define MEM_HI(_m) 0
+#define MEM_LO(_m) ((u64)_m)
+#elif __riscv_xlen == 32
+#define MEM_HI(_m) ((u64)_m >> 32)
+#define MEM_LO(_m) ((u64)_m & 0xFFFFFFFFUL)
+#else
+#error "Unknown __riscv_xlen"
+#endif
+
+static int arch_smp_setup_sbi_shmem(unsigned int cpu)
+{
+ union sbi_dbtr_shmem_entry *dbtr_shmem;
+ unsigned long shmem_pa;
+ struct sbiret ret;
+ int rc = 0;
+
+ dbtr_shmem = per_cpu_ptr(sbi_dbtr_shmem, cpu);
+ if (!dbtr_shmem) {
+ pr_err("Invalid per-cpu shared memory for debug triggers\n");
+ return -ENODEV;
+ }
+
+ shmem_pa = __pa(dbtr_shmem);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
+ MEM_LO(shmem_pa), MEM_HI(shmem_pa), 0, 0, 0, 0);
+
+ if (ret.error) {
+ switch (ret.error) {
+ case SBI_ERR_DENIED:
+ pr_warn("%s: Access denied for shared memory at %lx\n",
+ __func__, shmem_pa);
+ rc = -EPERM;
+ break;
+
+ case SBI_ERR_INVALID_PARAM:
+ case SBI_ERR_INVALID_ADDRESS:
+ pr_warn("%s: Invalid address parameter (%lu)\n",
+ __func__, ret.error);
+ rc = -EINVAL;
+ break;
+
+ case SBI_ERR_ALREADY_AVAILABLE:
+ pr_warn("%s: Shared memory is already set\n",
+ __func__);
+ rc = -EADDRINUSE;
+ break;
+
+ case SBI_ERR_FAILURE:
+ pr_err("%s: Internal sdtrig state error\n",
+ __func__);
+ rc = -ENXIO;
+ break;
+
+ default:
+ pr_warn("%s: Unknown error %lu\n", __func__, ret.error);
+ rc = -ENXIO;
+ break;
+ }
+ }
+
+ pr_info("CPU %d: HW Breakpoint shared memory registered.\n", cpu);
+
+ return rc;
+}
+
+static int arch_smp_teardown_sbi_shmem(unsigned int cpu)
+{
+ struct sbiret ret;
+
+ /* Disable shared memory */
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
+ -1UL, -1UL, 0, 0, 0, 0);
+
+ if (ret.error) {
+ switch (ret.error) {
+ case SBI_ERR_DENIED:
+ pr_err("%s: Access denied for shared memory.\n",
+ __func__);
+ break;
+
+ case SBI_ERR_INVALID_PARAM:
+ case SBI_ERR_INVALID_ADDRESS:
+ pr_err("%s: Invalid address parameter (%lu)\n",
+ __func__, ret.error);
+ break;
+
+ case SBI_ERR_ALREADY_AVAILABLE:
+ pr_err("%s: Shared memory is already set\n",
+ __func__);
+ break;
+ case SBI_ERR_FAILURE:
+ pr_err("%s: Internal sdtrig state error\n",
+ __func__);
+ break;
+ default:
+ pr_err("%s: Unknown error %lu\n", __func__, ret.error);
+ break;
+ }
+ }
+
+ pr_warn("CPU %d: HW Breakpoint shared memory disabled.\n", cpu);
+
+ return 0;
+}
+
+static void init_sbi_dbtr(void)
+{
+ unsigned long tdata1;
+ struct sbiret ret;
+
+ if (sbi_probe_extension(SBI_EXT_DBTR) <= 0) {
+ pr_warn("%s: SBI_EXT_DBTR is not supported\n", __func__);
+ dbtr_total_num = 0;
+ goto done;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
+ 0, 0, 0, 0, 0, 0);
+ if (ret.error) {
+ pr_warn("%s: Failed to detect triggers\n", __func__);
+ dbtr_total_num = 0;
+ goto done;
+ }
+
+ tdata1 = 0;
+ RV_DBTR_SET_TDATA1_TYPE(tdata1, RV_DBTR_TRIG_MCONTROL6);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
+ tdata1, 0, 0, 0, 0, 0);
+ if (ret.error) {
+ pr_warn("%s: failed to detect mcontrol6 triggers\n", __func__);
+ } else if (!ret.value) {
+ pr_warn("%s: type 6 triggers not available\n", __func__);
+ } else {
+ dbtr_total_num = ret.value;
+ dbtr_type = RV_DBTR_TRIG_MCONTROL6;
+ pr_warn("%s: mcontrol6 trigger available.\n", __func__);
+ goto done;
+ }
+
+ /* fallback to type 2 triggers if type 6 is not available */
+
+ tdata1 = 0;
+ RV_DBTR_SET_TDATA1_TYPE(tdata1, RV_DBTR_TRIG_MCONTROL);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
+ tdata1, 0, 0, 0, 0, 0);
+ if (ret.error) {
+ pr_warn("%s: failed to detect mcontrol triggers\n", __func__);
+ } else if (!ret.value) {
+ pr_warn("%s: type 2 triggers not available\n", __func__);
+ } else {
+ dbtr_total_num = ret.value;
+ dbtr_type = RV_DBTR_TRIG_MCONTROL;
+ goto done;
+ }
+
+done:
+ dbtr_init = 1;
+}
+
+int hw_breakpoint_slots(int type)
+{
+ /*
+ * We can be called early, so don't rely on
+ * static variables being initialised.
+ */
+
+ if (!dbtr_init)
+ init_sbi_dbtr();
+
+ return dbtr_total_num;
+}
+
+int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw)
+{
+ unsigned int len;
+ unsigned long va;
+
+ va = hw->address;
+ len = hw->len;
+
+ return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
+}
+
+static int rv_init_mcontrol_trigger(const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw)
+{
+ switch (attr->bp_type) {
+ case HW_BREAKPOINT_X:
+ hw->type = RV_DBTR_BP;
+ RV_DBTR_SET_MC_EXEC(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_R:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC_LOAD(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_W:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC_STORE(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_RW:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC_LOAD(hw->tdata1);
+ RV_DBTR_SET_MC_STORE(hw->tdata1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (attr->bp_len) {
+ case HW_BREAKPOINT_LEN_1:
+ hw->len = 1;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 1);
+ break;
+ case HW_BREAKPOINT_LEN_2:
+ hw->len = 2;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 2);
+ break;
+ case HW_BREAKPOINT_LEN_4:
+ hw->len = 4;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 3);
+ break;
+#if __riscv_xlen >= 64
+ case HW_BREAKPOINT_LEN_8:
+ hw->len = 8;
+ RV_DBTR_SET_MC_SIZELO(hw->tdata1, 1);
+ RV_DBTR_SET_MC_SIZEHI(hw->tdata1, 1);
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ RV_DBTR_SET_MC_TYPE(hw->tdata1, RV_DBTR_TRIG_MCONTROL);
+
+ CLEAR_DBTR_BIT(hw->tdata1, MC, DMODE);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, TIMING);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, SELECT);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, ACTION);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, CHAIN);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, MATCH);
+ CLEAR_DBTR_BIT(hw->tdata1, MC, M);
+
+ SET_DBTR_BIT(hw->tdata1, MC, S);
+ SET_DBTR_BIT(hw->tdata1, MC, U);
+
+ return 0;
+}
+
+static int rv_init_mcontrol6_trigger(const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw)
+{
+ switch (attr->bp_type) {
+ case HW_BREAKPOINT_X:
+ hw->type = RV_DBTR_BP;
+ RV_DBTR_SET_MC6_EXEC(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_R:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC6_LOAD(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_W:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC6_STORE(hw->tdata1);
+ break;
+ case HW_BREAKPOINT_RW:
+ hw->type = RV_DBTR_WP;
+ RV_DBTR_SET_MC6_STORE(hw->tdata1);
+ RV_DBTR_SET_MC6_LOAD(hw->tdata1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (attr->bp_len) {
+ case HW_BREAKPOINT_LEN_1:
+ hw->len = 1;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 1);
+ break;
+ case HW_BREAKPOINT_LEN_2:
+ hw->len = 2;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 2);
+ break;
+ case HW_BREAKPOINT_LEN_4:
+ hw->len = 4;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 3);
+ break;
+ case HW_BREAKPOINT_LEN_8:
+ hw->len = 8;
+ RV_DBTR_SET_MC6_SIZE(hw->tdata1, 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ RV_DBTR_SET_MC6_TYPE(hw->tdata1, RV_DBTR_TRIG_MCONTROL6);
+
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, DMODE);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, TIMING);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, SELECT);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, ACTION);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, CHAIN);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, MATCH);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, M);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, VS);
+ CLEAR_DBTR_BIT(hw->tdata1, MC6, VU);
+
+ SET_DBTR_BIT(hw->tdata1, MC6, S);
+ SET_DBTR_BIT(hw->tdata1, MC6, U);
+
+ return 0;
+}
+
+int hw_breakpoint_arch_parse(struct perf_event *bp,
+ const struct perf_event_attr *attr,
+ struct arch_hw_breakpoint *hw)
+{
+ int ret;
+
+ /* Breakpoint address */
+ hw->address = attr->bp_addr;
+ hw->tdata2 = attr->bp_addr;
+ hw->tdata3 = 0x0;
+
+ switch (dbtr_type) {
+ case RV_DBTR_TRIG_MCONTROL:
+ ret = rv_init_mcontrol_trigger(attr, hw);
+ break;
+ case RV_DBTR_TRIG_MCONTROL6:
+ ret = rv_init_mcontrol6_trigger(attr, hw);
+ break;
+ default:
+ pr_warn("unsupported trigger type\n");
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * HW Breakpoint/watchpoint handler
+ */
+static int hw_breakpoint_handler(struct die_args *args)
+{
+ int ret = NOTIFY_DONE;
+ struct arch_hw_breakpoint *bp;
+ struct perf_event *event;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ event = this_cpu_read(pcpu_hw_bp_events[i]);
+ if (!event)
+ continue;
+
+ bp = counter_arch_bp(event);
+ switch (bp->type) {
+ /* Breakpoint */
+ case RV_DBTR_BP:
+ if (bp->address == args->regs->epc) {
+ perf_bp_event(event, args->regs);
+ ret = NOTIFY_STOP;
+ }
+ break;
+
+ /* Watchpoint */
+ case RV_DBTR_WP:
+ if (bp->address == csr_read(CSR_STVAL)) {
+ perf_bp_event(event, args->regs);
+ ret = NOTIFY_STOP;
+ }
+ break;
+
+ default:
+ pr_warn("%s: Unknown type: %u\n", __func__, bp->type);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
+ unsigned long val, void *data)
+{
+ if (val != DIE_DEBUG)
+ return NOTIFY_DONE;
+
+ return hw_breakpoint_handler(data);
+}
+
+/* atomic: counter->ctx->lock is held */
+int arch_install_hw_breakpoint(struct perf_event *event)
+{
+ struct arch_hw_breakpoint *bp = counter_arch_bp(event);
+ union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
+ struct sbi_dbtr_data_msg *xmit;
+ struct sbi_dbtr_id_msg *recv;
+ struct perf_event **slot;
+ unsigned long idx;
+ struct sbiret ret;
+ int err = 0;
+
+ raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+
+ xmit = &shmem->data;
+ recv = &shmem->id;
+ xmit->tdata1 = cpu_to_le(bp->tdata1);
+ xmit->tdata2 = cpu_to_le(bp->tdata2);
+ xmit->tdata3 = cpu_to_le(bp->tdata3);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_INSTALL,
+ 1, 0, 0, 0, 0, 0);
+
+ if (ret.error) {
+ pr_warn("%s: failed to install trigger\n", __func__);
+ err = -EIO;
+ goto done;
+ }
+
+ idx = le_to_cpu(recv->idx);
+ if (idx >= dbtr_total_num) {
+ pr_warn("%s: invalid trigger index %lu\n", __func__, idx);
+ err = -EINVAL;
+ goto done;
+ }
+
+ slot = this_cpu_ptr(&pcpu_hw_bp_events[idx]);
+ if (*slot) {
+ pr_warn("%s: slot %lu is in use\n", __func__, idx);
+ err = -EBUSY;
+ goto done;
+ }
+
+ pr_debug("Trigger %lu installed at index 0x%lx\n", bp->tdata2, idx);
+
+ /* Save the event - to be looked up in handler */
+ *slot = event;
+
+done:
+ raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+ return err;
+}
+
+/* atomic: counter->ctx->lock is held */
+void arch_uninstall_hw_breakpoint(struct perf_event *event)
+{
+ struct sbiret ret;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ struct perf_event **slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event) {
+ *slot = NULL;
+ break;
+ }
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UNINSTALL,
+ i, 1, 0, 0, 0, 0);
+ if (ret.error)
+ pr_warn("%s: Failed to uninstall trigger %d.\n", __func__, i);
+}
+
+void arch_enable_hw_breakpoint(struct perf_event *event)
+{
+ struct sbiret ret;
+ int i;
+ struct perf_event **slot;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event)
+ break;
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_ENABLE,
+ i, 1, 0, 0, 0, 0);
+
+ if (ret.error) {
+ pr_warn("%s: Failed to install trigger %d\n", __func__, i);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(arch_enable_hw_breakpoint);
+
+void arch_update_hw_breakpoint(struct perf_event *event)
+{
+ struct arch_hw_breakpoint *bp = counter_arch_bp(event);
+ union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
+ struct sbi_dbtr_data_msg *xmit;
+ struct perf_event **slot;
+ struct sbiret ret;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event)
+ break;
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+
+ xmit = &shmem->data;
+ xmit->tdata1 = cpu_to_le(bp->tdata1);
+ xmit->tdata2 = cpu_to_le(bp->tdata2);
+ xmit->tdata3 = cpu_to_le(bp->tdata3);
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE,
+ i, 1, 0, 0, 0, 0);
+ if (ret.error)
+ pr_warn("%s: Failed to update trigger %d.\n", __func__, i);
+
+ raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock),
+ *this_cpu_ptr(&ecall_lock_flags));
+}
+EXPORT_SYMBOL_GPL(arch_update_hw_breakpoint);
+
+void arch_disable_hw_breakpoint(struct perf_event *event)
+{
+ struct sbiret ret;
+ int i;
+
+ for (i = 0; i < dbtr_total_num; i++) {
+ struct perf_event **slot = this_cpu_ptr(&pcpu_hw_bp_events[i]);
+
+ if (*slot == event)
+ break;
+ }
+
+ if (i == dbtr_total_num) {
+ pr_warn("%s: Breakpoint not installed.\n", __func__);
+ return;
+ }
+
+ ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE,
+ i, 1, 0, 0, 0, 0);
+
+ if (ret.error) {
+ pr_warn("%s: Failed to uninstall trigger %d.\n", __func__, i);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(arch_disable_hw_breakpoint);
+
+void hw_breakpoint_pmu_read(struct perf_event *bp)
+{
+ /* TODO */
+}
+
+void clear_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+ /* TODO */
+}
+
+void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+ /* TODO */
+}
+
+static int __init arch_hw_breakpoint_init(void)
+{
+ unsigned int cpu;
+ int rc = 0;
+
+ for_each_possible_cpu(cpu)
+ raw_spin_lock_init(&per_cpu(ecall_lock, cpu));
+
+ if (!dbtr_init)
+ init_sbi_dbtr();
+
+ if (dbtr_total_num) {
+ pr_info("%s: total number of type %d triggers: %u\n",
+ __func__, dbtr_type, dbtr_total_num);
+ } else {
+ pr_info("%s: No hardware triggers available\n", __func__);
+ goto out;
+ }
+
+ /* Allocate per-cpu shared memory */
+ sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
+ PAGE_SIZE);
+
+ if (!sbi_dbtr_shmem) {
+ pr_warn("%s: Failed to allocate shared memory.\n", __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Hotplug handler to register/unregister shared memory with SBI */
+ rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "riscv/hw_breakpoint:prepare",
+ arch_smp_setup_sbi_shmem,
+ arch_smp_teardown_sbi_shmem);
+
+ if (rc < 0) {
+ pr_warn("%s: Failed to setup CPU hotplug state\n", __func__);
+ free_percpu(sbi_dbtr_shmem);
+ return rc;
+ }
+ out:
+ return rc;
+}
+arch_initcall(arch_hw_breakpoint_init);
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 9c83848797a7..cc8ef120ff41 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -288,6 +288,12 @@ void handle_break(struct pt_regs *regs)
if (probe_breakpoint_handler(regs))
return;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ if (notify_die(DIE_DEBUG, "EBREAK", regs, 0, regs->cause, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
+#endif
+
current->thread.bad_cause = regs->cause;
if (user_mode(regs))
--
2.45.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
2025-07-10 12:52 ` Himanshu Chauhan
(?)
@ 2025-07-11 6:19 ` kernel test robot
-1 siblings, 0 replies; 14+ messages in thread
From: kernel test robot @ 2025-07-11 6:19 UTC (permalink / raw)
To: Himanshu Chauhan; +Cc: oe-kbuild-all
Hi Himanshu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master]
[also build test WARNING on v6.16-rc5 next-20250710]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Himanshu-Chauhan/riscv-Add-SBI-debug-trigger-extension-and-function-ids/20250710-205738
base: linus/master
patch link: https://lore.kernel.org/r/20250710125231.653967-3-hchauhan%40ventanamicro.com
patch subject: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
config: riscv-randconfig-r071-20250711 (https://download.01.org/0day-ci/archive/20250711/202507111419.Snh8ePO7-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111419.Snh8ePO7-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507111419.Snh8ePO7-lkp@intel.com/
All warnings (new ones prefixed by >>):
from ./arch/riscv/include/generated/asm/local64.h:1,
from include/linux/perf_event.h:26,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_update_hw_breakpoint':
include/linux/percpu-defs.h:220:52: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
220 | const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
| ^
include/linux/percpu-defs.h:263:2: note: in expansion of macro '__verify_pcpu_ptr'
263 | __verify_pcpu_ptr(ptr); \
| ^~~~~~~~~~~~~~~~~
include/linux/percpu-defs.h:267:26: note: in expansion of macro 'per_cpu_ptr'
267 | #define raw_cpu_ptr(ptr) per_cpu_ptr(ptr, 0)
| ^~~~~~~~~~~
include/linux/percpu-defs.h:268:27: note: in expansion of macro 'raw_cpu_ptr'
268 | #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr)
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:536:38: note: in expansion of macro 'this_cpu_ptr'
536 | union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:539:16: error: storage size of 'ret' isn't known
539 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:557:15: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
557 | xmit = &shmem->data;
| ^~
arch/riscv/kernel/hw_breakpoint.c:558:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
558 | xmit->tdata1 = cpu_to_le(bp->tdata1);
| ^~
arch/riscv/kernel/hw_breakpoint.c:559:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
559 | xmit->tdata2 = cpu_to_le(bp->tdata2);
| ^~
arch/riscv/kernel/hw_breakpoint.c:560:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
560 | xmit->tdata3 = cpu_to_le(bp->tdata3);
| ^~
arch/riscv/kernel/hw_breakpoint.c:562:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
562 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:562:32: error: 'SBI_EXT_DBTR_TRIG_UPDATE' undeclared (first use in this function)
562 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE,
| ^~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:539:16: warning: unused variable 'ret' [-Wunused-variable]
539 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_disable_hw_breakpoint':
arch/riscv/kernel/hw_breakpoint.c:574:16: error: storage size of 'ret' isn't known
574 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:589:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
589 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:589:32: error: 'SBI_EXT_DBTR_TRIG_DISABLE' undeclared (first use in this function)
589 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:574:16: warning: unused variable 'ret' [-Wunused-variable]
574 | struct sbiret ret;
| ^~~
In file included from include/linux/percpu.h:5,
from include/asm-generic/local64.h:5,
from ./arch/riscv/include/generated/asm/local64.h:1,
from include/linux/perf_event.h:26,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_hw_breakpoint_init':
arch/riscv/kernel/hw_breakpoint.c:634:40: error: invalid application of 'sizeof' to incomplete type 'union sbi_dbtr_shmem_entry'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
include/linux/alloc_tag.h:239:9: note: in definition of macro 'alloc_hooks_tag'
239 | typeof(_do_alloc) _res; \
| ^~~~~~~~~
include/linux/percpu.h:142:2: note: in expansion of macro 'alloc_hooks'
142 | alloc_hooks(pcpu_alloc_noprof(_size, _align, false, GFP_KERNEL))
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:19: note: in expansion of macro '__alloc_percpu'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:40: error: invalid application of 'sizeof' to incomplete type 'union sbi_dbtr_shmem_entry'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
include/linux/alloc_tag.h:243:10: note: in definition of macro 'alloc_hooks_tag'
243 | _res = _do_alloc; \
| ^~~~~~~~~
include/linux/percpu.h:142:2: note: in expansion of macro 'alloc_hooks'
142 | alloc_hooks(pcpu_alloc_noprof(_size, _align, false, GFP_KERNEL))
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:19: note: in expansion of macro '__alloc_percpu'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:40: error: invalid application of 'sizeof' to incomplete type 'union sbi_dbtr_shmem_entry'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
include/linux/alloc_tag.h:246:10: note: in definition of macro 'alloc_hooks_tag'
246 | _res = _do_alloc; \
| ^~~~~~~~~
include/linux/percpu.h:142:2: note: in expansion of macro 'alloc_hooks'
142 | alloc_hooks(pcpu_alloc_noprof(_size, _align, false, GFP_KERNEL))
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:19: note: in expansion of macro '__alloc_percpu'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^~~~~~~~~~~~~~
>> arch/riscv/kernel/hw_breakpoint.c:634:17: warning: assignment to 'union sbi_dbtr_shmem_entry *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
cc1: some warnings being treated as errors
vim +634 arch/riscv/kernel/hw_breakpoint.c
613
614 static int __init arch_hw_breakpoint_init(void)
615 {
616 unsigned int cpu;
617 int rc = 0;
618
619 for_each_possible_cpu(cpu)
620 raw_spin_lock_init(&per_cpu(ecall_lock, cpu));
621
622 if (!dbtr_init)
623 init_sbi_dbtr();
624
625 if (dbtr_total_num) {
626 pr_info("%s: total number of type %d triggers: %u\n",
627 __func__, dbtr_type, dbtr_total_num);
628 } else {
629 pr_info("%s: No hardware triggers available\n", __func__);
630 goto out;
631 }
632
633 /* Allocate per-cpu shared memory */
> 634 sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
2025-07-10 12:52 ` Himanshu Chauhan
(?)
(?)
@ 2025-07-11 9:39 ` kernel test robot
-1 siblings, 0 replies; 14+ messages in thread
From: kernel test robot @ 2025-07-11 9:39 UTC (permalink / raw)
To: Himanshu Chauhan; +Cc: oe-kbuild-all
Hi Himanshu,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v6.16-rc5 next-20250710]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Himanshu-Chauhan/riscv-Add-SBI-debug-trigger-extension-and-function-ids/20250710-205738
base: linus/master
patch link: https://lore.kernel.org/r/20250710125231.653967-3-hchauhan%40ventanamicro.com
patch subject: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
config: riscv-randconfig-r071-20250711 (https://download.01.org/0day-ci/archive/20250711/202507111733.mRGg8LQR-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111733.mRGg8LQR-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507111733.mRGg8LQR-lkp@intel.com/
All errors (new ones prefixed by >>):
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_smp_setup_sbi_shmem':
arch/riscv/kernel/hw_breakpoint.c:44:16: error: storage size of 'ret' isn't known
44 | struct sbiret ret;
| ^~~
In file included from include/asm-generic/percpu.h:7,
from ./arch/riscv/include/generated/asm/percpu.h:1,
from include/linux/irqflags.h:19,
from arch/riscv/include/asm/bitops.h:14,
from include/linux/bitops.h:67,
from include/linux/thread_info.h:27,
from include/asm-generic/preempt.h:5,
from ./arch/riscv/include/generated/asm/preempt.h:1,
from include/linux/preempt.h:79,
from include/linux/alloc_tag.h:11,
from include/linux/percpu.h:5,
from include/asm-generic/local64.h:5,
from ./arch/riscv/include/generated/asm/local64.h:1,
from include/linux/perf_event.h:26,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
>> include/linux/percpu-defs.h:220:52: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
220 | const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
| ^
include/linux/percpu-defs.h:263:2: note: in expansion of macro '__verify_pcpu_ptr'
263 | __verify_pcpu_ptr(ptr); \
| ^~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:47:15: note: in expansion of macro 'per_cpu_ptr'
47 | dbtr_shmem = per_cpu_ptr(sbi_dbtr_shmem, cpu);
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:55:8: error: implicit declaration of function 'sbi_ecall' [-Werror=implicit-function-declaration]
55 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
| ^~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:55:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
55 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:55:18: note: each undeclared identifier is reported only once for each function it appears in
arch/riscv/kernel/hw_breakpoint.c:55:32: error: 'SBI_EXT_DBTR_SETUP_SHMEM' undeclared (first use in this function)
55 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
| ^~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:60:8: error: 'SBI_ERR_DENIED' undeclared (first use in this function)
60 | case SBI_ERR_DENIED:
| ^~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:66:8: error: 'SBI_ERR_INVALID_PARAM' undeclared (first use in this function)
66 | case SBI_ERR_INVALID_PARAM:
| ^~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:67:8: error: 'SBI_ERR_INVALID_ADDRESS' undeclared (first use in this function)
67 | case SBI_ERR_INVALID_ADDRESS:
| ^~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:73:8: error: 'SBI_ERR_ALREADY_AVAILABLE' undeclared (first use in this function)
73 | case SBI_ERR_ALREADY_AVAILABLE:
| ^~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:79:8: error: 'SBI_ERR_FAILURE' undeclared (first use in this function)
79 | case SBI_ERR_FAILURE:
| ^~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:44:16: warning: unused variable 'ret' [-Wunused-variable]
44 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_smp_teardown_sbi_shmem':
arch/riscv/kernel/hw_breakpoint.c:99:16: error: storage size of 'ret' isn't known
99 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:102:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
102 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:102:32: error: 'SBI_EXT_DBTR_SETUP_SHMEM' undeclared (first use in this function)
102 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM,
| ^~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:107:8: error: 'SBI_ERR_DENIED' undeclared (first use in this function)
107 | case SBI_ERR_DENIED:
| ^~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:112:8: error: 'SBI_ERR_INVALID_PARAM' undeclared (first use in this function)
112 | case SBI_ERR_INVALID_PARAM:
| ^~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:113:8: error: 'SBI_ERR_INVALID_ADDRESS' undeclared (first use in this function)
113 | case SBI_ERR_INVALID_ADDRESS:
| ^~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:118:8: error: 'SBI_ERR_ALREADY_AVAILABLE' undeclared (first use in this function)
118 | case SBI_ERR_ALREADY_AVAILABLE:
| ^~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:122:8: error: 'SBI_ERR_FAILURE' undeclared (first use in this function)
122 | case SBI_ERR_FAILURE:
| ^~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:99:16: warning: unused variable 'ret' [-Wunused-variable]
99 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c: In function 'init_sbi_dbtr':
arch/riscv/kernel/hw_breakpoint.c:140:16: error: storage size of 'ret' isn't known
140 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:142:6: error: implicit declaration of function 'sbi_probe_extension' [-Werror=implicit-function-declaration]
142 | if (sbi_probe_extension(SBI_EXT_DBTR) <= 0) {
| ^~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:142:26: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
142 | if (sbi_probe_extension(SBI_EXT_DBTR) <= 0) {
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:148:32: error: 'SBI_EXT_DBTR_NUM_TRIGGERS' undeclared (first use in this function)
148 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:140:16: warning: unused variable 'ret' [-Wunused-variable]
140 | struct sbiret ret;
| ^~~
In file included from include/asm-generic/percpu.h:7,
from ./arch/riscv/include/generated/asm/percpu.h:1,
from include/linux/irqflags.h:19,
from arch/riscv/include/asm/bitops.h:14,
from include/linux/bitops.h:67,
from include/linux/thread_info.h:27,
from include/asm-generic/preempt.h:5,
from ./arch/riscv/include/generated/asm/preempt.h:1,
from include/linux/preempt.h:79,
from include/linux/alloc_tag.h:11,
from include/linux/percpu.h:5,
from include/asm-generic/local64.h:5,
from ./arch/riscv/include/generated/asm/local64.h:1,
from include/linux/perf_event.h:26,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_install_hw_breakpoint':
>> include/linux/percpu-defs.h:220:52: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
220 | const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
| ^
include/linux/percpu-defs.h:263:2: note: in expansion of macro '__verify_pcpu_ptr'
263 | __verify_pcpu_ptr(ptr); \
| ^~~~~~~~~~~~~~~~~
include/linux/percpu-defs.h:267:26: note: in expansion of macro 'per_cpu_ptr'
267 | #define raw_cpu_ptr(ptr) per_cpu_ptr(ptr, 0)
| ^~~~~~~~~~~
include/linux/percpu-defs.h:268:27: note: in expansion of macro 'raw_cpu_ptr'
268 | #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr)
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:428:38: note: in expansion of macro 'this_cpu_ptr'
428 | union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:433:16: error: storage size of 'ret' isn't known
433 | struct sbiret ret;
| ^~~
>> arch/riscv/kernel/hw_breakpoint.c:439:15: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
439 | xmit = &shmem->data;
| ^~
arch/riscv/kernel/hw_breakpoint.c:440:15: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
440 | recv = &shmem->id;
| ^~
arch/riscv/kernel/hw_breakpoint.c:441:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
441 | xmit->tdata1 = cpu_to_le(bp->tdata1);
| ^~
arch/riscv/kernel/hw_breakpoint.c:442:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
442 | xmit->tdata2 = cpu_to_le(bp->tdata2);
| ^~
arch/riscv/kernel/hw_breakpoint.c:443:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
443 | xmit->tdata3 = cpu_to_le(bp->tdata3);
| ^~
arch/riscv/kernel/hw_breakpoint.c:445:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
445 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_INSTALL,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:445:32: error: 'SBI_EXT_DBTR_TRIG_INSTALL' undeclared (first use in this function)
445 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_INSTALL,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
In file included from include/linux/byteorder/little_endian.h:5,
from arch/riscv/include/uapi/asm/byteorder.h:10,
from include/uapi/linux/perf_event.h:20,
from include/linux/perf_event.h:17,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
arch/riscv/kernel/hw_breakpoint.c:454:22: error: invalid use of undefined type 'struct sbi_dbtr_id_msg'
454 | idx = le_to_cpu(recv->idx);
| ^~
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition of macro '__le64_to_cpu'
33 | #define __le64_to_cpu(x) ((__force __u64)(__le64)(x))
| ^
arch/riscv/include/asm/hw_breakpoint.h:17:19: note: in expansion of macro 'le64_to_cpu'
17 | #define le_to_cpu le64_to_cpu
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:454:8: note: in expansion of macro 'le_to_cpu'
454 | idx = le_to_cpu(recv->idx);
| ^~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:433:16: warning: unused variable 'ret' [-Wunused-variable]
433 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_uninstall_hw_breakpoint':
arch/riscv/kernel/hw_breakpoint.c:482:16: error: storage size of 'ret' isn't known
482 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:499:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
499 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UNINSTALL,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:499:32: error: 'SBI_EXT_DBTR_TRIG_UNINSTALL' undeclared (first use in this function)
499 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UNINSTALL,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:482:16: warning: unused variable 'ret' [-Wunused-variable]
482 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_enable_hw_breakpoint':
arch/riscv/kernel/hw_breakpoint.c:507:16: error: storage size of 'ret' isn't known
507 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:523:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
523 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_ENABLE,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:523:32: error: 'SBI_EXT_DBTR_TRIG_ENABLE' undeclared (first use in this function)
523 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_ENABLE,
| ^~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:507:16: warning: unused variable 'ret' [-Wunused-variable]
507 | struct sbiret ret;
| ^~~
In file included from include/asm-generic/percpu.h:7,
from ./arch/riscv/include/generated/asm/percpu.h:1,
from include/linux/irqflags.h:19,
from arch/riscv/include/asm/bitops.h:14,
from include/linux/bitops.h:67,
from include/linux/thread_info.h:27,
from include/asm-generic/preempt.h:5,
from ./arch/riscv/include/generated/asm/preempt.h:1,
from include/linux/preempt.h:79,
from include/linux/alloc_tag.h:11,
from include/linux/percpu.h:5,
from include/asm-generic/local64.h:5,
from ./arch/riscv/include/generated/asm/local64.h:1,
from include/linux/perf_event.h:26,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_update_hw_breakpoint':
>> include/linux/percpu-defs.h:220:52: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
220 | const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
| ^
include/linux/percpu-defs.h:263:2: note: in expansion of macro '__verify_pcpu_ptr'
263 | __verify_pcpu_ptr(ptr); \
| ^~~~~~~~~~~~~~~~~
include/linux/percpu-defs.h:267:26: note: in expansion of macro 'per_cpu_ptr'
267 | #define raw_cpu_ptr(ptr) per_cpu_ptr(ptr, 0)
| ^~~~~~~~~~~
include/linux/percpu-defs.h:268:27: note: in expansion of macro 'raw_cpu_ptr'
268 | #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr)
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:536:38: note: in expansion of macro 'this_cpu_ptr'
536 | union sbi_dbtr_shmem_entry *shmem = this_cpu_ptr(sbi_dbtr_shmem);
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:539:16: error: storage size of 'ret' isn't known
539 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:557:15: error: invalid use of undefined type 'union sbi_dbtr_shmem_entry'
557 | xmit = &shmem->data;
| ^~
arch/riscv/kernel/hw_breakpoint.c:558:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
558 | xmit->tdata1 = cpu_to_le(bp->tdata1);
| ^~
arch/riscv/kernel/hw_breakpoint.c:559:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
559 | xmit->tdata2 = cpu_to_le(bp->tdata2);
| ^~
arch/riscv/kernel/hw_breakpoint.c:560:6: error: invalid use of undefined type 'struct sbi_dbtr_data_msg'
560 | xmit->tdata3 = cpu_to_le(bp->tdata3);
| ^~
arch/riscv/kernel/hw_breakpoint.c:562:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
562 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:562:32: error: 'SBI_EXT_DBTR_TRIG_UPDATE' undeclared (first use in this function)
562 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE,
| ^~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:539:16: warning: unused variable 'ret' [-Wunused-variable]
539 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_disable_hw_breakpoint':
arch/riscv/kernel/hw_breakpoint.c:574:16: error: storage size of 'ret' isn't known
574 | struct sbiret ret;
| ^~~
arch/riscv/kernel/hw_breakpoint.c:589:18: error: 'SBI_EXT_DBTR' undeclared (first use in this function)
589 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE,
| ^~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:589:32: error: 'SBI_EXT_DBTR_TRIG_DISABLE' undeclared (first use in this function)
589 | ret = sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:574:16: warning: unused variable 'ret' [-Wunused-variable]
574 | struct sbiret ret;
| ^~~
In file included from include/linux/percpu.h:5,
from include/asm-generic/local64.h:5,
from ./arch/riscv/include/generated/asm/local64.h:1,
from include/linux/perf_event.h:26,
from include/linux/hw_breakpoint.h:5,
from arch/riscv/kernel/hw_breakpoint.c:6:
arch/riscv/kernel/hw_breakpoint.c: In function 'arch_hw_breakpoint_init':
>> arch/riscv/kernel/hw_breakpoint.c:634:40: error: invalid application of 'sizeof' to incomplete type 'union sbi_dbtr_shmem_entry'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
include/linux/alloc_tag.h:239:9: note: in definition of macro 'alloc_hooks_tag'
239 | typeof(_do_alloc) _res; \
| ^~~~~~~~~
include/linux/percpu.h:142:2: note: in expansion of macro 'alloc_hooks'
142 | alloc_hooks(pcpu_alloc_noprof(_size, _align, false, GFP_KERNEL))
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:19: note: in expansion of macro '__alloc_percpu'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^~~~~~~~~~~~~~
>> arch/riscv/kernel/hw_breakpoint.c:634:40: error: invalid application of 'sizeof' to incomplete type 'union sbi_dbtr_shmem_entry'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
include/linux/alloc_tag.h:243:10: note: in definition of macro 'alloc_hooks_tag'
243 | _res = _do_alloc; \
| ^~~~~~~~~
include/linux/percpu.h:142:2: note: in expansion of macro 'alloc_hooks'
142 | alloc_hooks(pcpu_alloc_noprof(_size, _align, false, GFP_KERNEL))
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:19: note: in expansion of macro '__alloc_percpu'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^~~~~~~~~~~~~~
>> arch/riscv/kernel/hw_breakpoint.c:634:40: error: invalid application of 'sizeof' to incomplete type 'union sbi_dbtr_shmem_entry'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
include/linux/alloc_tag.h:246:10: note: in definition of macro 'alloc_hooks_tag'
246 | _res = _do_alloc; \
| ^~~~~~~~~
include/linux/percpu.h:142:2: note: in expansion of macro 'alloc_hooks'
142 | alloc_hooks(pcpu_alloc_noprof(_size, _align, false, GFP_KERNEL))
| ^~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:19: note: in expansion of macro '__alloc_percpu'
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^~~~~~~~~~~~~~
arch/riscv/kernel/hw_breakpoint.c:634:17: warning: assignment to 'union sbi_dbtr_shmem_entry *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
634 | sbi_dbtr_shmem = __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_num,
| ^
cc1: some warnings being treated as errors
vim +220 include/linux/percpu-defs.h
62fde54123fb64 Tejun Heo 2014-06-17 206
9c28278a24c01c Tejun Heo 2014-06-17 207 /*
6fbc07bbe2b5a8 Tejun Heo 2014-06-17 208 * __verify_pcpu_ptr() verifies @ptr is a percpu pointer without evaluating
6fbc07bbe2b5a8 Tejun Heo 2014-06-17 209 * @ptr and is invoked once before a percpu area is accessed by all
6fbc07bbe2b5a8 Tejun Heo 2014-06-17 210 * accessors and operations. This is performed in the generic part of
6fbc07bbe2b5a8 Tejun Heo 2014-06-17 211 * percpu and arch overrides don't need to worry about it; however, if an
6fbc07bbe2b5a8 Tejun Heo 2014-06-17 212 * arch wants to implement an arch-specific percpu accessor or operation,
6fbc07bbe2b5a8 Tejun Heo 2014-06-17 213 * it may use __verify_pcpu_ptr() to verify the parameters.
9c28278a24c01c Tejun Heo 2014-06-17 214 *
9c28278a24c01c Tejun Heo 2014-06-17 215 * + 0 is required in order to convert the pointer type from a
9c28278a24c01c Tejun Heo 2014-06-17 216 * potential array type to a pointer to a single item of the array.
9c28278a24c01c Tejun Heo 2014-06-17 217 */
eba117889ac444 Tejun Heo 2014-06-17 218 #define __verify_pcpu_ptr(ptr) \
eba117889ac444 Tejun Heo 2014-06-17 219 do { \
9c28278a24c01c Tejun Heo 2014-06-17 @220 const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
9c28278a24c01c Tejun Heo 2014-06-17 221 (void)__vpp_verify; \
9c28278a24c01c Tejun Heo 2014-06-17 222 } while (0)
9c28278a24c01c Tejun Heo 2014-06-17 223
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 1/2] riscv: Add SBI debug trigger extension and function ids
2025-07-10 12:52 ` Himanshu Chauhan
@ 2025-11-27 0:47 ` Paul Walmsley
-1 siblings, 0 replies; 14+ messages in thread
From: Paul Walmsley @ 2025-11-27 0:47 UTC (permalink / raw)
To: Himanshu Chauhan; +Cc: paul.walmsley, palmer, aou, linux-riscv, linux-kernel
On Thu, 10 Jul 2025, Himanshu Chauhan wrote:
> Debug trigger extension is an SBI extension to support native debugging
> in S-mode and VS-mode. This patch adds the extension and the function
> IDs defined by the extension.
>
> Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
Thanks, queued for v6.19.
- Paul
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 1/2] riscv: Add SBI debug trigger extension and function ids
@ 2025-11-27 0:47 ` Paul Walmsley
0 siblings, 0 replies; 14+ messages in thread
From: Paul Walmsley @ 2025-11-27 0:47 UTC (permalink / raw)
To: Himanshu Chauhan; +Cc: paul.walmsley, palmer, aou, linux-riscv, linux-kernel
On Thu, 10 Jul 2025, Himanshu Chauhan wrote:
> Debug trigger extension is an SBI extension to support native debugging
> in S-mode and VS-mode. This patch adds the extension and the function
> IDs defined by the extension.
>
> Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
Thanks, queued for v6.19.
- Paul
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
2025-07-10 12:52 ` Himanshu Chauhan
@ 2025-11-27 0:49 ` Paul Walmsley
-1 siblings, 0 replies; 14+ messages in thread
From: Paul Walmsley @ 2025-11-27 0:49 UTC (permalink / raw)
To: Himanshu Chauhan; +Cc: paul.walmsley, palmer, aou, linux-riscv, linux-kernel
On Thu, 10 Jul 2025, Himanshu Chauhan wrote:
> RISC-V hardware breakpoint framework is built on top of perf subsystem
> and uses SBI debug trigger extension to
> install/uninstall/update/enable/disable hardware triggers as specified
> in Sdtrig ISA extension.
>
> Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
Talking with Anup, it sounds like you're planning an updated version of
this one, so will hold off on it.
- Paul
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
@ 2025-11-27 0:49 ` Paul Walmsley
0 siblings, 0 replies; 14+ messages in thread
From: Paul Walmsley @ 2025-11-27 0:49 UTC (permalink / raw)
To: Himanshu Chauhan; +Cc: paul.walmsley, palmer, aou, linux-riscv, linux-kernel
On Thu, 10 Jul 2025, Himanshu Chauhan wrote:
> RISC-V hardware breakpoint framework is built on top of perf subsystem
> and uses SBI debug trigger extension to
> install/uninstall/update/enable/disable hardware triggers as specified
> in Sdtrig ISA extension.
>
> Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
Talking with Anup, it sounds like you're planning an updated version of
this one, so will hold off on it.
- Paul
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
2025-11-27 0:49 ` Paul Walmsley
@ 2025-11-27 10:17 ` Himanshu Chauhan
-1 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-11-27 10:17 UTC (permalink / raw)
To: Paul Walmsley; +Cc: paul.walmsley, palmer, aou, linux-riscv, linux-kernel
Hi Paul,
On Thu, Nov 27, 2025 at 6:19 AM Paul Walmsley <pjw@kernel.org> wrote:
>
> On Thu, 10 Jul 2025, Himanshu Chauhan wrote:
>
> > RISC-V hardware breakpoint framework is built on top of perf subsystem
> > and uses SBI debug trigger extension to
> > install/uninstall/update/enable/disable hardware triggers as specified
> > in Sdtrig ISA extension.
> >
> > Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
>
> Talking with Anup, it sounds like you're planning an updated version of
> this one, so will hold off on it.
That's correct. You can hold this version. I will be posting v2 for
this series which you can target for 6.20.
Regards
Himanshu
>
>
> - Paul
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints
@ 2025-11-27 10:17 ` Himanshu Chauhan
0 siblings, 0 replies; 14+ messages in thread
From: Himanshu Chauhan @ 2025-11-27 10:17 UTC (permalink / raw)
To: Paul Walmsley; +Cc: paul.walmsley, palmer, aou, linux-riscv, linux-kernel
Hi Paul,
On Thu, Nov 27, 2025 at 6:19 AM Paul Walmsley <pjw@kernel.org> wrote:
>
> On Thu, 10 Jul 2025, Himanshu Chauhan wrote:
>
> > RISC-V hardware breakpoint framework is built on top of perf subsystem
> > and uses SBI debug trigger extension to
> > install/uninstall/update/enable/disable hardware triggers as specified
> > in Sdtrig ISA extension.
> >
> > Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
>
> Talking with Anup, it sounds like you're planning an updated version of
> this one, so will hold off on it.
That's correct. You can hold this version. I will be posting v2 for
this series which you can target for 6.20.
Regards
Himanshu
>
>
> - Paul
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-11-27 10:17 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-10 12:52 [PATCH v1 0/2] riscv: Introduce support for hardware break/watchpoints Himanshu Chauhan
2025-07-10 12:52 ` Himanshu Chauhan
2025-07-10 12:52 ` [PATCH v1 1/2] riscv: Add SBI debug trigger extension and function ids Himanshu Chauhan
2025-07-10 12:52 ` Himanshu Chauhan
2025-11-27 0:47 ` Paul Walmsley
2025-11-27 0:47 ` Paul Walmsley
2025-07-10 12:52 ` [PATCH v1 2/2] riscv: Introduce support for hardware break/watchpoints Himanshu Chauhan
2025-07-10 12:52 ` Himanshu Chauhan
2025-07-11 6:19 ` kernel test robot
2025-07-11 9:39 ` kernel test robot
2025-11-27 0:49 ` Paul Walmsley
2025-11-27 0:49 ` Paul Walmsley
2025-11-27 10:17 ` Himanshu Chauhan
2025-11-27 10:17 ` Himanshu Chauhan
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.