diff mbox series

[1/2] riscv: Add CSR detection support

Message ID 20260319082911.3270255-1-ycliang@andestech.com
State New
Delegated to: Andes
Headers show
Series [1/2] riscv: Add CSR detection support | expand

Commit Message

Leo Yu-Chi Liang March 19, 2026, 8:29 a.m. UTC
Add a lightweight mechanism to safely probe CSRs that may not exist
on the current hart. This is useful for detecting optional extensions
(e.g. Smstateen) without relying on MARCHID or ISA string parsing.

The implementation consists of:
- A minimal expected trap handler (__riscv_expected_trap) that advances
  past the faulting instruction and signals the trap via register a4.
- csr_read_allowed() and csr_write_allowed() macros that temporarily
  swap the trap vector, attempt the CSR access, and report whether it
  trapped.

Ported from OpenSBI's sbi_csr_detect.h mechanism.

Signed-off-by: Leo Yu-Chi Liang <ycliang@andestech.com>
---
 arch/riscv/include/asm/csr_detect.h | 90 +++++++++++++++++++++++++++++
 arch/riscv/lib/Makefile             |  1 +
 arch/riscv/lib/csr_detect.S         | 23 ++++++++
 3 files changed, 114 insertions(+)
 create mode 100644 arch/riscv/include/asm/csr_detect.h
 create mode 100644 arch/riscv/lib/csr_detect.S

Comments

Heinrich Schuchardt March 24, 2026, 6:55 p.m. UTC | #1
Am 19. März 2026 09:29:10 MEZ schrieb Leo Yu-Chi Liang <ycliang@andestech.com>:
>Add a lightweight mechanism to safely probe CSRs that may not exist
>on the current hart. This is useful for detecting optional extensions
>(e.g. Smstateen) without relying on MARCHID or ISA string parsing.
>
>The implementation consists of:
>- A minimal expected trap handler (__riscv_expected_trap) that advances
>  past the faulting instruction and signals the trap via register a4.
>- csr_read_allowed() and csr_write_allowed() macros that temporarily
>  swap the trap vector, attempt the CSR access, and report whether it
>  trapped.
>
>Ported from OpenSBI's sbi_csr_detect.h mechanism.

We already have a more generic mechanism for catching exceptions on RISC-V. See functions  riscv_zkr_bind() and set_resume().

Do we really need a second mechanism?

Best regards

Heinrich


>
>Signed-off-by: Leo Yu-Chi Liang <ycliang@andestech.com>
>---
> arch/riscv/include/asm/csr_detect.h | 90 +++++++++++++++++++++++++++++
> arch/riscv/lib/Makefile             |  1 +
> arch/riscv/lib/csr_detect.S         | 23 ++++++++
> 3 files changed, 114 insertions(+)
> create mode 100644 arch/riscv/include/asm/csr_detect.h
> create mode 100644 arch/riscv/lib/csr_detect.S
>
>diff --git a/arch/riscv/include/asm/csr_detect.h b/arch/riscv/include/asm/csr_detect.h
>new file mode 100644
>index 00000000000..c058b6b85ea
>--- /dev/null
>+++ b/arch/riscv/include/asm/csr_detect.h
>@@ -0,0 +1,90 @@
>+/* SPDX-License-Identifier: GPL-2.0+ */
>+/*
>+ * Copyright (C) 2026 Andes Technology Corporation
>+ * Leo Yu-Chi Liang <ycliang@andestech.com>
>+ *
>+ * RISC-V CSR detection support
>+ *
>+ * Provides macros to safely probe CSRs that may not exist on the
>+ * current hart. Uses a lightweight trap handler that simply advances
>+ * past the faulting instruction and sets a flag.
>+ *
>+ * Ported from OpenSBI's sbi_csr_detect.h.
>+ */
>+
>+#ifndef _ASM_RISCV_CSR_DETECT_H
>+#define _ASM_RISCV_CSR_DETECT_H
>+
>+#include <asm/csr.h>
>+
>+#ifndef __ASSEMBLY__
>+
>+/* Two-level stringification so CSR macros are expanded before quoting */
>+#define __CSR_DETECT_XSTR(x)	#x
>+#define __CSR_DETECT_STR(x)	__CSR_DETECT_XSTR(x)
>+
>+#if CONFIG_IS_ENABLED(RISCV_SMODE)
>+#define CSR_XTVEC	CSR_STVEC
>+#else
>+#define CSR_XTVEC	CSR_MTVEC
>+#endif
>+
>+void __riscv_expected_trap(void);
>+
>+/**
>+ * csr_read_allowed() - Read a CSR, detecting illegal-instruction traps.
>+ * @csr_num:	CSR number (must be a compile-time constant).
>+ * @trap:	lvalue that is set to 1 if the access trapped, 0 otherwise.
>+ *
>+ * Returns the CSR value on success, or 0 if the CSR does not exist.
>+ *
>+ * This works by temporarily installing a minimal trap handler that
>+ * skips the faulting instruction and sets register a4 to 1. Interrupts
>+ * must be disabled when calling this macro (they normally are during
>+ * early init).
>+ */
>+#define csr_read_allowed(csr_num, trap)					\
>+({									\
>+	register unsigned long __mtvec =				\
>+		(unsigned long)__riscv_expected_trap;			\
>+	register unsigned long __trap asm("a4") = 0;			\
>+	register unsigned long __ret = 0;				\
>+	__asm__ __volatile__(						\
>+		"csrrw %[mtvec], "					\
>+			__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]\n"	\
>+		"csrr %[ret], " __ASM_STR(csr_num) "\n"		\
>+		"csrw "	__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]"	\
>+		: [mtvec] "+&r"(__mtvec), [ret] "=&r"(__ret),		\
>+		  [trap] "+&r"(__trap)					\
>+		:							\
>+		: "memory");						\
>+	trap = __trap;							\
>+	__ret;								\
>+})
>+
>+/**
>+ * csr_write_allowed() - Write a CSR, detecting illegal-instruction traps.
>+ * @csr_num:	CSR number (must be a compile-time constant).
>+ * @trap:	lvalue that is set to 1 if the access trapped, 0 otherwise.
>+ * @value:	Value to write.
>+ */
>+#define csr_write_allowed(csr_num, trap, value)				\
>+({									\
>+	register unsigned long __mtvec =				\
>+		(unsigned long)__riscv_expected_trap;			\
>+	register unsigned long __trap asm("a4") = 0;			\
>+	register unsigned long __val = (unsigned long)(value);		\
>+	__asm__ __volatile__(						\
>+		"csrrw %[mtvec], "					\
>+			__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]\n"	\
>+		"csrw " __ASM_STR(csr_num) ", %[val]\n"		\
>+		"csrw "	__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]"	\
>+		: [mtvec] "+&r"(__mtvec), [trap] "+&r"(__trap)		\
>+		: [val] "rK"(__val)					\
>+		: "memory");						\
>+	trap = __trap;							\
>+})
>+
>+#endif /* !__ASSEMBLY__ */
>+
>+#endif /* _ASM_RISCV_CSR_DETECT_H */
>diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
>index a527b3e9ae3..42463b8bed0 100644
>--- a/arch/riscv/lib/Makefile
>+++ b/arch/riscv/lib/Makefile
>@@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)LIB_BOOTM) += bootm.o
> obj-$(CONFIG_$(PHASE_)LIB_BOOTI) += image.o
> obj-$(CONFIG_CMD_GO) += boot.o
> obj-y	+= cache.o
>+obj-y	+= csr_detect.o
> obj-$(CONFIG_SIFIVE_CACHE) += sifive_cache.o
> obj-$(CONFIG_SYS_CACHE_THEAD_CMO) += thead_cmo.o
> ifeq ($(CONFIG_$(PHASE_)RISCV_MMODE),y)
>diff --git a/arch/riscv/lib/csr_detect.S b/arch/riscv/lib/csr_detect.S
>new file mode 100644
>index 00000000000..69c4b8e7933
>--- /dev/null
>+++ b/arch/riscv/lib/csr_detect.S
>@@ -0,0 +1,23 @@
>+/* SPDX-License-Identifier: GPL-2.0+ */
>+/*
>+ * RISC-V CSR detection support
>+ *
>+ * Copyright (C) 2025 Andes Technology Corporation
>+ * Leo Yu-Chi Liang <ycliang@andestech.com>
>+ *
>+ * Ported from OpenSBI's expected trap mechanism.
>+ */
>+
>+#include <asm/encoding.h>
>+
>+	.text
>+	.align 2
>+	.global __riscv_expected_trap
>+__riscv_expected_trap:
>+	/* Advance past the faulting instruction */
>+	csrr	a4, MODE_PREFIX(epc)
>+	addi	a4, a4, 4
>+	csrw	MODE_PREFIX(epc), a4
>+	/* Set a4 = 1 to signal that a trap occurred */
>+	li	a4, 1
>+	MODE_PREFIX(ret)
diff mbox series

Patch

diff --git a/arch/riscv/include/asm/csr_detect.h b/arch/riscv/include/asm/csr_detect.h
new file mode 100644
index 00000000000..c058b6b85ea
--- /dev/null
+++ b/arch/riscv/include/asm/csr_detect.h
@@ -0,0 +1,90 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Andes Technology Corporation
+ * Leo Yu-Chi Liang <ycliang@andestech.com>
+ *
+ * RISC-V CSR detection support
+ *
+ * Provides macros to safely probe CSRs that may not exist on the
+ * current hart. Uses a lightweight trap handler that simply advances
+ * past the faulting instruction and sets a flag.
+ *
+ * Ported from OpenSBI's sbi_csr_detect.h.
+ */
+
+#ifndef _ASM_RISCV_CSR_DETECT_H
+#define _ASM_RISCV_CSR_DETECT_H
+
+#include <asm/csr.h>
+
+#ifndef __ASSEMBLY__
+
+/* Two-level stringification so CSR macros are expanded before quoting */
+#define __CSR_DETECT_XSTR(x)	#x
+#define __CSR_DETECT_STR(x)	__CSR_DETECT_XSTR(x)
+
+#if CONFIG_IS_ENABLED(RISCV_SMODE)
+#define CSR_XTVEC	CSR_STVEC
+#else
+#define CSR_XTVEC	CSR_MTVEC
+#endif
+
+void __riscv_expected_trap(void);
+
+/**
+ * csr_read_allowed() - Read a CSR, detecting illegal-instruction traps.
+ * @csr_num:	CSR number (must be a compile-time constant).
+ * @trap:	lvalue that is set to 1 if the access trapped, 0 otherwise.
+ *
+ * Returns the CSR value on success, or 0 if the CSR does not exist.
+ *
+ * This works by temporarily installing a minimal trap handler that
+ * skips the faulting instruction and sets register a4 to 1. Interrupts
+ * must be disabled when calling this macro (they normally are during
+ * early init).
+ */
+#define csr_read_allowed(csr_num, trap)					\
+({									\
+	register unsigned long __mtvec =				\
+		(unsigned long)__riscv_expected_trap;			\
+	register unsigned long __trap asm("a4") = 0;			\
+	register unsigned long __ret = 0;				\
+	__asm__ __volatile__(						\
+		"csrrw %[mtvec], "					\
+			__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]\n"	\
+		"csrr %[ret], " __ASM_STR(csr_num) "\n"		\
+		"csrw "	__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]"	\
+		: [mtvec] "+&r"(__mtvec), [ret] "=&r"(__ret),		\
+		  [trap] "+&r"(__trap)					\
+		:							\
+		: "memory");						\
+	trap = __trap;							\
+	__ret;								\
+})
+
+/**
+ * csr_write_allowed() - Write a CSR, detecting illegal-instruction traps.
+ * @csr_num:	CSR number (must be a compile-time constant).
+ * @trap:	lvalue that is set to 1 if the access trapped, 0 otherwise.
+ * @value:	Value to write.
+ */
+#define csr_write_allowed(csr_num, trap, value)				\
+({									\
+	register unsigned long __mtvec =				\
+		(unsigned long)__riscv_expected_trap;			\
+	register unsigned long __trap asm("a4") = 0;			\
+	register unsigned long __val = (unsigned long)(value);		\
+	__asm__ __volatile__(						\
+		"csrrw %[mtvec], "					\
+			__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]\n"	\
+		"csrw " __ASM_STR(csr_num) ", %[val]\n"		\
+		"csrw "	__CSR_DETECT_STR(CSR_XTVEC) ", %[mtvec]"	\
+		: [mtvec] "+&r"(__mtvec), [trap] "+&r"(__trap)		\
+		: [val] "rK"(__val)					\
+		: "memory");						\
+	trap = __trap;							\
+})
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_RISCV_CSR_DETECT_H */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index a527b3e9ae3..42463b8bed0 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -12,6 +12,7 @@  obj-$(CONFIG_$(PHASE_)LIB_BOOTM) += bootm.o
 obj-$(CONFIG_$(PHASE_)LIB_BOOTI) += image.o
 obj-$(CONFIG_CMD_GO) += boot.o
 obj-y	+= cache.o
+obj-y	+= csr_detect.o
 obj-$(CONFIG_SIFIVE_CACHE) += sifive_cache.o
 obj-$(CONFIG_SYS_CACHE_THEAD_CMO) += thead_cmo.o
 ifeq ($(CONFIG_$(PHASE_)RISCV_MMODE),y)
diff --git a/arch/riscv/lib/csr_detect.S b/arch/riscv/lib/csr_detect.S
new file mode 100644
index 00000000000..69c4b8e7933
--- /dev/null
+++ b/arch/riscv/lib/csr_detect.S
@@ -0,0 +1,23 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * RISC-V CSR detection support
+ *
+ * Copyright (C) 2025 Andes Technology Corporation
+ * Leo Yu-Chi Liang <ycliang@andestech.com>
+ *
+ * Ported from OpenSBI's expected trap mechanism.
+ */
+
+#include <asm/encoding.h>
+
+	.text
+	.align 2
+	.global __riscv_expected_trap
+__riscv_expected_trap:
+	/* Advance past the faulting instruction */
+	csrr	a4, MODE_PREFIX(epc)
+	addi	a4, a4, 4
+	csrw	MODE_PREFIX(epc), a4
+	/* Set a4 = 1 to signal that a trap occurred */
+	li	a4, 1
+	MODE_PREFIX(ret)