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 <[email protected]>
---
 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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)
-- 
2.34.1


Reply via email to