From f71a8eaffba3271cf7cdad95572f6996f7523a5b Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:09 +1300 Subject: RISC-V ELF Machine Definition Define RISC-V ELF machine EM_RISCV 243 Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Signed-off-by: Sagar Karandikar Signed-off-by: Michael Clark --- include/elf.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/elf.h b/include/elf.h index 943ee21171..c0dc9bb5fd 100644 --- a/include/elf.h +++ b/include/elf.h @@ -119,6 +119,8 @@ typedef int64_t Elf64_Sxword; #define EM_UNICORE32 110 /* UniCore32 */ +#define EM_RISCV 243 /* RISC-V */ + /* * This is an interim value that we will use until the committee comes * up with a final number. -- cgit v1.2.3-55-g7522 From ea10325917c8a8f92611025c85950c00f826cb73 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:10 +1300 Subject: RISC-V Disassembler The RISC-V disassembler has no dependencies outside of the 'disas' directory so it can be applied independently. The majority of the disassembler is machine-generated from instruction set metadata: - https://github.com/michaeljclark/riscv-meta Expected checkpatch errors for consistency and brevity reasons: ERROR: line over 90 characters ERROR: trailing statements should be on next line ERROR: space prohibited between function name and open parenthesis '(' Reviewed-by: Richard Henderson Signed-off-by: Michael Clark --- disas.c | 2 + disas/Makefile.objs | 1 + disas/riscv.c | 3048 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/disas/bfd.h | 2 + 4 files changed, 3053 insertions(+) create mode 100644 disas/riscv.c (limited to 'include') diff --git a/disas.c b/disas.c index d4ad1089ef..5325b7e6be 100644 --- a/disas.c +++ b/disas.c @@ -522,6 +522,8 @@ void disas(FILE *out, void *code, unsigned long size) # ifdef _ARCH_PPC64 s.info.cap_mode = CS_MODE_64; # endif +#elif defined(__riscv__) + print_insn = print_insn_riscv; #elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS) print_insn = print_insn_arm_a64; s.info.cap_arch = CS_ARCH_ARM64; diff --git a/disas/Makefile.objs b/disas/Makefile.objs index 53556f8f5a..213be2fab2 100644 --- a/disas/Makefile.objs +++ b/disas/Makefile.objs @@ -17,6 +17,7 @@ common-obj-$(CONFIG_MIPS_DIS) += mips.o common-obj-$(CONFIG_NIOS2_DIS) += nios2.o common-obj-$(CONFIG_MOXIE_DIS) += moxie.o common-obj-$(CONFIG_PPC_DIS) += ppc.o +common-obj-$(CONFIG_RISCV_DIS) += riscv.o common-obj-$(CONFIG_S390_DIS) += s390.o common-obj-$(CONFIG_SH4_DIS) += sh4.o common-obj-$(CONFIG_SPARC_DIS) += sparc.o diff --git a/disas/riscv.c b/disas/riscv.c new file mode 100644 index 0000000000..3c17501120 --- /dev/null +++ b/disas/riscv.c @@ -0,0 +1,3048 @@ +/* + * QEMU RISC-V Disassembler + * + * Copyright (c) 2016-2017 Michael Clark + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "disas/bfd.h" + + +/* types */ + +typedef uint64_t rv_inst; +typedef uint16_t rv_opcode; + +/* enums */ + +typedef enum { + rv32, + rv64, + rv128 +} rv_isa; + +typedef enum { + rv_rm_rne = 0, + rv_rm_rtz = 1, + rv_rm_rdn = 2, + rv_rm_rup = 3, + rv_rm_rmm = 4, + rv_rm_dyn = 7, +} rv_rm; + +typedef enum { + rv_fence_i = 8, + rv_fence_o = 4, + rv_fence_r = 2, + rv_fence_w = 1, +} rv_fence; + +typedef enum { + rv_ireg_zero, + rv_ireg_ra, + rv_ireg_sp, + rv_ireg_gp, + rv_ireg_tp, + rv_ireg_t0, + rv_ireg_t1, + rv_ireg_t2, + rv_ireg_s0, + rv_ireg_s1, + rv_ireg_a0, + rv_ireg_a1, + rv_ireg_a2, + rv_ireg_a3, + rv_ireg_a4, + rv_ireg_a5, + rv_ireg_a6, + rv_ireg_a7, + rv_ireg_s2, + rv_ireg_s3, + rv_ireg_s4, + rv_ireg_s5, + rv_ireg_s6, + rv_ireg_s7, + rv_ireg_s8, + rv_ireg_s9, + rv_ireg_s10, + rv_ireg_s11, + rv_ireg_t3, + rv_ireg_t4, + rv_ireg_t5, + rv_ireg_t6, +} rv_ireg; + +typedef enum { + rvc_end, + rvc_simm_6, + rvc_imm_6, + rvc_imm_7, + rvc_imm_8, + rvc_imm_9, + rvc_imm_10, + rvc_imm_12, + rvc_imm_18, + rvc_imm_nz, + rvc_imm_x2, + rvc_imm_x4, + rvc_imm_x8, + rvc_imm_x16, + rvc_rd_b3, + rvc_rs1_b3, + rvc_rs2_b3, + rvc_rd_eq_rs1, + rvc_rd_eq_ra, + rvc_rd_eq_sp, + rvc_rd_eq_x0, + rvc_rs1_eq_sp, + rvc_rs1_eq_x0, + rvc_rs2_eq_x0, + rvc_rd_ne_x0_x2, + rvc_rd_ne_x0, + rvc_rs1_ne_x0, + rvc_rs2_ne_x0, + rvc_rs2_eq_rs1, + rvc_rs1_eq_ra, + rvc_imm_eq_zero, + rvc_imm_eq_n1, + rvc_imm_eq_p1, + rvc_csr_eq_0x001, + rvc_csr_eq_0x002, + rvc_csr_eq_0x003, + rvc_csr_eq_0xc00, + rvc_csr_eq_0xc01, + rvc_csr_eq_0xc02, + rvc_csr_eq_0xc80, + rvc_csr_eq_0xc81, + rvc_csr_eq_0xc82, +} rvc_constraint; + +typedef enum { + rv_codec_illegal, + rv_codec_none, + rv_codec_u, + rv_codec_uj, + rv_codec_i, + rv_codec_i_sh5, + rv_codec_i_sh6, + rv_codec_i_sh7, + rv_codec_i_csr, + rv_codec_s, + rv_codec_sb, + rv_codec_r, + rv_codec_r_m, + rv_codec_r4_m, + rv_codec_r_a, + rv_codec_r_l, + rv_codec_r_f, + rv_codec_cb, + rv_codec_cb_imm, + rv_codec_cb_sh5, + rv_codec_cb_sh6, + rv_codec_ci, + rv_codec_ci_sh5, + rv_codec_ci_sh6, + rv_codec_ci_16sp, + rv_codec_ci_lwsp, + rv_codec_ci_ldsp, + rv_codec_ci_lqsp, + rv_codec_ci_li, + rv_codec_ci_lui, + rv_codec_ci_none, + rv_codec_ciw_4spn, + rv_codec_cj, + rv_codec_cj_jal, + rv_codec_cl_lw, + rv_codec_cl_ld, + rv_codec_cl_lq, + rv_codec_cr, + rv_codec_cr_mv, + rv_codec_cr_jalr, + rv_codec_cr_jr, + rv_codec_cs, + rv_codec_cs_sw, + rv_codec_cs_sd, + rv_codec_cs_sq, + rv_codec_css_swsp, + rv_codec_css_sdsp, + rv_codec_css_sqsp, +} rv_codec; + +typedef enum { + rv_op_illegal = 0, + rv_op_lui = 1, + rv_op_auipc = 2, + rv_op_jal = 3, + rv_op_jalr = 4, + rv_op_beq = 5, + rv_op_bne = 6, + rv_op_blt = 7, + rv_op_bge = 8, + rv_op_bltu = 9, + rv_op_bgeu = 10, + rv_op_lb = 11, + rv_op_lh = 12, + rv_op_lw = 13, + rv_op_lbu = 14, + rv_op_lhu = 15, + rv_op_sb = 16, + rv_op_sh = 17, + rv_op_sw = 18, + rv_op_addi = 19, + rv_op_slti = 20, + rv_op_sltiu = 21, + rv_op_xori = 22, + rv_op_ori = 23, + rv_op_andi = 24, + rv_op_slli = 25, + rv_op_srli = 26, + rv_op_srai = 27, + rv_op_add = 28, + rv_op_sub = 29, + rv_op_sll = 30, + rv_op_slt = 31, + rv_op_sltu = 32, + rv_op_xor = 33, + rv_op_srl = 34, + rv_op_sra = 35, + rv_op_or = 36, + rv_op_and = 37, + rv_op_fence = 38, + rv_op_fence_i = 39, + rv_op_lwu = 40, + rv_op_ld = 41, + rv_op_sd = 42, + rv_op_addiw = 43, + rv_op_slliw = 44, + rv_op_srliw = 45, + rv_op_sraiw = 46, + rv_op_addw = 47, + rv_op_subw = 48, + rv_op_sllw = 49, + rv_op_srlw = 50, + rv_op_sraw = 51, + rv_op_ldu = 52, + rv_op_lq = 53, + rv_op_sq = 54, + rv_op_addid = 55, + rv_op_sllid = 56, + rv_op_srlid = 57, + rv_op_sraid = 58, + rv_op_addd = 59, + rv_op_subd = 60, + rv_op_slld = 61, + rv_op_srld = 62, + rv_op_srad = 63, + rv_op_mul = 64, + rv_op_mulh = 65, + rv_op_mulhsu = 66, + rv_op_mulhu = 67, + rv_op_div = 68, + rv_op_divu = 69, + rv_op_rem = 70, + rv_op_remu = 71, + rv_op_mulw = 72, + rv_op_divw = 73, + rv_op_divuw = 74, + rv_op_remw = 75, + rv_op_remuw = 76, + rv_op_muld = 77, + rv_op_divd = 78, + rv_op_divud = 79, + rv_op_remd = 80, + rv_op_remud = 81, + rv_op_lr_w = 82, + rv_op_sc_w = 83, + rv_op_amoswap_w = 84, + rv_op_amoadd_w = 85, + rv_op_amoxor_w = 86, + rv_op_amoor_w = 87, + rv_op_amoand_w = 88, + rv_op_amomin_w = 89, + rv_op_amomax_w = 90, + rv_op_amominu_w = 91, + rv_op_amomaxu_w = 92, + rv_op_lr_d = 93, + rv_op_sc_d = 94, + rv_op_amoswap_d = 95, + rv_op_amoadd_d = 96, + rv_op_amoxor_d = 97, + rv_op_amoor_d = 98, + rv_op_amoand_d = 99, + rv_op_amomin_d = 100, + rv_op_amomax_d = 101, + rv_op_amominu_d = 102, + rv_op_amomaxu_d = 103, + rv_op_lr_q = 104, + rv_op_sc_q = 105, + rv_op_amoswap_q = 106, + rv_op_amoadd_q = 107, + rv_op_amoxor_q = 108, + rv_op_amoor_q = 109, + rv_op_amoand_q = 110, + rv_op_amomin_q = 111, + rv_op_amomax_q = 112, + rv_op_amominu_q = 113, + rv_op_amomaxu_q = 114, + rv_op_ecall = 115, + rv_op_ebreak = 116, + rv_op_uret = 117, + rv_op_sret = 118, + rv_op_hret = 119, + rv_op_mret = 120, + rv_op_dret = 121, + rv_op_sfence_vm = 122, + rv_op_sfence_vma = 123, + rv_op_wfi = 124, + rv_op_csrrw = 125, + rv_op_csrrs = 126, + rv_op_csrrc = 127, + rv_op_csrrwi = 128, + rv_op_csrrsi = 129, + rv_op_csrrci = 130, + rv_op_flw = 131, + rv_op_fsw = 132, + rv_op_fmadd_s = 133, + rv_op_fmsub_s = 134, + rv_op_fnmsub_s = 135, + rv_op_fnmadd_s = 136, + rv_op_fadd_s = 137, + rv_op_fsub_s = 138, + rv_op_fmul_s = 139, + rv_op_fdiv_s = 140, + rv_op_fsgnj_s = 141, + rv_op_fsgnjn_s = 142, + rv_op_fsgnjx_s = 143, + rv_op_fmin_s = 144, + rv_op_fmax_s = 145, + rv_op_fsqrt_s = 146, + rv_op_fle_s = 147, + rv_op_flt_s = 148, + rv_op_feq_s = 149, + rv_op_fcvt_w_s = 150, + rv_op_fcvt_wu_s = 151, + rv_op_fcvt_s_w = 152, + rv_op_fcvt_s_wu = 153, + rv_op_fmv_x_s = 154, + rv_op_fclass_s = 155, + rv_op_fmv_s_x = 156, + rv_op_fcvt_l_s = 157, + rv_op_fcvt_lu_s = 158, + rv_op_fcvt_s_l = 159, + rv_op_fcvt_s_lu = 160, + rv_op_fld = 161, + rv_op_fsd = 162, + rv_op_fmadd_d = 163, + rv_op_fmsub_d = 164, + rv_op_fnmsub_d = 165, + rv_op_fnmadd_d = 166, + rv_op_fadd_d = 167, + rv_op_fsub_d = 168, + rv_op_fmul_d = 169, + rv_op_fdiv_d = 170, + rv_op_fsgnj_d = 171, + rv_op_fsgnjn_d = 172, + rv_op_fsgnjx_d = 173, + rv_op_fmin_d = 174, + rv_op_fmax_d = 175, + rv_op_fcvt_s_d = 176, + rv_op_fcvt_d_s = 177, + rv_op_fsqrt_d = 178, + rv_op_fle_d = 179, + rv_op_flt_d = 180, + rv_op_feq_d = 181, + rv_op_fcvt_w_d = 182, + rv_op_fcvt_wu_d = 183, + rv_op_fcvt_d_w = 184, + rv_op_fcvt_d_wu = 185, + rv_op_fclass_d = 186, + rv_op_fcvt_l_d = 187, + rv_op_fcvt_lu_d = 188, + rv_op_fmv_x_d = 189, + rv_op_fcvt_d_l = 190, + rv_op_fcvt_d_lu = 191, + rv_op_fmv_d_x = 192, + rv_op_flq = 193, + rv_op_fsq = 194, + rv_op_fmadd_q = 195, + rv_op_fmsub_q = 196, + rv_op_fnmsub_q = 197, + rv_op_fnmadd_q = 198, + rv_op_fadd_q = 199, + rv_op_fsub_q = 200, + rv_op_fmul_q = 201, + rv_op_fdiv_q = 202, + rv_op_fsgnj_q = 203, + rv_op_fsgnjn_q = 204, + rv_op_fsgnjx_q = 205, + rv_op_fmin_q = 206, + rv_op_fmax_q = 207, + rv_op_fcvt_s_q = 208, + rv_op_fcvt_q_s = 209, + rv_op_fcvt_d_q = 210, + rv_op_fcvt_q_d = 211, + rv_op_fsqrt_q = 212, + rv_op_fle_q = 213, + rv_op_flt_q = 214, + rv_op_feq_q = 215, + rv_op_fcvt_w_q = 216, + rv_op_fcvt_wu_q = 217, + rv_op_fcvt_q_w = 218, + rv_op_fcvt_q_wu = 219, + rv_op_fclass_q = 220, + rv_op_fcvt_l_q = 221, + rv_op_fcvt_lu_q = 222, + rv_op_fcvt_q_l = 223, + rv_op_fcvt_q_lu = 224, + rv_op_fmv_x_q = 225, + rv_op_fmv_q_x = 226, + rv_op_c_addi4spn = 227, + rv_op_c_fld = 228, + rv_op_c_lw = 229, + rv_op_c_flw = 230, + rv_op_c_fsd = 231, + rv_op_c_sw = 232, + rv_op_c_fsw = 233, + rv_op_c_nop = 234, + rv_op_c_addi = 235, + rv_op_c_jal = 236, + rv_op_c_li = 237, + rv_op_c_addi16sp = 238, + rv_op_c_lui = 239, + rv_op_c_srli = 240, + rv_op_c_srai = 241, + rv_op_c_andi = 242, + rv_op_c_sub = 243, + rv_op_c_xor = 244, + rv_op_c_or = 245, + rv_op_c_and = 246, + rv_op_c_subw = 247, + rv_op_c_addw = 248, + rv_op_c_j = 249, + rv_op_c_beqz = 250, + rv_op_c_bnez = 251, + rv_op_c_slli = 252, + rv_op_c_fldsp = 253, + rv_op_c_lwsp = 254, + rv_op_c_flwsp = 255, + rv_op_c_jr = 256, + rv_op_c_mv = 257, + rv_op_c_ebreak = 258, + rv_op_c_jalr = 259, + rv_op_c_add = 260, + rv_op_c_fsdsp = 261, + rv_op_c_swsp = 262, + rv_op_c_fswsp = 263, + rv_op_c_ld = 264, + rv_op_c_sd = 265, + rv_op_c_addiw = 266, + rv_op_c_ldsp = 267, + rv_op_c_sdsp = 268, + rv_op_c_lq = 269, + rv_op_c_sq = 270, + rv_op_c_lqsp = 271, + rv_op_c_sqsp = 272, + rv_op_nop = 273, + rv_op_mv = 274, + rv_op_not = 275, + rv_op_neg = 276, + rv_op_negw = 277, + rv_op_sext_w = 278, + rv_op_seqz = 279, + rv_op_snez = 280, + rv_op_sltz = 281, + rv_op_sgtz = 282, + rv_op_fmv_s = 283, + rv_op_fabs_s = 284, + rv_op_fneg_s = 285, + rv_op_fmv_d = 286, + rv_op_fabs_d = 287, + rv_op_fneg_d = 288, + rv_op_fmv_q = 289, + rv_op_fabs_q = 290, + rv_op_fneg_q = 291, + rv_op_beqz = 292, + rv_op_bnez = 293, + rv_op_blez = 294, + rv_op_bgez = 295, + rv_op_bltz = 296, + rv_op_bgtz = 297, + rv_op_ble = 298, + rv_op_bleu = 299, + rv_op_bgt = 300, + rv_op_bgtu = 301, + rv_op_j = 302, + rv_op_ret = 303, + rv_op_jr = 304, + rv_op_rdcycle = 305, + rv_op_rdtime = 306, + rv_op_rdinstret = 307, + rv_op_rdcycleh = 308, + rv_op_rdtimeh = 309, + rv_op_rdinstreth = 310, + rv_op_frcsr = 311, + rv_op_frrm = 312, + rv_op_frflags = 313, + rv_op_fscsr = 314, + rv_op_fsrm = 315, + rv_op_fsflags = 316, + rv_op_fsrmi = 317, + rv_op_fsflagsi = 318, +} rv_op; + +/* structures */ + +typedef struct { + uint64_t pc; + uint64_t inst; + int32_t imm; + uint16_t op; + uint8_t codec; + uint8_t rd; + uint8_t rs1; + uint8_t rs2; + uint8_t rs3; + uint8_t rm; + uint8_t pred; + uint8_t succ; + uint8_t aq; + uint8_t rl; +} rv_decode; + +typedef struct { + const int op; + const rvc_constraint *constraints; +} rv_comp_data; + +typedef struct { + const char * const name; + const rv_codec codec; + const char * const format; + const rv_comp_data *pseudo; + const int decomp_rv32; + const int decomp_rv64; + const int decomp_rv128; +} rv_opcode_data; + +/* register names */ + +static const char rv_ireg_name_sym[32][5] = { + "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", +}; + +static const char rv_freg_name_sym[32][5] = { + "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", + "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", + "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", + "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11", +}; + +/* instruction formats */ + +#define rv_fmt_none "O\t" +#define rv_fmt_rs1 "O\t1" +#define rv_fmt_offset "O\to" +#define rv_fmt_pred_succ "O\tp,s" +#define rv_fmt_rs1_rs2 "O\t1,2" +#define rv_fmt_rd_imm "O\t0,i" +#define rv_fmt_rd_offset "O\t0,o" +#define rv_fmt_rd_rs1_rs2 "O\t0,1,2" +#define rv_fmt_frd_rs1 "O\t3,1" +#define rv_fmt_rd_frs1 "O\t0,4" +#define rv_fmt_rd_frs1_frs2 "O\t0,4,5" +#define rv_fmt_frd_frs1_frs2 "O\t3,4,5" +#define rv_fmt_rm_frd_frs1 "O\tr,3,4" +#define rv_fmt_rm_frd_rs1 "O\tr,3,1" +#define rv_fmt_rm_rd_frs1 "O\tr,0,4" +#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5" +#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6" +#define rv_fmt_rd_rs1_imm "O\t0,1,i" +#define rv_fmt_rd_rs1_offset "O\t0,1,i" +#define rv_fmt_rd_offset_rs1 "O\t0,i(1)" +#define rv_fmt_frd_offset_rs1 "O\t3,i(1)" +#define rv_fmt_rd_csr_rs1 "O\t0,c,1" +#define rv_fmt_rd_csr_zimm "O\t0,c,7" +#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)" +#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)" +#define rv_fmt_rs1_rs2_offset "O\t1,2,o" +#define rv_fmt_rs2_rs1_offset "O\t2,1,o" +#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)" +#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)" +#define rv_fmt_rd "O\t0" +#define rv_fmt_rd_zimm "O\t0,7" +#define rv_fmt_rd_rs1 "O\t0,1" +#define rv_fmt_rd_rs2 "O\t0,2" +#define rv_fmt_rs1_offset "O\t1,o" +#define rv_fmt_rs2_offset "O\t2,o" + +/* pseudo-instruction constraints */ + +static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; +static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end }; +static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end }; +static const rvc_constraint rvcc_negw[] = { rvc_rs1_eq_x0, rvc_end }; +static const rvc_constraint rvcc_sext_w[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_seqz[] = { rvc_imm_eq_p1, rvc_end }; +static const rvc_constraint rvcc_snez[] = { rvc_rs1_eq_x0, rvc_end }; +static const rvc_constraint rvcc_sltz[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_sgtz[] = { rvc_rs1_eq_x0, rvc_end }; +static const rvc_constraint rvcc_fmv_s[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fabs_s[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fneg_s[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fmv_d[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fabs_d[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fneg_d[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fmv_q[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fabs_q[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_fneg_q[] = { rvc_rs2_eq_rs1, rvc_end }; +static const rvc_constraint rvcc_beqz[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_bnez[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_blez[] = { rvc_rs1_eq_x0, rvc_end }; +static const rvc_constraint rvcc_bgez[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_bltz[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_bgtz[] = { rvc_rs1_eq_x0, rvc_end }; +static const rvc_constraint rvcc_ble[] = { rvc_end }; +static const rvc_constraint rvcc_bleu[] = { rvc_end }; +static const rvc_constraint rvcc_bgt[] = { rvc_end }; +static const rvc_constraint rvcc_bgtu[] = { rvc_end }; +static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; +static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; +static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, rvc_end }; +static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, rvc_end }; +static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end }; +static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; +static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end }; +static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; +static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end }; +static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end }; +static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end }; +static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end }; +static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end }; +static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end }; +static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end }; +static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end }; + +/* pseudo-instruction metadata */ + +static const rv_comp_data rvcp_jal[] = { + { rv_op_j, rvcc_j }, + { rv_op_jal, rvcc_jal }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_jalr[] = { + { rv_op_ret, rvcc_ret }, + { rv_op_jr, rvcc_jr }, + { rv_op_jalr, rvcc_jalr }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_beq[] = { + { rv_op_beqz, rvcc_beqz }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_bne[] = { + { rv_op_bnez, rvcc_bnez }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_blt[] = { + { rv_op_bltz, rvcc_bltz }, + { rv_op_bgtz, rvcc_bgtz }, + { rv_op_bgt, rvcc_bgt }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_bge[] = { + { rv_op_blez, rvcc_blez }, + { rv_op_bgez, rvcc_bgez }, + { rv_op_ble, rvcc_ble }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_bltu[] = { + { rv_op_bgtu, rvcc_bgtu }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_bgeu[] = { + { rv_op_bleu, rvcc_bleu }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_addi[] = { + { rv_op_nop, rvcc_nop }, + { rv_op_mv, rvcc_mv }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_sltiu[] = { + { rv_op_seqz, rvcc_seqz }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_xori[] = { + { rv_op_not, rvcc_not }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_sub[] = { + { rv_op_neg, rvcc_neg }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_slt[] = { + { rv_op_sltz, rvcc_sltz }, + { rv_op_sgtz, rvcc_sgtz }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_sltu[] = { + { rv_op_snez, rvcc_snez }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_addiw[] = { + { rv_op_sext_w, rvcc_sext_w }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_subw[] = { + { rv_op_negw, rvcc_negw }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_csrrw[] = { + { rv_op_fscsr, rvcc_fscsr }, + { rv_op_fsrm, rvcc_fsrm }, + { rv_op_fsflags, rvcc_fsflags }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_csrrs[] = { + { rv_op_rdcycle, rvcc_rdcycle }, + { rv_op_rdtime, rvcc_rdtime }, + { rv_op_rdinstret, rvcc_rdinstret }, + { rv_op_rdcycleh, rvcc_rdcycleh }, + { rv_op_rdtimeh, rvcc_rdtimeh }, + { rv_op_rdinstreth, rvcc_rdinstreth }, + { rv_op_frcsr, rvcc_frcsr }, + { rv_op_frrm, rvcc_frrm }, + { rv_op_frflags, rvcc_frflags }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_csrrwi[] = { + { rv_op_fsrmi, rvcc_fsrmi }, + { rv_op_fsflagsi, rvcc_fsflagsi }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnj_s[] = { + { rv_op_fmv_s, rvcc_fmv_s }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnjn_s[] = { + { rv_op_fneg_s, rvcc_fneg_s }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnjx_s[] = { + { rv_op_fabs_s, rvcc_fabs_s }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnj_d[] = { + { rv_op_fmv_d, rvcc_fmv_d }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnjn_d[] = { + { rv_op_fneg_d, rvcc_fneg_d }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnjx_d[] = { + { rv_op_fabs_d, rvcc_fabs_d }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnj_q[] = { + { rv_op_fmv_q, rvcc_fmv_q }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnjn_q[] = { + { rv_op_fneg_q, rvcc_fneg_q }, + { rv_op_illegal, NULL } +}; + +static const rv_comp_data rvcp_fsgnjx_q[] = { + { rv_op_fabs_q, rvcc_fabs_q }, + { rv_op_illegal, NULL } +}; + +/* instruction metadata */ + +const rv_opcode_data opcode_data[] = { + { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, + { "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 }, + { "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 }, + { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal, 0, 0, 0 }, + { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr, 0, 0, 0 }, + { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq, 0, 0, 0 }, + { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne, 0, 0, 0 }, + { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt, 0, 0, 0 }, + { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge, 0, 0, 0 }, + { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu, 0, 0, 0 }, + { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu, 0, 0, 0 }, + { "lb", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "lh", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "lw", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, + { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, + { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, + { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi, 0, 0, 0 }, + { "slti", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu, 0, 0, 0 }, + { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori, 0, 0, 0 }, + { "ori", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "andi", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub, 0, 0, 0 }, + { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt, 0, 0, 0 }, + { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu, 0, 0, 0 }, + { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "or", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "and", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "fence", rv_codec_r_f, rv_fmt_pred_succ, NULL, 0, 0, 0 }, + { "fence.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "ld", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, + { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw, 0, 0, 0 }, + { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw, 0, 0, 0 }, + { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "lq", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, + { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, + { "addid", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "div", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, + { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, + { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, + { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "ecall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "ebreak", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "uret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "sret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "hret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "mret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "dret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "sfence.vm", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, + { "wfi", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw, 0, 0, 0 }, + { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs, 0, 0, 0 }, + { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1, NULL, 0, 0, 0 }, + { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi, 0, 0, 0 }, + { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 }, + { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 }, + { "flw", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, + { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, + { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s, 0, 0, 0 }, + { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s, 0, 0, 0 }, + { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s, 0, 0, 0 }, + { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fclass.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fld", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, + { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, + { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d, 0, 0, 0 }, + { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d, 0, 0, 0 }, + { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d, 0, 0, 0 }, + { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fclass.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, + { "flq", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, + { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, + { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, + { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q, 0, 0, 0 }, + { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q, 0, 0, 0 }, + { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q, 0, 0, 0 }, + { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fclass.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, + { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, + { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 }, + { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, + { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, + { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, 0 }, + { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, + { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, + { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, + { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui }, + { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli }, + { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai }, + { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi }, + { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub }, + { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor }, + { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or }, + { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, rv_op_and }, + { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, rv_op_subw }, + { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, rv_op_addw }, + { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal }, + { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq }, + { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne }, + { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli }, + { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld }, + { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, + { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, + { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr }, + { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, rv_op_ebreak, rv_op_ebreak }, + { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr }, + { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, rv_op_add }, + { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, rv_op_fsd }, + { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, + { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, + { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld }, + { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd }, + { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, rv_op_addiw }, + { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld }, + { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd }, + { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, + { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, + { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, + { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, + { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, + { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "neg", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, + { "negw", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, + { "sext.w", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "seqz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "snez", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, + { "sltz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "sgtz", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, + { "fmv.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fabs.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fneg.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fmv.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fabs.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fneg.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fmv.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fabs.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fneg.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "beqz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, + { "bnez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, + { "blez", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, + { "bgez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, + { "bltz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, + { "bgtz", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, + { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, + { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, + { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, + { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, + { "j", rv_codec_uj, rv_fmt_offset, NULL, 0, 0, 0 }, + { "ret", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, + { "jr", rv_codec_i, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "rdcycle", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "rdtime", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "rdinstret", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "rdcycleh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "rdtimeh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "rdinstreth", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "frcsr", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "frrm", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "frflags", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, + { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, + { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, +}; + +/* CSR names */ + +static const char *csr_name(int csrno) +{ + switch (csrno) { + case 0x0000: return "ustatus"; + case 0x0001: return "fflags"; + case 0x0002: return "frm"; + case 0x0003: return "fcsr"; + case 0x0004: return "uie"; + case 0x0005: return "utvec"; + case 0x0040: return "uscratch"; + case 0x0041: return "uepc"; + case 0x0042: return "ucause"; + case 0x0043: return "utval"; + case 0x0044: return "uip"; + case 0x0100: return "sstatus"; + case 0x0102: return "sedeleg"; + case 0x0103: return "sideleg"; + case 0x0104: return "sie"; + case 0x0105: return "stvec"; + case 0x0106: return "scounteren"; + case 0x0140: return "sscratch"; + case 0x0141: return "sepc"; + case 0x0142: return "scause"; + case 0x0143: return "stval"; + case 0x0144: return "sip"; + case 0x0180: return "satp"; + case 0x0200: return "hstatus"; + case 0x0202: return "hedeleg"; + case 0x0203: return "hideleg"; + case 0x0204: return "hie"; + case 0x0205: return "htvec"; + case 0x0240: return "hscratch"; + case 0x0241: return "hepc"; + case 0x0242: return "hcause"; + case 0x0243: return "hbadaddr"; + case 0x0244: return "hip"; + case 0x0300: return "mstatus"; + case 0x0301: return "misa"; + case 0x0302: return "medeleg"; + case 0x0303: return "mideleg"; + case 0x0304: return "mie"; + case 0x0305: return "mtvec"; + case 0x0306: return "mcounteren"; + case 0x0320: return "mucounteren"; + case 0x0321: return "mscounteren"; + case 0x0322: return "mhcounteren"; + case 0x0323: return "mhpmevent3"; + case 0x0324: return "mhpmevent4"; + case 0x0325: return "mhpmevent5"; + case 0x0326: return "mhpmevent6"; + case 0x0327: return "mhpmevent7"; + case 0x0328: return "mhpmevent8"; + case 0x0329: return "mhpmevent9"; + case 0x032a: return "mhpmevent10"; + case 0x032b: return "mhpmevent11"; + case 0x032c: return "mhpmevent12"; + case 0x032d: return "mhpmevent13"; + case 0x032e: return "mhpmevent14"; + case 0x032f: return "mhpmevent15"; + case 0x0330: return "mhpmevent16"; + case 0x0331: return "mhpmevent17"; + case 0x0332: return "mhpmevent18"; + case 0x0333: return "mhpmevent19"; + case 0x0334: return "mhpmevent20"; + case 0x0335: return "mhpmevent21"; + case 0x0336: return "mhpmevent22"; + case 0x0337: return "mhpmevent23"; + case 0x0338: return "mhpmevent24"; + case 0x0339: return "mhpmevent25"; + case 0x033a: return "mhpmevent26"; + case 0x033b: return "mhpmevent27"; + case 0x033c: return "mhpmevent28"; + case 0x033d: return "mhpmevent29"; + case 0x033e: return "mhpmevent30"; + case 0x033f: return "mhpmevent31"; + case 0x0340: return "mscratch"; + case 0x0341: return "mepc"; + case 0x0342: return "mcause"; + case 0x0343: return "mtval"; + case 0x0344: return "mip"; + case 0x0380: return "mbase"; + case 0x0381: return "mbound"; + case 0x0382: return "mibase"; + case 0x0383: return "mibound"; + case 0x0384: return "mdbase"; + case 0x0385: return "mdbound"; + case 0x03a0: return "pmpcfg3"; + case 0x03b0: return "pmpaddr0"; + case 0x03b1: return "pmpaddr1"; + case 0x03b2: return "pmpaddr2"; + case 0x03b3: return "pmpaddr3"; + case 0x03b4: return "pmpaddr4"; + case 0x03b5: return "pmpaddr5"; + case 0x03b6: return "pmpaddr6"; + case 0x03b7: return "pmpaddr7"; + case 0x03b8: return "pmpaddr8"; + case 0x03b9: return "pmpaddr9"; + case 0x03ba: return "pmpaddr10"; + case 0x03bb: return "pmpaddr11"; + case 0x03bc: return "pmpaddr12"; + case 0x03bd: return "pmpaddr14"; + case 0x03be: return "pmpaddr13"; + case 0x03bf: return "pmpaddr15"; + case 0x0780: return "mtohost"; + case 0x0781: return "mfromhost"; + case 0x0782: return "mreset"; + case 0x0783: return "mipi"; + case 0x0784: return "miobase"; + case 0x07a0: return "tselect"; + case 0x07a1: return "tdata1"; + case 0x07a2: return "tdata2"; + case 0x07a3: return "tdata3"; + case 0x07b0: return "dcsr"; + case 0x07b1: return "dpc"; + case 0x07b2: return "dscratch"; + case 0x0b00: return "mcycle"; + case 0x0b01: return "mtime"; + case 0x0b02: return "minstret"; + case 0x0b03: return "mhpmcounter3"; + case 0x0b04: return "mhpmcounter4"; + case 0x0b05: return "mhpmcounter5"; + case 0x0b06: return "mhpmcounter6"; + case 0x0b07: return "mhpmcounter7"; + case 0x0b08: return "mhpmcounter8"; + case 0x0b09: return "mhpmcounter9"; + case 0x0b0a: return "mhpmcounter10"; + case 0x0b0b: return "mhpmcounter11"; + case 0x0b0c: return "mhpmcounter12"; + case 0x0b0d: return "mhpmcounter13"; + case 0x0b0e: return "mhpmcounter14"; + case 0x0b0f: return "mhpmcounter15"; + case 0x0b10: return "mhpmcounter16"; + case 0x0b11: return "mhpmcounter17"; + case 0x0b12: return "mhpmcounter18"; + case 0x0b13: return "mhpmcounter19"; + case 0x0b14: return "mhpmcounter20"; + case 0x0b15: return "mhpmcounter21"; + case 0x0b16: return "mhpmcounter22"; + case 0x0b17: return "mhpmcounter23"; + case 0x0b18: return "mhpmcounter24"; + case 0x0b19: return "mhpmcounter25"; + case 0x0b1a: return "mhpmcounter26"; + case 0x0b1b: return "mhpmcounter27"; + case 0x0b1c: return "mhpmcounter28"; + case 0x0b1d: return "mhpmcounter29"; + case 0x0b1e: return "mhpmcounter30"; + case 0x0b1f: return "mhpmcounter31"; + case 0x0b80: return "mcycleh"; + case 0x0b81: return "mtimeh"; + case 0x0b82: return "minstreth"; + case 0x0b83: return "mhpmcounter3h"; + case 0x0b84: return "mhpmcounter4h"; + case 0x0b85: return "mhpmcounter5h"; + case 0x0b86: return "mhpmcounter6h"; + case 0x0b87: return "mhpmcounter7h"; + case 0x0b88: return "mhpmcounter8h"; + case 0x0b89: return "mhpmcounter9h"; + case 0x0b8a: return "mhpmcounter10h"; + case 0x0b8b: return "mhpmcounter11h"; + case 0x0b8c: return "mhpmcounter12h"; + case 0x0b8d: return "mhpmcounter13h"; + case 0x0b8e: return "mhpmcounter14h"; + case 0x0b8f: return "mhpmcounter15h"; + case 0x0b90: return "mhpmcounter16h"; + case 0x0b91: return "mhpmcounter17h"; + case 0x0b92: return "mhpmcounter18h"; + case 0x0b93: return "mhpmcounter19h"; + case 0x0b94: return "mhpmcounter20h"; + case 0x0b95: return "mhpmcounter21h"; + case 0x0b96: return "mhpmcounter22h"; + case 0x0b97: return "mhpmcounter23h"; + case 0x0b98: return "mhpmcounter24h"; + case 0x0b99: return "mhpmcounter25h"; + case 0x0b9a: return "mhpmcounter26h"; + case 0x0b9b: return "mhpmcounter27h"; + case 0x0b9c: return "mhpmcounter28h"; + case 0x0b9d: return "mhpmcounter29h"; + case 0x0b9e: return "mhpmcounter30h"; + case 0x0b9f: return "mhpmcounter31h"; + case 0x0c00: return "cycle"; + case 0x0c01: return "time"; + case 0x0c02: return "instret"; + case 0x0c80: return "cycleh"; + case 0x0c81: return "timeh"; + case 0x0c82: return "instreth"; + case 0x0d00: return "scycle"; + case 0x0d01: return "stime"; + case 0x0d02: return "sinstret"; + case 0x0d80: return "scycleh"; + case 0x0d81: return "stimeh"; + case 0x0d82: return "sinstreth"; + case 0x0e00: return "hcycle"; + case 0x0e01: return "htime"; + case 0x0e02: return "hinstret"; + case 0x0e80: return "hcycleh"; + case 0x0e81: return "htimeh"; + case 0x0e82: return "hinstreth"; + case 0x0f11: return "mvendorid"; + case 0x0f12: return "marchid"; + case 0x0f13: return "mimpid"; + case 0x0f14: return "mhartid"; + default: return NULL; + } +} + +/* decode opcode */ + +static void decode_inst_opcode(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + switch (((inst >> 0) & 0b11)) { + case 0: + switch (((inst >> 13) & 0b111)) { + case 0: op = rv_op_c_addi4spn; break; + case 1: + if (isa == rv128) { + op = rv_op_c_lq; + } else { + op = rv_op_c_fld; + } + break; + case 2: op = rv_op_c_lw; break; + case 3: + if (isa == rv32) { + op = rv_op_c_flw; + } else { + op = rv_op_c_ld; + } + break; + case 5: + if (isa == rv128) { + op = rv_op_c_sq; + } else { + op = rv_op_c_fsd; + } + break; + case 6: op = rv_op_c_sw; break; + case 7: + if (isa == rv32) { + op = rv_op_c_fsw; + } else { + op = rv_op_c_sd; + } + break; + } + break; + case 1: + switch (((inst >> 13) & 0b111)) { + case 0: + switch (((inst >> 2) & 0b11111111111)) { + case 0: op = rv_op_c_nop; break; + default: op = rv_op_c_addi; break; + } + break; + case 1: + if (isa == rv32) { + op = rv_op_c_jal; + } else { + op = rv_op_c_addiw; + } + break; + case 2: op = rv_op_c_li; break; + case 3: + switch (((inst >> 7) & 0b11111)) { + case 2: op = rv_op_c_addi16sp; break; + default: op = rv_op_c_lui; break; + } + break; + case 4: + switch (((inst >> 10) & 0b11)) { + case 0: + op = rv_op_c_srli; + break; + case 1: + op = rv_op_c_srai; + break; + case 2: op = rv_op_c_andi; break; + case 3: + switch (((inst >> 10) & 0b100) | ((inst >> 5) & 0b011)) { + case 0: op = rv_op_c_sub; break; + case 1: op = rv_op_c_xor; break; + case 2: op = rv_op_c_or; break; + case 3: op = rv_op_c_and; break; + case 4: op = rv_op_c_subw; break; + case 5: op = rv_op_c_addw; break; + } + break; + } + break; + case 5: op = rv_op_c_j; break; + case 6: op = rv_op_c_beqz; break; + case 7: op = rv_op_c_bnez; break; + } + break; + case 2: + switch (((inst >> 13) & 0b111)) { + case 0: + op = rv_op_c_slli; + break; + case 1: + if (isa == rv128) { + op = rv_op_c_lqsp; + } else { + op = rv_op_c_fldsp; + } + break; + case 2: op = rv_op_c_lwsp; break; + case 3: + if (isa == rv32) { + op = rv_op_c_flwsp; + } else { + op = rv_op_c_ldsp; + } + break; + case 4: + switch (((inst >> 12) & 0b1)) { + case 0: + switch (((inst >> 2) & 0b11111)) { + case 0: op = rv_op_c_jr; break; + default: op = rv_op_c_mv; break; + } + break; + case 1: + switch (((inst >> 2) & 0b11111)) { + case 0: + switch (((inst >> 7) & 0b11111)) { + case 0: op = rv_op_c_ebreak; break; + default: op = rv_op_c_jalr; break; + } + break; + default: op = rv_op_c_add; break; + } + break; + } + break; + case 5: + if (isa == rv128) { + op = rv_op_c_sqsp; + } else { + op = rv_op_c_fsdsp; break; + } + case 6: op = rv_op_c_swsp; break; + case 7: + if (isa == rv32) { + op = rv_op_c_fswsp; + } else { + op = rv_op_c_sdsp; + } + break; + } + break; + case 3: + switch (((inst >> 2) & 0b11111)) { + case 0: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_lb; break; + case 1: op = rv_op_lh; break; + case 2: op = rv_op_lw; break; + case 3: op = rv_op_ld; break; + case 4: op = rv_op_lbu; break; + case 5: op = rv_op_lhu; break; + case 6: op = rv_op_lwu; break; + case 7: op = rv_op_ldu; break; + } + break; + case 1: + switch (((inst >> 12) & 0b111)) { + case 2: op = rv_op_flw; break; + case 3: op = rv_op_fld; break; + case 4: op = rv_op_flq; break; + } + break; + case 3: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fence; break; + case 1: op = rv_op_fence_i; break; + case 2: op = rv_op_lq; break; + } + break; + case 4: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_addi; break; + case 1: + switch (((inst >> 27) & 0b11111)) { + case 0: op = rv_op_slli; break; + } + break; + case 2: op = rv_op_slti; break; + case 3: op = rv_op_sltiu; break; + case 4: op = rv_op_xori; break; + case 5: + switch (((inst >> 27) & 0b11111)) { + case 0: op = rv_op_srli; break; + case 8: op = rv_op_srai; break; + } + break; + case 6: op = rv_op_ori; break; + case 7: op = rv_op_andi; break; + } + break; + case 5: op = rv_op_auipc; break; + case 6: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_addiw; break; + case 1: + switch (((inst >> 25) & 0b1111111)) { + case 0: op = rv_op_slliw; break; + } + break; + case 5: + switch (((inst >> 25) & 0b1111111)) { + case 0: op = rv_op_srliw; break; + case 32: op = rv_op_sraiw; break; + } + break; + } + break; + case 8: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_sb; break; + case 1: op = rv_op_sh; break; + case 2: op = rv_op_sw; break; + case 3: op = rv_op_sd; break; + case 4: op = rv_op_sq; break; + } + break; + case 9: + switch (((inst >> 12) & 0b111)) { + case 2: op = rv_op_fsw; break; + case 3: op = rv_op_fsd; break; + case 4: op = rv_op_fsq; break; + } + break; + case 11: + switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 2: op = rv_op_amoadd_w; break; + case 3: op = rv_op_amoadd_d; break; + case 4: op = rv_op_amoadd_q; break; + case 10: op = rv_op_amoswap_w; break; + case 11: op = rv_op_amoswap_d; break; + case 12: op = rv_op_amoswap_q; break; + case 18: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_lr_w; break; + } + break; + case 19: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_lr_d; break; + } + break; + case 20: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_lr_q; break; + } + break; + case 26: op = rv_op_sc_w; break; + case 27: op = rv_op_sc_d; break; + case 28: op = rv_op_sc_q; break; + case 34: op = rv_op_amoxor_w; break; + case 35: op = rv_op_amoxor_d; break; + case 36: op = rv_op_amoxor_q; break; + case 66: op = rv_op_amoor_w; break; + case 67: op = rv_op_amoor_d; break; + case 68: op = rv_op_amoor_q; break; + case 98: op = rv_op_amoand_w; break; + case 99: op = rv_op_amoand_d; break; + case 100: op = rv_op_amoand_q; break; + case 130: op = rv_op_amomin_w; break; + case 131: op = rv_op_amomin_d; break; + case 132: op = rv_op_amomin_q; break; + case 162: op = rv_op_amomax_w; break; + case 163: op = rv_op_amomax_d; break; + case 164: op = rv_op_amomax_q; break; + case 194: op = rv_op_amominu_w; break; + case 195: op = rv_op_amominu_d; break; + case 196: op = rv_op_amominu_q; break; + case 226: op = rv_op_amomaxu_w; break; + case 227: op = rv_op_amomaxu_d; break; + case 228: op = rv_op_amomaxu_q; break; + } + break; + case 12: + switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + case 0: op = rv_op_add; break; + case 1: op = rv_op_sll; break; + case 2: op = rv_op_slt; break; + case 3: op = rv_op_sltu; break; + case 4: op = rv_op_xor; break; + case 5: op = rv_op_srl; break; + case 6: op = rv_op_or; break; + case 7: op = rv_op_and; break; + case 8: op = rv_op_mul; break; + case 9: op = rv_op_mulh; break; + case 10: op = rv_op_mulhsu; break; + case 11: op = rv_op_mulhu; break; + case 12: op = rv_op_div; break; + case 13: op = rv_op_divu; break; + case 14: op = rv_op_rem; break; + case 15: op = rv_op_remu; break; + case 256: op = rv_op_sub; break; + case 261: op = rv_op_sra; break; + } + break; + case 13: op = rv_op_lui; break; + case 14: + switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + case 0: op = rv_op_addw; break; + case 1: op = rv_op_sllw; break; + case 5: op = rv_op_srlw; break; + case 8: op = rv_op_mulw; break; + case 12: op = rv_op_divw; break; + case 13: op = rv_op_divuw; break; + case 14: op = rv_op_remw; break; + case 15: op = rv_op_remuw; break; + case 256: op = rv_op_subw; break; + case 261: op = rv_op_sraw; break; + } + break; + case 16: + switch (((inst >> 25) & 0b11)) { + case 0: op = rv_op_fmadd_s; break; + case 1: op = rv_op_fmadd_d; break; + case 3: op = rv_op_fmadd_q; break; + } + break; + case 17: + switch (((inst >> 25) & 0b11)) { + case 0: op = rv_op_fmsub_s; break; + case 1: op = rv_op_fmsub_d; break; + case 3: op = rv_op_fmsub_q; break; + } + break; + case 18: + switch (((inst >> 25) & 0b11)) { + case 0: op = rv_op_fnmsub_s; break; + case 1: op = rv_op_fnmsub_d; break; + case 3: op = rv_op_fnmsub_q; break; + } + break; + case 19: + switch (((inst >> 25) & 0b11)) { + case 0: op = rv_op_fnmadd_s; break; + case 1: op = rv_op_fnmadd_d; break; + case 3: op = rv_op_fnmadd_q; break; + } + break; + case 20: + switch (((inst >> 25) & 0b1111111)) { + case 0: op = rv_op_fadd_s; break; + case 1: op = rv_op_fadd_d; break; + case 3: op = rv_op_fadd_q; break; + case 4: op = rv_op_fsub_s; break; + case 5: op = rv_op_fsub_d; break; + case 7: op = rv_op_fsub_q; break; + case 8: op = rv_op_fmul_s; break; + case 9: op = rv_op_fmul_d; break; + case 11: op = rv_op_fmul_q; break; + case 12: op = rv_op_fdiv_s; break; + case 13: op = rv_op_fdiv_d; break; + case 15: op = rv_op_fdiv_q; break; + case 16: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fsgnj_s; break; + case 1: op = rv_op_fsgnjn_s; break; + case 2: op = rv_op_fsgnjx_s; break; + } + break; + case 17: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fsgnj_d; break; + case 1: op = rv_op_fsgnjn_d; break; + case 2: op = rv_op_fsgnjx_d; break; + } + break; + case 19: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fsgnj_q; break; + case 1: op = rv_op_fsgnjn_q; break; + case 2: op = rv_op_fsgnjx_q; break; + } + break; + case 20: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmin_s; break; + case 1: op = rv_op_fmax_s; break; + } + break; + case 21: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmin_d; break; + case 1: op = rv_op_fmax_d; break; + } + break; + case 23: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmin_q; break; + case 1: op = rv_op_fmax_q; break; + } + break; + case 32: + switch (((inst >> 20) & 0b11111)) { + case 1: op = rv_op_fcvt_s_d; break; + case 3: op = rv_op_fcvt_s_q; break; + } + break; + case 33: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_d_s; break; + case 3: op = rv_op_fcvt_d_q; break; + } + break; + case 35: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_q_s; break; + case 1: op = rv_op_fcvt_q_d; break; + } + break; + case 44: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fsqrt_s; break; + } + break; + case 45: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fsqrt_d; break; + } + break; + case 47: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fsqrt_q; break; + } + break; + case 80: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fle_s; break; + case 1: op = rv_op_flt_s; break; + case 2: op = rv_op_feq_s; break; + } + break; + case 81: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fle_d; break; + case 1: op = rv_op_flt_d; break; + case 2: op = rv_op_feq_d; break; + } + break; + case 83: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fle_q; break; + case 1: op = rv_op_flt_q; break; + case 2: op = rv_op_feq_q; break; + } + break; + case 96: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_w_s; break; + case 1: op = rv_op_fcvt_wu_s; break; + case 2: op = rv_op_fcvt_l_s; break; + case 3: op = rv_op_fcvt_lu_s; break; + } + break; + case 97: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_w_d; break; + case 1: op = rv_op_fcvt_wu_d; break; + case 2: op = rv_op_fcvt_l_d; break; + case 3: op = rv_op_fcvt_lu_d; break; + } + break; + case 99: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_w_q; break; + case 1: op = rv_op_fcvt_wu_q; break; + case 2: op = rv_op_fcvt_l_q; break; + case 3: op = rv_op_fcvt_lu_q; break; + } + break; + case 104: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_s_w; break; + case 1: op = rv_op_fcvt_s_wu; break; + case 2: op = rv_op_fcvt_s_l; break; + case 3: op = rv_op_fcvt_s_lu; break; + } + break; + case 105: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_d_w; break; + case 1: op = rv_op_fcvt_d_wu; break; + case 2: op = rv_op_fcvt_d_l; break; + case 3: op = rv_op_fcvt_d_lu; break; + } + break; + case 107: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_fcvt_q_w; break; + case 1: op = rv_op_fcvt_q_wu; break; + case 2: op = rv_op_fcvt_q_l; break; + case 3: op = rv_op_fcvt_q_lu; break; + } + break; + case 112: + switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_x_s; break; + case 1: op = rv_op_fclass_s; break; + } + break; + case 113: + switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_x_d; break; + case 1: op = rv_op_fclass_d; break; + } + break; + case 115: + switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_x_q; break; + case 1: op = rv_op_fclass_q; break; + } + break; + case 120: + switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_s_x; break; + } + break; + case 121: + switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_d_x; break; + } + break; + case 123: + switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_q_x; break; + } + break; + } + break; + case 22: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_addid; break; + case 1: + switch (((inst >> 26) & 0b111111)) { + case 0: op = rv_op_sllid; break; + } + break; + case 5: + switch (((inst >> 26) & 0b111111)) { + case 0: op = rv_op_srlid; break; + case 16: op = rv_op_sraid; break; + } + break; + } + break; + case 24: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_beq; break; + case 1: op = rv_op_bne; break; + case 4: op = rv_op_blt; break; + case 5: op = rv_op_bge; break; + case 6: op = rv_op_bltu; break; + case 7: op = rv_op_bgeu; break; + } + break; + case 25: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_jalr; break; + } + break; + case 27: op = rv_op_jal; break; + case 28: + switch (((inst >> 12) & 0b111)) { + case 0: + switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) { + case 0: + switch (((inst >> 15) & 0b1111111111)) { + case 0: op = rv_op_ecall; break; + case 32: op = rv_op_ebreak; break; + case 64: op = rv_op_uret; break; + } + break; + case 256: + switch (((inst >> 20) & 0b11111)) { + case 2: + switch (((inst >> 15) & 0b11111)) { + case 0: op = rv_op_sret; break; + } + break; + case 4: op = rv_op_sfence_vm; break; + case 5: + switch (((inst >> 15) & 0b11111)) { + case 0: op = rv_op_wfi; break; + } + break; + } + break; + case 288: op = rv_op_sfence_vma; break; + case 512: + switch (((inst >> 15) & 0b1111111111)) { + case 64: op = rv_op_hret; break; + } + break; + case 768: + switch (((inst >> 15) & 0b1111111111)) { + case 64: op = rv_op_mret; break; + } + break; + case 1952: + switch (((inst >> 15) & 0b1111111111)) { + case 576: op = rv_op_dret; break; + } + break; + } + break; + case 1: op = rv_op_csrrw; break; + case 2: op = rv_op_csrrs; break; + case 3: op = rv_op_csrrc; break; + case 5: op = rv_op_csrrwi; break; + case 6: op = rv_op_csrrsi; break; + case 7: op = rv_op_csrrci; break; + } + break; + case 30: + switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + case 0: op = rv_op_addd; break; + case 1: op = rv_op_slld; break; + case 5: op = rv_op_srld; break; + case 8: op = rv_op_muld; break; + case 12: op = rv_op_divd; break; + case 13: op = rv_op_divud; break; + case 14: op = rv_op_remd; break; + case 15: op = rv_op_remud; break; + case 256: op = rv_op_subd; break; + case 261: op = rv_op_srad; break; + } + break; + } + break; + } + dec->op = op; +} + +/* operand extractors */ + +static uint32_t operand_rd(rv_inst inst) +{ + return (inst << 52) >> 59; +} + +static uint32_t operand_rs1(rv_inst inst) +{ + return (inst << 44) >> 59; +} + +static uint32_t operand_rs2(rv_inst inst) +{ + return (inst << 39) >> 59; +} + +static uint32_t operand_rs3(rv_inst inst) +{ + return (inst << 32) >> 59; +} + +static uint32_t operand_aq(rv_inst inst) +{ + return (inst << 37) >> 63; +} + +static uint32_t operand_rl(rv_inst inst) +{ + return (inst << 38) >> 63; +} + +static uint32_t operand_pred(rv_inst inst) +{ + return (inst << 36) >> 60; +} + +static uint32_t operand_succ(rv_inst inst) +{ + return (inst << 40) >> 60; +} + +static uint32_t operand_rm(rv_inst inst) +{ + return (inst << 49) >> 61; +} + +static uint32_t operand_shamt5(rv_inst inst) +{ + return (inst << 39) >> 59; +} + +static uint32_t operand_shamt6(rv_inst inst) +{ + return (inst << 38) >> 58; +} + +static uint32_t operand_shamt7(rv_inst inst) +{ + return (inst << 37) >> 57; +} + +static uint32_t operand_crdq(rv_inst inst) +{ + return (inst << 59) >> 61; +} + +static uint32_t operand_crs1q(rv_inst inst) +{ + return (inst << 54) >> 61; +} + +static uint32_t operand_crs1rdq(rv_inst inst) +{ + return (inst << 54) >> 61; +} + +static uint32_t operand_crs2q(rv_inst inst) +{ + return (inst << 59) >> 61; +} + +static uint32_t operand_crd(rv_inst inst) +{ + return (inst << 52) >> 59; +} + +static uint32_t operand_crs1(rv_inst inst) +{ + return (inst << 52) >> 59; +} + +static uint32_t operand_crs1rd(rv_inst inst) +{ + return (inst << 52) >> 59; +} + +static uint32_t operand_crs2(rv_inst inst) +{ + return (inst << 57) >> 59; +} + +static uint32_t operand_cimmsh5(rv_inst inst) +{ + return (inst << 57) >> 59; +} + +static uint32_t operand_csr12(rv_inst inst) +{ + return (inst << 32) >> 52; +} + +static int32_t operand_imm12(rv_inst inst) +{ + return ((int64_t)inst << 32) >> 52; +} + +static int32_t operand_imm20(rv_inst inst) +{ + return (((int64_t)inst << 32) >> 44) << 12; +} + +static int32_t operand_jimm20(rv_inst inst) +{ + return (((int64_t)inst << 32) >> 63) << 20 | + ((inst << 33) >> 54) << 1 | + ((inst << 43) >> 63) << 11 | + ((inst << 44) >> 56) << 12; +} + +static int32_t operand_simm12(rv_inst inst) +{ + return (((int64_t)inst << 32) >> 57) << 5 | + (inst << 52) >> 59; +} + +static int32_t operand_sbimm12(rv_inst inst) +{ + return (((int64_t)inst << 32) >> 63) << 12 | + ((inst << 33) >> 58) << 5 | + ((inst << 52) >> 60) << 1 | + ((inst << 56) >> 63) << 11; +} + +static uint32_t operand_cimmsh6(rv_inst inst) +{ + return ((inst << 51) >> 63) << 5 | + (inst << 57) >> 59; +} + +static int32_t operand_cimmi(rv_inst inst) +{ + return (((int64_t)inst << 51) >> 63) << 5 | + (inst << 57) >> 59; +} + +static int32_t operand_cimmui(rv_inst inst) +{ + return (((int64_t)inst << 51) >> 63) << 17 | + ((inst << 57) >> 59) << 12; +} + +static uint32_t operand_cimmlwsp(rv_inst inst) +{ + return ((inst << 51) >> 63) << 5 | + ((inst << 57) >> 61) << 2 | + ((inst << 60) >> 62) << 6; +} + +static uint32_t operand_cimmldsp(rv_inst inst) +{ + return ((inst << 51) >> 63) << 5 | + ((inst << 57) >> 62) << 3 | + ((inst << 59) >> 61) << 6; +} + +static uint32_t operand_cimmlqsp(rv_inst inst) +{ + return ((inst << 51) >> 63) << 5 | + ((inst << 57) >> 63) << 4 | + ((inst << 58) >> 60) << 6; +} + +static int32_t operand_cimm16sp(rv_inst inst) +{ + return (((int64_t)inst << 51) >> 63) << 9 | + ((inst << 57) >> 63) << 4 | + ((inst << 58) >> 63) << 6 | + ((inst << 59) >> 62) << 7 | + ((inst << 61) >> 63) << 5; +} + +static int32_t operand_cimmj(rv_inst inst) +{ + return (((int64_t)inst << 51) >> 63) << 11 | + ((inst << 52) >> 63) << 4 | + ((inst << 53) >> 62) << 8 | + ((inst << 55) >> 63) << 10 | + ((inst << 56) >> 63) << 6 | + ((inst << 57) >> 63) << 7 | + ((inst << 58) >> 61) << 1 | + ((inst << 61) >> 63) << 5; +} + +static int32_t operand_cimmb(rv_inst inst) +{ + return (((int64_t)inst << 51) >> 63) << 8 | + ((inst << 52) >> 62) << 3 | + ((inst << 57) >> 62) << 6 | + ((inst << 59) >> 62) << 1 | + ((inst << 61) >> 63) << 5; +} + +static uint32_t operand_cimmswsp(rv_inst inst) +{ + return ((inst << 51) >> 60) << 2 | + ((inst << 55) >> 62) << 6; +} + +static uint32_t operand_cimmsdsp(rv_inst inst) +{ + return ((inst << 51) >> 61) << 3 | + ((inst << 54) >> 61) << 6; +} + +static uint32_t operand_cimmsqsp(rv_inst inst) +{ + return ((inst << 51) >> 62) << 4 | + ((inst << 53) >> 60) << 6; +} + +static uint32_t operand_cimm4spn(rv_inst inst) +{ + return ((inst << 51) >> 62) << 4 | + ((inst << 53) >> 60) << 6 | + ((inst << 57) >> 63) << 2 | + ((inst << 58) >> 63) << 3; +} + +static uint32_t operand_cimmw(rv_inst inst) +{ + return ((inst << 51) >> 61) << 3 | + ((inst << 57) >> 63) << 2 | + ((inst << 58) >> 63) << 6; +} + +static uint32_t operand_cimmd(rv_inst inst) +{ + return ((inst << 51) >> 61) << 3 | + ((inst << 57) >> 62) << 6; +} + +static uint32_t operand_cimmq(rv_inst inst) +{ + return ((inst << 51) >> 62) << 4 | + ((inst << 53) >> 63) << 8 | + ((inst << 57) >> 62) << 6; +} + +/* decode operands */ + +static void decode_inst_operands(rv_decode *dec) +{ + rv_inst inst = dec->inst; + dec->codec = opcode_data[dec->op].codec; + switch (dec->codec) { + case rv_codec_none: + dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->imm = 0; + break; + case rv_codec_u: + dec->rd = operand_rd(inst); + dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->imm = operand_imm20(inst); + break; + case rv_codec_uj: + dec->rd = operand_rd(inst); + dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->imm = operand_jimm20(inst); + break; + case rv_codec_i: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_imm12(inst); + break; + case rv_codec_i_sh5: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_shamt5(inst); + break; + case rv_codec_i_sh6: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_shamt6(inst); + break; + case rv_codec_i_sh7: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_shamt7(inst); + break; + case rv_codec_i_csr: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_csr12(inst); + break; + case rv_codec_s: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_simm12(inst); + break; + case rv_codec_sb: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_sbimm12(inst); + break; + case rv_codec_r: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = 0; + break; + case rv_codec_r_m: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = 0; + dec->rm = operand_rm(inst); + break; + case rv_codec_r4_m: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->rs3 = operand_rs3(inst); + dec->imm = 0; + dec->rm = operand_rm(inst); + break; + case rv_codec_r_a: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = 0; + dec->aq = operand_aq(inst); + dec->rl = operand_rl(inst); + break; + case rv_codec_r_l: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = 0; + dec->aq = operand_aq(inst); + dec->rl = operand_rl(inst); + break; + case rv_codec_r_f: + dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->pred = operand_pred(inst); + dec->succ = operand_succ(inst); + dec->imm = 0; + break; + case rv_codec_cb: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmb(inst); + break; + case rv_codec_cb_imm: + dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmi(inst); + break; + case rv_codec_cb_sh5: + dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmsh5(inst); + break; + case rv_codec_cb_sh6: + dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmsh6(inst); + break; + case rv_codec_ci: + dec->rd = dec->rs1 = operand_crs1rd(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmi(inst); + break; + case rv_codec_ci_sh5: + dec->rd = dec->rs1 = operand_crs1rd(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmsh5(inst); + break; + case rv_codec_ci_sh6: + dec->rd = dec->rs1 = operand_crs1rd(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmsh6(inst); + break; + case rv_codec_ci_16sp: + dec->rd = rv_ireg_sp; + dec->rs1 = rv_ireg_sp; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimm16sp(inst); + break; + case rv_codec_ci_lwsp: + dec->rd = operand_crd(inst); + dec->rs1 = rv_ireg_sp; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmlwsp(inst); + break; + case rv_codec_ci_ldsp: + dec->rd = operand_crd(inst); + dec->rs1 = rv_ireg_sp; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmldsp(inst); + break; + case rv_codec_ci_lqsp: + dec->rd = operand_crd(inst); + dec->rs1 = rv_ireg_sp; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmlqsp(inst); + break; + case rv_codec_ci_li: + dec->rd = operand_crd(inst); + dec->rs1 = rv_ireg_zero; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmi(inst); + break; + case rv_codec_ci_lui: + dec->rd = operand_crd(inst); + dec->rs1 = rv_ireg_zero; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmui(inst); + break; + case rv_codec_ci_none: + dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->imm = 0; + break; + case rv_codec_ciw_4spn: + dec->rd = operand_crdq(inst) + 8; + dec->rs1 = rv_ireg_sp; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimm4spn(inst); + break; + case rv_codec_cj: + dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmj(inst); + break; + case rv_codec_cj_jal: + dec->rd = rv_ireg_ra; + dec->rs1 = dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmj(inst); + break; + case rv_codec_cl_lw: + dec->rd = operand_crdq(inst) + 8; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmw(inst); + break; + case rv_codec_cl_ld: + dec->rd = operand_crdq(inst) + 8; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmd(inst); + break; + case rv_codec_cl_lq: + dec->rd = operand_crdq(inst) + 8; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = rv_ireg_zero; + dec->imm = operand_cimmq(inst); + break; + case rv_codec_cr: + dec->rd = dec->rs1 = operand_crs1rd(inst); + dec->rs2 = operand_crs2(inst); + dec->imm = 0; + break; + case rv_codec_cr_mv: + dec->rd = operand_crd(inst); + dec->rs1 = operand_crs2(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = 0; + break; + case rv_codec_cr_jalr: + dec->rd = rv_ireg_ra; + dec->rs1 = operand_crs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = 0; + break; + case rv_codec_cr_jr: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_crs1(inst); + dec->rs2 = rv_ireg_zero; + dec->imm = 0; + break; + case rv_codec_cs: + dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + dec->imm = 0; + break; + case rv_codec_cs_sw: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + dec->imm = operand_cimmw(inst); + break; + case rv_codec_cs_sd: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + dec->imm = operand_cimmd(inst); + break; + case rv_codec_cs_sq: + dec->rd = rv_ireg_zero; + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + dec->imm = operand_cimmq(inst); + break; + case rv_codec_css_swsp: + dec->rd = rv_ireg_zero; + dec->rs1 = rv_ireg_sp; + dec->rs2 = operand_crs2(inst); + dec->imm = operand_cimmswsp(inst); + break; + case rv_codec_css_sdsp: + dec->rd = rv_ireg_zero; + dec->rs1 = rv_ireg_sp; + dec->rs2 = operand_crs2(inst); + dec->imm = operand_cimmsdsp(inst); + break; + case rv_codec_css_sqsp: + dec->rd = rv_ireg_zero; + dec->rs1 = rv_ireg_sp; + dec->rs2 = operand_crs2(inst); + dec->imm = operand_cimmsqsp(inst); + break; + }; +} + +/* check constraint */ + +static bool check_constraints(rv_decode *dec, const rvc_constraint *c) +{ + int32_t imm = dec->imm; + uint8_t rd = dec->rd, rs1 = dec->rs1, rs2 = dec->rs2; + while (*c != rvc_end) { + switch (*c) { + case rvc_simm_6: + if (!(imm >= -32 && imm < 32)) { + return false; + } + break; + case rvc_imm_6: + if (!(imm <= 63)) { + return false; + } + break; + case rvc_imm_7: + if (!(imm <= 127)) { + return false; + } + break; + case rvc_imm_8: + if (!(imm <= 255)) { + return false; + } + break; + case rvc_imm_9: + if (!(imm <= 511)) { + return false; + } + break; + case rvc_imm_10: + if (!(imm <= 1023)) { + return false; + } + break; + case rvc_imm_12: + if (!(imm <= 4095)) { + return false; + } + break; + case rvc_imm_18: + if (!(imm <= 262143)) { + return false; + } + break; + case rvc_imm_nz: + if (!(imm != 0)) { + return false; + } + break; + case rvc_imm_x2: + if (!((imm & 0b1) == 0)) { + return false; + } + break; + case rvc_imm_x4: + if (!((imm & 0b11) == 0)) { + return false; + } + break; + case rvc_imm_x8: + if (!((imm & 0b111) == 0)) { + return false; + } + break; + case rvc_imm_x16: + if (!((imm & 0b1111) == 0)) { + return false; + } + break; + case rvc_rd_b3: + if (!(rd >= 8 && rd <= 15)) { + return false; + } + break; + case rvc_rs1_b3: + if (!(rs1 >= 8 && rs1 <= 15)) { + return false; + } + break; + case rvc_rs2_b3: + if (!(rs2 >= 8 && rs2 <= 15)) { + return false; + } + break; + case rvc_rd_eq_rs1: + if (!(rd == rs1)) { + return false; + } + break; + case rvc_rd_eq_ra: + if (!(rd == 1)) { + return false; + } + break; + case rvc_rd_eq_sp: + if (!(rd == 2)) { + return false; + } + break; + case rvc_rd_eq_x0: + if (!(rd == 0)) { + return false; + } + break; + case rvc_rs1_eq_sp: + if (!(rs1 == 2)) { + return false; + } + break; + case rvc_rs1_eq_x0: + if (!(rs1 == 0)) { + return false; + } + break; + case rvc_rs2_eq_x0: + if (!(rs2 == 0)) { + return false; + } + break; + case rvc_rd_ne_x0_x2: + if (!(rd != 0 && rd != 2)) { + return false; + } + break; + case rvc_rd_ne_x0: + if (!(rd != 0)) { + return false; + } + break; + case rvc_rs1_ne_x0: + if (!(rs1 != 0)) { + return false; + } + break; + case rvc_rs2_ne_x0: + if (!(rs2 != 0)) { + return false; + } + break; + case rvc_rs2_eq_rs1: + if (!(rs2 == rs1)) { + return false; + } + break; + case rvc_rs1_eq_ra: + if (!(rs1 == 1)) { + return false; + } + break; + case rvc_imm_eq_zero: + if (!(imm == 0)) { + return false; + } + break; + case rvc_imm_eq_n1: + if (!(imm == -1)) { + return false; + } + break; + case rvc_imm_eq_p1: + if (!(imm == 1)) { + return false; + } + break; + case rvc_csr_eq_0x001: + if (!(imm == 0x001)) { + return false; + } + break; + case rvc_csr_eq_0x002: + if (!(imm == 0x002)) { + return false; + } + break; + case rvc_csr_eq_0x003: + if (!(imm == 0x003)) { + return false; + } + break; + case rvc_csr_eq_0xc00: + if (!(imm == 0xc00)) { + return false; + } + break; + case rvc_csr_eq_0xc01: + if (!(imm == 0xc01)) { + return false; + } + break; + case rvc_csr_eq_0xc02: + if (!(imm == 0xc02)) { + return false; + } + break; + case rvc_csr_eq_0xc80: + if (!(imm == 0xc80)) { + return false; + } + break; + case rvc_csr_eq_0xc81: + if (!(imm == 0xc81)) { + return false; + } + break; + case rvc_csr_eq_0xc82: + if (!(imm == 0xc82)) { + return false; + } + break; + default: break; + } + c++; + } + return true; +} + +/* instruction length */ + +static size_t inst_length(rv_inst inst) +{ + /* NOTE: supports maximum instruction size of 64-bits */ + + /* instruction length coding + * + * aa - 16 bit aa != 11 + * bbb11 - 32 bit bbb != 111 + * 011111 - 48 bit + * 0111111 - 64 bit + */ + + return (inst & 0b11) != 0b11 ? 2 + : (inst & 0b11100) != 0b11100 ? 4 + : (inst & 0b111111) == 0b011111 ? 6 + : (inst & 0b1111111) == 0b0111111 ? 8 + : 0; +} + +/* format instruction */ + +static void append(char *s1, const char *s2, size_t n) +{ + size_t l1 = strlen(s1); + if (n - l1 - 1 > 0) { + strncat(s1, s2, n - l1); + } +} + +static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) +{ + char tmp[64]; + const char *fmt; + + if (dec->op == rv_op_illegal) { + size_t len = inst_length(dec->inst); + switch (len) { + case 2: + snprintf(buf, buflen, "(0x%04" PRIx64 ")", dec->inst); + break; + case 4: + snprintf(buf, buflen, "(0x%08" PRIx64 ")", dec->inst); + break; + case 6: + snprintf(buf, buflen, "(0x%012" PRIx64 ")", dec->inst); + break; + default: + snprintf(buf, buflen, "(0x%016" PRIx64 ")", dec->inst); + break; + } + return; + } + + fmt = opcode_data[dec->op].format; + while (*fmt) { + switch (*fmt) { + case 'O': + append(buf, opcode_data[dec->op].name, buflen); + break; + case '(': + append(buf, "(", buflen); + break; + case ',': + append(buf, ",", buflen); + break; + case ')': + append(buf, ")", buflen); + break; + case '0': + append(buf, rv_ireg_name_sym[dec->rd], buflen); + break; + case '1': + append(buf, rv_ireg_name_sym[dec->rs1], buflen); + break; + case '2': + append(buf, rv_ireg_name_sym[dec->rs2], buflen); + break; + case '3': + append(buf, rv_freg_name_sym[dec->rd], buflen); + break; + case '4': + append(buf, rv_freg_name_sym[dec->rs1], buflen); + break; + case '5': + append(buf, rv_freg_name_sym[dec->rs2], buflen); + break; + case '6': + append(buf, rv_freg_name_sym[dec->rs3], buflen); + break; + case '7': + snprintf(tmp, sizeof(tmp), "%d", dec->rs1); + append(buf, tmp, buflen); + break; + case 'i': + snprintf(tmp, sizeof(tmp), "%d", dec->imm); + append(buf, tmp, buflen); + break; + case 'o': + snprintf(tmp, sizeof(tmp), "%d", dec->imm); + append(buf, tmp, buflen); + while (strlen(buf) < tab * 2) { + append(buf, " ", buflen); + } + snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64, + dec->pc + dec->imm); + append(buf, tmp, buflen); + break; + case 'c': { + const char *name = csr_name(dec->imm & 0xfff); + if (name) { + append(buf, name, buflen); + } else { + snprintf(tmp, sizeof(tmp), "0x%03x", dec->imm & 0xfff); + append(buf, tmp, buflen); + } + break; + } + case 'r': + switch (dec->rm) { + case rv_rm_rne: + append(buf, "rne", buflen); + break; + case rv_rm_rtz: + append(buf, "rtz", buflen); + break; + case rv_rm_rdn: + append(buf, "rdn", buflen); + break; + case rv_rm_rup: + append(buf, "rup", buflen); + break; + case rv_rm_rmm: + append(buf, "rmm", buflen); + break; + case rv_rm_dyn: + append(buf, "dyn", buflen); + break; + default: + append(buf, "inv", buflen); + break; + } + break; + case 'p': + if (dec->pred & rv_fence_i) { + append(buf, "i", buflen); + } + if (dec->pred & rv_fence_o) { + append(buf, "o", buflen); + } + if (dec->pred & rv_fence_r) { + append(buf, "r", buflen); + } + if (dec->pred & rv_fence_w) { + append(buf, "w", buflen); + } + break; + case 's': + if (dec->succ & rv_fence_i) { + append(buf, "i", buflen); + } + if (dec->succ & rv_fence_o) { + append(buf, "o", buflen); + } + if (dec->succ & rv_fence_r) { + append(buf, "r", buflen); + } + if (dec->succ & rv_fence_w) { + append(buf, "w", buflen); + } + break; + case '\t': + while (strlen(buf) < tab) { + append(buf, " ", buflen); + } + break; + case 'A': + if (dec->aq) { + append(buf, ".aq", buflen); + } + break; + case 'R': + if (dec->rl) { + append(buf, ".rl", buflen); + } + break; + default: + break; + } + fmt++; + } +} + +/* lift instruction to pseudo-instruction */ + +static void decode_inst_lift_pseudo(rv_decode *dec) +{ + const rv_comp_data *comp_data = opcode_data[dec->op].pseudo; + if (!comp_data) { + return; + } + while (comp_data->constraints) { + if (check_constraints(dec, comp_data->constraints)) { + dec->op = comp_data->op; + dec->codec = opcode_data[dec->op].codec; + return; + } + comp_data++; + } +} + +/* decompress instruction */ + +static void decode_inst_decompress_rv32(rv_decode *dec) +{ + int decomp_op = opcode_data[dec->op].decomp_rv32; + if (decomp_op != rv_op_illegal) { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } +} + +static void decode_inst_decompress_rv64(rv_decode *dec) +{ + int decomp_op = opcode_data[dec->op].decomp_rv64; + if (decomp_op != rv_op_illegal) { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } +} + +static void decode_inst_decompress_rv128(rv_decode *dec) +{ + int decomp_op = opcode_data[dec->op].decomp_rv128; + if (decomp_op != rv_op_illegal) { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } +} + +static void decode_inst_decompress(rv_decode *dec, rv_isa isa) +{ + switch (isa) { + case rv32: + decode_inst_decompress_rv32(dec); + break; + case rv64: + decode_inst_decompress_rv64(dec); + break; + case rv128: + decode_inst_decompress_rv128(dec); + break; + } +} + +/* disassemble instruction */ + +static void +disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) +{ + rv_decode dec = { 0 }; + dec.pc = pc; + dec.inst = inst; + decode_inst_opcode(&dec, isa); + decode_inst_operands(&dec); + decode_inst_decompress(&dec, isa); + decode_inst_lift_pseudo(&dec); + format_inst(buf, buflen, 16, &dec); +} + +static int +print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) +{ + char buf[128] = { 0 }; + bfd_byte packet[2]; + rv_inst inst = 0; + size_t len = 2; + bfd_vma n; + int status; + + /* Instructions are made of 2-byte packets in little-endian order */ + for (n = 0; n < len; n += 2) { + status = (*info->read_memory_func)(memaddr + n, packet, 2, info); + if (status != 0) { + /* Don't fail just because we fell off the end. */ + if (n > 0) { + break; + } + (*info->memory_error_func)(status, memaddr, info); + return status; + } + inst |= ((rv_inst) bfd_getl16(packet)) << (8 * n); + if (n == 0) { + len = inst_length(inst); + } + } + + disasm_inst(buf, sizeof(buf), isa, memaddr, inst); + (*info->fprintf_func)(info->stream, "%s", buf); + + return len; +} + +int print_insn_riscv32(bfd_vma memaddr, struct disassemble_info *info) +{ + return print_insn_riscv(memaddr, info, rv32); +} + +int print_insn_riscv64(bfd_vma memaddr, struct disassemble_info *info) +{ + return print_insn_riscv(memaddr, info, rv64); +} diff --git a/include/disas/bfd.h b/include/disas/bfd.h index 932453750c..1f69a6e9d3 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -429,6 +429,8 @@ int print_insn_lm32 (bfd_vma, disassemble_info*); int print_insn_big_nios2 (bfd_vma, disassemble_info*); int print_insn_little_nios2 (bfd_vma, disassemble_info*); int print_insn_xtensa (bfd_vma, disassemble_info*); +int print_insn_riscv32 (bfd_vma, disassemble_info*); +int print_insn_riscv64 (bfd_vma, disassemble_info*); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ -- cgit v1.2.3-55-g7522 From a2480ffa88a247acbddcf6bb8c4cf69b1af0f48c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:12 +1300 Subject: Add symbol table callback interface to load_elf The RISC-V HTIF (Host Target Interface) console device requires access to the symbol table to locate the 'tohost' and 'fromhost' symbols. Reviewed-by: Richard Henderson Signed-off-by: Michael Clark --- hw/core/loader.c | 18 ++++++++++++++++-- include/hw/elf_ops.h | 34 +++++++++++++++++++++------------- include/hw/loader.h | 17 ++++++++++++++++- 3 files changed, 53 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/hw/core/loader.c b/hw/core/loader.c index 76b244c508..06bdbca537 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -449,6 +449,20 @@ int load_elf_ram(const char *filename, uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom) +{ + return load_elf_ram_sym(filename, translate_fn, translate_opaque, + pentry, lowaddr, highaddr, big_endian, + elf_machine, clear_lsb, data_swab, as, + load_rom, NULL); +} + +/* return < 0 if error, otherwise the number of bytes loaded in memory */ +int load_elf_ram_sym(const char *filename, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -488,11 +502,11 @@ int load_elf_ram(const char *filename, if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab, pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab, as, load_rom); + data_swab, as, load_rom, sym_cb); } else { ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab, as, load_rom); + data_swab, as, load_rom, sym_cb); } fail: diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index d192e7e2a3..b6e19e35d0 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -105,7 +105,7 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1) } static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, - int clear_lsb) + int clear_lsb, symbol_fn_t sym_cb) { struct elf_shdr *symtab, *strtab, *shdr_table = NULL; struct elf_sym *syms = NULL; @@ -133,10 +133,26 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, nsyms = symtab->sh_size / sizeof(struct elf_sym); + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) { + goto fail; + } + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) { + goto fail; + } + i = 0; while (i < nsyms) { - if (must_swab) + if (must_swab) { glue(bswap_sym, SZ)(&syms[i]); + } + if (sym_cb) { + sym_cb(str + syms[i].st_name, syms[i].st_info, + syms[i].st_value, syms[i].st_size); + } /* We are only interested in function symbols. Throw everything else away. */ if (syms[i].st_shndx == SHN_UNDEF || @@ -163,15 +179,6 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, } } - /* String table */ - if (symtab->sh_link >= ehdr->e_shnum) - goto fail; - strtab = &shdr_table[symtab->sh_link]; - - str = load_at(fd, strtab->sh_offset, strtab->sh_size); - if (!str) - goto fail; - /* Commit */ s = g_malloc0(sizeof(*s)); s->lookup_symbol = glue(lookup_symbol, SZ); @@ -264,7 +271,8 @@ static int glue(load_elf, SZ)(const char *name, int fd, int must_swab, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom) + AddressSpace *as, bool load_rom, + symbol_fn_t sym_cb) { struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; @@ -329,7 +337,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, if (pentry) *pentry = (uint64_t)(elf_sword)ehdr.e_entry; - glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb); + glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); size = ehdr.e_phnum * sizeof(phdr[0]); if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { diff --git a/include/hw/loader.h b/include/hw/loader.h index 2504cc2259..5ed3fd8ae6 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -64,7 +64,7 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_WRONG_ENDIAN -4 const char *load_elf_strerror(int error); -/** load_elf_ram: +/** load_elf_ram_sym: * @filename: Path of ELF file * @translate_fn: optional function to translate load addresses * @translate_opaque: opaque data passed to @translate_fn @@ -81,6 +81,7 @@ const char *load_elf_strerror(int error); * @as: The AddressSpace to load the ELF to. The value of address_space_memory * is used if nothing is supplied here. * @load_rom : Load ELF binary as ROM + * @sym_cb: Callback function for symbol table entries * * Load an ELF file's contents to the emulated system's address space. * Clients may optionally specify a callback to perform address @@ -93,6 +94,20 @@ const char *load_elf_strerror(int error); * If @elf_machine is EM_NONE then the machine type will be read from the * ELF header and no checks will be carried out against the machine type. */ +typedef void (*symbol_fn_t)(const char *st_name, int st_info, + uint64_t st_value, uint64_t st_size); + +int load_elf_ram_sym(const char *filename, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); + +/** load_elf_ram: + * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a + * symbol callback function + */ int load_elf_ram(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, -- cgit v1.2.3-55-g7522 From 5033606780b9743921de95adb295bf1a03135d2c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:12 +1300 Subject: RISC-V HTIF Console HTIF (Host Target Interface) provides console emulation for QEMU. HTIF allows identical copies of BBL (Berkeley Boot Loader) and linux to run on both Spike and QEMU. BBL provides HTIF console access via the SBI (Supervisor Binary Interface) and the linux kernel SBI console. The HTIT chardev implements the pre qom legacy interface consistent with the 16550a UART in 'hw/char/serial.c'. Reviewed-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Stefan O'Rear Signed-off-by: Michael Clark --- hw/riscv/riscv_htif.c | 258 ++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/riscv_htif.h | 61 ++++++++++ 2 files changed, 319 insertions(+) create mode 100644 hw/riscv/riscv_htif.c create mode 100644 include/hw/riscv/riscv_htif.h (limited to 'include') diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c new file mode 100644 index 0000000000..3e17f30251 --- /dev/null +++ b/hw/riscv/riscv_htif.c @@ -0,0 +1,258 @@ +/* + * QEMU RISC-V Host Target Interface (HTIF) Emulation + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This provides HTIF device emulation for QEMU. At the moment this allows + * for identical copies of bbl/linux to run on both spike and QEMU. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "hw/riscv/riscv_htif.h" +#include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" + +#define RISCV_DEBUG_HTIF 0 +#define HTIF_DEBUG(fmt, ...) \ + do { \ + if (RISCV_DEBUG_HTIF) { \ + qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ + } \ + } while (0) + +static uint64_t fromhost_addr, tohost_addr; + +void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, + uint64_t st_size) +{ + if (strcmp("fromhost", st_name) == 0) { + fromhost_addr = st_value; + if (st_size != 8) { + error_report("HTIF fromhost must be 8 bytes"); + exit(1); + } + } else if (strcmp("tohost", st_name) == 0) { + tohost_addr = st_value; + if (st_size != 8) { + error_report("HTIF tohost must be 8 bytes"); + exit(1); + } + } +} + +/* + * Called by the char dev to see if HTIF is ready to accept input. + */ +static int htif_can_recv(void *opaque) +{ + return 1; +} + +/* + * Called by the char dev to supply input to HTIF console. + * We assume that we will receive one character at a time. + */ +static void htif_recv(void *opaque, const uint8_t *buf, int size) +{ + HTIFState *htifstate = opaque; + + if (size != 1) { + return; + } + + /* TODO - we need to check whether mfromhost is zero which indicates + the device is ready to receive. The current implementation + will drop characters */ + + uint64_t val_written = htifstate->pending_read; + uint64_t resp = 0x100 | *buf; + + htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); +} + +/* + * Called by the char dev to supply special events to the HTIF console. + * Not used for HTIF. + */ +static void htif_event(void *opaque, int event) +{ + +} + +static int htif_be_change(void *opaque) +{ + HTIFState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, + htif_be_change, s, NULL, true); + + return 0; +} + +static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) +{ + uint8_t device = val_written >> 56; + uint8_t cmd = val_written >> 48; + uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; + int resp = 0; + + HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 + " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload); + + /* + * Currently, there is a fixed mapping of devices: + * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) + * 1: Console + */ + if (unlikely(device == 0x0)) { + /* frontend syscall handler, shutdown and exit code support */ + if (cmd == 0x0) { + if (payload & 0x1) { + /* exit code */ + int exit_code = payload >> 1; + exit(exit_code); + } else { + qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n"); + } + } else { + qemu_log("HTIF device %d: unknown command\n", device); + } + } else if (likely(device == 0x1)) { + /* HTIF Console */ + if (cmd == 0x0) { + /* this should be a queue, but not yet implemented as such */ + htifstate->pending_read = val_written; + htifstate->env->mtohost = 0; /* clear to indicate we read */ + return; + } else if (cmd == 0x1) { + qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1); + resp = 0x100 | (uint8_t)payload; + } else { + qemu_log("HTIF device %d: unknown command\n", device); + } + } else { + qemu_log("HTIF unknown device or command\n"); + HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 + " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); + } + /* + * - latest bbl does not set fromhost to 0 if there is a value in tohost + * - with this code enabled, qemu hangs waiting for fromhost to go to 0 + * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10 + * - HTIF needs protocol documentation and a more complete state machine + + while (!htifstate->fromhost_inprogress && + htifstate->env->mfromhost != 0x0) { + } + */ + htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); + htifstate->env->mtohost = 0; /* clear to indicate we read */ +} + +#define TOHOST_OFFSET1 (htifstate->tohost_offset) +#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4) +#define FROMHOST_OFFSET1 (htifstate->fromhost_offset) +#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4) + +/* CPU wants to read an HTIF register */ +static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) +{ + HTIFState *htifstate = opaque; + if (addr == TOHOST_OFFSET1) { + return htifstate->env->mtohost & 0xFFFFFFFF; + } else if (addr == TOHOST_OFFSET2) { + return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET1) { + return htifstate->env->mfromhost & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET2) { + return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF; + } else { + qemu_log("Invalid htif read: address %016" PRIx64 "\n", + (uint64_t)addr); + return 0; + } +} + +/* CPU wrote to an HTIF register */ +static void htif_mm_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + HTIFState *htifstate = opaque; + if (addr == TOHOST_OFFSET1) { + if (htifstate->env->mtohost == 0x0) { + htifstate->allow_tohost = 1; + htifstate->env->mtohost = value & 0xFFFFFFFF; + } else { + htifstate->allow_tohost = 0; + } + } else if (addr == TOHOST_OFFSET2) { + if (htifstate->allow_tohost) { + htifstate->env->mtohost |= value << 32; + htif_handle_tohost_write(htifstate, htifstate->env->mtohost); + } + } else if (addr == FROMHOST_OFFSET1) { + htifstate->fromhost_inprogress = 1; + htifstate->env->mfromhost = value & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET2) { + htifstate->env->mfromhost |= value << 32; + htifstate->fromhost_inprogress = 0; + } else { + qemu_log("Invalid htif write: address %016" PRIx64 "\n", + (uint64_t)addr); + } +} + +static const MemoryRegionOps htif_mm_ops = { + .read = htif_mm_read, + .write = htif_mm_write, +}; + +HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, + CPURISCVState *env, Chardev *chr) +{ + uint64_t base = MIN(tohost_addr, fromhost_addr); + uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base; + uint64_t tohost_offset = tohost_addr - base; + uint64_t fromhost_offset = fromhost_addr - base; + + HTIFState *s = g_malloc0(sizeof(HTIFState)); + s->address_space = address_space; + s->main_mem = main_mem; + s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); + s->env = env; + s->tohost_offset = tohost_offset; + s->fromhost_offset = fromhost_offset; + s->pending_read = 0; + s->allow_tohost = 0; + s->fromhost_inprogress = 0; + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, + htif_be_change, s, NULL, true); + if (base) { + memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, + TYPE_HTIF_UART, size); + memory_region_add_subregion(address_space, base, &s->mmio); + } + + return s; +} diff --git a/include/hw/riscv/riscv_htif.h b/include/hw/riscv/riscv_htif.h new file mode 100644 index 0000000000..fb5f88129e --- /dev/null +++ b/include/hw/riscv/riscv_htif.h @@ -0,0 +1,61 @@ +/* + * QEMU RISCV Host Target Interface (HTIF) Emulation + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_RISCV_HTIF_H +#define HW_RISCV_HTIF_H + +#include "hw/hw.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "sysemu/sysemu.h" +#include "exec/memory.h" +#include "target/riscv/cpu.h" + +#define TYPE_HTIF_UART "riscv.htif.uart" + +typedef struct HTIFState { + int allow_tohost; + int fromhost_inprogress; + + hwaddr tohost_offset; + hwaddr fromhost_offset; + uint64_t tohost_size; + uint64_t fromhost_size; + MemoryRegion mmio; + MemoryRegion *address_space; + MemoryRegion *main_mem; + void *main_mem_ram_ptr; + + CPURISCVState *env; + CharBackend chr; + uint64_t pending_read; +} HTIFState; + +extern const VMStateDescription vmstate_htif; +extern const MemoryRegionOps htif_io_ops; + +/* HTIF symbol callback */ +void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, + uint64_t st_size); + +/* legacy pre qom */ +HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, + CPURISCVState *env, Chardev *chr); + +#endif -- cgit v1.2.3-55-g7522 From 4b50b8d9f2bdc007d632a6d0781de1126c5d9c76 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:12 +1300 Subject: RISC-V HART Array Holds the state of a heterogenous array of RISC-V hardware threads. Reviewed-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Michael Clark --- hw/riscv/riscv_hart.c | 89 +++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/riscv_hart.h | 39 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 hw/riscv/riscv_hart.c create mode 100644 include/hw/riscv/riscv_hart.h (limited to 'include') diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c new file mode 100644 index 0000000000..14e3c186fe --- /dev/null +++ b/hw/riscv/riscv_hart.c @@ -0,0 +1,89 @@ +/* + * QEMU RISCV Hart Array + * + * Copyright (c) 2017 SiFive, Inc. + * + * Holds the state of a heterogenous array of RISC-V harts + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" + +static Property riscv_harts_props[] = { + DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1), + DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), + DEFINE_PROP_END_OF_LIST(), +}; + +static void riscv_harts_cpu_reset(void *opaque) +{ + RISCVCPU *cpu = opaque; + cpu_reset(CPU(cpu)); +} + +static void riscv_harts_realize(DeviceState *dev, Error **errp) +{ + RISCVHartArrayState *s = RISCV_HART_ARRAY(dev); + Error *err = NULL; + int n; + + s->harts = g_new0(RISCVCPU, s->num_harts); + + for (n = 0; n < s->num_harts; n++) { + + object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type); + s->harts[n].env.mhartid = n; + object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]), + &error_abort); + qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]); + object_property_set_bool(OBJECT(&s->harts[n]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } +} + +static void riscv_harts_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = riscv_harts_props; + dc->realize = riscv_harts_realize; +} + +static void riscv_harts_init(Object *obj) +{ + /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */ +} + +static const TypeInfo riscv_harts_info = { + .name = TYPE_RISCV_HART_ARRAY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RISCVHartArrayState), + .instance_init = riscv_harts_init, + .class_init = riscv_harts_class_init, +}; + +static void riscv_harts_register_types(void) +{ + type_register_static(&riscv_harts_info); +} + +type_init(riscv_harts_register_types) diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h new file mode 100644 index 0000000000..0671d88a44 --- /dev/null +++ b/include/hw/riscv/riscv_hart.h @@ -0,0 +1,39 @@ +/* + * QEMU RISC-V Hart Array interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * Holds the state of a heterogenous array of RISC-V harts + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_RISCV_HART_H +#define HW_RISCV_HART_H + +#define TYPE_RISCV_HART_ARRAY "riscv.hart_array" + +#define RISCV_HART_ARRAY(obj) \ + OBJECT_CHECK(RISCVHartArrayState, (obj), TYPE_RISCV_HART_ARRAY) + +typedef struct RISCVHartArrayState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + uint32_t num_harts; + char *cpu_type; + RISCVCPU *harts; +} RISCVHartArrayState; + +#endif -- cgit v1.2.3-55-g7522 From 1c77c410b684e987b8c3cd2e02e0460c7e008778 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:12 +1300 Subject: SiFive RISC-V CLINT Block The CLINT (Core Local Interruptor) device provides real-time clock, timer and interprocessor interrupts based on SiFive's CLINT specification. Acked-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Stefan O'Rear Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_clint.c | 254 ++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_clint.h | 50 ++++++++ 2 files changed, 304 insertions(+) create mode 100644 hw/riscv/sifive_clint.c create mode 100644 include/hw/riscv/sifive_clint.h (limited to 'include') diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c new file mode 100644 index 0000000000..4893453b70 --- /dev/null +++ b/hw/riscv/sifive_clint.c @@ -0,0 +1,254 @@ +/* + * SiFive CLINT (Core Local Interruptor) + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * + * This provides real-time clock, timer and interprocessor interrupts. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_clint.h" +#include "qemu/timer.h" + +/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */ +#define TIMER_FREQ (10 * 1000 * 1000) + +static uint64_t cpu_riscv_read_rtc(void) +{ + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ, + NANOSECONDS_PER_SECOND); +} + +/* + * Called when timecmp is written to update the QEMU timer or immediately + * trigger timer interrupt if mtimecmp <= current timer value. + */ +static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) +{ + uint64_t next; + uint64_t diff; + + uint64_t rtc_r = cpu_riscv_read_rtc(); + + cpu->env.timecmp = value; + if (cpu->env.timecmp <= rtc_r) { + /* if we're setting an MTIMECMP value in the "past", + immediately raise the timer interrupt */ + riscv_set_local_interrupt(cpu, MIP_MTIP, 1); + return; + } + + /* otherwise, set up the future timer interrupt */ + riscv_set_local_interrupt(cpu, MIP_MTIP, 0); + diff = cpu->env.timecmp - rtc_r; + /* back to ns (note args switched in muldiv64) */ + next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ); + timer_mod(cpu->env.timer, next); +} + +/* + * Callback used when the timer set using timer_mod expires. + * Should raise the timer interrupt line + */ +static void sifive_clint_timer_cb(void *opaque) +{ + RISCVCPU *cpu = opaque; + riscv_set_local_interrupt(cpu, MIP_MTIP, 1); +} + +/* CPU wants to read rtc or timecmp register */ +static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size) +{ + SiFiveCLINTState *clint = opaque; + if (addr >= clint->sip_base && + addr < clint->sip_base + (clint->num_harts << 2)) { + size_t hartid = (addr - clint->sip_base) >> 2; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x3) == 0) { + return (env->mip & MIP_MSIP) > 0; + } else { + error_report("clint: invalid read: %08x", (uint32_t)addr); + return 0; + } + } else if (addr >= clint->timecmp_base && + addr < clint->timecmp_base + (clint->num_harts << 3)) { + size_t hartid = (addr - clint->timecmp_base) >> 3; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x7) == 0) { + /* timecmp_lo */ + uint64_t timecmp = env->timecmp; + return timecmp & 0xFFFFFFFF; + } else if ((addr & 0x7) == 4) { + /* timecmp_hi */ + uint64_t timecmp = env->timecmp; + return (timecmp >> 32) & 0xFFFFFFFF; + } else { + error_report("clint: invalid read: %08x", (uint32_t)addr); + return 0; + } + } else if (addr == clint->time_base) { + /* time_lo */ + return cpu_riscv_read_rtc() & 0xFFFFFFFF; + } else if (addr == clint->time_base + 4) { + /* time_hi */ + return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF; + } + + error_report("clint: invalid read: %08x", (uint32_t)addr); + return 0; +} + +/* CPU wrote to rtc or timecmp register */ +static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SiFiveCLINTState *clint = opaque; + + if (addr >= clint->sip_base && + addr < clint->sip_base + (clint->num_harts << 2)) { + size_t hartid = (addr - clint->sip_base) >> 2; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x3) == 0) { + riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0); + } else { + error_report("clint: invalid sip write: %08x", (uint32_t)addr); + } + return; + } else if (addr >= clint->timecmp_base && + addr < clint->timecmp_base + (clint->num_harts << 3)) { + size_t hartid = (addr - clint->timecmp_base) >> 3; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x7) == 0) { + /* timecmp_lo */ + uint64_t timecmp = env->timecmp; + sifive_clint_write_timecmp(RISCV_CPU(cpu), + timecmp << 32 | (value & 0xFFFFFFFF)); + return; + } else if ((addr & 0x7) == 4) { + /* timecmp_hi */ + uint64_t timecmp = env->timecmp; + sifive_clint_write_timecmp(RISCV_CPU(cpu), + value << 32 | (timecmp & 0xFFFFFFFF)); + } else { + error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); + } + return; + } else if (addr == clint->time_base) { + /* time_lo */ + error_report("clint: time_lo write not implemented"); + return; + } else if (addr == clint->time_base + 4) { + /* time_hi */ + error_report("clint: time_hi write not implemented"); + return; + } + + error_report("clint: invalid write: %08x", (uint32_t)addr); +} + +static const MemoryRegionOps sifive_clint_ops = { + .read = sifive_clint_read, + .write = sifive_clint_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static Property sifive_clint_properties[] = { + DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0), + DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0), + DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0), + DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0), + DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sifive_clint_realize(DeviceState *dev, Error **errp) +{ + SiFiveCLINTState *s = SIFIVE_CLINT(dev); + memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s, + TYPE_SIFIVE_CLINT, s->aperture_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); +} + +static void sifive_clint_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = sifive_clint_realize; + dc->props = sifive_clint_properties; +} + +static const TypeInfo sifive_clint_info = { + .name = TYPE_SIFIVE_CLINT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveCLINTState), + .class_init = sifive_clint_class_init, +}; + +static void sifive_clint_register_types(void) +{ + type_register_static(&sifive_clint_info); +} + +type_init(sifive_clint_register_types) + + +/* + * Create CLINT device. + */ +DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, + uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base) +{ + int i; + for (i = 0; i < num_harts; i++) { + CPUState *cpu = qemu_get_cpu(i); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + continue; + } + env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &sifive_clint_timer_cb, cpu); + env->timecmp = 0; + } + + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_CLINT); + qdev_prop_set_uint32(dev, "num-harts", num_harts); + qdev_prop_set_uint32(dev, "sip-base", sip_base); + qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base); + qdev_prop_set_uint32(dev, "time-base", time_base); + qdev_prop_set_uint32(dev, "aperture-size", size); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h new file mode 100644 index 0000000000..aaa2a58c6e --- /dev/null +++ b/include/hw/riscv/sifive_clint.h @@ -0,0 +1,50 @@ +/* + * SiFive CLINT (Core Local Interruptor) interface + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_CLINT_H +#define HW_SIFIVE_CLINT_H + +#define TYPE_SIFIVE_CLINT "riscv.sifive.clint" + +#define SIFIVE_CLINT(obj) \ + OBJECT_CHECK(SiFiveCLINTState, (obj), TYPE_SIFIVE_CLINT) + +typedef struct SiFiveCLINTState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t num_harts; + uint32_t sip_base; + uint32_t timecmp_base; + uint32_t time_base; + uint32_t aperture_size; +} SiFiveCLINTState; + +DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, + uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base); + +enum { + SIFIVE_SIP_BASE = 0x0, + SIFIVE_TIMECMP_BASE = 0x4000, + SIFIVE_TIME_BASE = 0xBFF8 +}; + +#endif -- cgit v1.2.3-55-g7522 From 1e24429e40df81270012538851c75e30c53eec21 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:13 +1300 Subject: SiFive RISC-V PLIC Block The PLIC (Platform Level Interrupt Controller) device provides a parameterizable interrupt controller based on SiFive's PLIC specification. Acked-by: Richard Henderson Signed-off-by: Stefan O'Rear Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_plic.c | 505 +++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_plic.h | 85 +++++++ 2 files changed, 590 insertions(+) create mode 100644 hw/riscv/sifive_plic.c create mode 100644 include/hw/riscv/sifive_plic.h (limited to 'include') diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c new file mode 100644 index 0000000000..874de2ebaf --- /dev/null +++ b/hw/riscv/sifive_plic.c @@ -0,0 +1,505 @@ +/* + * SiFive PLIC (Platform Level Interrupt Controller) + * + * Copyright (c) 2017 SiFive, Inc. + * + * This provides a parameterizable interrupt controller based on SiFive's PLIC. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_plic.h" + +#define RISCV_DEBUG_PLIC 0 + +static PLICMode char_to_mode(char c) +{ + switch (c) { + case 'U': return PLICMode_U; + case 'S': return PLICMode_S; + case 'H': return PLICMode_H; + case 'M': return PLICMode_M; + default: + error_report("plic: invalid mode '%c'", c); + exit(1); + } +} + +static char mode_to_char(PLICMode m) +{ + switch (m) { + case PLICMode_U: return 'U'; + case PLICMode_S: return 'S'; + case PLICMode_H: return 'H'; + case PLICMode_M: return 'M'; + default: return '?'; + } +} + +static void sifive_plic_print_state(SiFivePLICState *plic) +{ + int i; + int addrid; + + /* pending */ + qemu_log("pending : "); + for (i = plic->bitfield_words - 1; i >= 0; i--) { + qemu_log("%08x", plic->pending[i]); + } + qemu_log("\n"); + + /* pending */ + qemu_log("claimed : "); + for (i = plic->bitfield_words - 1; i >= 0; i--) { + qemu_log("%08x", plic->claimed[i]); + } + qemu_log("\n"); + + for (addrid = 0; addrid < plic->num_addrs; addrid++) { + qemu_log("hart%d-%c enable: ", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode)); + for (i = plic->bitfield_words - 1; i >= 0; i--) { + qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]); + } + qemu_log("\n"); + } +} + +static +void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending) +{ + qemu_mutex_lock(&plic->lock); + uint32_t word = irq >> 5; + if (pending) { + plic->pending[word] |= (1 << (irq & 31)); + } else { + plic->pending[word] &= ~(1 << (irq & 31)); + } + qemu_mutex_unlock(&plic->lock); +} + +static +void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed) +{ + qemu_mutex_lock(&plic->lock); + uint32_t word = irq >> 5; + if (claimed) { + plic->claimed[word] |= (1 << (irq & 31)); + } else { + plic->claimed[word] &= ~(1 << (irq & 31)); + } + qemu_mutex_unlock(&plic->lock); +} + +static +int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) +{ + int i, j, count = 0; + for (i = 0; i < plic->bitfield_words; i++) { + uint32_t pending_enabled_not_claimed = + (plic->pending[i] & ~plic->claimed[i]) & + plic->enable[addrid * plic->bitfield_words + i]; + if (!pending_enabled_not_claimed) { + continue; + } + for (j = 0; j < 32; j++) { + int irq = (i << 5) + j; + uint32_t prio = plic->source_priority[irq]; + int enabled = pending_enabled_not_claimed & (1 << j); + if (enabled && prio > plic->target_priority[addrid]) { + count++; + } + } + } + return count; +} + +static void sifive_plic_update(SiFivePLICState *plic) +{ + int addrid; + + /* raise irq on harts where this irq is enabled */ + for (addrid = 0; addrid < plic->num_addrs; addrid++) { + uint32_t hartid = plic->addr_config[addrid].hartid; + PLICMode mode = plic->addr_config[addrid].mode; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + continue; + } + int level = sifive_plic_num_irqs_pending(plic, addrid) > 0; + switch (mode) { + case PLICMode_M: + riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); + break; + case PLICMode_S: + riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level); + break; + default: + break; + } + } + + if (RISCV_DEBUG_PLIC) { + sifive_plic_print_state(plic); + } +} + +void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq) +{ + sifive_plic_set_pending(plic, irq, true); + sifive_plic_update(plic); +} + +void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq) +{ + sifive_plic_set_pending(plic, irq, false); + sifive_plic_update(plic); +} + +static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) +{ + int i, j; + for (i = 0; i < plic->bitfield_words; i++) { + uint32_t pending_enabled_not_claimed = + (plic->pending[i] & ~plic->claimed[i]) & + plic->enable[addrid * plic->bitfield_words + i]; + if (!pending_enabled_not_claimed) { + continue; + } + for (j = 0; j < 32; j++) { + int irq = (i << 5) + j; + uint32_t prio = plic->source_priority[irq]; + int enabled = pending_enabled_not_claimed & (1 << j); + if (enabled && prio > plic->target_priority[addrid]) { + sifive_plic_set_pending(plic, irq, false); + sifive_plic_set_claimed(plic, irq, true); + return irq; + } + } + } + return 0; +} + +static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) +{ + SiFivePLICState *plic = opaque; + + /* writes must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + if (addr >= plic->priority_base && /* 4 bytes per source */ + addr < plic->priority_base + (plic->num_sources << 2)) + { + uint32_t irq = (addr - plic->priority_base) >> 2; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read priority: irq=%d priority=%d\n", + irq, plic->source_priority[irq]); + } + return plic->source_priority[irq]; + } else if (addr >= plic->pending_base && /* 1 bit per source */ + addr < plic->pending_base + (plic->num_sources >> 3)) + { + uint32_t word = (addr - plic->priority_base) >> 2; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read pending: word=%d value=%d\n", + word, plic->pending[word]); + } + return plic->pending[word]; + } else if (addr >= plic->enable_base && /* 1 bit per source */ + addr < plic->enable_base + plic->num_addrs * plic->enable_stride) + { + uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; + uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; + if (wordid < plic->bitfield_words) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), wordid, + plic->enable[addrid * plic->bitfield_words + wordid]); + } + return plic->enable[addrid * plic->bitfield_words + wordid]; + } + } else if (addr >= plic->context_base && /* 1 bit per source */ + addr < plic->context_base + plic->num_addrs * plic->context_stride) + { + uint32_t addrid = (addr - plic->context_base) / plic->context_stride; + uint32_t contextid = (addr & (plic->context_stride - 1)); + if (contextid == 0) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read priority: hart%d-%c priority=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + plic->target_priority[addrid]); + } + return plic->target_priority[addrid]; + } else if (contextid == 4) { + uint32_t value = sifive_plic_claim(plic, addrid); + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read claim: hart%d-%c irq=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + value); + sifive_plic_print_state(plic); + } + return value; + } + } + +err: + error_report("plic: invalid register read: %08x", (uint32_t)addr); + return 0; +} + +static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SiFivePLICState *plic = opaque; + + /* writes must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + if (addr >= plic->priority_base && /* 4 bytes per source */ + addr < plic->priority_base + (plic->num_sources << 2)) + { + uint32_t irq = (addr - plic->priority_base) >> 2; + plic->source_priority[irq] = value & 7; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write priority: irq=%d priority=%d\n", + irq, plic->source_priority[irq]); + } + return; + } else if (addr >= plic->pending_base && /* 1 bit per source */ + addr < plic->pending_base + (plic->num_sources >> 3)) + { + error_report("plic: invalid pending write: %08x", (uint32_t)addr); + return; + } else if (addr >= plic->enable_base && /* 1 bit per source */ + addr < plic->enable_base + plic->num_addrs * plic->enable_stride) + { + uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; + uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; + if (wordid < plic->bitfield_words) { + plic->enable[addrid * plic->bitfield_words + wordid] = value; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), wordid, + plic->enable[addrid * plic->bitfield_words + wordid]); + } + return; + } + } else if (addr >= plic->context_base && /* 4 bytes per reg */ + addr < plic->context_base + plic->num_addrs * plic->context_stride) + { + uint32_t addrid = (addr - plic->context_base) / plic->context_stride; + uint32_t contextid = (addr & (plic->context_stride - 1)); + if (contextid == 0) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write priority: hart%d-%c priority=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + plic->target_priority[addrid]); + } + if (value <= plic->num_priorities) { + plic->target_priority[addrid] = value; + sifive_plic_update(plic); + } + return; + } else if (contextid == 4) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write claim: hart%d-%c irq=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + (uint32_t)value); + } + if (value < plic->num_sources) { + sifive_plic_set_claimed(plic, value, false); + sifive_plic_update(plic); + } + return; + } + } + +err: + error_report("plic: invalid register write: %08x", (uint32_t)addr); +} + +static const MemoryRegionOps sifive_plic_ops = { + .read = sifive_plic_read, + .write = sifive_plic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static Property sifive_plic_properties[] = { + DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), + DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), + DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), + DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), + DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), + DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), + DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), + DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), + DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), + DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +/* + * parse PLIC hart/mode address offset config + * + * "M" 1 hart with M mode + * "MS,MS" 2 harts, 0-1 with M and S mode + * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode + */ +static void parse_hart_config(SiFivePLICState *plic) +{ + int addrid, hartid, modes; + const char *p; + char c; + + /* count and validate hart/mode combinations */ + addrid = 0, hartid = 0, modes = 0; + p = plic->hart_config; + while ((c = *p++)) { + if (c == ',') { + addrid += __builtin_popcount(modes); + modes = 0; + hartid++; + } else { + int m = 1 << char_to_mode(c); + if (modes == (modes | m)) { + error_report("plic: duplicate mode '%c' in config: %s", + c, plic->hart_config); + exit(1); + } + modes |= m; + } + } + if (modes) { + addrid += __builtin_popcount(modes); + } + hartid++; + + /* store hart/mode combinations */ + plic->num_addrs = addrid; + plic->addr_config = g_new(PLICAddr, plic->num_addrs); + addrid = 0, hartid = 0; + p = plic->hart_config; + while ((c = *p++)) { + if (c == ',') { + hartid++; + } else { + plic->addr_config[addrid].addrid = addrid; + plic->addr_config[addrid].hartid = hartid; + plic->addr_config[addrid].mode = char_to_mode(c); + addrid++; + } + } +} + +static void sifive_plic_irq_request(void *opaque, int irq, int level) +{ + SiFivePLICState *plic = opaque; + if (RISCV_DEBUG_PLIC) { + qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level); + } + sifive_plic_set_pending(plic, irq, level > 0); + sifive_plic_update(plic); +} + +static void sifive_plic_realize(DeviceState *dev, Error **errp) +{ + SiFivePLICState *plic = SIFIVE_PLIC(dev); + int i; + + memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, + TYPE_SIFIVE_PLIC, plic->aperture_size); + parse_hart_config(plic); + qemu_mutex_init(&plic->lock); + plic->bitfield_words = (plic->num_sources + 31) >> 5; + plic->source_priority = g_new0(uint32_t, plic->num_sources); + plic->target_priority = g_new(uint32_t, plic->num_addrs); + plic->pending = g_new0(uint32_t, plic->bitfield_words); + plic->claimed = g_new0(uint32_t, plic->bitfield_words); + plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); + plic->irqs = g_new0(qemu_irq, plic->num_sources + 1); + for (i = 0; i <= plic->num_sources; i++) { + plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i); + } +} + +static void sifive_plic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = sifive_plic_properties; + dc->realize = sifive_plic_realize; +} + +static const TypeInfo sifive_plic_info = { + .name = TYPE_SIFIVE_PLIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFivePLICState), + .class_init = sifive_plic_class_init, +}; + +static void sifive_plic_register_types(void) +{ + type_register_static(&sifive_plic_info); +} + +type_init(sifive_plic_register_types) + +/* + * Create PLIC device. + */ +DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, + uint32_t num_sources, uint32_t num_priorities, + uint32_t priority_base, uint32_t pending_base, + uint32_t enable_base, uint32_t enable_stride, + uint32_t context_base, uint32_t context_stride, + uint32_t aperture_size) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC); + assert(enable_stride == (enable_stride & -enable_stride)); + assert(context_stride == (context_stride & -context_stride)); + qdev_prop_set_string(dev, "hart-config", hart_config); + qdev_prop_set_uint32(dev, "num-sources", num_sources); + qdev_prop_set_uint32(dev, "num-priorities", num_priorities); + qdev_prop_set_uint32(dev, "priority-base", priority_base); + qdev_prop_set_uint32(dev, "pending-base", pending_base); + qdev_prop_set_uint32(dev, "enable-base", enable_base); + qdev_prop_set_uint32(dev, "enable-stride", enable_stride); + qdev_prop_set_uint32(dev, "context-base", context_base); + qdev_prop_set_uint32(dev, "context-stride", context_stride); + qdev_prop_set_uint32(dev, "aperture-size", aperture_size); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h new file mode 100644 index 0000000000..11a5a98df1 --- /dev/null +++ b/include/hw/riscv/sifive_plic.h @@ -0,0 +1,85 @@ +/* + * SiFive PLIC (Platform Level Interrupt Controller) interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * This provides a RISC-V PLIC device + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_PLIC_H +#define HW_SIFIVE_PLIC_H + +#include "hw/irq.h" + +#define TYPE_SIFIVE_PLIC "riscv.sifive.plic" + +#define SIFIVE_PLIC(obj) \ + OBJECT_CHECK(SiFivePLICState, (obj), TYPE_SIFIVE_PLIC) + +typedef enum PLICMode { + PLICMode_U, + PLICMode_S, + PLICMode_H, + PLICMode_M +} PLICMode; + +typedef struct PLICAddr { + uint32_t addrid; + uint32_t hartid; + PLICMode mode; +} PLICAddr; + +typedef struct SiFivePLICState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t num_addrs; + uint32_t bitfield_words; + PLICAddr *addr_config; + uint32_t *source_priority; + uint32_t *target_priority; + uint32_t *pending; + uint32_t *claimed; + uint32_t *enable; + QemuMutex lock; + qemu_irq *irqs; + + /* config */ + char *hart_config; + uint32_t num_sources; + uint32_t num_priorities; + uint32_t priority_base; + uint32_t pending_base; + uint32_t enable_base; + uint32_t enable_stride; + uint32_t context_base; + uint32_t context_stride; + uint32_t aperture_size; +} SiFivePLICState; + +void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq); +void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq); + +DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, + uint32_t num_sources, uint32_t num_priorities, + uint32_t priority_base, uint32_t pending_base, + uint32_t enable_base, uint32_t enable_stride, + uint32_t context_base, uint32_t context_stride, + uint32_t aperture_size); + +#endif + -- cgit v1.2.3-55-g7522 From 5b4beba1246ff163415bde41cd76935012b16823 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:13 +1300 Subject: RISC-V Spike Machines RISC-V machines compatble with Spike aka riscv-isa-sim, the RISC-V Instruction Set Simulator. The following machines are implemented: - 'spike_v1.9.1'; HTIF console, config-string, Privileged ISA Version 1.9.1 - 'spike_v1.10'; HTIF console, device-tree, Privileged ISA Version 1.10 Acked-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Michael Clark --- hw/riscv/spike.c | 376 +++++++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/spike.h | 53 +++++++ 2 files changed, 429 insertions(+) create mode 100644 hw/riscv/spike.c create mode 100644 include/hw/riscv/spike.h (limited to 'include') diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c new file mode 100644 index 0000000000..2d1f114d40 --- /dev/null +++ b/hw/riscv/spike.c @@ -0,0 +1,376 @@ +/* + * QEMU RISC-V Spike Board + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This provides a RISC-V Board with the following devices: + * + * 0) HTIF Console and Poweroff + * 1) CLINT (Timer and IPI) + * 2) PLIC (Platform Level Interrupt Controller) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_htif.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/spike.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} spike_memmap[] = { + [SPIKE_MROM] = { 0x1000, 0x2000 }, + [SPIKE_CLINT] = { 0x2000000, 0x10000 }, + [SPIKE_DRAM] = { 0x80000000, 0x0 }, +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf_ram_sym(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0, + NULL, true, htif_symbol_callback) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, + uint64_t mem_size, const char *cmdline) +{ + void *fdt; + int cpu; + uint32_t *cells; + char *nodename; + + fdt = s->fdt = create_device_tree(&s->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/htif"); + qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0"); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + + nodename = g_strdup_printf("/memory@%lx", + (long)memmap[SPIKE_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base, + mem_size >> 32, mem_size); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + char *isa = riscv_isa_string(&s->soc.harts[cpu]); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); + qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); + qemu_fdt_add_subnode(fdt, intc); + qemu_fdt_setprop_cell(fdt, intc, "phandle", 1); + qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1); + qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); + g_free(isa); + g_free(intc); + g_free(nodename); + } + + cells = g_new0(uint32_t, s->soc.num_harts * 4); + for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/clint@%lx", + (long)memmap[SPIKE_CLINT].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SPIKE_CLINT].base, + 0x0, memmap[SPIKE_CLINT].size); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + g_free(cells); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } + +static void spike_v1_10_0_board_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = spike_memmap; + + SpikeState *s = g_new0(SpikeState, 1); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* register system main memory (actual RAM) */ + memory_region_init_ram(main_mem, NULL, "riscv.spike.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base, + main_mem); + + /* create device tree */ + create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", + s->fdt_size + 0x2000, &error_fatal); + memory_region_add_subregion(system_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ + 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ +#if defined(TARGET_RISCV32) + 0x0182a283, /* lw t0, 24(t0) */ +#elif defined(TARGET_RISCV64) + 0x0182b283, /* ld t0, 24(t0) */ +#endif + 0x00028067, /* jr t0 */ + 0x00000000, + memmap[SPIKE_DRAM].base, /* start: .dword DRAM_BASE */ + 0x00000000, + /* dtb: */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + + /* copy in the device tree */ + qemu_fdt_dumpdtb(s->fdt, s->fdt_size); + cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), + s->fdt, s->fdt_size); + + /* initialize HTIF using symbols found in load_kernel */ + htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]); + + /* Core Local Interruptor (timer and IPI) */ + sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, + smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); +} + +static void spike_v1_09_1_board_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = spike_memmap; + + SpikeState *s = g_new0(SpikeState, 1); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* register system main memory (actual RAM) */ + memory_region_init_ram(main_mem, NULL, "riscv.spike.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base, + main_mem); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", + 0x40000, &error_fatal); + memory_region_add_subregion(system_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui */ + 0x00028067, /* jump to DRAM_BASE */ + 0x00000000, /* reserved */ + memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string pointer */ + 0, 0, 0, 0 /* trap vector */ + }; + + /* part one of config string - before memory size specified */ + const char *config_string_tmpl = + "platform {\n" + " vendor ucb;\n" + " arch spike;\n" + "};\n" + "rtc {\n" + " addr 0x%" PRIx64 "x;\n" + "};\n" + "ram {\n" + " 0 {\n" + " addr 0x%" PRIx64 "x;\n" + " size 0x%" PRIx64 "x;\n" + " };\n" + "};\n" + "core {\n" + " 0" " {\n" + " " "0 {\n" + " isa %s;\n" + " timecmp 0x%" PRIx64 "x;\n" + " ipi 0x%" PRIx64 "x;\n" + " };\n" + " };\n" + "};\n"; + + /* build config string with supplied memory size */ + char *isa = riscv_isa_string(&s->soc.harts[0]); + size_t config_string_size = strlen(config_string_tmpl) + 48; + char *config_string = malloc(config_string_size); + snprintf(config_string, config_string_size, config_string_tmpl, + (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE, + (uint64_t)memmap[SPIKE_DRAM].base, + (uint64_t)ram_size, isa, + (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE, + (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE); + g_free(isa); + size_t config_string_len = strlen(config_string); + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + + /* copy in the config string */ + cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), + config_string, config_string_len); + + /* initialize HTIF using symbols found in load_kernel */ + htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]); + + /* Core Local Interruptor (timer and IPI) */ + sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, + smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); +} + +static const TypeInfo spike_v_1_09_1_device = { + .name = TYPE_RISCV_SPIKE_V1_09_1_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SpikeState), +}; + +static const TypeInfo spike_v_1_10_0_device = { + .name = TYPE_RISCV_SPIKE_V1_10_0_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SpikeState), +}; + +static void spike_v1_09_1_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)"; + mc->init = spike_v1_09_1_board_init; + mc->max_cpus = 1; +} + +static void spike_v1_10_0_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)"; + mc->init = spike_v1_10_0_board_init; + mc->max_cpus = 1; + mc->is_default = 1; +} + +DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init) +DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init) + +static void riscv_spike_board_register_types(void) +{ + type_register_static(&spike_v_1_09_1_device); + type_register_static(&spike_v_1_10_0_device); +} + +type_init(riscv_spike_board_register_types); diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h new file mode 100644 index 0000000000..cb55a14d30 --- /dev/null +++ b/include/hw/riscv/spike.h @@ -0,0 +1,53 @@ +/* + * Spike machine interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SPIKE_H +#define HW_SPIKE_H + +#define TYPE_RISCV_SPIKE_V1_09_1_BOARD "riscv.spike_v1_9_1" +#define TYPE_RISCV_SPIKE_V1_10_0_BOARD "riscv.spike_v1_10" + +#define SPIKE(obj) \ + OBJECT_CHECK(SpikeState, (obj), TYPE_RISCV_SPIKE_BOARD) + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState soc; + void *fdt; + int fdt_size; +} SpikeState; + + +enum { + SPIKE_MROM, + SPIKE_CLINT, + SPIKE_DRAM +}; + +#if defined(TARGET_RISCV32) +#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV32GCSU_V1_09_1 +#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0 +#elif defined(TARGET_RISCV64) +#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV64GCSU_V1_09_1 +#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0 +#endif + +#endif -- cgit v1.2.3-55-g7522 From 88a07990fa282e4b63845223e90d759ef6811264 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:13 +1300 Subject: SiFive RISC-V Test Finisher Test finisher memory mapped device used to exit simulation. Acked-by: Richard Henderson Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_test.c | 93 ++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_test.h | 42 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 hw/riscv/sifive_test.c create mode 100644 include/hw/riscv/sifive_test.h (limited to 'include') diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c new file mode 100644 index 0000000000..8abd2cd525 --- /dev/null +++ b/hw/riscv/sifive_test.c @@ -0,0 +1,93 @@ +/* + * QEMU SiFive Test Finisher + * + * Copyright (c) 2018 SiFive, Inc. + * + * Test finisher memory mapped device used to exit simulation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_test.h" + +static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size) +{ + return 0; +} + +static void sifive_test_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + if (addr == 0) { + int status = val64 & 0xffff; + int code = (val64 >> 16) & 0xffff; + switch (status) { + case FINISHER_FAIL: + exit(code); + case FINISHER_PASS: + exit(0); + default: + break; + } + } + hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n", + __func__, (int)addr, val64); +} + +static const MemoryRegionOps sifive_test_ops = { + .read = sifive_test_read, + .write = sifive_test_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_test_init(Object *obj) +{ + SiFiveTestState *s = SIFIVE_TEST(obj); + + memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s, + TYPE_SIFIVE_TEST, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const TypeInfo sifive_test_info = { + .name = TYPE_SIFIVE_TEST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveTestState), + .instance_init = sifive_test_init, +}; + +static void sifive_test_register_types(void) +{ + type_register_static(&sifive_test_info); +} + +type_init(sifive_test_register_types) + + +/* + * Create Test device. + */ +DeviceState *sifive_test_create(hwaddr addr) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_test.h new file mode 100644 index 0000000000..71d4c9fad7 --- /dev/null +++ b/include/hw/riscv/sifive_test.h @@ -0,0 +1,42 @@ +/* + * QEMU Test Finisher interface + * + * Copyright (c) 2018 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_TEST_H +#define HW_SIFIVE_TEST_H + +#define TYPE_SIFIVE_TEST "riscv.sifive.test" + +#define SIFIVE_TEST(obj) \ + OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST) + +typedef struct SiFiveTestState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; +} SiFiveTestState; + +enum { + FINISHER_FAIL = 0x3333, + FINISHER_PASS = 0x5555 +}; + +DeviceState *sifive_test_create(hwaddr addr); + +#endif -- cgit v1.2.3-55-g7522 From 04331d0b56a0cab2e40a39135a92a15266b37c36 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:13 +1300 Subject: RISC-V VirtIO Machine RISC-V machine with device-tree, 16550a UART and VirtIO MMIO. The following machine is implemented: - 'virt'; CLINT, PLIC, 16550A UART, VirtIO MMIO, device-tree Acked-by: Richard Henderson Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/virt.c | 420 ++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/virt.h | 74 +++++++++ 2 files changed, 494 insertions(+) create mode 100644 hw/riscv/virt.c create mode 100644 include/hw/riscv/virt.h (limited to 'include') diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c new file mode 100644 index 0000000000..e2c214e86a --- /dev/null +++ b/hw/riscv/virt.c @@ -0,0 +1,420 @@ +/* + * QEMU RISC-V VirtIO Board + * + * Copyright (c) 2017 SiFive, Inc. + * + * RISC-V machine with 16550a UART and VirtIO MMIO + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_htif.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_test.h" +#include "hw/riscv/virt.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} virt_memmap[] = { + [VIRT_DEBUG] = { 0x0, 0x100 }, + [VIRT_MROM] = { 0x1000, 0x2000 }, + [VIRT_TEST] = { 0x4000, 0x1000 }, + [VIRT_CLINT] = { 0x2000000, 0x10000 }, + [VIRT_PLIC] = { 0xc000000, 0x4000000 }, + [VIRT_UART0] = { 0x10000000, 0x100 }, + [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, + [VIRT_DRAM] = { 0x80000000, 0x0 }, +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, + 0, ELF_MACHINE, 1, 0) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +static hwaddr load_initrd(const char *filename, uint64_t mem_size, + uint64_t kernel_entry, hwaddr *start) +{ + int size; + + /* We want to put the initrd far enough into RAM that when the + * kernel is uncompressed it will not clobber the initrd. However + * on boards without much RAM we must ensure that we still leave + * enough room for a decent sized initrd, and on boards with large + * amounts of RAM we must avoid the initrd being so far up in RAM + * that it is outside lowmem and inaccessible to the kernel. + * So for boards with less than 256MB of RAM we put the initrd + * halfway into RAM, and for boards with 256MB of RAM or more we put + * the initrd at 128MB. + */ + *start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024); + + size = load_ramdisk(filename, *start, mem_size - *start); + if (size == -1) { + size = load_image_targphys(filename, *start, mem_size - *start); + if (size == -1) { + error_report("qemu: could not load ramdisk '%s'", filename); + exit(1); + } + } + return *start + size; +} + +static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, + uint64_t mem_size, const char *cmdline) +{ + void *fdt; + int cpu; + uint32_t *cells; + char *nodename; + uint32_t plic_phandle, phandle = 1; + int i; + + fdt = s->fdt = create_device_tree(&s->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + + nodename = g_strdup_printf("/memory@%lx", + (long)memmap[VIRT_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[VIRT_DRAM].base >> 32, memmap[VIRT_DRAM].base, + mem_size >> 32, mem_size); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + int cpu_phandle = phandle++; + nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + char *isa = riscv_isa_string(&s->soc.harts[cpu]); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); + qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); + qemu_fdt_add_subnode(fdt, intc); + qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle); + qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle); + qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); + g_free(isa); + g_free(intc); + g_free(nodename); + } + + cells = g_new0(uint32_t, s->soc.num_harts * 4); + for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/clint@%lx", + (long)memmap[VIRT_CLINT].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_CLINT].base, + 0x0, memmap[VIRT_CLINT].size); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + g_free(cells); + g_free(nodename); + + plic_phandle = phandle++; + cells = g_new0(uint32_t, s->soc.num_harts * 4); + for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/interrupt-controller@%lx", + (long)memmap[VIRT_PLIC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_PLIC].base, + 0x0, memmap[VIRT_PLIC].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", VIRTIO_NDEV); + qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle); + plic_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(cells); + g_free(nodename); + + for (i = 0; i < VIRTIO_COUNT; i++) { + nodename = g_strdup_printf("/virtio_mmio@%lx", + (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size)); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, + 0x0, memmap[VIRT_VIRTIO].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i); + g_free(nodename); + } + + nodename = g_strdup_printf("/test@%lx", + (long)memmap[VIRT_TEST].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_TEST].base, + 0x0, memmap[VIRT_TEST].size); + + nodename = g_strdup_printf("/uart@%lx", + (long)memmap[VIRT_UART0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_UART0].base, + 0x0, memmap[VIRT_UART0].size); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + g_free(nodename); + + return fdt; +} + +static void riscv_virt_board_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = virt_memmap; + + RISCVVirtState *s = g_new0(RISCVVirtState, 1); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + char *plic_hart_config; + size_t plic_hart_config_len; + int i; + void *fdt; + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* register system main memory (actual RAM) */ + memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, + main_mem); + + /* create device tree */ + fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom", + s->fdt_size + 0x2000, &error_fatal); + memory_region_add_subregion(system_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + uint64_t kernel_entry = load_kernel(machine->kernel_filename); + + if (machine->initrd_filename) { + hwaddr start; + hwaddr end = load_initrd(machine->initrd_filename, + machine->ram_size, kernel_entry, + &start); + qemu_fdt_setprop_cell(fdt, "/chosen", + "linux,initrd-start", start); + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + end); + } + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ + 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ +#if defined(TARGET_RISCV32) + 0x0182a283, /* lw t0, 24(t0) */ +#elif defined(TARGET_RISCV64) + 0x0182b283, /* ld t0, 24(t0) */ +#endif + 0x00028067, /* jr t0 */ + 0x00000000, + memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */ + 0x00000000, + /* dtb: */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec)); + + /* copy in the device tree */ + qemu_fdt_dumpdtb(s->fdt, s->fdt_size); + cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec), + s->fdt, s->fdt_size); + + /* create PLIC hart topology configuration string */ + plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus; + plic_hart_config = g_malloc0(plic_hart_config_len); + for (i = 0; i < smp_cpus; i++) { + if (i != 0) { + strncat(plic_hart_config, ",", plic_hart_config_len); + } + strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len); + plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1); + } + + /* MMIO */ + s->plic = sifive_plic_create(memmap[VIRT_PLIC].base, + plic_hart_config, + VIRT_PLIC_NUM_SOURCES, + VIRT_PLIC_NUM_PRIORITIES, + VIRT_PLIC_PRIORITY_BASE, + VIRT_PLIC_PENDING_BASE, + VIRT_PLIC_ENABLE_BASE, + VIRT_PLIC_ENABLE_STRIDE, + VIRT_PLIC_CONTEXT_BASE, + VIRT_PLIC_CONTEXT_STRIDE, + memmap[VIRT_PLIC].size); + sifive_clint_create(memmap[VIRT_CLINT].base, + memmap[VIRT_CLINT].size, smp_cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + sifive_test_create(memmap[VIRT_TEST].base); + + for (i = 0; i < VIRTIO_COUNT; i++) { + sysbus_create_simple("virtio-mmio", + memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, + SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]); + } + + serial_mm_init(system_memory, memmap[VIRT_UART0].base, + 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193, + serial_hds[0], DEVICE_LITTLE_ENDIAN); +} + +static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_virt_board_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_virt_board_sysbus_device_init; +} + +static const TypeInfo riscv_virt_board_device = { + .name = TYPE_RISCV_VIRT_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RISCVVirtState), + .class_init = riscv_virt_board_class_init, +}; + +static void riscv_virt_board_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)"; + mc->init = riscv_virt_board_init; + mc->max_cpus = 8; /* hardcoded limit in BBL */ +} + +DEFINE_MACHINE("virt", riscv_virt_board_machine_init) + +static void riscv_virt_board_register_types(void) +{ + type_register_static(&riscv_virt_board_device); +} + +type_init(riscv_virt_board_register_types); diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h new file mode 100644 index 0000000000..7525647e63 --- /dev/null +++ b/include/hw/riscv/virt.h @@ -0,0 +1,74 @@ +/* + * SiFive VirtIO Board + * + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_VIRT_H +#define HW_VIRT_H + +#define TYPE_RISCV_VIRT_BOARD "riscv.virt" +#define VIRT(obj) \ + OBJECT_CHECK(RISCVVirtState, (obj), TYPE_RISCV_VIRT_BOARD) + +enum { ROM_BASE = 0x1000 }; + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState soc; + DeviceState *plic; + void *fdt; + int fdt_size; +} RISCVVirtState; + +enum { + VIRT_DEBUG, + VIRT_MROM, + VIRT_TEST, + VIRT_CLINT, + VIRT_PLIC, + VIRT_UART0, + VIRT_VIRTIO, + VIRT_DRAM +}; + + +enum { + UART0_IRQ = 10, + VIRTIO_IRQ = 1, /* 1 to 8 */ + VIRTIO_COUNT = 8, + VIRTIO_NDEV = 10 +}; + +#define VIRT_PLIC_HART_CONFIG "MS" +#define VIRT_PLIC_NUM_SOURCES 127 +#define VIRT_PLIC_NUM_PRIORITIES 7 +#define VIRT_PLIC_PRIORITY_BASE 0x0 +#define VIRT_PLIC_PENDING_BASE 0x1000 +#define VIRT_PLIC_ENABLE_BASE 0x2000 +#define VIRT_PLIC_ENABLE_STRIDE 0x80 +#define VIRT_PLIC_CONTEXT_BASE 0x200000 +#define VIRT_PLIC_CONTEXT_STRIDE 0x1000 + +#if defined(TARGET_RISCV32) +#define VIRT_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0 +#elif defined(TARGET_RISCV64) +#define VIRT_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0 +#endif + +#endif -- cgit v1.2.3-55-g7522 From bb72692cbdbeeef88f4dd1828c1ad6f92cd57b7e Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:14 +1300 Subject: SiFive RISC-V UART Device QEMU model of the UART on the SiFive E300 and U500 series SOCs. BBL supports the SiFive UART for early console access via the SBI (Supervisor Binary Interface) and the linux kernel SBI console. The SiFive UART implements the pre qom legacy interface consistent with the 16550a UART in 'hw/char/serial.c'. Acked-by: Richard Henderson Signed-off-by: Stefan O'Rear Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_uart.c | 176 +++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_uart.h | 71 +++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 hw/riscv/sifive_uart.c create mode 100644 include/hw/riscv/sifive_uart.h (limited to 'include') diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c new file mode 100644 index 0000000000..b0c3798cf2 --- /dev/null +++ b/hw/riscv/sifive_uart.c @@ -0,0 +1,176 @@ +/* + * QEMU model of the UART on the SiFive E300 and U500 series SOCs. + * + * Copyright (c) 2016 Stefan O'Rear + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_uart.h" + +/* + * Not yet implemented: + * + * Transmit FIFO using "qemu/fifo8.h" + * SIFIVE_UART_IE_TXWM interrupts + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark + * Rx FIFO watermark interrupt trigger threshold + * Tx FIFO watermark interrupt trigger threshold. + */ + +static void update_irq(SiFiveUARTState *s) +{ + int cond = 0; + if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) { + cond = 1; + } + if (cond) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static uint64_t +uart_read(void *opaque, hwaddr addr, unsigned int size) +{ + SiFiveUARTState *s = opaque; + unsigned char r; + switch (addr) { + case SIFIVE_UART_RXFIFO: + if (s->rx_fifo_len) { + r = s->rx_fifo[0]; + memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1); + s->rx_fifo_len--; + qemu_chr_fe_accept_input(&s->chr); + update_irq(s); + return r; + } + return 0x80000000; + + case SIFIVE_UART_TXFIFO: + return 0; /* Should check tx fifo */ + case SIFIVE_UART_IE: + return s->ie; + case SIFIVE_UART_IP: + return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0; + case SIFIVE_UART_TXCTRL: + return s->txctrl; + case SIFIVE_UART_RXCTRL: + return s->rxctrl; + case SIFIVE_UART_DIV: + return s->div; + } + + hw_error("%s: bad read: addr=0x%x\n", + __func__, (int)addr); + return 0; +} + +static void +uart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + SiFiveUARTState *s = opaque; + uint32_t value = val64; + unsigned char ch = value; + + switch (addr) { + case SIFIVE_UART_TXFIFO: + qemu_chr_fe_write(&s->chr, &ch, 1); + return; + case SIFIVE_UART_IE: + s->ie = val64; + update_irq(s); + return; + case SIFIVE_UART_TXCTRL: + s->txctrl = val64; + return; + case SIFIVE_UART_RXCTRL: + s->rxctrl = val64; + return; + case SIFIVE_UART_DIV: + s->div = val64; + return; + } + hw_error("%s: bad write: addr=0x%x v=0x%x\n", + __func__, (int)addr, (int)value); +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + SiFiveUARTState *s = opaque; + + /* Got a byte. */ + if (s->rx_fifo_len >= sizeof(s->rx_fifo)) { + printf("WARNING: UART dropped char.\n"); + return; + } + s->rx_fifo[s->rx_fifo_len++] = *buf; + + update_irq(s); +} + +static int uart_can_rx(void *opaque) +{ + SiFiveUARTState *s = opaque; + + return s->rx_fifo_len < sizeof(s->rx_fifo); +} + +static void uart_event(void *opaque, int event) +{ +} + +static int uart_be_change(void *opaque) +{ + SiFiveUARTState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, + uart_be_change, s, NULL, true); + + return 0; +} + +/* + * Create UART device. + */ +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, + Chardev *chr, qemu_irq irq) +{ + SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState)); + s->irq = irq; + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, + uart_be_change, s, NULL, true); + memory_region_init_io(&s->mmio, NULL, &uart_ops, s, + TYPE_SIFIVE_UART, SIFIVE_UART_MAX); + memory_region_add_subregion(address_space, base, &s->mmio); + return s; +} diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h new file mode 100644 index 0000000000..504f18a60f --- /dev/null +++ b/include/hw/riscv/sifive_uart.h @@ -0,0 +1,71 @@ +/* + * SiFive UART interface + * + * Copyright (c) 2016 Stefan O'Rear + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_UART_H +#define HW_SIFIVE_UART_H + +enum { + SIFIVE_UART_TXFIFO = 0, + SIFIVE_UART_RXFIFO = 4, + SIFIVE_UART_TXCTRL = 8, + SIFIVE_UART_TXMARK = 10, + SIFIVE_UART_RXCTRL = 12, + SIFIVE_UART_RXMARK = 14, + SIFIVE_UART_IE = 16, + SIFIVE_UART_IP = 20, + SIFIVE_UART_DIV = 24, + SIFIVE_UART_MAX = 32 +}; + +enum { + SIFIVE_UART_IE_TXWM = 1, /* Transmit watermark interrupt enable */ + SIFIVE_UART_IE_RXWM = 2 /* Receive watermark interrupt enable */ +}; + +enum { + SIFIVE_UART_IP_TXWM = 1, /* Transmit watermark interrupt pending */ + SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */ +}; + +#define TYPE_SIFIVE_UART "riscv.sifive.uart" + +#define SIFIVE_UART(obj) \ + OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART) + +typedef struct SiFiveUARTState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq irq; + MemoryRegion mmio; + CharBackend chr; + uint8_t rx_fifo[8]; + unsigned int rx_fifo_len; + uint32_t ie; + uint32_t ip; + uint32_t txctrl; + uint32_t rxctrl; + uint32_t div; +} SiFiveUARTState; + +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, + Chardev *chr, qemu_irq irq); + +#endif -- cgit v1.2.3-55-g7522 From e6b8552c655aad405e7dc28d84b4a6d5324f1b92 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:14 +1300 Subject: SiFive RISC-V PRCI Block Simple model of the PRCI (Power, Reset, Clock, Interrupt) to emulate register reads made by the SDK BSP. Acked-by: Richard Henderson Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_prci.c | 89 ++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_prci.h | 37 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 hw/riscv/sifive_prci.c create mode 100644 include/hw/riscv/sifive_prci.h (limited to 'include') diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c new file mode 100644 index 0000000000..0910ea32c1 --- /dev/null +++ b/hw/riscv/sifive_prci.c @@ -0,0 +1,89 @@ +/* + * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) + * + * Copyright (c) 2017 SiFive, Inc. + * + * Simple model of the PRCI to emulate register reads made by the SDK BSP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_prci.h" + +/* currently implements enough to mock freedom-e-sdk BSP clock programming */ + +static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size) +{ + if (addr == 0 /* PRCI_HFROSCCFG */) { + return 1 << 31; /* ROSC_RDY */ + } + if (addr == 8 /* PRCI_PLLCFG */) { + return 1 << 31; /* PLL_LOCK */ + } + hw_error("%s: read: addr=0x%x\n", __func__, (int)addr); + return 0; +} + +static void sifive_prci_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + /* discard writes */ +} + +static const MemoryRegionOps sifive_prci_ops = { + .read = sifive_prci_read, + .write = sifive_prci_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_prci_init(Object *obj) +{ + SiFivePRCIState *s = SIFIVE_PRCI(obj); + + memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s, + TYPE_SIFIVE_PRCI, 0x8000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const TypeInfo sifive_prci_info = { + .name = TYPE_SIFIVE_PRCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFivePRCIState), + .instance_init = sifive_prci_init, +}; + +static void sifive_prci_register_types(void) +{ + type_register_static(&sifive_prci_info); +} + +type_init(sifive_prci_register_types) + + +/* + * Create PRCI device. + */ +DeviceState *sifive_prci_create(hwaddr addr) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h new file mode 100644 index 0000000000..b6f4c486cc --- /dev/null +++ b/include/hw/riscv/sifive_prci.h @@ -0,0 +1,37 @@ +/* + * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_PRCI_H +#define HW_SIFIVE_PRCI_H + +#define TYPE_SIFIVE_PRCI "riscv.sifive.prci" + +#define SIFIVE_PRCI(obj) \ + OBJECT_CHECK(SiFivePRCIState, (obj), TYPE_SIFIVE_PRCI) + +typedef struct SiFivePRCIState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; +} SiFivePRCIState; + +DeviceState *sifive_prci_create(hwaddr addr); + +#endif -- cgit v1.2.3-55-g7522 From eb637edb1241aff1442579475da303ee5b672910 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:14 +1300 Subject: SiFive Freedom E Series RISC-V Machine This provides a RISC-V Board compatible with the the SiFive Freedom E SDK. The following machine is implemented: - 'sifive_e'; CLINT, PLIC, UART, AON, GPIO, QSPI, PWM Acked-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_e.c | 234 ++++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_e.h | 79 +++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 hw/riscv/sifive_e.c create mode 100644 include/hw/riscv/sifive_e.h (limited to 'include') diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c new file mode 100644 index 0000000000..19eca36ff4 --- /dev/null +++ b/hw/riscv/sifive_e.c @@ -0,0 +1,234 @@ +/* + * QEMU RISC-V Board Compatible with SiFive Freedom E SDK + * + * Copyright (c) 2017 SiFive, Inc. + * + * Provides a board compatible with the SiFive Freedom E SDK: + * + * 0) UART + * 1) CLINT (Core Level Interruptor) + * 2) PLIC (Platform Level Interrupt Controller) + * 3) PRCI (Power, Reset, Clock, Interrupt) + * 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM + * 5) Flash memory emulated as RAM + * + * The Mask ROM reset vector jumps to the flash payload at 0x2040_0000. + * The OTP ROM and Flash boot code will be emulated in a future version. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_prci.h" +#include "hw/riscv/sifive_uart.h" +#include "hw/riscv/sifive_e.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} sifive_e_memmap[] = { + [SIFIVE_E_DEBUG] = { 0x0, 0x100 }, + [SIFIVE_E_MROM] = { 0x1000, 0x2000 }, + [SIFIVE_E_OTP] = { 0x20000, 0x2000 }, + [SIFIVE_E_CLINT] = { 0x2000000, 0x10000 }, + [SIFIVE_E_PLIC] = { 0xc000000, 0x4000000 }, + [SIFIVE_E_AON] = { 0x10000000, 0x8000 }, + [SIFIVE_E_PRCI] = { 0x10008000, 0x8000 }, + [SIFIVE_E_OTP_CTRL] = { 0x10010000, 0x1000 }, + [SIFIVE_E_GPIO0] = { 0x10012000, 0x1000 }, + [SIFIVE_E_UART0] = { 0x10013000, 0x1000 }, + [SIFIVE_E_QSPI0] = { 0x10014000, 0x1000 }, + [SIFIVE_E_PWM0] = { 0x10015000, 0x1000 }, + [SIFIVE_E_UART1] = { 0x10023000, 0x1000 }, + [SIFIVE_E_QSPI1] = { 0x10024000, 0x1000 }, + [SIFIVE_E_PWM1] = { 0x10025000, 0x1000 }, + [SIFIVE_E_QSPI2] = { 0x10034000, 0x1000 }, + [SIFIVE_E_PWM2] = { 0x10035000, 0x1000 }, + [SIFIVE_E_XIP] = { 0x20000000, 0x20000000 }, + [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, + 0, ELF_MACHINE, 1, 0) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +static void sifive_mmio_emulate(MemoryRegion *parent, const char *name, + uintptr_t offset, uintptr_t length) +{ + MemoryRegion *mock_mmio = g_new(MemoryRegion, 1); + memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal); + memory_region_add_subregion(parent, offset, mock_mmio); +} + +static void riscv_sifive_e_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = sifive_e_memmap; + + SiFiveEState *s = g_new0(SiFiveEState, 1); + MemoryRegion *sys_mem = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* Data Tightly Integrated Memory */ + memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram", + memmap[SIFIVE_E_DTIM].size, &error_fatal); + memory_region_add_subregion(sys_mem, + memmap[SIFIVE_E_DTIM].base, main_mem); + + /* Mask ROM */ + memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom", + memmap[SIFIVE_E_MROM].size, &error_fatal); + memory_region_add_subregion(sys_mem, + memmap[SIFIVE_E_MROM].base, mask_rom); + + /* MMIO */ + s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base, + (char *)SIFIVE_E_PLIC_HART_CONFIG, + SIFIVE_E_PLIC_NUM_SOURCES, + SIFIVE_E_PLIC_NUM_PRIORITIES, + SIFIVE_E_PLIC_PRIORITY_BASE, + SIFIVE_E_PLIC_PENDING_BASE, + SIFIVE_E_PLIC_ENABLE_BASE, + SIFIVE_E_PLIC_ENABLE_STRIDE, + SIFIVE_E_PLIC_CONTEXT_BASE, + SIFIVE_E_PLIC_CONTEXT_STRIDE, + memmap[SIFIVE_E_PLIC].size); + sifive_clint_create(memmap[SIFIVE_E_CLINT].base, + memmap[SIFIVE_E_CLINT].size, smp_cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon", + memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size); + sifive_prci_create(memmap[SIFIVE_E_PRCI].base); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0", + memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size); + sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base, + serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0", + memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", + memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); + /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, + serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */ + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", + memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", + memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2", + memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2", + memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size); + + /* Flash memory */ + memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip", + memmap[SIFIVE_E_XIP].size, &error_fatal); + memory_region_set_readonly(xip_mem, true); + memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem); + + /* Mask ROM reset vector */ + uint32_t reset_vec[2] = { + 0x204002b7, /* 0x1000: lui t0,0x20400 */ + 0x00028067, /* 0x1004: jr t0 */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec)); + memory_region_set_readonly(mask_rom, true); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } +} + +static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_sifive_e_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_sifive_e_sysbus_device_init; +} + +static const TypeInfo riscv_sifive_e_device = { + .name = TYPE_SIFIVE_E, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveEState), + .class_init = riscv_sifive_e_class_init, +}; + +static void riscv_sifive_e_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Board compatible with SiFive E SDK"; + mc->init = riscv_sifive_e_init; + mc->max_cpus = 1; +} + +DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init) + +static void riscv_sifive_e_register_types(void) +{ + type_register_static(&riscv_sifive_e_device); +} + +type_init(riscv_sifive_e_register_types); diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h new file mode 100644 index 0000000000..0aebc576c1 --- /dev/null +++ b/include/hw/riscv/sifive_e.h @@ -0,0 +1,79 @@ +/* + * SiFive E series machine interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_E_H +#define HW_SIFIVE_E_H + +#define TYPE_SIFIVE_E "riscv.sifive_e" + +#define SIFIVE_E(obj) \ + OBJECT_CHECK(SiFiveEState, (obj), TYPE_SIFIVE_E) + +typedef struct SiFiveEState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState soc; + DeviceState *plic; +} SiFiveEState; + +enum { + SIFIVE_E_DEBUG, + SIFIVE_E_MROM, + SIFIVE_E_OTP, + SIFIVE_E_CLINT, + SIFIVE_E_PLIC, + SIFIVE_E_AON, + SIFIVE_E_PRCI, + SIFIVE_E_OTP_CTRL, + SIFIVE_E_GPIO0, + SIFIVE_E_UART0, + SIFIVE_E_QSPI0, + SIFIVE_E_PWM0, + SIFIVE_E_UART1, + SIFIVE_E_QSPI1, + SIFIVE_E_PWM1, + SIFIVE_E_QSPI2, + SIFIVE_E_PWM2, + SIFIVE_E_XIP, + SIFIVE_E_DTIM +}; + +enum { + SIFIVE_E_UART0_IRQ = 3, + SIFIVE_E_UART1_IRQ = 4 +}; + +#define SIFIVE_E_PLIC_HART_CONFIG "M" +#define SIFIVE_E_PLIC_NUM_SOURCES 127 +#define SIFIVE_E_PLIC_NUM_PRIORITIES 7 +#define SIFIVE_E_PLIC_PRIORITY_BASE 0x0 +#define SIFIVE_E_PLIC_PENDING_BASE 0x1000 +#define SIFIVE_E_PLIC_ENABLE_BASE 0x2000 +#define SIFIVE_E_PLIC_ENABLE_STRIDE 0x80 +#define SIFIVE_E_PLIC_CONTEXT_BASE 0x200000 +#define SIFIVE_E_PLIC_CONTEXT_STRIDE 0x1000 + +#if defined(TARGET_RISCV32) +#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E31 +#elif defined(TARGET_RISCV64) +#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E51 +#endif + +#endif -- cgit v1.2.3-55-g7522 From a7240d1e4aac4cd4542d68f3cc722939550da6af Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:31:14 +1300 Subject: SiFive Freedom U Series RISC-V Machine This provides a RISC-V Board compatible with the the SiFive Freedom U SDK. The following machine is implemented: - 'sifive_u'; CLINT, PLIC, UART, device-tree Acked-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Clark --- hw/riscv/sifive_u.c | 339 ++++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/sifive_u.h | 69 +++++++++ 2 files changed, 408 insertions(+) create mode 100644 hw/riscv/sifive_u.c create mode 100644 include/hw/riscv/sifive_u.h (limited to 'include') diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c new file mode 100644 index 0000000000..1c2deefa6c --- /dev/null +++ b/hw/riscv/sifive_u.c @@ -0,0 +1,339 @@ +/* + * QEMU RISC-V Board Compatible with SiFive Freedom U SDK + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * + * Provides a board compatible with the SiFive Freedom U SDK: + * + * 0) UART + * 1) CLINT (Core Level Interruptor) + * 2) PLIC (Platform Level Interrupt Controller) + * + * This board currently uses a hardcoded devicetree that indicates one hart. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_uart.h" +#include "hw/riscv/sifive_prci.h" +#include "hw/riscv/sifive_u.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} sifive_u_memmap[] = { + [SIFIVE_U_DEBUG] = { 0x0, 0x100 }, + [SIFIVE_U_MROM] = { 0x1000, 0x2000 }, + [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, + [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, + [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, + [SIFIVE_U_UART1] = { 0x10023000, 0x1000 }, + [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, + 0, ELF_MACHINE, 1, 0) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, + uint64_t mem_size, const char *cmdline) +{ + void *fdt; + int cpu; + uint32_t *cells; + char *nodename; + uint32_t plic_phandle; + + fdt = s->fdt = create_device_tree(&s->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + + nodename = g_strdup_printf("/memory@%lx", + (long)memmap[SIFIVE_U_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_DRAM].base, + mem_size >> 32, mem_size); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + char *isa = riscv_isa_string(&s->soc.harts[cpu]); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); + qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); + qemu_fdt_add_subnode(fdt, intc); + qemu_fdt_setprop_cell(fdt, intc, "phandle", 1); + qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1); + qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); + g_free(isa); + g_free(intc); + g_free(nodename); + } + + cells = g_new0(uint32_t, s->soc.num_harts * 4); + for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/clint@%lx", + (long)memmap[SIFIVE_U_CLINT].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_CLINT].base, + 0x0, memmap[SIFIVE_U_CLINT].size); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + g_free(cells); + g_free(nodename); + + cells = g_new0(uint32_t, s->soc.num_harts * 4); + for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/interrupt-controller@%lx", + (long)memmap[SIFIVE_U_PLIC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_PLIC].base, + 0x0, memmap[SIFIVE_U_PLIC].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4); + qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2); + qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2); + plic_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(cells); + g_free(nodename); + + nodename = g_strdup_printf("/uart@%lx", + (long)memmap[SIFIVE_U_UART0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_UART0].base, + 0x0, memmap[SIFIVE_U_UART0].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + g_free(nodename); +} + +static void riscv_sifive_u_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = sifive_u_memmap; + + SiFiveUState *s = g_new0(SiFiveUState, 1); + MemoryRegion *sys_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* register RAM */ + memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base, + main_mem); + + /* create device tree */ + create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom", + memmap[SIFIVE_U_MROM].base, &error_fatal); + memory_region_set_readonly(boot_rom, true); + memory_region_add_subregion(sys_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ + 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ +#if defined(TARGET_RISCV32) + 0x0182a283, /* lw t0, 24(t0) */ +#elif defined(TARGET_RISCV64) + 0x0182b283, /* ld t0, 24(t0) */ +#endif + 0x00028067, /* jr t0 */ + 0x00000000, + memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */ + 0x00000000, + /* dtb: */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec)); + + /* copy in the device tree */ + qemu_fdt_dumpdtb(s->fdt, s->fdt_size); + cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base + + sizeof(reset_vec), s->fdt, s->fdt_size); + + /* MMIO */ + s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, + (char *)SIFIVE_U_PLIC_HART_CONFIG, + SIFIVE_U_PLIC_NUM_SOURCES, + SIFIVE_U_PLIC_NUM_PRIORITIES, + SIFIVE_U_PLIC_PRIORITY_BASE, + SIFIVE_U_PLIC_PENDING_BASE, + SIFIVE_U_PLIC_ENABLE_BASE, + SIFIVE_U_PLIC_ENABLE_STRIDE, + SIFIVE_U_PLIC_CONTEXT_BASE, + SIFIVE_U_PLIC_CONTEXT_STRIDE, + memmap[SIFIVE_U_PLIC].size); + sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base, + serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); + /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base, + serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ + sifive_clint_create(memmap[SIFIVE_U_CLINT].base, + memmap[SIFIVE_U_CLINT].size, smp_cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); +} + +static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_sifive_u_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_sifive_u_sysbus_device_init; +} + +static const TypeInfo riscv_sifive_u_device = { + .name = TYPE_SIFIVE_U, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveUState), + .class_init = riscv_sifive_u_class_init, +}; + +static void riscv_sifive_u_register_types(void) +{ + type_register_static(&riscv_sifive_u_device); +} + +type_init(riscv_sifive_u_register_types); + +static void riscv_sifive_u_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Board compatible with SiFive U SDK"; + mc->init = riscv_sifive_u_init; + mc->max_cpus = 1; +} + +DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init) diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h new file mode 100644 index 0000000000..662e8a1c1a --- /dev/null +++ b/include/hw/riscv/sifive_u.h @@ -0,0 +1,69 @@ +/* + * SiFive U series machine interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_U_H +#define HW_SIFIVE_U_H + +#define TYPE_SIFIVE_U "riscv.sifive_u" + +#define SIFIVE_U(obj) \ + OBJECT_CHECK(SiFiveUState, (obj), TYPE_SIFIVE_U) + +typedef struct SiFiveUState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState soc; + DeviceState *plic; + void *fdt; + int fdt_size; +} SiFiveUState; + +enum { + SIFIVE_U_DEBUG, + SIFIVE_U_MROM, + SIFIVE_U_CLINT, + SIFIVE_U_PLIC, + SIFIVE_U_UART0, + SIFIVE_U_UART1, + SIFIVE_U_DRAM +}; + +enum { + SIFIVE_U_UART0_IRQ = 3, + SIFIVE_U_UART1_IRQ = 4 +}; + +#define SIFIVE_U_PLIC_HART_CONFIG "MS" +#define SIFIVE_U_PLIC_NUM_SOURCES 127 +#define SIFIVE_U_PLIC_NUM_PRIORITIES 7 +#define SIFIVE_U_PLIC_PRIORITY_BASE 0x0 +#define SIFIVE_U_PLIC_PENDING_BASE 0x1000 +#define SIFIVE_U_PLIC_ENABLE_BASE 0x2000 +#define SIFIVE_U_PLIC_ENABLE_STRIDE 0x80 +#define SIFIVE_U_PLIC_CONTEXT_BASE 0x200000 +#define SIFIVE_U_PLIC_CONTEXT_STRIDE 0x1000 + +#if defined(TARGET_RISCV32) +#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U34 +#elif defined(TARGET_RISCV64) +#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U54 +#endif + +#endif -- cgit v1.2.3-55-g7522 From 25fa194b7b11901561532e435beb83d046899f7a Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 01:32:59 +1300 Subject: RISC-V Build Infrastructure This adds RISC-V into the build system enabling the following targets: - riscv32-softmmu - riscv64-softmmu - riscv32-linux-user - riscv64-linux-user This adds defaults configs for RISC-V, enables the build for the RISC-V CPU core, hardware, and Linux User Emulation. The 'qemu-binfmt-conf.sh' script is updated to add the RISC-V ELF magic. Expected checkpatch errors for consistency reasons: ERROR: line over 90 characters FILE: scripts/qemu-binfmt-conf.sh Reviewed-by: Richard Henderson Signed-off-by: Sagar Karandikar Signed-off-by: Michael Clark --- arch_init.c | 2 ++ configure | 13 +++++++++++++ cpus.c | 6 ++++++ default-configs/riscv32-linux-user.mak | 1 + default-configs/riscv32-softmmu.mak | 4 ++++ default-configs/riscv64-linux-user.mak | 1 + default-configs/riscv64-softmmu.mak | 4 ++++ hw/riscv/Makefile.objs | 11 +++++++++++ include/sysemu/arch_init.h | 1 + qapi/misc.json | 17 ++++++++++++++++- scripts/qemu-binfmt-conf.sh | 13 ++++++++++++- target/riscv/Makefile.objs | 1 + 12 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 default-configs/riscv32-linux-user.mak create mode 100644 default-configs/riscv32-softmmu.mak create mode 100644 default-configs/riscv64-linux-user.mak create mode 100644 default-configs/riscv64-softmmu.mak create mode 100644 hw/riscv/Makefile.objs create mode 100644 target/riscv/Makefile.objs (limited to 'include') diff --git a/arch_init.c b/arch_init.c index 46d03f550d..6ee07478bd 100644 --- a/arch_init.c +++ b/arch_init.c @@ -71,6 +71,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_OPENRISC #elif defined(TARGET_PPC) #define QEMU_ARCH QEMU_ARCH_PPC +#elif defined(TARGET_RISCV) +#define QEMU_ARCH QEMU_ARCH_RISCV #elif defined(TARGET_S390X) #define QEMU_ARCH QEMU_ARCH_S390X #elif defined(TARGET_SH4) diff --git a/configure b/configure index 6f3921c02a..f74e1f3b7c 100755 --- a/configure +++ b/configure @@ -6797,6 +6797,16 @@ case "$target_name" in echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; + riscv32) + TARGET_BASE_ARCH=riscv + TARGET_ABI_DIR=riscv + mttcg=yes + ;; + riscv64) + TARGET_BASE_ARCH=riscv + TARGET_ABI_DIR=riscv + mttcg=yes + ;; sh4|sh4eb) TARGET_ARCH=sh4 bflt="yes" @@ -6966,6 +6976,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do ppc*) disas_config "PPC" ;; + riscv) + disas_config "RISCV" + ;; s390*) disas_config "S390" ;; diff --git a/cpus.c b/cpus.c index 9bcff7d63c..865cffd025 100644 --- a/cpus.c +++ b/cpus.c @@ -2081,6 +2081,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) #elif defined(TARGET_SPARC) SPARCCPU *sparc_cpu = SPARC_CPU(cpu); CPUSPARCState *env = &sparc_cpu->env; +#elif defined(TARGET_RISCV) + RISCVCPU *riscv_cpu = RISCV_CPU(cpu); + CPURISCVState *env = &riscv_cpu->env; #elif defined(TARGET_MIPS) MIPSCPU *mips_cpu = MIPS_CPU(cpu); CPUMIPSState *env = &mips_cpu->env; @@ -2120,6 +2123,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) #elif defined(TARGET_S390X) info->value->arch = CPU_INFO_ARCH_S390; info->value->u.s390.cpu_state = env->cpu_state; +#elif defined(TARGET_RISCV) + info->value->arch = CPU_INFO_ARCH_RISCV; + info->value->u.riscv.pc = env->pc; #else info->value->arch = CPU_INFO_ARCH_OTHER; #endif diff --git a/default-configs/riscv32-linux-user.mak b/default-configs/riscv32-linux-user.mak new file mode 100644 index 0000000000..865b362f5a --- /dev/null +++ b/default-configs/riscv32-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for riscv-linux-user diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak new file mode 100644 index 0000000000..f9e742120c --- /dev/null +++ b/default-configs/riscv32-softmmu.mak @@ -0,0 +1,4 @@ +# Default configuration for riscv-softmmu + +CONFIG_SERIAL=y +CONFIG_VIRTIO=y diff --git a/default-configs/riscv64-linux-user.mak b/default-configs/riscv64-linux-user.mak new file mode 100644 index 0000000000..865b362f5a --- /dev/null +++ b/default-configs/riscv64-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for riscv-linux-user diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak new file mode 100644 index 0000000000..f9e742120c --- /dev/null +++ b/default-configs/riscv64-softmmu.mak @@ -0,0 +1,4 @@ +# Default configuration for riscv-softmmu + +CONFIG_SERIAL=y +CONFIG_VIRTIO=y diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs new file mode 100644 index 0000000000..1dde01d39d --- /dev/null +++ b/hw/riscv/Makefile.objs @@ -0,0 +1,11 @@ +obj-y += riscv_htif.o +obj-y += riscv_hart.o +obj-y += sifive_e.o +obj-y += sifive_clint.o +obj-y += sifive_prci.o +obj-y += sifive_plic.o +obj-y += sifive_test.o +obj-y += sifive_u.o +obj-y += sifive_uart.o +obj-y += spike.o +obj-y += virt.o diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index cecd494159..32abdfe6a1 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -24,6 +24,7 @@ enum { QEMU_ARCH_TRICORE = (1 << 16), QEMU_ARCH_NIOS2 = (1 << 17), QEMU_ARCH_HPPA = (1 << 18), + QEMU_ARCH_RISCV = (1 << 19), }; extern const uint32_t arch_type; diff --git a/qapi/misc.json b/qapi/misc.json index bd04469a4b..bcd5d10778 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -320,10 +320,12 @@ # # @s390: since 2.12 # +# @riscv: since 2.12 +# # Since: 2.6 ## { 'enum': 'CpuInfoArch', - 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] } + 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'riscv', 'other' ] } ## # @CpuInfo: @@ -363,6 +365,7 @@ 'mips': 'CpuInfoMIPS', 'tricore': 'CpuInfoTricore', 's390': 'CpuInfoS390', + 'riscv': 'CpuInfoRISCV', 'other': 'CpuInfoOther' } } ## @@ -422,6 +425,17 @@ ## { 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } } +## +# @CpuInfoRISCV: +# +# Additional information about a virtual RISCV CPU +# +# @pc: the instruction pointer +# +# Since 2.12 +## +{ 'struct': 'CpuInfoRISCV', 'data': { 'pc': 'int' } } + ## # @CpuInfoOther: # @@ -533,6 +547,7 @@ 'mips': 'CpuInfoOther', 'tricore': 'CpuInfoOther', 's390': 'CpuInfoS390', + 'riscv': 'CpuInfoRISCV', 'other': 'CpuInfoOther' } } ## diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index ea5a748745..bdb21bdd58 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -4,7 +4,7 @@ qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \ mips mipsel mipsn32 mipsn32el mips64 mips64el \ -sh4 sh4eb s390x aarch64 aarch64_be hppa" +sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -100,6 +100,14 @@ hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' hppa_family=hppa +riscv32_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00' +riscv32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +riscv32_family=riscv + +riscv64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00' +riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +riscv64_family=riscv + qemu_get_family() { cpu=${HOST_ARCH:-$(uname -m)} case "$cpu" in @@ -124,6 +132,9 @@ qemu_get_family() { sparc*) echo "sparc" ;; + riscv*) + echo "riscv" + ;; *) echo "$cpu" ;; diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs new file mode 100644 index 0000000000..abd0a7cde3 --- /dev/null +++ b/target/riscv/Makefile.objs @@ -0,0 +1 @@ +obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o -- cgit v1.2.3-55-g7522