Module Name:    src
Committed By:   dholland
Date:           Wed Apr 14 06:32:20 UTC 2021

Modified Files:
        src/sys/arch/riscv/include: db_machdep.h insn.h
        src/sys/arch/riscv/riscv: db_disasm.c db_machdep.c

Log Message:
Add a ddb disassembler for riscv.

builds, but not really tested yet.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/riscv/include/db_machdep.h \
    src/sys/arch/riscv/include/insn.h
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/riscv/riscv/db_disasm.c
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/riscv/riscv/db_machdep.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/riscv/include/db_machdep.h
diff -u src/sys/arch/riscv/include/db_machdep.h:1.2 src/sys/arch/riscv/include/db_machdep.h:1.3
--- src/sys/arch/riscv/include/db_machdep.h:1.2	Mon Nov  6 03:47:48 2017
+++ src/sys/arch/riscv/include/db_machdep.h	Wed Apr 14 06:32:20 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: db_machdep.h,v 1.2 2017/11/06 03:47:48 christos Exp $ */
+/* $NetBSD: db_machdep.h,v 1.3 2021/04/14 06:32:20 dholland Exp $ */
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -62,6 +62,17 @@ extern const uint32_t __cpu_Debugger_ins
 #define	BKPT_SIZE	(sizeof(uint32_t))	/* size of breakpoint inst */
 #define	BKPT_SET(inst, addr)	(BKPT_INST)
 
+/*
+ * XXX with the C extension there's also a 16-bit-wide breakpoint
+ * instruction, the idea being that you use it when inserting a
+ * breakpoint into a stream of 16-bit instructions, but it looks like
+ * MI ddb can't cope with having two sizes :-(
+ */
+#if 0
+#define BKPT_INST_2	0x9002
+#define BKPT_SIZE_2	(sizeof(uint16_t))
+#endif
+
 #define	IS_BREAKPOINT_TRAP(type, code)	((type) == CAUSE_BREAKPOINT)
 #define IS_WATCHPOINT_TRAP(type, code)	(0)
 
Index: src/sys/arch/riscv/include/insn.h
diff -u src/sys/arch/riscv/include/insn.h:1.2 src/sys/arch/riscv/include/insn.h:1.3
--- src/sys/arch/riscv/include/insn.h:1.2	Wed Nov  4 07:09:45 2020
+++ src/sys/arch/riscv/include/insn.h	Wed Apr 14 06:32:20 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: insn.h,v 1.2 2020/11/04 07:09:45 skrll Exp $ */
+/* $NetBSD: insn.h,v 1.3 2021/04/14 06:32:20 dholland Exp $ */
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -32,8 +32,14 @@
 #ifndef _RISCV_INSN_H_
 #define _RISCV_INSN_H_
 
+/*
+ * I have corrected and updated this, but it's the wrong way to do it.
+ * It's still used by ddb_machdep.c but that code should be fixed to
+ * use the newer stuff below. - dholland
+ */
 union riscv_insn {
 	uint32_t val;
+	/* register ops */
 	struct {
 		unsigned int r_opcode : 7;
 		unsigned int r_rd : 5;
@@ -42,14 +48,26 @@ union riscv_insn {
 		unsigned int r_rs2 : 5;
 		unsigned int r_funct7 : 7;
 	} type_r;
+	/* 32-bit shifts */
+	struct {
+		unsigned int rs32_opcode : 7;
+		unsigned int rs32_rd : 5;
+		unsigned int rs32_funct3 : 3;
+		unsigned int rs32_rs1 : 5;
+		unsigned int rs32_shamt : 5;
+		unsigned int rs32_funct7 : 7;
+	} type_rs32;
+	/* 64-bit shifts */
 	struct {
-		unsigned int rs_opcode : 7;
-		unsigned int rs_rd : 5;
-		unsigned int rs_funct3 : 3;
-		unsigned int rs_rs1 : 5;
-		unsigned int rs_shmat : 6;
-		unsigned int rs_funct6 : 6;
-	} type_rs;
+		unsigned int rs64_opcode : 7;
+		unsigned int rs64_rd : 5;
+		unsigned int rs64_funct3 : 3;
+		unsigned int rs64_rs1 : 5;
+		unsigned int rs64_shamt : 6;
+		unsigned int rs64_zero : 1;
+		unsigned int rs64_funct5 : 5;
+	} type_rs64;
+	/* atomics */
 	struct {
 		unsigned int ra_opcode : 7;
 		unsigned int ra_rd : 5;
@@ -58,17 +76,29 @@ union riscv_insn {
 		unsigned int ra_rs2 : 5;
 		unsigned int ra_rl : 1;
 		unsigned int ra_aq : 1;
-		unsigned int ra_funct5 : 6;
+		unsigned int ra_funct5 : 5;
 	} type_ra;
+	/* certain fpu ops */
 	struct {
 		unsigned int rf_opcode : 7;
 		unsigned int rf_rd : 5;
 		unsigned int rf_rm : 3;
 		unsigned int rf_rs1 : 5;
 		unsigned int rf_rs2 : 5;
-		unsigned int rf_funct2 : 2;
-		unsigned int rf_rs3 : 5;
+		unsigned int rf_size : 2;
+		unsigned int rf_funct5 : 5;
 	} type_rf;
+	/* other fpu ops */
+	struct {
+		unsigned int rf4_opcode : 7;
+		unsigned int rf4_rd : 5;
+		unsigned int rf4_rm : 3;
+		unsigned int rf4_rs1 : 5;
+		unsigned int rf4_rs2 : 5;
+		unsigned int rf4_size : 2;
+		unsigned int rf4_rs3 : 5;
+	} type_rf4;
+	/* immediates */
 	struct {
 		unsigned int i_opcode : 7;
 		unsigned int i_rd : 5;
@@ -76,6 +106,7 @@ union riscv_insn {
 		unsigned int i_rs1 : 5;
 		signed int i_imm11to0 : 12;
 	} type_i;
+	/* stores */
 	struct {
 		unsigned int s_opcode : 7;
 		unsigned int s_imm4_to_0 : 5;
@@ -84,33 +115,241 @@ union riscv_insn {
 		unsigned int s_rs2 : 5;
 		signed int s_imm11_to_5 : 7;
 	} type_s;
+	/* branches */
 	struct {
-		unsigned int sb_opcode : 7;
-		unsigned int sb_imm11 : 1;
-		unsigned int sb_imm4to1 : 4;
-		unsigned int sb_funct3 : 3;
-		unsigned int sb_rs1 : 5;
-		unsigned int sb_rs2 : 5;
-		unsigned int sb_imm10to5 : 6;
-		signed int sb_imm12 : 1;
-	} type_sb;
+		unsigned int b_opcode : 7;
+		unsigned int b_imm11 : 1;
+		unsigned int b_imm4to1 : 4;
+		unsigned int b_funct3 : 3;
+		unsigned int b_rs1 : 5;
+		unsigned int b_rs2 : 5;
+		unsigned int b_imm10to5 : 6;
+		signed int b_imm12 : 1;
+	} type_b;
+	/* large immediate constants */
 	struct {
 		unsigned int u_opcode : 7;
 		unsigned int u_rd : 5;
 		signed int u_imm31to12 : 20;
 	} type_u;
+	/* large immediate jumps */
 	struct {
-		unsigned int uj_opcode : 7;
-		unsigned int uj_rd : 5;
-		unsigned int uj_imm19to12 : 9;
-		unsigned int uj_imm11 : 1;
-		unsigned int uj_imm10to1 : 9;
-		signed int uj_imm20 : 1;
-	} type_uj;
+		unsigned int j_opcode : 7;
+		unsigned int j_rd : 5;
+		unsigned int j_imm19to12 : 9;
+		unsigned int j_imm11 : 1;
+		unsigned int j_imm10to1 : 9;
+		signed int j_imm20 : 1;
+	} type_j;
 };
 
+/*
+ * old macro still in use with the above
+ * (XXX, doesn't handle 16-bit instructions)
+ */
+
 #define OPCODE_P(i, x)		(((i) & 0b1111111) == ((OPCODE_##x<<2)|0b11))
 
+////////////////////////////////////////////////////////////
+
+/*
+ * Instruction size
+ */
+
+/* cumulative size tests */
+#define INSN_SIZE_IS_16(insn) (((insn) & 0b11) != 0b11)
+#define INSN_SIZE_IS_32(insn) (((insn) & 0b11100) != 0b11100)
+#define INSN_SIZE_IS_48(insn) (((insn) & 0b100000) != 0b100000)
+#define INSN_SIZE_IS_64(insn) (((insn) & 0b1000000) != 0b1000000)
+
+/* returns 1-5 for the number of uint16s */
+#define INSN_HALFWORDS(insn) \
+	(INSN_SIZE_IS_16(insn) ? 1 : \
+	 INSN_SIZE_IS_32(insn) ? 2 : \
+	 INSN_SIZE_IS_48(insn) ? 3 : \
+	 INSN_SIZE_IS_64(insn) ? 4 : \
+	 5)
+
+#define INSN_SIZE(insn) (INSN_HALFWORDS(insn) * sizeof(uint16_t))
+
+/*
+ * sign-extend x from the bottom k bits
+ */
+#define SIGNEXT32(x, k) ( \
+		( ((x) & (1U << ((k)-1))) ? (0xffffffffU << (k)) : 0U) | \
+		((x) & (0xffffffffU >> (32 - (k))))			 \
+	)
+
+/*
+ * Field extractors for 32-bit instructions
+ */
+
+#define INSN_OPCODE32(insn)	(((insn) & 0x0000007f) >> 2)
+#define INSN_RD(insn)		(((insn) & 0x00000f80) >> 7)
+#define INSN_FUNCT3(insn)	(((insn) & 0x00007000) >> 12)
+#define INSN_RS1(insn)		(((insn) & 0x000f8000) >> 15)
+#define INSN_RS2(insn)		(((insn) & 0x01f00000) >> 20)
+#define INSN_FUNCT7(insn)	(((insn) & 0xfe000000) >> 25)
+
+/* U-type immediate, just the top bits of the instruction */
+#define INSN_IMM_U(insn)	 ((insn) & 0xfffff000)
+
+/* I-type immediate, upper 12 bits sign-extended */
+#define INSN_IMM_I(insn)	SIGNEXT32(((insn) & 0xfff00000) >> 20, 12)
+
+/* S-type immediate (stores), pasted from funct7 field and rd field */
+#define INSN_IMM_S_raw(insn)	((INSN_FUNCT7(insn) << 5) | INSN_RD(insn))
+#define INSN_IMM_S(insn)	SIGNEXT32(INSN_IMM_S_raw(insn), 12)
+
+/* B-type immediate (branches), pasted messily from funct7 and rd fields */
+#define INSN_IMM_B_raw(insn)			\
+	(((insn & 0x80000000) >> (31-12)) |	\
+	 ((insn & 0x00000080) << (11-7))  |	\
+	 ((insn & 0x7e000000) >> (25-5))  |	\
+	 ((insn & 0x00000f00) >> (8-1)))
+#define INSN_IMM_B(insn)	SIGNEXT32(INSN_IMM_B_raw(insn), 13)
+
+/* J-type immediate (jumps), rehash of the U immediate field */
+#define INSN_IMM_J_raw(insn)			\
+	(((insn & 0x80000000) >> (31-20)) |	\
+	 ((insn & 0x000ff000) >> (12-12)) |	\
+	 ((insn & 0x00100000) >> (20-11)) |	\
+	 ((insn & 0x7fe00000) >> (21-1)))
+#define INSN_IMM_J(insn)	SIGNEXT32(INSN_IMM_J_raw(insn), 21)
+
+/*
+ * Field extractors for 16-bit instructions
+ */
+
+#define INSN16_QUADRANT(insn)	((insn) & 0b11)
+
+/*
+ * In the manual there's
+ *    FUNCT3 (bits 13-15)
+ *    FUNCT4 (bits 12-15)
+ *    FUNCT6 (bits 10-15)
+ *    FUNCT2 (bits 5-6)
+ *
+ * but this does not reflect the actual usage correctly. So I've got
+ *    FUNCT3 (bits 13-15)
+ *    FUNCT2a (bits 10-11)
+ *    FUNCT1b (bit 12)
+ *    FUNCT2b (bits 5-6)
+ *    FUNCT3b (FUNCT1b pasted to FUNCT3b)
+ *
+ * Quadrant 0 just uses FUNCT3;
+ * Quadrant 1 goes FUNCT3 -> FUNCT2a -> FUNCT3b,
+ * Quadrant 2 goes FUNCT3 -> FUNCT1b.
+ */
+#define INSN16_FUNCT3(insn)	(((insn) && 0xe000) >> 13)
+#define INSN16_FUNCT2a(insn)	(((insn) && 0x0c00) >> 10)
+#define INSN16_FUNCT1b(insn)	(((insn) && 0x1000) >> 12)
+#define INSN16_FUNCT2b(insn)	(((insn) && 0x0060) >> 5)
+#define INSN16_FUNCT3c(insn)	\
+	((INSN16_FUNCT1b(insn) << 2) | INSN16_FUNCT2b(insn))
+
+/* full-size register fields */
+#define INSN16_RS1(insn)	(((insn) & 0x0f80) >> 7)  /* bits 7-11 */
+#define INSN16_RS2(insn)	(((insn) & 0x007c) >> 7)  /* bits 2-6 */
+
+/* small register fields, for registers 8-15 */
+#define INSN16_RS1x(insn)	((((insn) & 0x0380) >> 7) + 8)	/* bits 7-9 */
+#define INSN16_RS2x(insn)	((((insn) & 0x001c) >> 2) + 8)	/* bits 2-4 */
+
+/* CI format immediates, for word/double/quad offsets, zero-extended */
+#define INSN16_IMM_CI_W(insn) \
+	((((insn) & 0b0001000000000000) >> (12-5)) |	\
+	 (((insn) & 0b0000000001110000) >> (4-2)) |	\
+	 (((insn) & 0b0000000000001100) << (6-2)))
+#define INSN16_IMM_CI_D(insn) \
+	((((insn) & 0b0001000000000000) >> (12-5)) |	\
+	 (((insn) & 0b0000000001100000) >> (4-2)) |	\
+	 (((insn) & 0b0000000000011100) << (6-2)))
+#define INSN16_IMM_CI_Q(insn) \
+	((((insn) & 0b0001000000000000) >> (12-5)) |	\
+	 (((insn) & 0b0000000001000000) >> (4-2)) |	\
+	 (((insn) & 0b0000000000111100) << (6-2)))
+
+/* additional CI format immediates for constants, sign-extended */
+#define INSN16_IMM_CI_K_raw(insn) \
+	((((insn) & 0b0001000000000000) >> (12-5)) |	\
+	 (((insn) & 0b0000000001111100) >> (2-0)))
+#define INSN16_IMM_CI_K(insn) SIGNEXT32(INSN16_IMM_CI_K_raw(insn), 6)
+#define INSN16_IMM_CI_K12(insn) SIGNEXT32(INSN16_IMM_CI_K_raw(insn) << 12, 18)
+
+/* and another one, sign-extended */
+#define INSN16_IMM_CI_K4_raw(insn) \
+	((((insn) & 0b0001000000000000) >> (12-9)) |	\
+	 (((insn) & 0b0000000001000000) >> (6-4)) |	\
+	 (((insn) & 0b0000000000100000) << (6-5)) |	\
+	 (((insn) & 0b0000000000011000) << (7-3)) |	\
+	 (((insn) & 0b0000000000000100) << (5-2)))
+#define INSN16_IMM_CI_K4(insn) SIGNEXT32(INSN16_IMM_CI_K4_raw(insn), 10)
+
+
+/* CSS format immediates, for word/double/quad offsets, zero-extended */
+#define INSN16_IMM_CSS_W(insn) \
+	((((insn) & 0b0001111000000000) >> (9-2)) |	\
+	 (((insn) & 0b0000000110000000) >> (7-6)))
+#define INSN16_IMM_CSS_D(insn) \
+	((((insn) & 0b0001110000000000) >> (9-2)) |	\
+	 (((insn) & 0b0000001110000000) >> (7-6)))
+#define INSN16_IMM_CSS_Q(insn) \
+	((((insn) & 0b0001100000000000) >> (9-2)) |	\
+	 (((insn) & 0b0000011110000000) >> (7-6)))
+
+/* CL format immediates, for word/double/quad offsets, zero-extended */
+#define INSN16_IMM_CL_W(insn) \
+	((((insn) & 0b0001110000000000) >> (10-3)) |	\
+	 (((insn) & 0b0000000001000000) >> (6-2)) |	\
+	 (((insn) & 0b0000000000100000) << (6-5)))
+#define INSN16_IMM_CL_D(insn) \
+	((((insn) & 0b0001110000000000) >> (10-3)) |	\
+	 (((insn) & 0b0000000001100000) << (6-5)))
+#define INSN16_IMM_CL_Q(insn) \
+	((((insn) & 0b0001100000000000) >> (11-4)) |	\
+	 (((insn) & 0b0000010000000000) >> (10-8)) |	\
+	 (((insn) & 0b0000000001100000) << (6-5)))
+
+/* CS format immediates are the same as the CL ones */
+#define INSN16_IMM_CS_W(insn) INSN16_IMM_CL_W(insn)
+#define INSN16_IMM_CS_D(insn) INSN16_IMM_CL_D(insn)
+#define INSN16_IMM_CS_Q(insn) INSN16_IMM_CL_Q(insn)
+
+/* CJ format immedate, sign extended */
+#define INSN16_IMM_CJ_raw(insn) \
+	((((insn) & 0b0001000000000000) >> (12-11)) |	\
+	 (((insn) & 0b0000100000000000) >> (11-4)) |	\
+	 (((insn) & 0b0000011000000000) >> (9-8)) |	\
+	 (((insn) & 0b0000000100000000) << (10-8)) |	\
+	 (((insn) & 0b0000000010000000) >> (7-6)) |	\
+	 (((insn) & 0b0000000001000000) << (7-6)) |	\
+	 (((insn) & 0b0000000000111000) >> (3-1)) |	\
+	 (((insn) & 0b0000000000000100) << (5-2)))
+#define INSN16_IMM_CJ(insn) SIGNEXT32(INSN16_IMM_CJ_raw(insn), 12)
+
+/* CB format immediate, sign extended */
+#define INSN16_IMM_CB_raw(insn) \
+	((((insn) & 0b0001000000000000) >> (12-8)) |	\
+	 (((insn) & 0b0000110000000000) >> (10-3)) |	\
+	 (((insn) & 0b0000000001100000) << (6-5)) |	\
+	 (((insn) & 0b0000000000011000) >> (3-1)) |	\
+	 (((insn) & 0b0000000000000100) << (5-2)))
+#define INSN16_IMM_CB(insn) SIGNEXT32(INSN16_IMM_CB_raw(insn), 9)
+
+/* also some CB instructions use the CI_K immediate */
+
+/* CIW format immediate, zero-extended */
+#define INSN16_IMM_CIW(insn) \
+	((((insn) & 0b0001100000000000) >> (11-4)) |	\
+	 (((insn) & 0b0000011110000000) >> (7-6)) |	\
+	 (((insn) & 0b0000000001000000) >> (6-2)) |	\
+	 (((insn) & 0b0000000000100000) >> (5-3)))
+
+/*
+ * Values for the primary opcode field (bits 2-6) in 32-bit instructions
+ */
+
 #define	OPCODE_LOAD		0b00000
 #define	OPCODE_LOADFP		0b00001
 #define	OPCODE_CUSTOM0		0b00010
@@ -131,8 +370,8 @@ union riscv_insn {
 
 #define	OPCODE_MADD		0b10000		// FMADD.[S,D]
 #define	OPCODE_MSUB		0b10001		// FMSUB.[S,D]
-#define	OPCODE_NMSUB		0b10010		// FNMADD.[S,D]
-#define	OPCODE_NMADD		0b10011		// FNMSUB.[S,D]
+#define	OPCODE_NMSUB		0b10010		// FNMSUB.[S,D]
+#define	OPCODE_NMADD		0b10011		// FNMADD.[S,D]
 #define	OPCODE_OPFP		0b10100
 #define	OPCODE_rsvd21		0b10101
 #define	OPCODE_CUSTOM2		0b10110
@@ -147,7 +386,71 @@ union riscv_insn {
 #define	OPCODE_CUSTOM3		0b11110
 #define	OPCODE_X80		0b11111
 
-// LOAD (0x00000)
+/*
+ * Values for the secondary/tertiary opcode field funct7 in 32-bit instructions
+ * for various primary and secondary opcodes.
+ *
+ * Note: for many of these the opcodes shown are the top 5 bits and the
+ * bottom two serve a separate purpose.
+ */
+
+// primary is OP (0b01100, 12)
+#define OP_ARITH		0b0000000
+#define OP_MULDIV		0b0000001
+#define OP_NARITH		0b0100000
+
+// primary is OPFP (0b10100, 20), top 5 bits
+// bottom 2 bits give the size
+#define OPFP_ADD		0b00000
+#define OPFP_SUB		0b00001
+#define OPFP_MUL		0b00010
+#define OPFP_DIV		0b00011
+#define OPFP_SGNJ		0b00100
+#define OPFP_MINMAX		0b00101
+#define OPFP_CVTFF		0b01000
+#define OPFP_SQRT		0b01011
+#define OPFP_CMP		0b10100
+#define OPFP_CVTIF		0b11000
+#define OPFP_CVTFI		0b11010
+#define OPFP_MVFI_CLASS		0b11100
+#define OPFP_MVIF		0b11110
+
+// primary is OPFP (0b10100, 20), bottom two bits (operand size)
+// these bits also give the size for FMADD/FMSUB/FNMSUB/FNMADD
+// and for FCVTFF they appear at the bottom of the rs2 field as well
+#define OPFP_S			0b00
+#define OPFP_D			0b01
+#define OPFP_Q			0b11
+
+// primary is AMO (0b01011, 11), top 5 bits
+// (bottom two bits are ACQUIRE and RELEASE flags respectively)
+// funct3 gives the operand size
+#define AMO_ADD			0b00000
+#define AMO_SWAP		0b00001
+#define AMO_LR			0b00010
+#define AMO_SC			0b00011
+#define AMO_XOR			0b00100
+#define AMO_OR			0b01000
+#define AMO_AND			0b01100
+#define AMO_MIN			0b10000
+#define AMO_MAX			0b10100
+#define AMO_MINU		0b11000
+#define AMO_MAXU		0b11100
+
+// primary is SYSTEM (0b11100, 28) and funct3 is PRIV (0)
+#define PRIV_USER		0b0000000
+#define PRIV_SYSTEM		0b0001000
+#define PRIV_SFENCE_VMA		0b0001001
+#define PRIV_HFENCE_BVMA	0b0010001
+#define PRIV_MACHINE		0b0011000
+#define PRIV_HFENCE_GVMA	0b1010001
+
+/*
+ * Values for the secondary/tertiary opcode field funct3 in 32-bit instructions
+ * for various primary and secondary opcodes.
+ */
+
+// primary is LOAD (0x00000)
 #define LOAD_LB			0b000
 #define LOAD_LH			0b001
 #define LOAD_LW			0b010
@@ -156,101 +459,104 @@ union riscv_insn {
 #define LOAD_LHU		0b101
 #define LOAD_LWU		0b110		// RV64I
 
-// LOADFP (0x00001)
+// primary is LOADFP (0x00001)
 #define LOADFP_FLW		0b010
 #define LOADFP_FLD		0b011
+#define LOADFP_FLQ		0b100
 
-// MISCMEM (0x00010)
+// primary is MISCMEM (0x00011, 3)
 #define MISCMEM_FENCE		0b000
 #define MISCMEM_FENCEI		0b001
 
-// OPIMM (0b00100) and OPIMM32 (0b00110) -- see OP (0b01100)
-
-// AUIPC (0b00101) - no functions
-
-// STORE (0b01000)
+// primary is STORE (0b01000, 8)
 #define STORE_SB		0b000
 #define STORE_SH		0b001
 #define STORE_SW		0b010
 #define STORE_SD		0b011		// RV64I
 
-// STOREFP (0b01001)
+// primary is STOREFP (0b01001, 9)
 #define STOREFP_FSW		0b010
 #define STOREFP_FSD		0b011
+#define STOREFP_FSQ		0b100
 
-// AMO (0b01011)
+// primary is AMO (0b01011, 11), funct7 gives the operation
 #define AMO_W			0b010
-#define AMO_D			0b011
-
-// AMO funct5
-#define AMO_ADD			0b00000
-#define AMO_SWAP		0b00001
-#define AMO_LR			0b00010
-#define AMO_SC			0b00011
-#define AMO_XOR			0b00100
-#define AMO_OR			0b01000
-#define AMO_AND			0b01100
-#define AMO_MIN			0b10000
-#define AMO_MAX			0b10100
-#define AMO_MINU		0b11000
-#define AMO_MAXU		0b11100
-
-// OPIMM (0b00100), OPIMM32 (0b00110), OP (0b01100), OP32 (0b01110)
-#define OP_ADDSUB		0b000
-#define OP_SLL			0b001
-#define OP_SLT			0b010
-#define OP_SLTU			0b011
-#define OP_XOR			0b100
-#define OP_SRX			0b101
-#define OP_OR			0b110
-#define OP_AND			0b111
-
-#define OP_FUNCT6_SRX_SRL	0b000000
-#define OP_FUNCT6_SRX_SRA	0b010000
-
-#define OP_FUNCT7_ADD		0b0000000
-#define OP_FUNCT7_SUB		0b0100000
-
-#define OP_MUL			0b000
-#define OP_MULH			0b001
-#define OP_MULHSU		0b010
-#define OP_MULHU		0b011
-#define OP_DIV			0b100
-#define OP_DIVU			0b101
-#define OP_REM			0b110
-#define OP_REMU			0b111
-
-#define OP_FUNCT7_MULDIV	0b0000001
-
-// LUI (0b01101) - no functions
-
-// MADD (0b10000)
-
-#define MXXX_S			0b00
-//#define MXXX_S			0b01
-
-// MSUB (0b10001)
-// NMADD (0b10010)
-// NMSUB (0b10011)
-// OPFP (0b10100)
+#define AMO_D			0b011		// RV64I
 
-#define OPFP_ADD		0b00000
-#define OPFP_SUB		0b00001
-#define OPFP_MUL		0b00010
-#define OPFP_DIV		0b00011
-#define OPFP_SGNJ		0b00100
-#define OPFP_MINMAX		0b00101
-#define OPFP_SQRT		0b01011
-#define OPFP_CMP		0b10100
-#define OPFP_CVT		0b11000
-#define OPFP_MV			0b11100
-#define OPFP_MV			0b11100
-
-#define SJGN_SGNJ		0b000
-#define SJGN_SGNJN		0b001
-#define SJGN_SGNJX		0b010
+// primary is
+//    OPIMM   (0b00100, 4)
+//    OPIMM32 (0b00110, 6)
+//    OP      (0b01100, 12) when funct7 is ARITH or NARITH
+//    OP32    (0b01110, 14)
+//                                               OP	OP	OP32	OP32
+// given:			   OPIMM OPIMM32 ARITH	NARITH  ARITH	NARITH
+#define OP_ADDSUB	0b000	// addi  addiw   add	sub	addw	subw
+#define OP_SLL		0b001	// (1)   (2)     sll	-	sllw	-
+#define OP_SLT		0b010	// slti  -       slt	-	-	-
+#define OP_SLTU		0b011	// sltiu -       sltu	-	-	-
+#define OP_XOR		0b100	// xori  -       xor	-	-	-
+#define OP_SRX		0b101	// (3)   (4)     srl    sra	srlw	sraw
+#define OP_OR		0b110	// ori   -       or	-	-	-
+#define OP_AND		0b111	// andi  -       and	-	-	-
+//
+// (1) slli when funct7 is ARITH
+// (2) slliw when funct7 is ARITH
+// (3) srli when funct7 is ARITH, srai when funct7 is NARITH
+// (4) srliw when funct7 is ARITH, sraiw when funct7 is NARITH
+//
+// caution: on RV64, the immediate field of slli/srli/srai bleeds into
+// funct7, so you have to actually only test the upper 6 bits of funct7.
+// (and if RV128 ever actually happens, the upper 5 bits; conveniently
+// that aligns with other uses of those 5 bits)
+//
+
+// primary is
+//    OP      (0b01100, 12) when funct7 is MULDIV
+//    OP32    (0b01110, 14) when funct7 is MULDIV
+//
+// given:				   OP      OP32
+#define OP_MUL			0b000	// mul     mulw
+#define OP_MULH			0b001	// mulh    -
+#define OP_MULHSU		0b010	// mulhsu  -
+#define OP_MULHU		0b011   // mulhu   -
+#define OP_DIV			0b100	// div     divw
+#define OP_DIVU			0b101	// divu    divuw
+#define OP_REM			0b110	// rem     remw
+#define OP_REMU			0b111	// remu    remuw
+
+// primary is
+//    MADD    (0b10000, 16)
+//    MSUB    (0b10001, 17)
+//    NMADD   (0b10010, 18)
+//    NMSUB   (0b10011, 19)
+//    OPFP    (0b10100, 20) when funct7 is
+//                ADD SUB MULDIV SQRT CVTFF CVTIF CVTFI
+#define ROUND_RNE		0b000
+#define ROUND_RTZ		0b001
+#define ROUND_RDN		0b010
+#define ROUND_RUP		0b011
+#define ROUND_RMM		0b100
+#define ROUND_DYN		0b111
+
+// primary is OPFP (0b10100, 20) and funct7 is FSGNJ (0b00100)
+#define SGN_SGNJ		0b000
+#define SGN_SGNJN		0b001
+#define SGN_SGNJX		0b010
+
+// primary is OPFP (0b10100, 20) and funct7 is MINMAX (0b00101)
+#define MINMAX_MIN		0b000
+#define MINMAX_MAX		0b001
+
+// primary is OPFP (0b10100, 20) and funct7 is CMP (0b10100)
+#define CMP_LE			0b000
+#define CMP_LT			0b001
+#define CMP_EQ			0b010
+
+// primary is OPFP (0b10100, 20) and funct7 is MVFI_CLASS (0b11110)
+#define MVFI_CLASS_MVFI		0b000
+#define MVFI_CLASS_CLASS	0b001
 
-// BRANCH (0b11000)
+// primary is BRANCH (0b11000, 24)
 #define BRANCH_BEQ		0b000
 #define BRANCH_BNE		0b001
 #define BRANCH_BLT		0b100
@@ -258,20 +564,206 @@ union riscv_insn {
 #define BRANCH_BLTU		0b110
 #define BRANCH_BGEU		0b111
 
-// JALR (0b11001) - no functions
-// JAL (0b11011) - no functions
-// SYSTEM (0b11100)
-
-#define SYSTEM_SFUNC		0b000
-#define	SYSTEM_RDREG		0b010
-
-#define SFUNC_RS_SCALL		0b00000
-#define SFUNC_RS_SBREAK		0b00001
-
-#define RDREG_LO		0b1100000
-#define RDREG_HI		0b1100100
-#define RDREG_RS_CYCLE		0b00000
-#define RDREG_RS_TIME		0b00001
-#define RDREG_RS_INSTRET	0b00010
+// primary is SYSTEM (0b11100, 28)
+#define SYSTEM_PRIV		0b000
+#define SYSTEM_CSRRW		0b001
+#define SYSTEM_CSRRS		0b010
+#define SYSTEM_CSRRC		0b011
+#define SYSTEM_CSRRWI		0b101
+#define SYSTEM_CSRRSI		0b110
+#define SYSTEM_CSRRCI		0b111
+
+// the funct3 field is not used for
+//    AUIPC   (0b00101, 5)
+//    LUI     (0b01101, 13)
+//    JALR    (0b11001, 25) (or rather, it's always 0)
+//    JAL     (0b11011, 27)
+
+// and for these there are no standard instructions defined anyhow
+//    CUSTOM0 (0b00010, 2)
+//    CUSTOM1 (0b01010, 10)
+//    CUSTOM2 (0b10110, 22)
+//    CUSTOM3 (0b11110, 30)
+//    rsvd21  (0b10101, 21)
+//    rsvd29  (0b11101, 29)
+//    X48a    (0b00111, 7)
+//    X64     (0b01111, 15)
+//    X48b    (0b10111, 23)
+//    X80     (0b11111, 31)
+
+/*
+ * quaternary opcodes in rs2 field of 32-bit instructions
+ */
+
+// primary is SYSTEM (0b11100, 28)
+// funct3 is PRIV (0)
+// funct7 is USER (0)
+#define USER_ECALL		0b00000
+#define USER_EBREAK		0b00001
+#define USER_URET		0b00010
+
+// primary is SYSTEM (0b11100, 28)
+// funct3 is PRIV (0)
+// funct7 is SYSTEM (0b0001000, 16)
+#define SYSTEM_SRET		0b00010
+#define SYSTEM_WFI		0b00101
+
+// primary is SYSTEM (0b11100, 28)
+// funct3 is PRIV (0)
+// funct7 is MACHINE (0b0011000, 48)
+#define MACHINE_MRET		0b00010
+
+// primary is OPFP (0b10100, 20)
+// funct7 is CVTFI or CVTIF
+#define CVT_W			0b00000
+#define CVT_WU			0b00001
+#define CVT_L			0b00010
+#define CVT_LU			0b00011
+
+/*
+ * code bits for fence instruction
+ */
+
+#define FENCE_INPUT	0b1000
+#define FENCE_OUTPUT	0b0100
+#define FENCE_READ	0b0010
+#define FENCE_WRITE	0b0001
+
+#define FENCE_FM_NORMAL	0b0000
+#define FENCE_FM_TSO	0b1000
+
+/*
+ * AMO aq/rl bits in funct7
+ */
+#define AMO_AQ		0b0000010
+#define AMO_RL		0b0000001
+
+/*
+ * Opcode values for 16-bit instructions.
+ */
+
+#define OPCODE16_Q0	0b00	/* quadrant 0 */
+#define OPCODE16_Q1	0b01	/* quadrant 1 */
+#define OPCODE16_Q2	0b11	/* quadrant 2 */
+
+/* quadrant 0 */
+#define Q0_ADDI4SPN	0b000
+#define Q0_FLD_LQ	0b001	/* RV32/RV64 FLD, RV128 LQ */
+#define Q0_LW		0b010
+#define Q0_FLW_LD	0b011	/* RV32 FLW, RV64/RV128 LD */
+#define Q0_RESERVED	0b100
+#define Q0_FSD_SQ	0b101	/* RV32/RV64 FSD, RV128 SQ */
+#define Q0_SW		0b110
+#define Q0_FSW_SD	0b111	/* RV32 FSW, RV64/RV128 SD */
+
+/* quadrant 1 */
+#define Q1_NOP_ADDI	0b000	/* NOP when rs1/rd == 0, ADDI otherwise */
+#define Q1_JAL_ADDIW	0b001	/* RV32 JAL, RV64/RV128 ADDIW */
+#define Q1_LI		0b010
+#define Q1_ADDI16SP_LUI	0b011	/* ADDI16SP when rs1/rd == sp, else LUI */
+#define Q1_MISC		0b100
+#define Q1_J		0b101
+#define Q1_BEQZ		0b110
+#define Q1_BNEZ		0b111
+
+/* funct2a values for Q1_MISC */
+#define Q1MISC_SRLI	0b00
+#define Q1MISC_SRAI	0b01
+#define Q1MISC_ANDI	0b10
+#define Q1MISC_MORE	0b11
+
+/* funct3b values for Q1MISC_MORE */
+#define Q1MORE_SUB	0b000
+#define Q1MORE_XOR	0b001
+#define Q1MORE_OR	0b010
+#define Q1MORE_AND	0b011
+#define Q1MORE_SUBW	0b100	/* RV64/RV128 */
+#define Q1MORE_ADDW	0b101	/* RV64/RV128 */
+
+/* quadrant 2 */
+#define Q2_SLLI		0b000
+#define Q2_FLDSP_LQSP	0b001	/* RV32/RV64 FLDSP, RV128 LQSP */
+#define Q2_LWSP		0b010
+#define Q2_FLWSP_LDSP	0b011	/* RV32 FLWSP, RV64/RV128 LDSP */
+#define Q2_MISC		0b100
+#define Q2_FSDSP_SQSP	0b101	/* RV32/RV64 FSDSP, RV128 SQSP */
+#define Q2_SWSP		0b110
+#define Q2_FSWSP_SDSP	0b111	/* RV32/RV64 FSWSP, RV128 SDSP */
+
+/* funct1b values for Q2_MISC */
+#define Q2MISC_JR_MV	0b0	/* JR when rs2 == 0, MV otherwise */
+#define Q2MISC_EBREAK_JALR_ADD	0b1 /* EBREAK rs1==0; JALR rs2==0; else ADD */
+
+/*
+ * CSR numbers
+ */
+
+// fpu
+#define CSR_FFLAGS		0x001
+#define CSR_FRM			0x002
+#define CSR_FCSR		0x003
+
+// perfcounters
+#define CSR_CYCLE		0xc00
+#define CSR_TIME		0xc01
+#define CSR_INSTRET		0xc02
+#define CSR_HPMCOUNTER(n)	(0xc00+(n))	// n in 3..31
+#define CSR_CYCLEH		0xc80	// RV32I
+#define CSR_TIMEH		0xc81	// RV32I
+#define CSR_INSTRETH		0xc82	// RV32I
+#define CSR_HPMCOUNTERH(n)	(0xc80+(n))	// n in 3..31
+
+// system
+#define CSR_SSTATUS		0x100
+//	CSR_SEDELEG		0x102
+//	CSR_SIDELEG		0x103
+#define CSR_SIE			0x104
+#define CSR_STVEC		0x105
+#define CSR_SCOUNTEREN		0x106
+#define CSR_SSCRATCH		0x140
+#define CSR_SEPC		0x141
+#define CSR_SCAUSE		0x142
+#define CSR_STVAL		0x143
+#define CSR_SIP			0x144
+#define CSR_SATP		0x180
+
+// machine
+#define CSR_MVENDORID		0xf11
+#define CSR_MARCHID		0xf12
+#define CSR_MIMPID		0xf13
+#define CSR_MHARTID		0xf14
+#define CSR_MSTATUS		0x300
+#define CSR_MISA		0x301
+#define CSR_MEDELEG		0x302
+#define CSR_MIDELEG		0x303
+#define CSR_MIE			0x304
+#define CSR_MTVEC		0x305
+#define CSR_MCOUNTEREN		0x306
+#define CSR_MSCRATCH		0x340
+#define CSR_MEPC		0x341
+#define CSR_MCAUSE		0x342
+#define CSR_MTVAL		0x343
+#define CSR_MIP			0x344
+#define CSR_PMPCFG(n)		(0x3a0 + (n))	// n in 0..3
+#define CSR_PMPADDR(n)		(0x3b0 + (n))	// n in 0..15
+#define CSR_MCYCLE		0xb00
+#define CSR_MINSTRET		0xb02
+#define CSR_MHPMCOUNTER(n)	(0xb00+(n))	// n in 3..31
+#define CSR_MCYCLEH		0xb80	// RV32I
+#define CSR_MINSTRETH		0xb82	// RV32I
+#define CSR_MHPMCOUNTERH(n)	(0xb80+(n))	// n in 3..31
+#define CSR_MCOUNTINHIBIT	0x320
+#define CSR_MHPMEVENT(n)	(0x320+(n))	// n in 3..31
+
+// debug
+#define CSR_TSELECT		0x7a0
+#define CSR_TDATA1		0x7a1
+#define CSR_TDATA2		0x7a2
+#define CSR_TDATA3		0x7a3
+#define CSR_DCSR		0x7b0
+#define CSR_DPC			0x7b1
+#define CSR_DSCRATCH0		0x7b2
+#define CSR_DSCRATCH1		0x7b3
+
 
 #endif /* _RISCV_INSN_H_ */

Index: src/sys/arch/riscv/riscv/db_disasm.c
diff -u src/sys/arch/riscv/riscv/db_disasm.c:1.2 src/sys/arch/riscv/riscv/db_disasm.c:1.3
--- src/sys/arch/riscv/riscv/db_disasm.c:1.2	Wed Nov  4 07:09:46 2020
+++ src/sys/arch/riscv/riscv/db_disasm.c	Wed Apr 14 06:32:20 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: db_disasm.c,v 1.2 2020/11/04 07:09:46 skrll Exp $	*/
+/*	$NetBSD: db_disasm.c,v 1.3 2021/04/14 06:32:20 dholland Exp $	*/
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 
-__RCSID("$NetBSD: db_disasm.c,v 1.2 2020/11/04 07:09:46 skrll Exp $");
+__RCSID("$NetBSD: db_disasm.c,v 1.3 2021/04/14 06:32:20 dholland Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -39,20 +39,1259 @@ __RCSID("$NetBSD: db_disasm.c,v 1.2 2020
 #include <riscv/db_machdep.h>
 #include <riscv/insn.h>
 
+#include <ddb/db_user.h>
 #include <ddb/db_interface.h>
+#include <ddb/db_output.h>
+#include <ddb/db_extern.h>
+#include <ddb/db_sym.h>
+
+////////////////////////////////////////////////////////////
+// registers
+
+static const char *riscv_registers[32] = {
+	"zero", "ra", "sp", "gp", "tp",
+	"t0", "t1", "t2",
+	"s0", "s1",
+	"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
+	"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
+	"t3", "t4", "t5", "t6"
+};
+
+/* XXX this should be in MI ddb */
+static void
+db_print_addr(db_addr_t loc)
+{
+	db_expr_t diff;
+	db_sym_t sym;
+	const char *symname;
+
+	diff = INT_MAX;
+	symname = NULL;
+	sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
+	db_symbol_values(sym, &symname, 0);
+
+	if (symname) {
+		if (diff == 0)
+			db_printf("%s", symname);
+		else
+			db_printf("<%s+%"DDB_EXPR_FMT"x>", symname, diff);
+		db_printf("\t[addr:%#"PRIxVADDR"]", loc);
+	} else {
+		db_printf("%#"PRIxVADDR, loc);
+	}
+}
+
+////////////////////////////////////////////////////////////
+// 16-bit instructions
+
+/*
+ * This is too tightly wedged in to make it worth trying to make it
+ * table-driven. But I'm going to hack it up to use a 1-level switch.
+ *
+ * Note that quite a few instructions depend on the architecture word
+ * size. I've used the #defines for that to conditionalize it, on the
+ * grounds that ddb is disassembling itself so the build machine
+ * version is the target machine version. This is not true for crash
+ * necessarily but I don't think 
+ */
+
+#define COMBINE(op, q) (((op) << 2) | (q))
+
+#define IN_Q0(op) COMBINE(op, OPCODE16_Q0)
+#define IN_Q1(op) COMBINE(op, OPCODE16_Q1)
+#define IN_Q2(op) COMBINE(op, OPCODE16_Q2)
+
+static
+int
+db_disasm_16(db_addr_t loc, uint32_t insn, bool altfmt)
+{
+	/* note: insn needs to be uint32_t for immediate computations */
+
+	uint32_t imm;
+	unsigned rd, rs1, rs2;
+
+	switch (COMBINE(INSN16_FUNCT3(insn), INSN16_QUADRANT(insn))) {
+	    case IN_Q0(Q0_ADDI4SPN):
+		rd = INSN16_RS2x(insn);
+		imm = INSN16_IMM_CIW(insn);
+		db_printf("c.addi4spn %s, 0x%x\n", riscv_registers[rd], imm);
+		break;
+	    case IN_Q0(Q0_FLD_LQ):
+		rs1 = INSN16_RS1x(insn);
+		rd = INSN16_RS2x(insn);
+#if __riscv_xlen < 128
+		imm = INSN16_IMM_CL_D(insn);
+		db_printf("c.fld f%d, %d(%s)\n", rd, (int32_t)imm, 
+			  riscv_registers[rs1]);
+#else
+		imm = INSN16_IMM_CL_Q(insn);
+		db_printf("c.lq %s, %d(%s)\n", riscv_registers[rd],
+			  (int32_t)imm, riscv_registers[rs1]);
+#endif
+		break;
+	    case IN_Q0(Q0_LW):
+		rs1 = INSN16_RS1x(insn);
+		rd = INSN16_RS2x(insn);
+		imm = INSN16_IMM_CL_W(insn);
+		db_printf("c.lw %s, %d(%s)\n", riscv_registers[rd],
+			  (int32_t)imm, riscv_registers[rs1]);
+		break;
+	    case IN_Q0(Q0_FLW_LD):
+		rs1 = INSN16_RS1x(insn);
+		rd = INSN16_RS2x(insn);
+#if __riscv_xlen == 32
+		imm = INSN16_IMM_CL_W(insn);
+		db_printf("c.flw f%d, %d(%s)\n", rd, (int32_t)imm, 
+			  riscv_registers[rs1]);
+#else
+		imm = INSN16_IMM_CL_D(insn);
+		db_printf("c.ld %s, %d(%s)\n", riscv_registers[rd],
+			  (int32_t)imm, riscv_registers[rs1]);
+#endif
+		break;
+	    case IN_Q0(Q0_FSD_SQ):
+		rs1 = INSN16_RS1x(insn);
+		rs2 = INSN16_RS2x(insn);
+#if __riscv_xlen < 128
+		imm = INSN16_IMM_CS_D(insn);
+		db_printf("c.fsd f%d, %d(%s)\n", rs2, (int32_t)imm, 
+			  riscv_registers[rs1]);
+#else
+		imm = INSN16_IMM_CS_Q(insn);
+		db_printf("c.sq %s, %d(%s)\n", riscv_registers[rs2],
+			  (int32_t)imm, riscv_registers[rs1]);
+#endif
+		break;
+	    case IN_Q0(Q0_SW):
+		rs1 = INSN16_RS1x(insn);
+		rs2 = INSN16_RS2x(insn);
+		imm = INSN16_IMM_CS_W(insn);
+		db_printf("c.sw %s, %d(%s)\n", riscv_registers[rs2],
+			  (int32_t)imm, riscv_registers[rs1]);
+		break;
+	    case IN_Q0(Q0_FSW_SD):
+		rs1 = INSN16_RS1x(insn);
+		rs2 = INSN16_RS2x(insn);
+#if __riscv_xlen == 32
+		imm = INSN16_IMM_CS_W(insn);
+		db_printf("c.fsw f%d, %d(%s)\n", rs2, (int32_t)imm, 
+			  riscv_registers[rs1]);
+#else
+		imm = INSN16_IMM_CS_D(insn);
+		db_printf("c.sd %s, %d(%s)\n", riscv_registers[rs2],
+			  (int32_t)imm, riscv_registers[rs1]);
+#endif
+		break;
+	    case IN_Q1(Q1_NOP_ADDI):
+		rd = INSN16_RS1x(insn);
+		imm = INSN16_IMM_CI_K(insn);
+		if (rd == 0 && imm == 0) {
+			db_printf("c.nop %s, %d(%s)\n", riscv_registers[rs2],
+				  (int32_t)imm, riscv_registers[rs1]);
+		}
+		else if (rd == 0 && imm != 0) {
+			/* undefined hint */
+			return EINVAL;
+		}
+		else if (rd != 0 && imm == 0) {
+			/* undefined hint */
+			return EINVAL;
+		}
+		else {
+			db_printf("c.addi %s, %s, 0x%x\n", riscv_registers[rd],
+				  riscv_registers[rd], imm);
+		}
+		break;
+	    case IN_Q1(Q1_JAL_ADDIW):
+#if __riscv_xlen == 32
+		imm = INSN16_IMM_CJ(insn);
+		db_printf("c.jal ");
+		db_print_addr(loc + (int32_t)imm);
+		db_printf("\n");
+#else
+		db_printf("c.addiw %s, %s, 0x%x\n", riscv_registers[rd],
+			  riscv_registers[rd], imm);
+#endif
+		break;
+	    case IN_Q1(Q1_LI):
+		rd = INSN16_RS1(insn);
+		imm = INSN16_IMM_CI_K(insn);
+		db_printf("c.li %s, 0x%x\n", riscv_registers[rd], imm);
+		break;
+	    case IN_Q1(Q1_ADDI16SP_LUI):
+		rd = INSN16_RS1(insn);
+		if (rd == 2/*sp*/) {
+			imm = INSN16_IMM_CI_K4(insn);
+			db_printf("c.add16sp sp, 0x%x\n", imm);
+		}
+		else {
+			imm = INSN16_IMM_CI_K12(insn);
+			db_printf("c.lui %s, 0x%x\n", riscv_registers[rd],
+				  imm);
+		}
+		break;
+	    case IN_Q1(Q1_MISC):
+		imm = INSN16_IMM_CI_K(insn);
+		rd = INSN16_RS1x(insn);
+		switch (INSN16_FUNCT2a(insn)) {
+		    case Q1MISC_SRLI:
+		    case Q1MISC_SRAI:
+#if __riscv_xlen == 32
+			if (imm > 31) {
+				/* reserved */
+				return EINVAL;
+			}
+#elif __riscv_xlen == 64
+			/* drop the sign-extension */
+			imm &= 63;
+#elif __riscv_xlen == 128
+			if (imm == 0) {
+				imm = 64;
+			}
+			else {
+				imm &= 127;
+			}
+#endif
+			db_printf("c.%s %s, %d\n",
+				  INSN16_FUNCT2a(insn) == Q1MISC_SRLI ?
+					  "srli" : "srai",
+				  riscv_registers[rd], imm);
+			break;
+		    case Q1MISC_ANDI:
+			db_printf("c.andi %s, %d\n", riscv_registers[rd], imm);
+			break;
+		    case Q1MISC_MORE:
+			rs2 = INSN16_RS2x(insn);
+			switch (INSN16_FUNCT3c(insn)) {
+			    case Q1MORE_SUB:
+				db_printf("c.sub");
+				break;
+			    case Q1MORE_XOR:
+				db_printf("c.xor");
+				break;
+			    case Q1MORE_OR:
+				db_printf("c.or");
+				break;
+			    case Q1MORE_AND:
+				db_printf("c.and");
+				break;
+			    case Q1MORE_SUBW:
+				db_printf("c.subw");
+				break;
+			    case Q1MORE_ADDW:
+				db_printf("c.addw");
+				break;
+			    default:
+				return EINVAL;
+			}
+			db_printf(" %s, %s, %s\n", riscv_registers[rd],
+				  riscv_registers[rd], riscv_registers[rs2]);
+			break;
+		}
+		break;
+	    case IN_Q1(Q1_J):
+		imm = INSN16_IMM_CJ(insn);
+		db_printf("c.j ");
+		db_print_addr(loc + (int32_t)imm);
+		db_printf("\n");
+		break;
+	    case IN_Q1(Q1_BEQZ):
+		rs1 = INSN16_RS1x(insn);
+		imm = INSN16_IMM_CB(insn);
+		db_printf("c.beqz %s, ", riscv_registers[rs1]);
+		db_print_addr(loc + (int32_t)imm);
+		db_printf("\n");
+		break;
+	    case IN_Q1(Q1_BNEZ):
+		rs1 = INSN16_RS1x(insn);
+		imm = INSN16_IMM_CB(insn);
+		db_printf("c.bnez %s, ", riscv_registers[rs1]);
+		db_print_addr(loc + (int32_t)imm);
+		db_printf("\n");
+		break;
+	    case IN_Q2(Q2_SLLI):
+		rd = INSN16_RS1(insn);
+		imm = INSN16_IMM_CI_K(insn);
+#if __riscv_xlen == 32
+		if (imm > 31) {
+				/* reserved */
+			return EINVAL;
+		}
+#elif __riscv_xlen == 64
+		/* drop the sign-extension */
+		imm &= 63;
+#elif __riscv_xlen == 128
+		if (imm == 0) {
+			imm = 64;
+		}
+		else {
+			/*
+			 * XXX the manual is not clear on
+			 * whether this is like c.srli/c.srai
+			 * or truncates to 6 bits. I'm assuming
+			 * the former.
+			 */
+			imm &= 127;
+		}
+#endif
+		db_printf("c.slli %s, %d\n", riscv_registers[rd], imm);
+		break;
+	    case IN_Q2(Q2_FLDSP_LQSP):
+		rd = INSN16_RS1(insn);
+#if __riscv_xlen < 128
+		imm = INSN16_IMM_CI_D(insn);
+		db_printf("c.fldsp f%d, %d(sp)\n", rd, imm);
+#else
+		if (rd == 0) {
+			/* reserved */
+			return EINVAL;
+		}
+		imm = INSN16_IMM_CI_Q(insn);
+		db_printf("c.lqsp %s, %d(sp)\n", riscv_registers[rd], imm);
+#endif
+		break;
+	    case IN_Q2(Q2_LWSP):
+		rd = INSN16_RS1(insn);
+		if (rd == 0) {
+			/* reserved */
+			return EINVAL;
+		}
+		imm = INSN16_IMM_CI_W(insn);
+		db_printf("c.lwsp %s, %d(sp)\n", riscv_registers[rd], imm);
+		break;
+	    case IN_Q2(Q2_FLWSP_LDSP):
+		rd = INSN16_RS1(insn);
+#if __riscv_xlen == 32
+		imm = INSN16_IMM_CI_W(insn);
+		db_printf("c.flwsp f%d, %d(sp)\n", rd, imm);
+#else
+		if (rd == 0) {
+			/* reserved */
+			return EINVAL;
+		}
+		imm = INSN16_IMM_CI_D(insn);
+		db_printf("c.ldsp %s, %d(sp)\n", riscv_registers[rd], imm);
+#endif
+		break;
+	    case IN_Q2(Q2_MISC):
+		switch (INSN16_FUNCT1b(insn)) {
+		    case Q2MISC_JR_MV:
+			rs1 = INSN16_RS1(insn);
+			rs2 = INSN16_RS2(insn);
+			if (rs1 == 0) {
+				return EINVAL;
+			}
+			else if (rs2 == 0) {
+				db_printf("c.jr %s\n", riscv_registers[rs1]);
+			}
+			else {
+				db_printf("c.mv %s, %s\n",
+					  riscv_registers[rs1],
+					  riscv_registers[rs2]);
+			}
+			break;
+		    case Q2MISC_EBREAK_JALR_ADD:
+			if (rs1 == 0 && rs2 == 0) {
+				db_printf("c.ebreak\n");
+			}
+			else if (rs2 == 0) {
+				db_printf("c.jalr %s\n", riscv_registers[rs1]);
+			}
+			else if (rs1 == 0) {
+				return EINVAL;
+			}
+			else {
+				db_printf("c.add %s, %s, %s\n",
+					  riscv_registers[rs1],
+					  riscv_registers[rs1],
+					  riscv_registers[rs2]);
+			}
+		    break;
+		}
+		break;
+	    case IN_Q2(Q2_FSDSP_SQSP):
+		rs2 = INSN16_RS2(insn);
+#if __riscv_xlen < 128
+		imm = INSN16_IMM_CSS_D(insn);
+		db_printf("c.fsdsp f%d, %d(sp)\n", rs2, imm);
+#else
+		imm = INSN16_IMM_CSS_Q(insn);
+		db_printf("c.sqsp %s, %d(sp)\n", riscv_registers[rs2], imm);
+#endif
+		break;
+	    case IN_Q2(Q2_SWSP):
+		rs2 = INSN16_RS2(insn);
+		imm = INSN16_IMM_CSS_W(insn);
+		db_printf("c.swsp %s, %d(sp)\n", riscv_registers[rs2], imm);
+		break;
+	    case IN_Q2(Q2_FSWSP_SDSP):
+		rs2 = INSN16_RS2(insn);
+#if __riscv_xlen == 32
+		imm = INSN16_IMM_CSS_W(insn);
+		db_printf("c.fswsp f%d, %d(sp)\n", rs2, imm);
+#else
+		imm = INSN16_IMM_CSS_D(insn);
+		db_printf("c.sdsp %s, %d(sp)\n", riscv_registers[rs2], imm);
+#endif
+		break;
+	    default:
+		/* 0b11 marks 32-bit instructions and shouldn't come here */
+		KASSERT(INSN16_QUADRANT(insn) != 0b11);
+		return EINVAL;
+	}
+	return 0;
+}
+
+////////////////////////////////////////////////////////////
+// 32-bit instructions
+
+/* match flags */
+#define CHECK_F3	0x0001		/* check funct3 field */
+#define CHECK_F7	0x0002		/* check funct7 field */
+#define CHECK_F5	0x0004		/* check top of funct7 field only */
+#define CHECK_RS2	0x0008		/* check rs2 as quaternary opcode */
+#define SHIFT32		0x0010		/* 32-bit immediate shift */
+#define SHIFT64		0x0020		/* 64-bit immediate shift */
+#define RD_0		0x0040		/* rd field should be 0 */
+#define RS1_0		0x0080		/* rs1 field should be 0 */
+#define RS2_0		0x0100		/* rs2 field should be 0 */
+#define IMM_0		0x0200		/* immediate value should be 0 */
+#define F3AMO		0x0400		/* expect amo size in funct3 */
+#define F3ROUND		0x0800		/* expect fp rounding mode in funct3 */
+#define F7SIZE		0x1000		/* expect fp size in funct7:0-1 */
+#define RS2_FSIZE	0x2000		/* expect fp size in rs2 */
+#define FENCEFM		0x4000		/* fence mode in top 4 bits of imm */
+/* do not add more without increasing the field size below */
+
+#ifdef _LP64 /* disassembling ourself so can use our build flags... */
+#define SHIFTNATIVE SHIFT64
+#else
+#define SHIFTNATIVE SHIFT32
+#endif
+
+/* print flags */
+#define MEMORYIMM	0x001		/* print immediate as 0(reg) */
+#define FENCEIMM	0x002		/* print fence instruction codes */
+#define DECIMM		0x004  		/* print immediate in decimal */
+#define BRANCHIMM	0x008		/* print immediate as branch target */
+#define CSRIMM		0x010		/* immediate is csr number */
+#define CSRIIMM		0x020		/* ... also an immediate in rs1 */
+#define AMOAQRL		0x040		/* print acquire/release bits of amo */
+#define RS2SIZE_FIRST	0x080		/* print rs2 size before funct7 size */
+#define RD_FREG		0x100		/* rd is a fpu reg */
+#define RS1_FREG	0x200		/* rs1 is a fpu reg */
+#define RS2_FREG	0x400		/* rs2 is a fpu reg */
+/* do not add more without increasing the field size below */
+
+#define ALL_FREG (RD_FREG | RS1_FREG | RS2_FREG)
+
+/* entries for matching within a major opcode */
+struct riscv_disasm_insn {
+	const char *name;
+	unsigned int matchflags: 15,
+		funct3: 3,
+		funct7: 7,
+		rs2: 5;
+	unsigned int printflags: 11;
+};
+
+/* format codes */
+#define FMT_R	0
+#define FMT_R4	1
+#define FMT_I	2
+#define FMT_In	3
+#define FMT_S	4
+#define FMT_B	5
+#define FMT_U	6
+#define FMT_J	7
+#define FMT_UNKNOWN 8
+#define FMT_ASSERT 9
+
+/* top-level opcode dispatch */
+struct riscv_disasm32_entry {
+	uint8_t fmt;
+	union {
+		// R4, In, U, J
+		const char *name;
+		// R, I, S, B
+	        struct {
+			const struct riscv_disasm_insn *v;
+			unsigned n;
+		} entries;
+	} u;
+};
+
+#define INSN_F3(n, f3, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F3 | moretests,	\
+		.funct3 = f3, .funct7 = 0, .rs2 = 0,	\
+		.printflags = pflags,			\
+	}
+
+#define INSN_F5(n, f5, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | CHECK_F5 | moretests,	\
+		.funct3 = 0, .funct7 = f5 << 2,	.rs2 = 0, \
+		.printflags = pflags,			\
+	}
+
+#define INSN_F53(n, f5, f3, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | CHECK_F5 | CHECK_F3 | moretests, \
+		.funct3 = f3, .funct7 = f5 << 2, .rs2 = 0, \
+		.printflags = pflags,			\
+	}
+
+#define INSN_F7(n, f7, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | moretests,	\
+		.funct3 = 0, .funct7 = f7, .rs2 = 0,	\
+		.printflags = pflags,			\
+	}
+
+#define INSN_F73(n, f7, f3, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | CHECK_F3 | moretests,	\
+		.funct3 = f3, .funct7 = f7, .rs2 = 0,	\
+		.printflags = pflags,			\
+	}
+
+#define INSN_F37(n, f3, f7, moretests, pflags) \
+	INSN_F73(n, f7, f3, moretests, pflags)
+
+#define INSN_USER(n, rs2val, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | CHECK_F3 | CHECK_RS2 | moretests, \
+		.funct3 = SYSTEM_PRIV, .funct7 = PRIV_USER, \
+		.rs2 = rs2val,				\
+		.printflags = pflags,			\
+	}
+
+#define INSN_SYSTEM(n, rs2val, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | CHECK_F3 | CHECK_RS2 | moretests, \
+		.funct3 = SYSTEM_PRIV, .funct7 = PRIV_SYSTEM, \
+		.rs2 = rs2val,				\
+		.printflags = pflags,			\
+	}
+
+#define INSN_MACHINE(n, rs2val, moretests, pflags) \
+	{						\
+		.name = n,				\
+		.matchflags = CHECK_F7 | CHECK_F3 | CHECK_RS2 | moretests, \
+		.funct3 = SYSTEM_PRIV, .funct7 = PRIV_MACHINE, \
+		.rs2 = rs2val,				\
+		.printflags = pflags,			\
+	}
+
+static const struct riscv_disasm_insn riscv_disasm_miscmem[] = {
+	INSN_F3("fence",   MISCMEM_FENCE,  RD_0 | RS1_0 | FENCEFM, FENCEIMM),
+	INSN_F3("fence.i", MISCMEM_FENCEI, RD_0 | RS1_0 | IMM_0, 0),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_load[] = {
+	INSN_F3("lb", LOAD_LB, 0, MEMORYIMM),
+	INSN_F3("lh", LOAD_LH, 0, MEMORYIMM),
+	INSN_F3("lw", LOAD_LW, 0, MEMORYIMM),
+	INSN_F3("ld", LOAD_LD, 0, MEMORYIMM),
+	INSN_F3("lbu", LOAD_LBU, 0, MEMORYIMM),
+	INSN_F3("lhu", LOAD_LHU, 0, MEMORYIMM),
+	INSN_F3("lwu", LOAD_LWU, 0, MEMORYIMM),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_loadfp[] = {
+	INSN_F3("flw", LOADFP_FLW, 0, MEMORYIMM | RD_FREG),
+	INSN_F3("fld", LOADFP_FLD, 0, MEMORYIMM | RD_FREG),
+	INSN_F3("flq", LOADFP_FLQ, 0, MEMORYIMM | RD_FREG),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_opimm[] = {
+	INSN_F3("addi", OP_ADDSUB, 0, 0),
+	INSN_F3("slti", OP_SLT, 0, 0),
+	INSN_F3("sltiu", OP_SLTU, 0, 0),
+	INSN_F3("xori", OP_XOR, 0, 0),
+	INSN_F3("ori", OP_OR, 0, 0),
+	INSN_F3("andi", OP_AND, 0, 0),
+	INSN_F37("slli", OP_SLL, OP_ARITH, SHIFTNATIVE, DECIMM),
+	INSN_F37("srli", OP_SRX, OP_ARITH, SHIFTNATIVE, DECIMM),
+	INSN_F37("srai", OP_SRX, OP_NARITH, SHIFTNATIVE, DECIMM),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_opimm32[] = {
+	INSN_F3("addiw", OP_ADDSUB, 0, 0),
+	INSN_F37("slliw", OP_SLL, OP_ARITH, SHIFT32, DECIMM),
+	INSN_F37("srliw", OP_SRX, OP_ARITH, SHIFT32, DECIMM),
+	INSN_F37("sraiw", OP_SRX, OP_NARITH, SHIFT32, DECIMM),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_store[] = {
+	INSN_F3("sb", STORE_SB, 0, 0),
+	INSN_F3("sh", STORE_SH, 0, 0),
+	INSN_F3("sw", STORE_SW, 0, 0),
+	INSN_F3("sd", STORE_SD, 0, 0),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_storefp[] = {
+	INSN_F3("fsw", STOREFP_FSW, 0, RS2_FREG),
+	INSN_F3("fsd", STOREFP_FSD, 0, RS2_FREG),
+	INSN_F3("fsq", STOREFP_FSQ, 0, RS2_FREG),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_branch[] = {
+	INSN_F3("beq", BRANCH_BEQ, 0, BRANCHIMM),
+	INSN_F3("bne", BRANCH_BNE, 0, BRANCHIMM),
+	INSN_F3("blt", BRANCH_BLT, 0, BRANCHIMM),
+	INSN_F3("bge", BRANCH_BGE, 0, BRANCHIMM),
+	INSN_F3("bltu", BRANCH_BLTU, 0, BRANCHIMM),
+	INSN_F3("bgeu", BRANCH_BGEU, 0, BRANCHIMM),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_system[] = {
+	INSN_F3("csrw", SYSTEM_CSRRW, RD_0, CSRIMM),
+	INSN_F3("csrrw", SYSTEM_CSRRW, 0, CSRIMM),
+	INSN_F3("csrr", SYSTEM_CSRRS, RS1_0, CSRIMM),
+	INSN_F3("csrrs", SYSTEM_CSRRS, 0, CSRIMM),
+	INSN_F3("csrrc", SYSTEM_CSRRC, 0, CSRIMM),
+	INSN_F3("csrwi", SYSTEM_CSRRW, RD_0, CSRIIMM),
+	INSN_F3("csrrwi", SYSTEM_CSRRW, 0, CSRIIMM),
+	INSN_F3("csrrsi", SYSTEM_CSRRS, 0, CSRIIMM),
+	INSN_F3("csrrci", SYSTEM_CSRRC, 0, CSRIIMM),
+	INSN_F37("sfence.vma", SYSTEM_PRIV, PRIV_SFENCE_VMA, RD_0, 0),
+	INSN_F37("hfence.bvma", SYSTEM_PRIV, PRIV_HFENCE_BVMA, 0, 0),
+	INSN_F37("hfence.gvma", SYSTEM_PRIV, PRIV_HFENCE_GVMA, 0, 0),
+	INSN_USER("ecall", USER_ECALL, RD_0 | RS1_0, 0),
+	INSN_USER("ebreak", USER_EBREAK, RD_0 | RS1_0, 0),
+	INSN_USER("uret", USER_URET, RD_0 | RS1_0, 0),
+	INSN_SYSTEM("sret", SYSTEM_SRET, RD_0 | RS1_0, 0),
+	INSN_SYSTEM("wfi", SYSTEM_WFI, RD_0 | RS1_0, 0),
+	INSN_MACHINE("mret", MACHINE_MRET, RD_0 | RS1_0, 0),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_amo[] = {
+	INSN_F5("amoadd",  AMO_ADD,  F3AMO, AMOAQRL),
+	INSN_F5("amoswap", AMO_SWAP, F3AMO, AMOAQRL),
+	INSN_F5("lr",      AMO_LR,   F3AMO, AMOAQRL),
+	INSN_F5("sc",      AMO_SC,   F3AMO, AMOAQRL),
+	INSN_F5("amoxor",  AMO_XOR,  F3AMO, AMOAQRL),
+	INSN_F5("amoor",   AMO_OR,   F3AMO, AMOAQRL),
+	INSN_F5("amoand",  AMO_AND,  F3AMO, AMOAQRL),
+	INSN_F5("amomin",  AMO_MIN,  F3AMO, AMOAQRL),
+	INSN_F5("amomax",  AMO_MAX,  F3AMO, AMOAQRL),
+	INSN_F5("amominu", AMO_MINU, F3AMO, AMOAQRL),
+	INSN_F5("amomaxu", AMO_MAXU, F3AMO, AMOAQRL),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_op[] = {
+	INSN_F37("add", OP_ADDSUB, OP_ARITH, 0, 0),
+	INSN_F37("sub", OP_ADDSUB, OP_NARITH, 0, 0),
+	INSN_F37("sll", OP_SLL,    OP_ARITH, 0, 0),
+	INSN_F37("slt", OP_SLT,    OP_ARITH, 0, 0),
+	INSN_F37("sltu", OP_SLTU,  OP_ARITH, 0, 0),
+	INSN_F37("xor", OP_XOR,    OP_ARITH, 0, 0),
+	INSN_F37("srl", OP_SRX,    OP_ARITH, 0, 0),
+	INSN_F37("sra", OP_SRX,    OP_NARITH, 0, 0),
+	INSN_F37("or",  OP_OR,     OP_ARITH, 0, 0),
+	INSN_F37("and", OP_AND,    OP_ARITH, 0, 0),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_op32[] = {
+	INSN_F37("addw", OP_ADDSUB, OP_ARITH, 0, 0),
+	INSN_F37("subw", OP_ADDSUB, OP_NARITH, 0, 0),
+	INSN_F37("sllw", OP_SLL,    OP_ARITH, 0, 0),
+	INSN_F37("srlw", OP_SRX,    OP_ARITH, 0, 0),
+	INSN_F37("sraw", OP_SRX,    OP_NARITH, 0, 0),
+};
+
+static const struct riscv_disasm_insn riscv_disasm_opfp[] = {
+	INSN_F5("fadd", OPFP_ADD, F7SIZE|F3ROUND, ALL_FREG),
+	INSN_F5("fsub", OPFP_SUB, F7SIZE|F3ROUND, ALL_FREG),
+	INSN_F5("fmul", OPFP_MUL, F7SIZE|F3ROUND, ALL_FREG),
+	INSN_F5("fdiv", OPFP_DIV, F7SIZE|F3ROUND, ALL_FREG),
+	INSN_F53("fsgnj", OPFP_SGNJ, SGN_SGNJ, F7SIZE, ALL_FREG),
+	INSN_F53("fsgnjn", OPFP_SGNJ, SGN_SGNJN, F7SIZE, ALL_FREG),
+	INSN_F53("fsgnjx", OPFP_SGNJ, SGN_SGNJX, F7SIZE, ALL_FREG),
+	INSN_F53("fmin", OPFP_MINMAX, MINMAX_MIN, F7SIZE, ALL_FREG),
+	INSN_F53("fmax", OPFP_MINMAX, MINMAX_MAX, F7SIZE, ALL_FREG),
+	INSN_F5("fcvt", OPFP_CVTFF, F7SIZE|F3ROUND|RS2_FSIZE, ALL_FREG),
+	INSN_F5("fsqrt", OPFP_SQRT, F7SIZE|F3ROUND|RS2_0, ALL_FREG),
+	INSN_F53("fle", OPFP_CMP, CMP_LE, F7SIZE, ALL_FREG),
+	INSN_F53("flt", OPFP_CMP, CMP_LT, F7SIZE, ALL_FREG),
+	INSN_F53("feq", OPFP_CMP, CMP_EQ, F7SIZE, ALL_FREG),
+	INSN_F5("fcvt", OPFP_CVTIF, F7SIZE|F3ROUND|RS2_FSIZE,
+		RS2SIZE_FIRST | RD_FREG),
+	INSN_F5("fcvt", OPFP_CVTFI, F7SIZE|F3ROUND|RS2_FSIZE, RS1_FREG),
+	INSN_F53("fclass", OPFP_MVFI_CLASS, MVFI_CLASS_CLASS, F7SIZE|RS2_0,
+		 ALL_FREG),
+	INSN_F73("fmv.x.w", (OPFP_MVFI_CLASS << 2) | OPFP_S, MVFI_CLASS_MVFI,
+		 F7SIZE|RS2_0, RS1_FREG),
+	INSN_F73("fmv.w.x", (OPFP_MVIF << 2) | OPFP_S, 0,
+		 F7SIZE|RS2_0, RD_FREG),
+	INSN_F73("fmv.x.d", (OPFP_MVFI_CLASS << 2) | OPFP_D, MVFI_CLASS_MVFI,
+		 F7SIZE|RS2_0, RS1_FREG),
+	INSN_F73("fmv.d.x", (OPFP_MVIF << 2) | OPFP_D, 0,
+		 F7SIZE|RS2_0, RD_FREG),
+};
+
+#define TABLE(table) \
+	.u.entries.v = table, .u.entries.n = __arraycount(table)
+
+static const struct riscv_disasm32_entry riscv_disasm32[32] = {
+	[OPCODE_AUIPC] =   { .fmt = FMT_U, .u.name = "auipc" },
+	[OPCODE_LUI] =     { .fmt = FMT_U, .u.name = "lui" },
+	[OPCODE_JAL] =     { .fmt = FMT_J, .u.name = "jal" },
+	[OPCODE_JALR] =    { .fmt = FMT_In, .u.name = "jalr" },
+	[OPCODE_MISCMEM] = { .fmt = FMT_I, TABLE(riscv_disasm_miscmem) },
+	[OPCODE_LOAD] =    { .fmt = FMT_I, TABLE(riscv_disasm_load) },
+	[OPCODE_LOADFP] =  { .fmt = FMT_I, TABLE(riscv_disasm_loadfp) },
+	[OPCODE_OPIMM] =   { .fmt = FMT_I, TABLE(riscv_disasm_opimm) },
+	[OPCODE_OPIMM32] = { .fmt = FMT_I, TABLE(riscv_disasm_opimm32) },
+	[OPCODE_STORE] =   { .fmt = FMT_S, TABLE(riscv_disasm_store) },
+	[OPCODE_STOREFP] = { .fmt = FMT_S, TABLE(riscv_disasm_storefp) },
+	[OPCODE_BRANCH] =  { .fmt = FMT_B, TABLE(riscv_disasm_branch) },
+	[OPCODE_SYSTEM] =  { .fmt = FMT_R, TABLE(riscv_disasm_system) },
+	[OPCODE_AMO] =     { .fmt = FMT_R, TABLE(riscv_disasm_amo) },
+	[OPCODE_OP] =      { .fmt = FMT_R, TABLE(riscv_disasm_op) },
+	[OPCODE_OP32] =    { .fmt = FMT_R, TABLE(riscv_disasm_op32) },
+	[OPCODE_OPFP] =    { .fmt = FMT_R, TABLE(riscv_disasm_opfp) },
+	[OPCODE_MADD] =    { .fmt = FMT_R4, .u.name = "fmadd" },
+	[OPCODE_MSUB] =    { .fmt = FMT_R4, .u.name = "fmsub" },
+	[OPCODE_NMADD] =   { .fmt = FMT_R4, .u.name = "fnmadd" },
+	[OPCODE_NMSUB] =   { .fmt = FMT_R4, .u.name = "fnmsub" },
+	[OPCODE_CUSTOM0] = { .fmt = FMT_UNKNOWN },
+	[OPCODE_CUSTOM1] = { .fmt = FMT_UNKNOWN },
+	[OPCODE_CUSTOM2] = { .fmt = FMT_UNKNOWN },
+	[OPCODE_CUSTOM3] = { .fmt = FMT_UNKNOWN },
+	[OPCODE_rsvd21] =  { .fmt = FMT_UNKNOWN },
+	[OPCODE_rsvd26] =  { .fmt = FMT_UNKNOWN },
+	[OPCODE_rsvd29] =  { .fmt = FMT_UNKNOWN },
+	[OPCODE_X48a] =    { .fmt = FMT_ASSERT },
+	[OPCODE_X48b] =    { .fmt = FMT_ASSERT },
+	[OPCODE_X64] =     { .fmt = FMT_ASSERT },
+	[OPCODE_X80] =     { .fmt = FMT_ASSERT },
+};
+
+static
+const struct riscv_disasm_insn *
+riscv_disasm_match(const struct riscv_disasm_insn *table, unsigned num,
+		   uint32_t insn, uint32_t imm)
+{
+	unsigned i, f3, f7, testf7;
+	const struct riscv_disasm_insn *info;
+
+	f3 = INSN_FUNCT3(insn);
+	f7 = INSN_FUNCT7(insn);
+	for (i=0; i<num; i++) {
+		info = &table[i];
+
+		/* always check funct3 first */
+		if (info->matchflags & CHECK_F3) {
+			if (info->funct3 != f3) {
+				continue;
+			}
+		}
+
+		/* now funct7 */
+		testf7 = f7;
+		if (info->matchflags & SHIFT64) {
+			/* shift count leaks into the bottom bit of funct7 */
+			testf7 &= 0b1111110;
+		}
+		if (info->matchflags & CHECK_F5) {
+			/* other stuff in the bottom two bits, don't look */
+			testf7 &= 0b1111100;
+		}
+		if (info->matchflags & CHECK_F7) {
+			if (info->funct7 != testf7) {
+				continue;
+			}
+		}
+
+		/* finally rs2 as the 4th opcode field */
+		if (info->matchflags & CHECK_RS2) {
+			if (info->rs2 != INSN_RS2(insn)) {
+				continue;
+			}
+		}
+
+		/* check fields that are supposed to be 0 */
+		if (info->matchflags & RD_0) {
+			if (INSN_RD(insn) != 0) {
+				continue;
+			}
+		}
+		if (info->matchflags & RS1_0) {
+			if (INSN_RS1(insn) != 0) {
+				continue;
+			}
+		}
+		if (info->matchflags & RS2_0) {
+			/* this could be folded into CHECK_RS2 */
+			/* (but would make the initializations uglier) */
+			if (INSN_RS2(insn) != 0) {
+				continue;
+			}
+		}
+		if (info->matchflags & IMM_0) {
+			if (imm != 0) {
+				continue;
+			}
+		}
+
+		/* other checks */
+		if (info->matchflags & F3AMO) {
+			if (f3 != AMO_W && f3 != AMO_D) {
+				continue;
+			}
+		}
+		if (info->matchflags & F3ROUND) {
+			switch (f3) {
+			    case ROUND_RNE:
+			    case ROUND_RTZ:
+			    case ROUND_RDN:
+			    case ROUND_RUP:
+			    case ROUND_RMM:
+			    case ROUND_DYN:
+				break;
+			    default:
+				continue;
+			}
+		}
+		if (info->matchflags & F7SIZE) {
+			/* fp size bits at bottom of funct7 */
+			switch (f7 & 3) {
+			    case OPFP_S:
+			    case OPFP_D:
+			    case OPFP_Q:
+				break;
+			    default:
+				continue;
+			}
+		}
+		if (info->matchflags & RS2_FSIZE) {
+			/* fp size bits in rs2 field */
+			switch (INSN_RS2(insn)) {
+			    case OPFP_S:
+			    case OPFP_D:
+			    case OPFP_Q:
+				break;
+			    default:
+				continue;
+			}
+		}
+		if (info->matchflags & FENCEFM) {
+			/* imm is 12 bits, upper 4 are a fence mode */
+			switch (imm >> 8) {
+			    case FENCE_FM_NORMAL:
+			    case FENCE_FM_TSO:
+				break;
+			    default:
+				continue;
+			}
+		}
+
+		/* passed all tests */
+		return info;
+	}
+	/* no match */
+	return NULL;
+}
+
+static
+void
+db_print_riscv_fencebits(unsigned bits)
+{
+	if (bits == 0) {
+		db_printf("0");
+	}
+	else {
+		db_printf("%s%s%s%s",
+			  (bits & FENCE_INPUT) ? "i" : "",
+			  (bits & FENCE_OUTPUT) ? "o" : "",
+			  (bits & FENCE_READ) ? "r" : "",
+			  (bits & FENCE_WRITE) ? "w" : "");
+	}
+}
+
+static
+void
+db_print_riscv_reg(unsigned reg, bool isfreg)
+{
+	if (isfreg) {
+		db_printf("f%d", reg);
+	}
+	else {
+		db_printf("%s", riscv_registers[reg]);
+	}
+}
+
+static
+const char *
+riscv_fp_size(unsigned fpsize)
+{
+	switch (fpsize) {
+	    case OPFP_S: return ".s";
+	    case OPFP_D: return ".d";
+	    case OPFP_Q: return ".q";
+	    default: KASSERT(0); return ".?";
+	}
+}
+
+static
+const char *
+riscv_fp_round(unsigned round)
+{
+	switch (round) {
+	    case ROUND_RNE: return ".rne";
+	    case ROUND_RTZ: return ".rtz";
+	    case ROUND_RDN: return ".rdn";
+	    case ROUND_RUP: return ".rup";
+	    case ROUND_RMM: return ".rmm";
+	    case ROUND_DYN: return "";
+	    default: KASSERT(0); return ".?";
+	}
+}
+
+
+static
+void
+db_print_riscv_insnname(uint32_t insn, const struct riscv_disasm_insn *info)
+{
+	db_printf("%s", info->name);
+
+	/* accumulated mode cruft on the name */
+	if (info->matchflags & F3AMO) {
+		db_printf("%s", INSN_FUNCT3(insn) == AMO_W ? ".w" : ".d");
+	}
+	if ((info->matchflags & RS2_FSIZE) &&
+	    (info->printflags & RS2SIZE_FIRST)) {
+		db_printf("%s", riscv_fp_size(INSN_RS2(insn)));
+	}
+	if (info->matchflags & F7SIZE) {
+		db_printf("%s", riscv_fp_size(INSN_FUNCT7(insn) & 3));
+	}
+	if ((info->matchflags & RS2_FSIZE) &&
+	    (info->printflags & RS2SIZE_FIRST) == 0) {
+		db_printf("%s", riscv_fp_size(INSN_RS2(insn)));
+	}
+	if (info->matchflags & F3ROUND) {
+		db_printf("%s", riscv_fp_round(INSN_FUNCT3(insn)));
+	}
+	if (info->matchflags & FENCEFM) {
+		/*
+		 * The fence mode is the top 4 bits of the instruction,
+		 * which is the top 4 bits of funct7, so get it from
+		 * there. Elsewhere in this file it's defined in terms
+		 * of the immediate though. XXX tidy up
+		 */
+		if ((INSN_FUNCT7(insn) >> 3) == FENCE_FM_TSO) {
+			db_printf(".tso");
+		}
+	}
+	if (info->printflags & AMOAQRL) {
+		db_printf("%s%s",
+			  INSN_FUNCT7(insn) & AMO_AQ ? ".aq" : "",
+			  INSN_FUNCT7(insn) & AMO_RL ? ".rl" : "");
+	}
+}
+
+static
+int
+db_disasm_32(db_addr_t loc, uint32_t insn, bool altfmt)
+{
+	unsigned opcode;
+	const struct riscv_disasm32_entry *d;
+	unsigned numtable;
+	const struct riscv_disasm_insn *table, *info;
+	const char *sep = " ";
+	uint32_t imm;
+
+	opcode = INSN_OPCODE32(insn);
+	d = &riscv_disasm32[opcode];
+	switch (d->fmt) {
+	    case FMT_R:
+		/* register ops */
+		table = d->u.entries.v;
+		numtable = d->u.entries.n;
+		info = riscv_disasm_match(table, numtable, insn, 0);
+		if (info == NULL) {
+			return EINVAL;
+		}
+
+		/* name */
+		db_print_riscv_insnname(insn, info);
+
+		/* rd */
+		if ((info->matchflags & RD_0) == 0) {
+			db_printf("%s", sep);
+			db_print_riscv_reg(INSN_RD(insn),
+					   info->printflags & RD_FREG);
+			sep = ", ";
+		}
+
+		/* rs1 */
+		if ((info->matchflags & RS1_0) == 0) {
+			db_printf("%s", sep);
+			db_print_riscv_reg(INSN_RS1(insn),
+					   info->printflags & RS1_FREG);
+			sep = ", ";
+		}
+
+		/* rs2 */
+		if ((info->matchflags & RS2_0) == 0 &&
+		    (info->matchflags && CHECK_RS2) == 0) {
+			db_printf("%s", sep);
+			db_print_riscv_reg(INSN_RS2(insn),
+					   info->printflags & RS2_FREG);
+		}
+		db_printf("\n");
+
+		break;
+	    case FMT_R4:
+		db_printf("%s%s%s f%d, f%d, f%d, f%d", d->u.name,
+			  riscv_fp_size(INSN_FUNCT7(insn) & 3),
+			  riscv_fp_round(INSN_FUNCT3(insn)),
+			  INSN_RD(insn),
+			  INSN_RS1(insn),
+			  INSN_RS2(insn),
+			  INSN_FUNCT7(insn) >> 2);
+	        break;
+	    case FMT_I:
+		/* immediates */
+		imm = INSN_IMM_I(insn);
+
+		table = d->u.entries.v;
+		numtable = d->u.entries.n;
+		info = riscv_disasm_match(table, numtable, insn, imm);
+		if (info == NULL) {
+			return EINVAL;
+		}
+
+		if (info->matchflags & SHIFT32) {
+			imm &= 31;
+		}
+		else if (info->matchflags & SHIFT64) {
+			imm &= 63;
+		}
+
+		/* name */
+		db_print_riscv_insnname(insn, info);
+
+		/* rd */
+		if ((info->matchflags & RD_0) == 0) {
+			db_printf("%s", sep);
+			db_print_riscv_reg(INSN_RD(insn),
+					   info->printflags & RD_FREG);
+			sep = ", ";
+		}
+
+		if (info->printflags & MEMORYIMM) {
+			db_printf("%s", sep);
+			db_printf("%d", (int32_t)imm);
+			db_print_riscv_reg(INSN_RS1(insn),
+					   info->printflags & RS1_FREG);
+		}
+		else if (info->printflags & CSRIMM) {
+			db_printf("%s", sep);
+			/* CSR number is the immediate and comes first */
+			db_printf("%d, ", (int32_t)imm);
+			db_print_riscv_reg(INSN_RS1(insn),
+					   info->printflags & RS1_FREG);
+		}
+		else if (info->printflags & CSRIIMM) {
+			db_printf("%s", sep);
+			/* CSR number is the immediate and comes first */
+			db_printf("%d, ", (int32_t)imm);
+			/* the immediate value is in the RS1 field */
+			db_printf("%d", INSN_RS1(insn));
+		}
+		else {
+			/* rs1 */
+			if ((info->matchflags & RS1_0) == 0) {
+				db_printf("%s", sep);
+				db_print_riscv_reg(INSN_RS1(insn),
+						  info->printflags & RS1_FREG);
+				sep = ", ";
+			}
+
+			/* imm */
+			if (info->matchflags & IMM_0) {
+				/* nothing */
+			}
+			else if (info->printflags & FENCEIMM) {
+				unsigned pred, succ;
+
+				/* fm is part of the name, doesn't go here */
+				pred = (imm >> 4) & 0xf;
+				succ = imm & 0xf;
+				db_print_riscv_fencebits(pred);
+				db_printf(", ");
+				db_print_riscv_fencebits(succ);
+			}
+			else if (info->printflags & BRANCHIMM) {
+				/* should be B format and not come here */
+				KASSERT(0);
+			}
+			else if (info->printflags & DECIMM) {
+				db_printf("%s%d", sep, (int32_t)imm);
+			}
+			else {
+				db_printf("%s0x%x", sep, imm);
+			}
+		}
+		break;
+	    case FMT_In:
+		/* same as I but funct3 should be 0 so just one case */
+		if (INSN_FUNCT3(insn) != 0) {
+			return EINVAL;
+		}
+		db_printf("%s %s, %s, 0x%x\n",
+			  d->u.name,
+			  riscv_registers[INSN_RD(insn)],
+			  riscv_registers[INSN_RS1(insn)],
+			  INSN_IMM_I(insn));
+		break;
+	    case FMT_S:
+		/* stores */
+		imm = INSN_IMM_S(insn);
+
+		table = d->u.entries.v;
+		numtable = d->u.entries.n;
+		info = riscv_disasm_match(table, numtable, insn, imm);
+		if (info == NULL) {
+			return EINVAL;
+		}
+
+		KASSERT((info->matchflags & (RS1_0 | RS2_0 | CHECK_RS2)) == 0);
+		KASSERT(info->printflags & MEMORYIMM);
+
+		/* name */
+		db_print_riscv_insnname(insn, info);
+
+		db_print_riscv_reg(INSN_RS2(insn),
+				   info->printflags & RS2_FREG);
+		sep = ", ";
+
+		db_printf("%s", sep);
+		db_printf("%d", (int32_t)imm);
+		db_print_riscv_reg(INSN_RS1(insn),
+				   info->printflags & RS1_FREG);
+		break;
+	    case FMT_B:
+		/* branches */
+		imm = INSN_IMM_B(insn);
+
+		table = d->u.entries.v;
+		numtable = d->u.entries.n;
+		info = riscv_disasm_match(table, numtable, insn, imm);
+		if (info == NULL) {
+			return EINVAL;
+		}
+
+		KASSERT((info->matchflags & (RS1_0 | RS2_0 | CHECK_RS2)) == 0);
+		KASSERT(info->printflags & BRANCHIMM);
+
+		/* name */
+		db_print_riscv_insnname(insn, info);
+
+		db_print_riscv_reg(INSN_RS1(insn),
+				   info->printflags & RS1_FREG);
+		db_printf(", ");
+
+		db_print_riscv_reg(INSN_RS2(insn),
+				   info->printflags & RS2_FREG);
+		db_printf(", ");
+		db_print_addr(loc + (int32_t)imm);
+		db_printf("\n");
+		break;
+	    case FMT_U:
+		/* large immediates */
+		db_printf("%s %s, 0x%x\n",
+			  d->u.name,
+			  riscv_registers[INSN_RD(insn)],
+			  INSN_IMM_U(insn));
+		break;
+	    case FMT_J:
+		/* jal */
+		db_printf("%s %s, ",
+			  d->u.name,
+			  riscv_registers[INSN_RD(insn)]);
+		db_print_addr(loc + (int32_t)INSN_IMM_J(insn));
+		db_printf("\n");
+		break;
+	    case FMT_UNKNOWN:
+		/* reserved, custom, etc. */
+		return EINVAL;
+	    case FMT_ASSERT:
+		/* shouldn't have come here */
+		KASSERTMSG(false, "db_disasm_32: non-32-bit instruction");
+		return EINVAL;
+	}
+	return 0;
+}
+
+////////////////////////////////////////////////////////////
+
+static
+void
+db_disasm_unknown(const uint16_t *insn, unsigned n)
+{
+	unsigned i;
+
+	db_printf(".insn%u 0x", n*16);
+	for (i=n; i-- > 0; ) {
+		db_printf("%02x", insn[i]);
+	}
+	db_printf("\n");
+}
 
 db_addr_t
 db_disasm(db_addr_t loc, bool altfmt)
 {
-	union riscv_insn insn;
+	/* instructions are up to 5 halfwords */
+	uint16_t insn[5];
+	unsigned n, i;
+	uint32_t insn32;
 
 	if ((intptr_t) loc >= 0) {
-		printf("%s: %#"PRIxVADDR" is not a kernel address\n",
+		db_printf("%s: %#"PRIxVADDR" is not a kernel address\n",
 		    __func__, loc);
 		return loc;
 	}
 
-	insn.val = *(const uint32_t *)loc;
-	printf(".word %#x\n", insn.val);
-	return loc + 4;
+	/*
+	 * Fetch the instruction. The first halfword tells us how many
+	 * more there are, and they're always in little-endian order.
+	 */
+	insn[0] = ((const uint16_t *)loc)[0];
+	n = INSN_HALFWORDS(insn[0]);
+	KASSERT(n > 0 && n <= 5);
+	for (i = 1; i < n; i++) {
+		insn[i] = ((const uint16_t *)loc)[i];
+	}
+
+	switch (n) {
+	    case 1:
+		if (db_disasm_16(loc, insn[0], altfmt) != 0) {
+			db_disasm_unknown(insn, n);
+		}
+		break;
+	    case 2:
+		insn32 = ((uint32_t)insn[1] << 16) | insn[0];
+		if (db_disasm_32(loc, insn32, altfmt) != 0) {
+			db_disasm_unknown(insn, n);
+		}
+		break;
+	    default:
+		/* no standard instructions of size 3+ */
+		db_disasm_unknown(insn, n);
+		break;
+	}
+	return loc + n * sizeof(uint16_t);
 }

Index: src/sys/arch/riscv/riscv/db_machdep.c
diff -u src/sys/arch/riscv/riscv/db_machdep.c:1.6 src/sys/arch/riscv/riscv/db_machdep.c:1.7
--- src/sys/arch/riscv/riscv/db_machdep.c:1.6	Wed Nov  4 07:09:46 2020
+++ src/sys/arch/riscv/riscv/db_machdep.c	Wed Apr 14 06:32:20 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: db_machdep.c,v 1.6 2020/11/04 07:09:46 skrll Exp $	*/
+/*	$NetBSD: db_machdep.c,v 1.7 2021/04/14 06:32:20 dholland Exp $	*/
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 
-__RCSID("$NetBSD: db_machdep.c,v 1.6 2020/11/04 07:09:46 skrll Exp $");
+__RCSID("$NetBSD: db_machdep.c,v 1.7 2021/04/14 06:32:20 dholland Exp $");
 
 #include <sys/param.h>
 
@@ -110,6 +110,11 @@ db_rw_ddbreg(const struct db_variable *v
 
 // These are for the software implementation of single-stepping.
 //
+// XXX none of this checks for 16-bit instructions; it should all be
+// converted to the newer decoding macros. Also, XXX it does not look
+// like the MI parts in ddb is going to work in the presence of 16-bit
+// instructions anyway.
+//
 // returns true is the instruction might branch
 bool
 inst_branch(uint32_t insn)
@@ -122,7 +127,7 @@ bool
 inst_call(uint32_t insn)
 {
 	const union riscv_insn ri = { .val = insn };
-	return (OPCODE_P(insn, JAL) && ri.type_uj.uj_rd == 1)
+	return (OPCODE_P(insn, JAL) && ri.type_u.u_rd == 1)
 	    || (OPCODE_P(insn, JALR) && ri.type_i.i_rd == 1);
 }
 
@@ -169,21 +174,21 @@ branch_taken(uint32_t insn, db_addr_t pc
 		return i.type_i.i_imm11to0 + get_reg_value(tf, i.type_i.i_rs1);
 	}
 	if (OPCODE_P(insn, JAL)) {
-		displacement = i.type_uj.uj_imm20 << 20;
-		displacement |= i.type_uj.uj_imm19to12 << 12;
-		displacement |= i.type_uj.uj_imm11 << 11;
-		displacement |= i.type_uj.uj_imm10to1 << 1;
+		displacement = i.type_j.j_imm20 << 20;
+		displacement |= i.type_j.j_imm19to12 << 12;
+		displacement |= i.type_j.j_imm11 << 11;
+		displacement |= i.type_j.j_imm10to1 << 1;
 	} else {
 		KASSERT(OPCODE_P(insn, BRANCH));
-		register_t rs1 = get_reg_value(tf, i.type_sb.sb_rs1);
-		register_t rs2 = get_reg_value(tf, i.type_sb.sb_rs2);
+		register_t rs1 = get_reg_value(tf, i.type_b.b_rs1);
+		register_t rs2 = get_reg_value(tf, i.type_b.b_rs2);
 		bool branch_p; // = false;
-		switch (i.type_sb.sb_funct3 & 0b110U) {
+		switch (i.type_b.b_funct3 & 0b110U) {
 		case 0b000U:
 			branch_p = (rs1 == rs2);
 			break;
 		case 0b010U:
-			branch_p = ((rs1 & (1 << (i.type_sb.sb_rs2))) != 0);
+			branch_p = ((rs1 & (1 << (i.type_b.b_rs2))) != 0);
 			break;
 		case 0b100U:
 			branch_p = (rs1 < rs2);
@@ -194,16 +199,16 @@ branch_taken(uint32_t insn, db_addr_t pc
 			break;
 		}
 
-		if (i.type_sb.sb_funct3 & 1)
+		if (i.type_b.b_funct3 & 1)
 			branch_p = !branch_p;
 
 		if (!branch_p) {
 			displacement = 4;
 		} else {
-			displacement = i.type_sb.sb_imm12 << 12;
-			displacement |= i.type_sb.sb_imm11 << 11;
-			displacement |= i.type_sb.sb_imm10to5 << 5;
-			displacement |= i.type_sb.sb_imm4to1 << 1;
+			displacement = i.type_b.b_imm12 << 12;
+			displacement |= i.type_b.b_imm11 << 11;
+			displacement |= i.type_b.b_imm10to5 << 5;
+			displacement |= i.type_b.b_imm4to1 << 1;
 		}
 	}
 

Reply via email to