Various parts of the kernel decode and read instruction from memory.
Functions like get_insn, GET_INSN_LENGTH and riscv_insn_is_c are defined
in multiple places. Consolidate these functions into the insn.h and the
newly added insn.c.
Signed-off-by: Jesse Taube <jesse@rivosinc.com>
---
RFC -> V1:
- No change
V2 -> V1:
- No change
---
arch/riscv/include/asm/bug.h | 12 ---
arch/riscv/include/asm/insn.h | 131 ++++++++++++++++++++++-
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/insn.c | 151 +++++++++++++++++++++++++++
arch/riscv/kernel/kgdb.c | 102 +-----------------
arch/riscv/kernel/probes/kprobes.c | 1 +
arch/riscv/kernel/traps.c | 5 +-
arch/riscv/kernel/traps_misaligned.c | 93 ++++-------------
8 files changed, 309 insertions(+), 187 deletions(-)
create mode 100644 arch/riscv/kernel/insn.c
diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h
index 1aaea81fb141..a2777eb67ad1 100644
--- a/arch/riscv/include/asm/bug.h
+++ b/arch/riscv/include/asm/bug.h
@@ -12,21 +12,9 @@
#include <asm/asm.h>
-#define __INSN_LENGTH_MASK _UL(0x3)
-#define __INSN_LENGTH_32 _UL(0x3)
-#define __COMPRESSED_INSN_MASK _UL(0xffff)
-
#define __BUG_INSN_32 _UL(0x00100073) /* ebreak */
#define __BUG_INSN_16 _UL(0x9002) /* c.ebreak */
-#define GET_INSN_LENGTH(insn) \
-({ \
- unsigned long __len; \
- __len = ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32) ? \
- 4UL : 2UL; \
- __len; \
-})
-
typedef u32 bug_insn_t;
#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
diff --git a/arch/riscv/include/asm/insn.h b/arch/riscv/include/asm/insn.h
index 09fde95a5e8f..ba74e5b8262c 100644
--- a/arch/riscv/include/asm/insn.h
+++ b/arch/riscv/include/asm/insn.h
@@ -64,6 +64,7 @@
#define RVG_RS2_OPOFF 20
#define RVG_RD_OPOFF 7
#define RVG_RS1_MASK GENMASK(4, 0)
+#define RVG_RS2_MASK GENMASK(4, 0)
#define RVG_RD_MASK GENMASK(4, 0)
/* The bit field of immediate value in RVC J instruction */
@@ -121,17 +122,27 @@
#define RVC_C0_RS1_OPOFF 7
#define RVC_C0_RS2_OPOFF 2
#define RVC_C0_RD_OPOFF 2
+#define RVC_C0_RS1_MASK GENMASK(2, 0)
+#define RVC_C0_RS2_MASK GENMASK(2, 0)
+#define RVC_C0_RD_MASK GENMASK(2, 0)
+#define RVC_C0_REG_OFFSET 8
/* The register offset in RVC op=C1 instruction */
#define RVC_C1_RS1_OPOFF 7
#define RVC_C1_RS2_OPOFF 2
#define RVC_C1_RD_OPOFF 7
+#define RVC_C1_RS1_MASK GENMASK(2, 0)
+#define RVC_C1_RS2_MASK GENMASK(2, 0)
+#define RVC_C1_RD_MASK GENMASK(2, 0)
+#define RVC_C1_REG_OFFSET 8
/* The register offset in RVC op=C2 instruction */
#define RVC_C2_RS1_OPOFF 7
#define RVC_C2_RS2_OPOFF 2
#define RVC_C2_RD_OPOFF 7
#define RVC_C2_RS1_MASK GENMASK(4, 0)
+#define RVC_C2_RS2_MASK GENMASK(4, 0)
+#define RVC_C2_RD_MASK GENMASK(4, 0)
/* parts of opcode for RVG*/
#define RVG_OPCODE_FENCE 0x0f
@@ -226,12 +237,26 @@
#define RVC_MASK_C_EBREAK 0xffff
#define RVG_MASK_EBREAK 0xffffffff
#define RVG_MASK_SRET 0xffffffff
+#define RVC_MASK_C GENMASK(15, 0)
#define __INSN_LENGTH_MASK _UL(0x3)
#define __INSN_LENGTH_GE_32 _UL(0x3)
#define __INSN_OPCODE_MASK _UL(0x7F)
#define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH)
+#define GET_INSN_LENGTH(insn) \
+({ \
+ unsigned long __len; \
+ __len = ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_GE_32) ? \
+ 4UL : 2UL; \
+ __len; \
+})
+
+static __always_inline bool riscv_insn_is_c(u32 code)
+{
+ return (code & (__INSN_LENGTH_MASK)) != (__INSN_LENGTH_GE_32);
+}
+
#define __RISCV_INSN_FUNCS(name, mask, val) \
static __always_inline bool riscv_insn_is_##name(u32 code) \
{ \
@@ -260,7 +285,7 @@ __RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ)
__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK)
__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK)
__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET)
-__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE);
+__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE)
/* special case to catch _any_ system instruction */
static __always_inline bool riscv_insn_is_system(u32 code)
@@ -295,6 +320,10 @@ static __always_inline bool riscv_insn_is_c_jalr(u32 code)
({typeof(x) x_ = (x); \
(RV_X(x_, RVG_RS1_OPOFF, RVG_RS1_MASK)); })
+#define RV_EXTRACT_RS2_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X(x_, RVG_RS2_OPOFF, RVG_RS2_MASK)); })
+
#define RV_EXTRACT_RD_REG(x) \
({typeof(x) x_ = (x); \
(RV_X(x_, RVG_RD_OPOFF, RVG_RD_MASK)); })
@@ -322,9 +351,41 @@ static __always_inline bool riscv_insn_is_c_jalr(u32 code)
(RV_X(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) | \
(RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); })
+#define RVC_EXTRACT_C0_RS1_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C0_RS1_OPOFF, RVC_C0_RS1_MASK)); })
+
+#define RVC_EXTRACT_C0_RS2_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C0_RS2_OPOFF, RVC_C0_RS2_MASK)); })
+
+#define RVC_EXTRACT_C0_RD_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C0_RD_OPOFF, RVC_C0_RD_MASK)); })
+
+#define RVC_EXTRACT_C1_RS1_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C1_RS1_OPOFF, RVC_C1_RS1_MASK)); })
+
+#define RVC_EXTRACT_C1_RS2_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C1_RS2_OPOFF, RVC_C1_RS2_MASK)); })
+
+#define RVC_EXTRACT_C1_RD_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C1_RD_OPOFF, RVC_C1_RD_MASK)); })
+
#define RVC_EXTRACT_C2_RS1_REG(x) \
({typeof(x) x_ = (x); \
- (RV_X(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })
+ (RVC_X(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })
+
+#define RVC_EXTRACT_C2_RS2_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C2_RS2_OPOFF, RVC_C2_RS2_MASK)); })
+
+#define RVC_EXTRACT_C2_RD_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_C2_RD_OPOFF, RVC_C2_RD_MASK)); })
#define RVC_EXTRACT_JTYPE_IMM(x) \
({typeof(x) x_ = (x); \
@@ -354,6 +415,66 @@ static __always_inline bool riscv_insn_is_c_jalr(u32 code)
#define RVV_EXRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x)
+/*
+ * Get the rs1 register number from RV or RVC instruction.
+ *
+ * @insn: instruction to process
+ * Return: rs1 register
+ */
+static inline unsigned int riscv_insn_extract_rs1_reg(u32 insn)
+{
+ switch (RVC_INSN_OPCODE_MASK & insn) {
+ case RVC_OPCODE_C0:
+ return RVC_EXTRACT_C0_RS1_REG(insn) + RVC_C0_REG_OFFSET;
+ case RVC_OPCODE_C1:
+ return RVC_EXTRACT_C1_RS1_REG(insn) + RVC_C1_REG_OFFSET;
+ case RVC_OPCODE_C2:
+ return RVC_EXTRACT_C2_RS1_REG(insn);
+ default:
+ return RV_EXTRACT_RS1_REG(insn);
+ }
+}
+
+/*
+ * Get the rs2 register number from RV or RVC instruction.
+ *
+ * @insn: instruction to process
+ * Return: rs2 register
+ */
+static inline unsigned int riscv_insn_extract_rs2_reg(u32 insn)
+{
+ switch (RVC_INSN_OPCODE_MASK & insn) {
+ case RVC_OPCODE_C0:
+ return RVC_EXTRACT_C0_RS2_REG(insn) + RVC_C0_REG_OFFSET;
+ case RVC_OPCODE_C1:
+ return RVC_EXTRACT_C1_RS2_REG(insn) + RVC_C1_REG_OFFSET;
+ case RVC_OPCODE_C2:
+ return RVC_EXTRACT_C2_RS2_REG(insn);
+ default:
+ return RV_EXTRACT_RS2_REG(insn);
+ }
+}
+
+/*
+ * Get the rd register number from RV or RVC instruction.
+ *
+ * @insn: instruction to process
+ * Return: rd register
+ */
+static inline unsigned int riscv_insn_extract_rd_reg(u32 insn)
+{
+ switch (RVC_INSN_OPCODE_MASK & insn) {
+ case RVC_OPCODE_C0:
+ return RVC_EXTRACT_C0_RD_REG(insn) + RVC_C0_REG_OFFSET;
+ case RVC_OPCODE_C1:
+ return RVC_EXTRACT_C1_RD_REG(insn) + RVC_C1_REG_OFFSET;
+ case RVC_OPCODE_C2:
+ return RVC_EXTRACT_C2_RD_REG(insn);
+ default:
+ return RV_EXTRACT_RD_REG(insn);
+ }
+}
+
/*
* Get the immediate from a J-type instruction.
*
@@ -428,4 +549,10 @@ static inline void riscv_insn_insert_utype_itype_imm(u32 *utype_insn, u32 *itype
*utype_insn |= (imm & RV_U_IMM_31_12_MASK) + ((imm & BIT(11)) << 1);
*itype_insn |= ((imm & RV_I_IMM_11_0_MASK) << RV_I_IMM_11_0_OPOFF);
}
+
+#include <asm/ptrace.h>
+
+int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn);
+unsigned long get_step_address(struct pt_regs *regs, u32 code);
+
#endif /* _ASM_RISCV_INSN_H */
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index f7480c9c6f8d..4f719b09e5ad 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_RISCV_ALTERNATIVE) += alternative.o
obj-y += cpu.o
obj-y += cpufeature.o
obj-y += entry.o
+obj-y += insn.o
obj-y += irq.o
obj-y += process.o
obj-y += ptrace.o
diff --git a/arch/riscv/kernel/insn.c b/arch/riscv/kernel/insn.c
new file mode 100644
index 000000000000..dd2a6ef9fd25
--- /dev/null
+++ b/arch/riscv/kernel/insn.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Rivos, Inc
+ */
+#include <asm/insn.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+
+#define __read_insn(regs, insn, insn_addr, type) \
+({ \
+ int __ret; \
+ \
+ if (user_mode(regs)) { \
+ __ret = get_user(insn, (type __user *) insn_addr); \
+ } else { \
+ insn = *(type *)insn_addr; \
+ __ret = 0; \
+ } \
+ \
+ __ret; \
+})
+
+/*
+ * Update a set of two instructions (U-type + I-type) with an immediate value.
+ *
+ * Used for example in auipc+jalrs pairs the U-type instructions contains
+ * a 20bit upper immediate representing bits[31:12], while the I-type
+ * instruction contains a 12bit immediate representing bits[11:0].
+ *
+ * This also takes into account that both separate immediates are
+ * considered as signed values, so if the I-type immediate becomes
+ * negative (BIT(11) set) the U-type part gets adjusted.
+ *
+ * @regs: pointer to the utype instruction of the pair
+ * @epc: pointer to the itype instruction of the pair
+ * @r_insn: the immediate to insert into the two instructions
+ * Return: combined immediate
+ */
+int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn)
+{
+ ulong insn = 0;
+
+ if (epc & 0x2) {
+ ulong tmp = 0;
+
+ if (__read_insn(regs, insn, epc, u16))
+ return -EFAULT;
+ /* __get_user() uses regular "lw" which sign extend the loaded
+ * value make sure to clear higher order bits in case we "or" it
+ * below with the upper 16 bits half.
+ */
+ insn &= RVC_MASK_C;
+ if (riscv_insn_is_c(insn)) {
+ *r_insn = insn;
+ return 0;
+ }
+ epc += sizeof(u16);
+ if (__read_insn(regs, tmp, epc, u16))
+ return -EFAULT;
+ *r_insn = (tmp << 16) | insn;
+
+ return 0;
+ } else {
+ if (__read_insn(regs, insn, epc, u32))
+ return -EFAULT;
+ if (!riscv_insn_is_c(insn)) {
+ *r_insn = insn;
+ return 0;
+ }
+ insn &= RVC_MASK_C;
+ *r_insn = insn;
+
+ return 0;
+ }
+}
+
+/* Calculate the new address for after a step */
+unsigned long get_step_address(struct pt_regs *regs, u32 code)
+{
+ unsigned long pc = regs->epc;
+ unsigned int rs1_num, rs2_num;
+
+ if ((code & __INSN_LENGTH_MASK) != __INSN_LENGTH_GE_32) {
+ if (riscv_insn_is_c_jalr(code) ||
+ riscv_insn_is_c_jr(code)) {
+ rs1_num = riscv_insn_extract_rs1_reg(code);
+ return regs_get_register(regs, rs1_num);
+ } else if (riscv_insn_is_c_j(code) ||
+ riscv_insn_is_c_jal(code)) {
+ return RVC_EXTRACT_JTYPE_IMM(code) + pc;
+ } else if (riscv_insn_is_c_beqz(code)) {
+ rs1_num = riscv_insn_extract_rs1_reg(code);
+ if (!rs1_num || regs_get_register(regs, rs1_num) == 0)
+ return RVC_EXTRACT_BTYPE_IMM(code) + pc;
+ else
+ return pc + 2;
+ } else if (riscv_insn_is_c_bnez(code)) {
+ rs1_num = riscv_insn_extract_rs1_reg(RVC_C1_RS1_OPOFF);
+ if (rs1_num && regs_get_register(regs, rs1_num) != 0)
+ return RVC_EXTRACT_BTYPE_IMM(code) + pc;
+ else
+ return pc + 2;
+ } else {
+ return pc + 2;
+ }
+ } else {
+ if ((code & __INSN_OPCODE_MASK) == __INSN_BRANCH_OPCODE) {
+ bool result = false;
+ long imm = RV_EXTRACT_BTYPE_IMM(code);
+ unsigned long rs1_val = 0, rs2_val = 0;
+
+ rs1_num = riscv_insn_extract_rs1_reg(code);
+ rs2_num = riscv_insn_extract_rs2_reg(code);
+ if (rs1_num)
+ rs1_val = regs_get_register(regs, rs1_num);
+ if (rs2_num)
+ rs2_val = regs_get_register(regs, rs2_num);
+
+ if (riscv_insn_is_beq(code))
+ result = (rs1_val == rs2_val) ? true : false;
+ else if (riscv_insn_is_bne(code))
+ result = (rs1_val != rs2_val) ? true : false;
+ else if (riscv_insn_is_blt(code))
+ result =
+ ((long)rs1_val <
+ (long)rs2_val) ? true : false;
+ else if (riscv_insn_is_bge(code))
+ result =
+ ((long)rs1_val >=
+ (long)rs2_val) ? true : false;
+ else if (riscv_insn_is_bltu(code))
+ result = (rs1_val < rs2_val) ? true : false;
+ else if (riscv_insn_is_bgeu(code))
+ result = (rs1_val >= rs2_val) ? true : false;
+ if (result)
+ return imm + pc;
+ else
+ return pc + 4;
+ } else if (riscv_insn_is_jal(code)) {
+ return RV_EXTRACT_JTYPE_IMM(code) + pc;
+ } else if (riscv_insn_is_jalr(code)) {
+ rs1_num = riscv_insn_extract_rs1_reg(code);
+ return RV_EXTRACT_ITYPE_IMM(code) +
+ (rs1_num ? regs_get_register(regs, rs1_num) : 0);
+ } else if (riscv_insn_is_sret(code)) {
+ return pc;
+ } else {
+ return pc + 4;
+ }
+ }
+}
diff --git a/arch/riscv/kernel/kgdb.c b/arch/riscv/kernel/kgdb.c
index 9f3db3503dab..aafc1424fc81 100644
--- a/arch/riscv/kernel/kgdb.c
+++ b/arch/riscv/kernel/kgdb.c
@@ -23,111 +23,19 @@ enum {
static unsigned long stepped_address;
static unsigned int stepped_opcode;
-static int decode_register_index(unsigned long opcode, int offset)
-{
- return (opcode >> offset) & 0x1F;
-}
-
-static int decode_register_index_short(unsigned long opcode, int offset)
-{
- return ((opcode >> offset) & 0x7) + 8;
-}
-
-/* Calculate the new address for after a step */
-static int get_step_address(struct pt_regs *regs, unsigned long *next_addr)
-{
- unsigned long pc = regs->epc;
- unsigned long *regs_ptr = (unsigned long *)regs;
- unsigned int rs1_num, rs2_num;
- int op_code;
-
- if (get_kernel_nofault(op_code, (void *)pc))
- return -EINVAL;
- if ((op_code & __INSN_LENGTH_MASK) != __INSN_LENGTH_GE_32) {
- if (riscv_insn_is_c_jalr(op_code) ||
- riscv_insn_is_c_jr(op_code)) {
- rs1_num = decode_register_index(op_code, RVC_C2_RS1_OPOFF);
- *next_addr = regs_ptr[rs1_num];
- } else if (riscv_insn_is_c_j(op_code) ||
- riscv_insn_is_c_jal(op_code)) {
- *next_addr = RVC_EXTRACT_JTYPE_IMM(op_code) + pc;
- } else if (riscv_insn_is_c_beqz(op_code)) {
- rs1_num = decode_register_index_short(op_code,
- RVC_C1_RS1_OPOFF);
- if (!rs1_num || regs_ptr[rs1_num] == 0)
- *next_addr = RVC_EXTRACT_BTYPE_IMM(op_code) + pc;
- else
- *next_addr = pc + 2;
- } else if (riscv_insn_is_c_bnez(op_code)) {
- rs1_num =
- decode_register_index_short(op_code, RVC_C1_RS1_OPOFF);
- if (rs1_num && regs_ptr[rs1_num] != 0)
- *next_addr = RVC_EXTRACT_BTYPE_IMM(op_code) + pc;
- else
- *next_addr = pc + 2;
- } else {
- *next_addr = pc + 2;
- }
- } else {
- if ((op_code & __INSN_OPCODE_MASK) == __INSN_BRANCH_OPCODE) {
- bool result = false;
- long imm = RV_EXTRACT_BTYPE_IMM(op_code);
- unsigned long rs1_val = 0, rs2_val = 0;
-
- rs1_num = decode_register_index(op_code, RVG_RS1_OPOFF);
- rs2_num = decode_register_index(op_code, RVG_RS2_OPOFF);
- if (rs1_num)
- rs1_val = regs_ptr[rs1_num];
- if (rs2_num)
- rs2_val = regs_ptr[rs2_num];
-
- if (riscv_insn_is_beq(op_code))
- result = (rs1_val == rs2_val) ? true : false;
- else if (riscv_insn_is_bne(op_code))
- result = (rs1_val != rs2_val) ? true : false;
- else if (riscv_insn_is_blt(op_code))
- result =
- ((long)rs1_val <
- (long)rs2_val) ? true : false;
- else if (riscv_insn_is_bge(op_code))
- result =
- ((long)rs1_val >=
- (long)rs2_val) ? true : false;
- else if (riscv_insn_is_bltu(op_code))
- result = (rs1_val < rs2_val) ? true : false;
- else if (riscv_insn_is_bgeu(op_code))
- result = (rs1_val >= rs2_val) ? true : false;
- if (result)
- *next_addr = imm + pc;
- else
- *next_addr = pc + 4;
- } else if (riscv_insn_is_jal(op_code)) {
- *next_addr = RV_EXTRACT_JTYPE_IMM(op_code) + pc;
- } else if (riscv_insn_is_jalr(op_code)) {
- rs1_num = decode_register_index(op_code, RVG_RS1_OPOFF);
- if (rs1_num)
- *next_addr = ((unsigned long *)regs)[rs1_num];
- *next_addr += RV_EXTRACT_ITYPE_IMM(op_code);
- } else if (riscv_insn_is_sret(op_code)) {
- *next_addr = pc;
- } else {
- *next_addr = pc + 4;
- }
- }
- return 0;
-}
-
static int do_single_step(struct pt_regs *regs)
{
/* Determine where the target instruction will send us to */
- unsigned long addr = 0;
- int error = get_step_address(regs, &addr);
+ unsigned long addr, insn;
+ int error = get_insn(regs, regs->epc, &insn);
if (error)
return error;
+ addr = get_step_address(regs, insn);
+
/* Store the op code in the stepped address */
- error = get_kernel_nofault(stepped_opcode, (void *)addr);
+ error = get_insn(regs, addr, stepped_opcode);
if (error)
return error;
diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c
index c0738d6c6498..6a9cfb0b664a 100644
--- a/arch/riscv/kernel/probes/kprobes.c
+++ b/arch/riscv/kernel/probes/kprobes.c
@@ -12,6 +12,7 @@
#include <asm/sections.h>
#include <asm/cacheflush.h>
#include <asm/bug.h>
+#include <asm/insn.h>
#include <asm/text-patching.h>
#include "decode-insn.h"
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 9c83848797a7..938a8b841f94 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -26,6 +26,7 @@
#include <asm/bug.h>
#include <asm/cfi.h>
#include <asm/csr.h>
+#include <asm/insn.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/syscall.h>
@@ -409,10 +410,10 @@ int is_valid_bugaddr(unsigned long pc)
return 0;
if (get_kernel_nofault(insn, (bug_insn_t *)pc))
return 0;
- if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
+ if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_GE_32)
return (insn == __BUG_INSN_32);
else
- return ((insn & __COMPRESSED_INSN_MASK) == __BUG_INSN_16);
+ return ((insn & RVC_MASK_C) == __BUG_INSN_16);
}
#endif /* CONFIG_GENERIC_BUG */
diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c
index 77c788660223..42a50e21b1d2 100644
--- a/arch/riscv/kernel/traps_misaligned.c
+++ b/arch/riscv/kernel/traps_misaligned.c
@@ -10,12 +10,13 @@
#include <linux/irq.h>
#include <linux/stringify.h>
-#include <asm/processor.h>
-#include <asm/ptrace.h>
+#include <asm/cpufeature.h>
#include <asm/csr.h>
#include <asm/entry-common.h>
#include <asm/hwprobe.h>
-#include <asm/cpufeature.h>
+#include <asm/insn.h>
+#include <asm/processor.h>
+#include <asm/ptrace.h>
#include <asm/vector.h>
#define INSN_MATCH_LB 0x3
@@ -112,25 +113,22 @@
#define SH_RS2 20
#define SH_RS2C 2
-#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1))
-#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \
- (RV_X(x, 10, 3) << 3) | \
- (RV_X(x, 5, 1) << 6))
-#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \
- (RV_X(x, 5, 2) << 6))
-#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \
+#define RVC_LW_IMM(x) ((RV_X(x, 6, 0x1) << 2) | \
+ (RV_X(x, 10, 0x7) << 3) | \
+ (RV_X(x, 5, 0x1) << 6))
+#define RVC_LD_IMM(x) ((RV_X(x, 10, 0x7) << 3) | \
+ (RV_X(x, 5, 0x3) << 6))
+#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 0x7) << 2) | \
+ (RV_X(x, 12, 0x1) << 5) | \
+ (RV_X(x, 2, 0x3) << 6))
+#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 0x3) << 3) | \
(RV_X(x, 12, 1) << 5) | \
- (RV_X(x, 2, 2) << 6))
-#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \
- (RV_X(x, 12, 1) << 5) | \
- (RV_X(x, 2, 3) << 6))
-#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \
- (RV_X(x, 7, 2) << 6))
-#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \
- (RV_X(x, 7, 3) << 6))
-#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3))
-#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3))
-#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5)
+ (RV_X(x, 2, 0x7) << 6))
+#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 0xf) << 2) | \
+ (RV_X(x, 7, 0x3) << 6))
+#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 0x7) << 3) | \
+ (RV_X(x, 7, 0x7) << 6))
+#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 0x7))
#define SHIFT_RIGHT(x, y) \
((y) < 0 ? ((x) << -(y)) : ((x) >> (y)))
@@ -146,7 +144,6 @@
#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs))
#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs))
-#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs))
#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs))
#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs))
#define GET_SP(regs) (*REG_PTR(2, 0, regs))
@@ -270,58 +267,6 @@ static unsigned long get_f32_rs(unsigned long insn, u8 fp_reg_offset,
#define GET_F32_RS2C(insn, regs) (get_f32_rs(insn, 2, regs))
#define GET_F32_RS2S(insn, regs) (get_f32_rs(RVC_RS2S(insn), 0, regs))
-#define __read_insn(regs, insn, insn_addr, type) \
-({ \
- int __ret; \
- \
- if (user_mode(regs)) { \
- __ret = get_user(insn, (type __user *) insn_addr); \
- } else { \
- insn = *(type *)insn_addr; \
- __ret = 0; \
- } \
- \
- __ret; \
-})
-
-static inline int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn)
-{
- ulong insn = 0;
-
- if (epc & 0x2) {
- ulong tmp = 0;
-
- if (__read_insn(regs, insn, epc, u16))
- return -EFAULT;
- /* __get_user() uses regular "lw" which sign extend the loaded
- * value make sure to clear higher order bits in case we "or" it
- * below with the upper 16 bits half.
- */
- insn &= GENMASK(15, 0);
- if ((insn & __INSN_LENGTH_MASK) != __INSN_LENGTH_32) {
- *r_insn = insn;
- return 0;
- }
- epc += sizeof(u16);
- if (__read_insn(regs, tmp, epc, u16))
- return -EFAULT;
- *r_insn = (tmp << 16) | insn;
-
- return 0;
- } else {
- if (__read_insn(regs, insn, epc, u32))
- return -EFAULT;
- if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32) {
- *r_insn = insn;
- return 0;
- }
- insn &= GENMASK(15, 0);
- *r_insn = insn;
-
- return 0;
- }
-}
-
union reg_data {
u8 data_bytes[8];
ulong data_ulong;
--
2.43.0
On Fri, Aug 22, 2025 at 11:17 PM Jesse Taube <jesse@rivosinc.com> wrote:
>
> Various parts of the kernel decode and read instruction from memory.
> Functions like get_insn, GET_INSN_LENGTH and riscv_insn_is_c are defined
> in multiple places. Consolidate these functions into the insn.h and the
> newly added insn.c.
>
> Signed-off-by: Jesse Taube <jesse@rivosinc.com>
> ---
> RFC -> V1:
> - No change
> V2 -> V1:
> - No change
> ---
> arch/riscv/include/asm/bug.h | 12 ---
> arch/riscv/include/asm/insn.h | 131 ++++++++++++++++++++++-
> arch/riscv/kernel/Makefile | 1 +
> arch/riscv/kernel/insn.c | 151 +++++++++++++++++++++++++++
> arch/riscv/kernel/kgdb.c | 102 +-----------------
> arch/riscv/kernel/probes/kprobes.c | 1 +
> arch/riscv/kernel/traps.c | 5 +-
> arch/riscv/kernel/traps_misaligned.c | 93 ++++-------------
> 8 files changed, 309 insertions(+), 187 deletions(-)
> create mode 100644 arch/riscv/kernel/insn.c
>
> diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h
> index 1aaea81fb141..a2777eb67ad1 100644
> --- a/arch/riscv/include/asm/bug.h
> +++ b/arch/riscv/include/asm/bug.h
> @@ -12,21 +12,9 @@
>
> #include <asm/asm.h>
>
> -#define __INSN_LENGTH_MASK _UL(0x3)
> -#define __INSN_LENGTH_32 _UL(0x3)
> -#define __COMPRESSED_INSN_MASK _UL(0xffff)
> -
> #define __BUG_INSN_32 _UL(0x00100073) /* ebreak */
> #define __BUG_INSN_16 _UL(0x9002) /* c.ebreak */
>
> -#define GET_INSN_LENGTH(insn) \
> -({ \
> - unsigned long __len; \
> - __len = ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32) ? \
> - 4UL : 2UL; \
> - __len; \
> -})
> -
> typedef u32 bug_insn_t;
>
> #ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
> diff --git a/arch/riscv/include/asm/insn.h b/arch/riscv/include/asm/insn.h
> index 09fde95a5e8f..ba74e5b8262c 100644
> --- a/arch/riscv/include/asm/insn.h
> +++ b/arch/riscv/include/asm/insn.h
> @@ -64,6 +64,7 @@
> #define RVG_RS2_OPOFF 20
> #define RVG_RD_OPOFF 7
> #define RVG_RS1_MASK GENMASK(4, 0)
> +#define RVG_RS2_MASK GENMASK(4, 0)
> #define RVG_RD_MASK GENMASK(4, 0)
>
> /* The bit field of immediate value in RVC J instruction */
> @@ -121,17 +122,27 @@
> #define RVC_C0_RS1_OPOFF 7
> #define RVC_C0_RS2_OPOFF 2
> #define RVC_C0_RD_OPOFF 2
> +#define RVC_C0_RS1_MASK GENMASK(2, 0)
> +#define RVC_C0_RS2_MASK GENMASK(2, 0)
> +#define RVC_C0_RD_MASK GENMASK(2, 0)
> +#define RVC_C0_REG_OFFSET 8
>
> /* The register offset in RVC op=C1 instruction */
> #define RVC_C1_RS1_OPOFF 7
> #define RVC_C1_RS2_OPOFF 2
> #define RVC_C1_RD_OPOFF 7
> +#define RVC_C1_RS1_MASK GENMASK(2, 0)
> +#define RVC_C1_RS2_MASK GENMASK(2, 0)
> +#define RVC_C1_RD_MASK GENMASK(2, 0)
> +#define RVC_C1_REG_OFFSET 8
>
> /* The register offset in RVC op=C2 instruction */
> #define RVC_C2_RS1_OPOFF 7
> #define RVC_C2_RS2_OPOFF 2
> #define RVC_C2_RD_OPOFF 7
> #define RVC_C2_RS1_MASK GENMASK(4, 0)
> +#define RVC_C2_RS2_MASK GENMASK(4, 0)
> +#define RVC_C2_RD_MASK GENMASK(4, 0)
>
> /* parts of opcode for RVG*/
> #define RVG_OPCODE_FENCE 0x0f
> @@ -226,12 +237,26 @@
> #define RVC_MASK_C_EBREAK 0xffff
> #define RVG_MASK_EBREAK 0xffffffff
> #define RVG_MASK_SRET 0xffffffff
> +#define RVC_MASK_C GENMASK(15, 0)
>
> #define __INSN_LENGTH_MASK _UL(0x3)
> #define __INSN_LENGTH_GE_32 _UL(0x3)
> #define __INSN_OPCODE_MASK _UL(0x7F)
> #define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH)
>
> +#define GET_INSN_LENGTH(insn) \
> +({ \
> + unsigned long __len; \
> + __len = ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_GE_32) ? \
> + 4UL : 2UL; \
> + __len; \
> +})
> +
> +static __always_inline bool riscv_insn_is_c(u32 code)
> +{
> + return (code & (__INSN_LENGTH_MASK)) != (__INSN_LENGTH_GE_32);
> +}
> +
> #define __RISCV_INSN_FUNCS(name, mask, val) \
> static __always_inline bool riscv_insn_is_##name(u32 code) \
> { \
> @@ -260,7 +285,7 @@ __RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ)
> __RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK)
> __RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK)
> __RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET)
> -__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE);
> +__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE)
>
> /* special case to catch _any_ system instruction */
> static __always_inline bool riscv_insn_is_system(u32 code)
> @@ -295,6 +320,10 @@ static __always_inline bool riscv_insn_is_c_jalr(u32 code)
> ({typeof(x) x_ = (x); \
> (RV_X(x_, RVG_RS1_OPOFF, RVG_RS1_MASK)); })
>
> +#define RV_EXTRACT_RS2_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RV_X(x_, RVG_RS2_OPOFF, RVG_RS2_MASK)); })
> +
> #define RV_EXTRACT_RD_REG(x) \
> ({typeof(x) x_ = (x); \
> (RV_X(x_, RVG_RD_OPOFF, RVG_RD_MASK)); })
> @@ -322,9 +351,41 @@ static __always_inline bool riscv_insn_is_c_jalr(u32 code)
> (RV_X(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) | \
> (RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); })
>
> +#define RVC_EXTRACT_C0_RS1_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C0_RS1_OPOFF, RVC_C0_RS1_MASK)); })
> +
> +#define RVC_EXTRACT_C0_RS2_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C0_RS2_OPOFF, RVC_C0_RS2_MASK)); })
> +
> +#define RVC_EXTRACT_C0_RD_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C0_RD_OPOFF, RVC_C0_RD_MASK)); })
> +
> +#define RVC_EXTRACT_C1_RS1_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C1_RS1_OPOFF, RVC_C1_RS1_MASK)); })
> +
> +#define RVC_EXTRACT_C1_RS2_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C1_RS2_OPOFF, RVC_C1_RS2_MASK)); })
> +
> +#define RVC_EXTRACT_C1_RD_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C1_RD_OPOFF, RVC_C1_RD_MASK)); })
> +
> #define RVC_EXTRACT_C2_RS1_REG(x) \
> ({typeof(x) x_ = (x); \
> - (RV_X(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })
> + (RVC_X(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })
> +
> +#define RVC_EXTRACT_C2_RS2_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C2_RS2_OPOFF, RVC_C2_RS2_MASK)); })
> +
> +#define RVC_EXTRACT_C2_RD_REG(x) \
> + ({typeof(x) x_ = (x); \
> + (RVC_X(x_, RVC_C2_RD_OPOFF, RVC_C2_RD_MASK)); })
>
> #define RVC_EXTRACT_JTYPE_IMM(x) \
> ({typeof(x) x_ = (x); \
> @@ -354,6 +415,66 @@ static __always_inline bool riscv_insn_is_c_jalr(u32 code)
>
> #define RVV_EXRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x)
>
> +/*
> + * Get the rs1 register number from RV or RVC instruction.
> + *
> + * @insn: instruction to process
> + * Return: rs1 register
> + */
> +static inline unsigned int riscv_insn_extract_rs1_reg(u32 insn)
> +{
> + switch (RVC_INSN_OPCODE_MASK & insn) {
> + case RVC_OPCODE_C0:
> + return RVC_EXTRACT_C0_RS1_REG(insn) + RVC_C0_REG_OFFSET;
> + case RVC_OPCODE_C1:
> + return RVC_EXTRACT_C1_RS1_REG(insn) + RVC_C1_REG_OFFSET;
> + case RVC_OPCODE_C2:
> + return RVC_EXTRACT_C2_RS1_REG(insn);
> + default:
> + return RV_EXTRACT_RS1_REG(insn);
> + }
> +}
> +
> +/*
> + * Get the rs2 register number from RV or RVC instruction.
> + *
> + * @insn: instruction to process
> + * Return: rs2 register
> + */
> +static inline unsigned int riscv_insn_extract_rs2_reg(u32 insn)
> +{
> + switch (RVC_INSN_OPCODE_MASK & insn) {
> + case RVC_OPCODE_C0:
> + return RVC_EXTRACT_C0_RS2_REG(insn) + RVC_C0_REG_OFFSET;
> + case RVC_OPCODE_C1:
> + return RVC_EXTRACT_C1_RS2_REG(insn) + RVC_C1_REG_OFFSET;
> + case RVC_OPCODE_C2:
> + return RVC_EXTRACT_C2_RS2_REG(insn);
> + default:
> + return RV_EXTRACT_RS2_REG(insn);
> + }
> +}
> +
> +/*
> + * Get the rd register number from RV or RVC instruction.
> + *
> + * @insn: instruction to process
> + * Return: rd register
> + */
> +static inline unsigned int riscv_insn_extract_rd_reg(u32 insn)
> +{
> + switch (RVC_INSN_OPCODE_MASK & insn) {
> + case RVC_OPCODE_C0:
> + return RVC_EXTRACT_C0_RD_REG(insn) + RVC_C0_REG_OFFSET;
> + case RVC_OPCODE_C1:
> + return RVC_EXTRACT_C1_RD_REG(insn) + RVC_C1_REG_OFFSET;
> + case RVC_OPCODE_C2:
> + return RVC_EXTRACT_C2_RD_REG(insn);
> + default:
> + return RV_EXTRACT_RD_REG(insn);
> + }
> +}
> +
> /*
> * Get the immediate from a J-type instruction.
> *
> @@ -428,4 +549,10 @@ static inline void riscv_insn_insert_utype_itype_imm(u32 *utype_insn, u32 *itype
> *utype_insn |= (imm & RV_U_IMM_31_12_MASK) + ((imm & BIT(11)) << 1);
> *itype_insn |= ((imm & RV_I_IMM_11_0_MASK) << RV_I_IMM_11_0_OPOFF);
> }
> +
> +#include <asm/ptrace.h>
> +
> +int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn);
> +unsigned long get_step_address(struct pt_regs *regs, u32 code);
> +
> #endif /* _ASM_RISCV_INSN_H */
> diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> index f7480c9c6f8d..4f719b09e5ad 100644
> --- a/arch/riscv/kernel/Makefile
> +++ b/arch/riscv/kernel/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_RISCV_ALTERNATIVE) += alternative.o
> obj-y += cpu.o
> obj-y += cpufeature.o
> obj-y += entry.o
> +obj-y += insn.o
> obj-y += irq.o
> obj-y += process.o
> obj-y += ptrace.o
> diff --git a/arch/riscv/kernel/insn.c b/arch/riscv/kernel/insn.c
> new file mode 100644
> index 000000000000..dd2a6ef9fd25
> --- /dev/null
> +++ b/arch/riscv/kernel/insn.c
> @@ -0,0 +1,151 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2025 Rivos, Inc
> + */
> +#include <asm/insn.h>
> +#include <asm/ptrace.h>
> +#include <asm/uaccess.h>
> +
> +#define __read_insn(regs, insn, insn_addr, type) \
> +({ \
> + int __ret; \
> + \
> + if (user_mode(regs)) { \
> + __ret = get_user(insn, (type __user *) insn_addr); \
> + } else { \
> + insn = *(type *)insn_addr; \
> + __ret = 0; \
> + } \
> + \
> + __ret; \
> +})
> +
> +/*
> + * Update a set of two instructions (U-type + I-type) with an immediate value.
> + *
> + * Used for example in auipc+jalrs pairs the U-type instructions contains
> + * a 20bit upper immediate representing bits[31:12], while the I-type
> + * instruction contains a 12bit immediate representing bits[11:0].
> + *
> + * This also takes into account that both separate immediates are
> + * considered as signed values, so if the I-type immediate becomes
> + * negative (BIT(11) set) the U-type part gets adjusted.
> + *
> + * @regs: pointer to the utype instruction of the pair
> + * @epc: pointer to the itype instruction of the pair
> + * @r_insn: the immediate to insert into the two instructions
> + * Return: combined immediate
> + */
> +int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn)
> +{
> + ulong insn = 0;
> +
> + if (epc & 0x2) {
> + ulong tmp = 0;
> +
> + if (__read_insn(regs, insn, epc, u16))
> + return -EFAULT;
> + /* __get_user() uses regular "lw" which sign extend the loaded
> + * value make sure to clear higher order bits in case we "or" it
> + * below with the upper 16 bits half.
> + */
> + insn &= RVC_MASK_C;
> + if (riscv_insn_is_c(insn)) {
> + *r_insn = insn;
> + return 0;
> + }
> + epc += sizeof(u16);
> + if (__read_insn(regs, tmp, epc, u16))
> + return -EFAULT;
> + *r_insn = (tmp << 16) | insn;
> +
> + return 0;
> + } else {
> + if (__read_insn(regs, insn, epc, u32))
> + return -EFAULT;
> + if (!riscv_insn_is_c(insn)) {
> + *r_insn = insn;
> + return 0;
> + }
> + insn &= RVC_MASK_C;
> + *r_insn = insn;
> +
> + return 0;
> + }
> +}
> +
> +/* Calculate the new address for after a step */
> +unsigned long get_step_address(struct pt_regs *regs, u32 code)
> +{
> + unsigned long pc = regs->epc;
> + unsigned int rs1_num, rs2_num;
> +
> + if ((code & __INSN_LENGTH_MASK) != __INSN_LENGTH_GE_32) {
> + if (riscv_insn_is_c_jalr(code) ||
> + riscv_insn_is_c_jr(code)) {
> + rs1_num = riscv_insn_extract_rs1_reg(code);
> + return regs_get_register(regs, rs1_num);
> + } else if (riscv_insn_is_c_j(code) ||
> + riscv_insn_is_c_jal(code)) {
> + return RVC_EXTRACT_JTYPE_IMM(code) + pc;
> + } else if (riscv_insn_is_c_beqz(code)) {
> + rs1_num = riscv_insn_extract_rs1_reg(code);
> + if (!rs1_num || regs_get_register(regs, rs1_num) == 0)
> + return RVC_EXTRACT_BTYPE_IMM(code) + pc;
> + else
> + return pc + 2;
> + } else if (riscv_insn_is_c_bnez(code)) {
> + rs1_num = riscv_insn_extract_rs1_reg(RVC_C1_RS1_OPOFF);
> + if (rs1_num && regs_get_register(regs, rs1_num) != 0)
> + return RVC_EXTRACT_BTYPE_IMM(code) + pc;
> + else
> + return pc + 2;
> + } else {
> + return pc + 2;
> + }
> + } else {
> + if ((code & __INSN_OPCODE_MASK) == __INSN_BRANCH_OPCODE) {
> + bool result = false;
> + long imm = RV_EXTRACT_BTYPE_IMM(code);
> + unsigned long rs1_val = 0, rs2_val = 0;
> +
> + rs1_num = riscv_insn_extract_rs1_reg(code);
> + rs2_num = riscv_insn_extract_rs2_reg(code);
> + if (rs1_num)
> + rs1_val = regs_get_register(regs, rs1_num);
> + if (rs2_num)
> + rs2_val = regs_get_register(regs, rs2_num);
> +
> + if (riscv_insn_is_beq(code))
> + result = (rs1_val == rs2_val) ? true : false;
> + else if (riscv_insn_is_bne(code))
> + result = (rs1_val != rs2_val) ? true : false;
> + else if (riscv_insn_is_blt(code))
> + result =
> + ((long)rs1_val <
> + (long)rs2_val) ? true : false;
> + else if (riscv_insn_is_bge(code))
> + result =
> + ((long)rs1_val >=
> + (long)rs2_val) ? true : false;
> + else if (riscv_insn_is_bltu(code))
> + result = (rs1_val < rs2_val) ? true : false;
> + else if (riscv_insn_is_bgeu(code))
> + result = (rs1_val >= rs2_val) ? true : false;
> + if (result)
> + return imm + pc;
> + else
> + return pc + 4;
> + } else if (riscv_insn_is_jal(code)) {
> + return RV_EXTRACT_JTYPE_IMM(code) + pc;
> + } else if (riscv_insn_is_jalr(code)) {
> + rs1_num = riscv_insn_extract_rs1_reg(code);
> + return RV_EXTRACT_ITYPE_IMM(code) +
> + (rs1_num ? regs_get_register(regs, rs1_num) : 0);
> + } else if (riscv_insn_is_sret(code)) {
> + return pc;
> + } else {
> + return pc + 4;
> + }
> + }
> +}
> diff --git a/arch/riscv/kernel/kgdb.c b/arch/riscv/kernel/kgdb.c
> index 9f3db3503dab..aafc1424fc81 100644
> --- a/arch/riscv/kernel/kgdb.c
> +++ b/arch/riscv/kernel/kgdb.c
> @@ -23,111 +23,19 @@ enum {
> static unsigned long stepped_address;
> static unsigned int stepped_opcode;
>
> -static int decode_register_index(unsigned long opcode, int offset)
> -{
> - return (opcode >> offset) & 0x1F;
> -}
> -
> -static int decode_register_index_short(unsigned long opcode, int offset)
> -{
> - return ((opcode >> offset) & 0x7) + 8;
> -}
> -
> -/* Calculate the new address for after a step */
> -static int get_step_address(struct pt_regs *regs, unsigned long *next_addr)
> -{
> - unsigned long pc = regs->epc;
> - unsigned long *regs_ptr = (unsigned long *)regs;
> - unsigned int rs1_num, rs2_num;
> - int op_code;
> -
> - if (get_kernel_nofault(op_code, (void *)pc))
> - return -EINVAL;
> - if ((op_code & __INSN_LENGTH_MASK) != __INSN_LENGTH_GE_32) {
> - if (riscv_insn_is_c_jalr(op_code) ||
> - riscv_insn_is_c_jr(op_code)) {
> - rs1_num = decode_register_index(op_code, RVC_C2_RS1_OPOFF);
> - *next_addr = regs_ptr[rs1_num];
> - } else if (riscv_insn_is_c_j(op_code) ||
> - riscv_insn_is_c_jal(op_code)) {
> - *next_addr = RVC_EXTRACT_JTYPE_IMM(op_code) + pc;
> - } else if (riscv_insn_is_c_beqz(op_code)) {
> - rs1_num = decode_register_index_short(op_code,
> - RVC_C1_RS1_OPOFF);
> - if (!rs1_num || regs_ptr[rs1_num] == 0)
> - *next_addr = RVC_EXTRACT_BTYPE_IMM(op_code) + pc;
> - else
> - *next_addr = pc + 2;
> - } else if (riscv_insn_is_c_bnez(op_code)) {
> - rs1_num =
> - decode_register_index_short(op_code, RVC_C1_RS1_OPOFF);
> - if (rs1_num && regs_ptr[rs1_num] != 0)
> - *next_addr = RVC_EXTRACT_BTYPE_IMM(op_code) + pc;
> - else
> - *next_addr = pc + 2;
> - } else {
> - *next_addr = pc + 2;
> - }
> - } else {
> - if ((op_code & __INSN_OPCODE_MASK) == __INSN_BRANCH_OPCODE) {
> - bool result = false;
> - long imm = RV_EXTRACT_BTYPE_IMM(op_code);
> - unsigned long rs1_val = 0, rs2_val = 0;
> -
> - rs1_num = decode_register_index(op_code, RVG_RS1_OPOFF);
> - rs2_num = decode_register_index(op_code, RVG_RS2_OPOFF);
> - if (rs1_num)
> - rs1_val = regs_ptr[rs1_num];
> - if (rs2_num)
> - rs2_val = regs_ptr[rs2_num];
> -
> - if (riscv_insn_is_beq(op_code))
> - result = (rs1_val == rs2_val) ? true : false;
> - else if (riscv_insn_is_bne(op_code))
> - result = (rs1_val != rs2_val) ? true : false;
> - else if (riscv_insn_is_blt(op_code))
> - result =
> - ((long)rs1_val <
> - (long)rs2_val) ? true : false;
> - else if (riscv_insn_is_bge(op_code))
> - result =
> - ((long)rs1_val >=
> - (long)rs2_val) ? true : false;
> - else if (riscv_insn_is_bltu(op_code))
> - result = (rs1_val < rs2_val) ? true : false;
> - else if (riscv_insn_is_bgeu(op_code))
> - result = (rs1_val >= rs2_val) ? true : false;
> - if (result)
> - *next_addr = imm + pc;
> - else
> - *next_addr = pc + 4;
> - } else if (riscv_insn_is_jal(op_code)) {
> - *next_addr = RV_EXTRACT_JTYPE_IMM(op_code) + pc;
> - } else if (riscv_insn_is_jalr(op_code)) {
> - rs1_num = decode_register_index(op_code, RVG_RS1_OPOFF);
> - if (rs1_num)
> - *next_addr = ((unsigned long *)regs)[rs1_num];
> - *next_addr += RV_EXTRACT_ITYPE_IMM(op_code);
> - } else if (riscv_insn_is_sret(op_code)) {
> - *next_addr = pc;
> - } else {
> - *next_addr = pc + 4;
> - }
> - }
> - return 0;
> -}
> -
> static int do_single_step(struct pt_regs *regs)
> {
> /* Determine where the target instruction will send us to */
> - unsigned long addr = 0;
> - int error = get_step_address(regs, &addr);
> + unsigned long addr, insn;
> + int error = get_insn(regs, regs->epc, &insn);
>
> if (error)
> return error;
>
> + addr = get_step_address(regs, insn);
> +
> /* Store the op code in the stepped address */
> - error = get_kernel_nofault(stepped_opcode, (void *)addr);
> + error = get_insn(regs, addr, stepped_opcode);
This line gives following compile error:
arch/riscv/kernel/kgdb.c: In function 'do_single_step':
arch/riscv/kernel/kgdb.c:38:38: error: passing argument 3 of
'get_insn' makes pointer from integer without a cast
[-Wint-conversion]
38 | error = get_insn(regs, addr, stepped_opcode);
| ^~~~~~~~~~~~~~
| |
| unsigned int
In file included from arch/riscv/kernel/kgdb.c:14:
./arch/riscv/include/asm/insn.h:555:54: note: expected 'ulong *'
{aka 'long unsigned int *'} but argument is of type 'unsigned int'
555 | int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn);
| ~~~~~~~^~~~~~
If you are touching some source then at least compile test it.
Regards,
Anup
© 2016 - 2026 Red Hat, Inc.