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; } }