diff options
69 files changed, 13310 insertions, 27 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index b7c4130388..f409f3b158 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -233,6 +233,17 @@ F: hw/ppc/ F: include/hw/ppc/ F: disas/ppc.c +RISC-V +M: Michael Clark <mjc@sifive.com> +M: Palmer Dabbelt <palmer@sifive.com> +M: Sagar Karandikar <sagark@eecs.berkeley.edu> +M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> +S: Maintained +F: target/riscv/ +F: hw/riscv/ +F: include/hw/riscv/ +F: disas/riscv.c + S390 M: Richard Henderson <rth@twiddle.net> M: Alexander Graf <agraf@suse.de> 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) @@ -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" ;; @@ -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 @@ -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 <michaeljclark@mac.com> + * 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 <http://www.gnu.org/licenses/>. + */ + +#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/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 9ccb59422c..27834af0de 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -114,7 +114,8 @@ float32 float32_default_nan(float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) return const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE) + defined(TARGET_XTENSA) || defined(TARGET_S390X) || \ + defined(TARGET_TRICORE) || defined(TARGET_RISCV) return const_float32(0x7FC00000); #elif defined(TARGET_HPPA) return const_float32(0x7FA00000); @@ -139,7 +140,7 @@ float64 float64_default_nan(float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) return const_float64(LIT64(0x7FFFFFFFFFFFFFFF)); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_S390X) + defined(TARGET_S390X) || defined(TARGET_RISCV) return const_float64(LIT64(0x7FF8000000000000)); #elif defined(TARGET_HPPA) return const_float64(LIT64(0x7FF4000000000000)); @@ -203,7 +204,7 @@ float128 float128_default_nan(float_status *status) r.high = LIT64(0x7FFF7FFFFFFFFFFF); } else { r.low = LIT64(0x0000000000000000); -#if defined(TARGET_S390X) || defined(TARGET_PPC) +#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV) r.high = LIT64(0x7FFF800000000000); #else r.high = LIT64(0xFFFF800000000000); 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 @@ -450,6 +450,20 @@ int load_elf_ram(const char *filename, 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/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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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 <http://www.gnu.org/licenses/>. + */ + +#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/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. */ 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. 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, 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 + 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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 <http://www.gnu.org/licenses/>. + */ + +#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 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/linux-user/elfload.c b/linux-user/elfload.c index e3689c658a..5fc130cc20 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1295,6 +1295,28 @@ static inline void init_thread(struct target_pt_regs *regs, #endif /* TARGET_TILEGX */ +#ifdef TARGET_RISCV + +#define ELF_START_MMAP 0x80000000 +#define ELF_ARCH EM_RISCV + +#ifdef TARGET_RISCV32 +#define ELF_CLASS ELFCLASS32 +#else +#define ELF_CLASS ELFCLASS64 +#endif + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + regs->sepc = infop->entry; + regs->sp = infop->start_stack; +} + +#define ELF_EXEC_PAGESIZE 4096 + +#endif /* TARGET_RISCV */ + #ifdef TARGET_HPPA #define ELF_START_MMAP 0x80000000 diff --git a/linux-user/main.c b/linux-user/main.c index bbeb78fb89..7bc9bc79b0 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3653,6 +3653,100 @@ void cpu_loop(CPUTLGState *env) #endif +#ifdef TARGET_RISCV + +void cpu_loop(CPURISCVState *env) +{ + CPUState *cs = CPU(riscv_env_get_cpu(env)); + int trapnr, signum, sigcode; + target_ulong sigaddr; + target_ulong ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + signum = 0; + sigcode = 0; + sigaddr = 0; + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + case RISCV_EXCP_U_ECALL: + env->pc += 4; + if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) { + /* riscv_flush_icache_syscall is a no-op in QEMU as + self-modifying code is automatically detected */ + ret = 0; + } else { + ret = do_syscall(env, + env->gpr[xA7], + env->gpr[xA0], + env->gpr[xA1], + env->gpr[xA2], + env->gpr[xA3], + env->gpr[xA4], + env->gpr[xA5], + 0, 0); + } + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gpr[xA0] = ret; + } + if (cs->singlestep_enabled) { + goto gdbstep; + } + break; + case RISCV_EXCP_ILLEGAL_INST: + signum = TARGET_SIGILL; + sigcode = TARGET_ILL_ILLOPC; + break; + case RISCV_EXCP_BREAKPOINT: + signum = TARGET_SIGTRAP; + sigcode = TARGET_TRAP_BRKPT; + sigaddr = env->pc; + break; + case RISCV_EXCP_INST_PAGE_FAULT: + case RISCV_EXCP_LOAD_PAGE_FAULT: + case RISCV_EXCP_STORE_PAGE_FAULT: + signum = TARGET_SIGSEGV; + sigcode = TARGET_SEGV_MAPERR; + break; + case EXCP_DEBUG: + gdbstep: + signum = gdb_handlesig(cs, TARGET_SIGTRAP); + sigcode = TARGET_TRAP_BRKPT; + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", + trapnr); + exit(EXIT_FAILURE); + } + + if (signum) { + target_siginfo_t info = { + .si_signo = signum, + .si_errno = 0, + .si_code = sigcode, + ._sifields._sigfault._addr = sigaddr + }; + queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); + } + + process_pending_signals(env); + } +} + +#endif /* TARGET_RISCV */ + #ifdef TARGET_HPPA static abi_ulong hppa_lws(CPUHPPAState *env) @@ -4803,6 +4897,11 @@ int main(int argc, char **argv, char **envp) env->pc = regs->pc; cpu_set_sr(env, regs->sr); } +#elif defined(TARGET_RISCV) + { + env->pc = regs->sepc; + env->gpr[xSP] = regs->sp; + } #elif defined(TARGET_SH4) { int i; diff --git a/linux-user/riscv/syscall_nr.h b/linux-user/riscv/syscall_nr.h new file mode 100644 index 0000000000..7e30f1f1ef --- /dev/null +++ b/linux-user/riscv/syscall_nr.h @@ -0,0 +1,287 @@ +/* + * Syscall numbers from asm-generic, common for most + * of recently-added arches including RISC-V. + */ + +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#ifdef TARGET_RISCV32 +#define TARGET_NR_fcntl64 25 +#else +#define TARGET_NR_fcntl 25 +#endif +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_renameat 38 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs 43 +#define TARGET_NR_fstatfs 44 +#define TARGET_NR_truncate 45 +#define TARGET_NR_ftruncate 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_lseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_newfstatat 79 +#define TARGET_NR_fstat 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#ifdef TARGET_RISCV32 +#define TARGET_NR_mmap2 222 +#define TARGET_NR_fadvise64_64 223 +#else +#define TARGET_NR_mmap 222 +#define TARGET_NR_fadvise64 223 +#endif +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 + +#define TARGET_NR_syscalls (TARGET_NR_copy_file_range + 1) diff --git a/linux-user/riscv/target_cpu.h b/linux-user/riscv/target_cpu.h new file mode 100644 index 0000000000..c5549b1120 --- /dev/null +++ b/linux-user/riscv/target_cpu.h @@ -0,0 +1,18 @@ +#ifndef TARGET_CPU_H +#define TARGET_CPU_H + +static inline void cpu_clone_regs(CPURISCVState *env, target_ulong newsp) +{ + if (newsp) { + env->gpr[xSP] = newsp; + } + + env->gpr[xA0] = 0; +} + +static inline void cpu_set_tls(CPURISCVState *env, target_ulong newtls) +{ + env->gpr[xTP] = newtls; +} + +#endif diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h new file mode 100644 index 0000000000..a6716a6aac --- /dev/null +++ b/linux-user/riscv/target_elf.h @@ -0,0 +1,14 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef RISCV_TARGET_ELF_H +#define RISCV_TARGET_ELF_H +static inline const char *cpu_get_model(uint32_t eflags) +{ + return "any"; +} +#endif diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h new file mode 100644 index 0000000000..ce77f752e3 --- /dev/null +++ b/linux-user/riscv/target_signal.h @@ -0,0 +1,23 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_int ss_flags; + abi_ulong ss_size; +} target_stack_t; + +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_SIGSTKSZ 8192 + +static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) +{ + return state->gpr[xSP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/riscv/target_structs.h b/linux-user/riscv/target_structs.h new file mode 100644 index 0000000000..4f0462c497 --- /dev/null +++ b/linux-user/riscv/target_structs.h @@ -0,0 +1,46 @@ +/* + * RISC-V specific structures for linux-user + * + * This is a copy of ../aarch64/target_structs.h atm. + * + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h new file mode 100644 index 0000000000..d4e109a27f --- /dev/null +++ b/linux-user/riscv/target_syscall.h @@ -0,0 +1,56 @@ +/* + * This struct defines the way the registers are stored on the + * stack during a system call. + * + * Reference: linux/arch/riscv/include/uapi/asm/ptrace.h + */ + +struct target_pt_regs { + abi_long sepc; + abi_long ra; + abi_long sp; + abi_long gp; + abi_long tp; + abi_long t0; + abi_long t1; + abi_long t2; + abi_long s0; + abi_long s1; + abi_long a0; + abi_long a1; + abi_long a2; + abi_long a3; + abi_long a4; + abi_long a5; + abi_long a6; + abi_long a7; + abi_long s2; + abi_long s3; + abi_long s4; + abi_long s5; + abi_long s6; + abi_long s7; + abi_long s8; + abi_long s9; + abi_long s10; + abi_long s11; + abi_long t3; + abi_long t4; + abi_long t5; + abi_long t6; +}; + +#ifdef TARGET_RISCV32 +#define UNAME_MACHINE "riscv32" +#else +#define UNAME_MACHINE "riscv64" +#endif +#define UNAME_MINIMUM_RELEASE "3.8.0" + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +/* clone(flags, newsp, ptidptr, tls, ctidptr) for RISC-V */ +/* This comes from linux/kernel/fork.c, CONFIG_CLONE_BACKWARDS */ +#define TARGET_CLONE_BACKWARDS diff --git a/linux-user/riscv/termbits.h b/linux-user/riscv/termbits.h new file mode 100644 index 0000000000..7e4e230588 --- /dev/null +++ b/linux-user/riscv/termbits.h @@ -0,0 +1,222 @@ +/* from asm/termbits.h */ +/* NOTE: exactly the same as i386 */ + +#define TARGET_NCCS 19 + +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0020000 +#define TARGET_IUTF8 0040000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA B19200 +#define TARGET_EXTB B38400 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 +#define TARGET_CBAUDEX 0010000 +#define TARGET_B57600 0010001 +#define TARGET_B115200 0010002 +#define TARGET_B230400 0010003 +#define TARGET_B460800 0010004 +#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 + +/* c_cc character offsets */ +#define TARGET_VINTR 0 +#define TARGET_VQUIT 1 +#define TARGET_VERASE 2 +#define TARGET_VKILL 3 +#define TARGET_VEOF 4 +#define TARGET_VTIME 5 +#define TARGET_VMIN 6 +#define TARGET_VSWTC 7 +#define TARGET_VSTART 8 +#define TARGET_VSTOP 9 +#define TARGET_VSUSP 10 +#define TARGET_VEOL 11 +#define TARGET_VREPRINT 12 +#define TARGET_VDISCARD 13 +#define TARGET_VWERASE 14 +#define TARGET_VLNEXT 15 +#define TARGET_VEOL2 16 + +/* ioctls */ + +#define TARGET_TCGETS 0x5401 +#define TARGET_TCSETS 0x5402 +#define TARGET_TCSETSW 0x5403 +#define TARGET_TCSETSF 0x5404 +#define TARGET_TCGETA 0x5405 +#define TARGET_TCSETA 0x5406 +#define TARGET_TCSETAW 0x5407 +#define TARGET_TCSETAF 0x5408 +#define TARGET_TCSBRK 0x5409 +#define TARGET_TCXONC 0x540A +#define TARGET_TCFLSH 0x540B + +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP 0x540F +#define TARGET_TIOCSPGRP 0x5410 +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ TARGET_FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ +#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ +#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TARGET_TIOCGPTN TARGET_IOR('T', 0x30, unsigned int) + /* Get Pty Number (of pty-mux device) */ +#define TARGET_TIOCSPTLCK TARGET_IOW('T', 0x31, int) + /* Lock/unlock Pty */ +#define TARGET_TIOCGPTPEER TARGET_IO('T', 0x41) + /* Safely open the slave */ + +#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TARGET_TIOCMIWAIT 0x545C + /* wait for a change on serial input line(s) */ +#define TARGET_TIOCGICOUNT 0x545D + /* read serial port inline interrupt counts */ +#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ + +/* Used for packet mode */ +#define TARGET_TIOCPKT_DATA 0 +#define TARGET_TIOCPKT_FLUSHREAD 1 +#define TARGET_TIOCPKT_FLUSHWRITE 2 +#define TARGET_TIOCPKT_STOP 4 +#define TARGET_TIOCPKT_START 8 +#define TARGET_TIOCPKT_NOSTOP 16 +#define TARGET_TIOCPKT_DOSTOP 32 + +#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 9a380b9e31..4d3f244612 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -535,6 +535,7 @@ static void force_sig(int sig) * up the signal frame. oldsig is the signal we were trying to handle * at the point of failure. */ +#if !defined(TARGET_RISCV) static void force_sigsegv(int oldsig) { if (oldsig == SIGSEGV) { @@ -547,6 +548,8 @@ static void force_sigsegv(int oldsig) } #endif +#endif + /* abort execution with signal */ static void QEMU_NORETURN dump_core_and_abort(int target_sig) { @@ -6385,6 +6388,203 @@ long do_rt_sigreturn(CPUTLGState *env) return -TARGET_QEMU_ESIGRETURN; } +#elif defined(TARGET_RISCV) + +/* Signal handler invocation must be transparent for the code being + interrupted. Complete CPU (hart) state is saved on entry and restored + before returning from the handler. Process sigmask is also saved to block + signals while the handler is running. The handler gets its own stack, + which also doubles as storage for the CPU state and sigmask. + + The code below is qemu re-implementation of arch/riscv/kernel/signal.c */ + +struct target_sigcontext { + abi_long pc; + abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */ + uint64_t fpr[32]; + uint32_t fcsr; +}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ + +struct target_ucontext { + unsigned long uc_flags; + struct target_ucontext *uc_link; + target_stack_t uc_stack; + struct target_sigcontext uc_mcontext; + target_sigset_t uc_sigmask; +}; + +struct target_rt_sigframe { + uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */ + struct target_siginfo info; + struct target_ucontext uc; +}; + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPURISCVState *regs, size_t framesize) +{ + abi_ulong sp = regs->gpr[xSP]; + int onsigstack = on_sig_stack(sp); + + /* redzone */ + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp -= framesize; + sp &= ~3UL; /* align sp on 4-byte boundary */ + + /* If we are on the alternate signal stack and would overflow it, don't. + Return an always-bogus address instead so we will die with SIGSEGV. */ + if (onsigstack && !likely(on_sig_stack(sp))) { + return -1L; + } + + return sp; +} + +static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) +{ + int i; + + __put_user(env->pc, &sc->pc); + + for (i = 1; i < 32; i++) { + __put_user(env->gpr[i], &sc->gpr[i - 1]); + } + for (i = 0; i < 32; i++) { + __put_user(env->fpr[i], &sc->fpr[i]); + } + + uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ + __put_user(fcsr, &sc->fcsr); +} + +static void setup_ucontext(struct target_ucontext *uc, + CPURISCVState *env, target_sigset_t *set) +{ + abi_ulong ss_sp = (target_ulong)target_sigaltstack_used.ss_sp; + abi_ulong ss_flags = sas_ss_flags(env->gpr[xSP]); + abi_ulong ss_size = target_sigaltstack_used.ss_size; + + __put_user(0, &(uc->uc_flags)); + __put_user(0, &(uc->uc_link)); + + __put_user(ss_sp, &(uc->uc_stack.ss_sp)); + __put_user(ss_flags, &(uc->uc_stack.ss_flags)); + __put_user(ss_size, &(uc->uc_stack.ss_size)); + + int i; + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &(uc->uc_sigmask.sig[i])); + } + + setup_sigcontext(&uc->uc_mcontext, env); +} + +static inline void install_sigtramp(uint32_t *tramp) +{ + __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ + __put_user(0x00000073, tramp + 1); /* ecall */ +} + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPURISCVState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto badframe; + } + + setup_ucontext(&frame->uc, env, set); + tswap_siginfo(&frame->info, info); + install_sigtramp(frame->tramp); + + env->pc = ka->_sa_handler; + env->gpr[xSP] = frame_addr; + env->gpr[xA0] = sig; + env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp); + + return; + +badframe: + unlock_user_struct(frame, frame_addr, 1); + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV); +} + +static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) +{ + int i; + + __get_user(env->pc, &sc->pc); + + for (i = 1; i < 32; ++i) { + __get_user(env->gpr[i], &sc->gpr[i - 1]); + } + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i], &sc->fpr[i]); + } + + uint32_t fcsr; + __get_user(fcsr, &sc->fcsr); + csr_write_helper(env, fcsr, CSR_FCSR); +} + +static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) +{ + sigset_t blocked; + target_sigset_t target_set; + int i; + + target_sigemptyset(&target_set); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i])); + } + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(env, &uc->uc_mcontext); +} + +long do_rt_sigreturn(CPURISCVState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = env->gpr[xSP]; + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + restore_ucontext(env, &frame->uc); + + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.uc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} + #elif defined(TARGET_HPPA) struct target_sigcontext { @@ -6676,7 +6876,8 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, #if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \ || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \ || defined(TARGET_PPC64) || defined(TARGET_HPPA) \ - || defined(TARGET_NIOS2) || defined(TARGET_X86_64) + || defined(TARGET_NIOS2) || defined(TARGET_X86_64) \ + || defined(TARGET_RISCV) /* These targets do not have traditional signals. */ setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); #else diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e24f43c4a2..a8abfd421d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8555,9 +8555,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_ioctl: ret = do_ioctl(arg1, arg2, arg3); break; +#ifdef TARGET_NR_fcntl case TARGET_NR_fcntl: ret = do_fcntl(arg1, arg2, arg3); break; +#endif #ifdef TARGET_NR_mpx case TARGET_NR_mpx: goto unimplemented; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index a35c52a60a..e00e1b3862 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -71,7 +71,7 @@ || defined(TARGET_M68K) || defined(TARGET_CRIS) \ || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \ || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \ - || defined(TARGET_NIOS2) + || defined(TARGET_NIOS2) || defined(TARGET_RISCV) #define TARGET_IOC_SIZEBITS 14 #define TARGET_IOC_DIRBITS 2 @@ -435,7 +435,8 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \ || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) + || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ + || defined(TARGET_RISCV) #if defined(TARGET_SPARC) #define TARGET_SA_NOCLDSTOP 8u @@ -2093,7 +2094,7 @@ struct target_stat { unsigned int __unused[2]; }; #elif defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) || \ - defined(TARGET_NIOS2) + defined(TARGET_NIOS2) || defined(TARGET_RISCV) /* These are the asm-generic versions of the stat and stat64 structures */ @@ -2120,6 +2121,7 @@ struct target_stat { unsigned int __unused5; }; +#if !defined(TARGET_RISCV64) #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { uint64_t st_dev; @@ -2143,6 +2145,7 @@ struct target_stat64 { unsigned int __unused4; unsigned int __unused5; }; +#endif #elif defined(TARGET_HPPA) @@ -2258,8 +2261,8 @@ struct target_statfs64 { uint32_t f_spare[6]; }; #elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \ - defined(TARGET_SPARC64) || defined(TARGET_AARCH64)) && \ - !defined(TARGET_ABI32) + defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \ + defined(TARGET_RISCV)) && !defined(TARGET_ABI32) struct target_statfs { abi_long f_type; abi_long f_bsize; 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' } } ## @@ -423,6 +426,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: # # No additional information is available about the virtual CPU @@ -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 diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c new file mode 100644 index 0000000000..4851890844 --- /dev/null +++ b/target/riscv/cpu.c @@ -0,0 +1,432 @@ +/* + * QEMU RISC-V CPU + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "qapi/error.h" +#include "migration/vmstate.h" + +/* RISC-V CPU definitions */ + +static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG"; + +const char * const riscv_int_regnames[] = { + "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 " +}; + +const char * const riscv_fpr_regnames[] = { + "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" +}; + +const char * const riscv_excp_names[] = { + "misaligned_fetch", + "fault_fetch", + "illegal_instruction", + "breakpoint", + "misaligned_load", + "fault_load", + "misaligned_store", + "fault_store", + "user_ecall", + "supervisor_ecall", + "hypervisor_ecall", + "machine_ecall", + "exec_page_fault", + "load_page_fault", + "reserved", + "store_page_fault" +}; + +const char * const riscv_intr_names[] = { + "u_software", + "s_software", + "h_software", + "m_software", + "u_timer", + "s_timer", + "h_timer", + "m_timer", + "u_external", + "s_external", + "h_external", + "m_external", + "coprocessor", + "host" +}; + +typedef struct RISCVCPUInfo { + const int bit_widths; + const char *name; + void (*initfn)(Object *obj); +} RISCVCPUInfo; + +static void set_misa(CPURISCVState *env, target_ulong misa) +{ + env->misa = misa; +} + +static void set_versions(CPURISCVState *env, int user_ver, int priv_ver) +{ + env->user_ver = user_ver; + env->priv_ver = priv_ver; +} + +static void set_feature(CPURISCVState *env, int feature) +{ + env->features |= (1ULL << feature); +} + +static void set_resetvec(CPURISCVState *env, int resetvec) +{ +#ifndef CONFIG_USER_ONLY + env->resetvec = resetvec; +#endif +} + +static void riscv_any_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); +} + +static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_MMU); +} + +static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_MMU); +} + +static void rv32imacu_nommu_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); +} + +static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_MMU); +} + +static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_MMU); +} + +static void rv64imacu_nommu_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); +} + +static const RISCVCPUInfo riscv_cpus[] = { + { 96, TYPE_RISCV_CPU_ANY, riscv_any_cpu_init }, + { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init }, + { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init }, + { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init }, + { 32, TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init }, + { 32, TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init }, + { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init }, + { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init }, + { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init }, + { 64, TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init }, + { 64, TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init }, + { 0, NULL, NULL } +}; + +static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + char **cpuname; + + cpuname = g_strsplit(cpu_model, ",", 1); + typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]); + oc = object_class_by_name(typename); + g_strfreev(cpuname); + g_free(typename); + if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) || + object_class_is_abstract(oc)) { + return NULL; + } + return oc; +} + +static void riscv_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + int i; + + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); +#ifndef CONFIG_USER_ONLY + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", + (target_ulong)atomic_read(&env->mip)); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc); + cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause); +#endif + + for (i = 0; i < 32; i++) { + cpu_fprintf(f, " %s " TARGET_FMT_lx, + riscv_int_regnames[i], env->gpr[i]); + if ((i & 3) == 3) { + cpu_fprintf(f, "\n"); + } + } + for (i = 0; i < 32; i++) { + cpu_fprintf(f, " %s %016" PRIx64, + riscv_fpr_regnames[i], env->fpr[i]); + if ((i & 3) == 3) { + cpu_fprintf(f, "\n"); + } + } +} + +static void riscv_cpu_set_pc(CPUState *cs, vaddr value) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + env->pc = value; +} + +static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + env->pc = tb->pc; +} + +static bool riscv_cpu_has_work(CPUState *cs) +{ +#ifndef CONFIG_USER_ONLY + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + /* + * Definition of the WFI instruction requires it to ignore the privilege + * mode and delegation registers, but respect individual enables + */ + return (atomic_read(&env->mip) & env->mie) != 0; +#else + return true; +#endif +} + +void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} + +static void riscv_cpu_reset(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + CPURISCVState *env = &cpu->env; + + mcc->parent_reset(cs); +#ifndef CONFIG_USER_ONLY + env->priv = PRV_M; + env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); + env->mcause = 0; + env->pc = env->resetvec; +#endif + cs->exception_index = EXCP_NONE; + set_default_nan_mode(1, &env->fp_status); +} + +static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) +{ +#if defined(TARGET_RISCV32) + info->print_insn = print_insn_riscv32; +#elif defined(TARGET_RISCV64) + info->print_insn = print_insn_riscv64; +#endif +} + +static void riscv_cpu_realize(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + qemu_init_vcpu(cs); + cpu_reset(cs); + + mcc->parent_realize(dev, errp); +} + +static void riscv_cpu_init(Object *obj) +{ + CPUState *cs = CPU(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + + cs->env_ptr = &cpu->env; +} + +static const VMStateDescription vmstate_riscv_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + +static void riscv_cpu_class_init(ObjectClass *c, void *data) +{ + RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); + CPUClass *cc = CPU_CLASS(c); + DeviceClass *dc = DEVICE_CLASS(c); + + mcc->parent_realize = dc->realize; + dc->realize = riscv_cpu_realize; + + mcc->parent_reset = cc->reset; + cc->reset = riscv_cpu_reset; + + cc->class_by_name = riscv_cpu_class_by_name; + cc->has_work = riscv_cpu_has_work; + cc->do_interrupt = riscv_cpu_do_interrupt; + cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt; + cc->dump_state = riscv_cpu_dump_state; + cc->set_pc = riscv_cpu_set_pc; + cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb; + cc->gdb_read_register = riscv_cpu_gdb_read_register; + cc->gdb_write_register = riscv_cpu_gdb_write_register; + cc->gdb_num_core_regs = 65; + cc->gdb_stop_before_watchpoint = true; + cc->disas_set_info = riscv_cpu_disas_set_info; +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault; +#else + cc->do_unaligned_access = riscv_cpu_do_unaligned_access; + cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug; +#endif +#ifdef CONFIG_TCG + cc->tcg_initialize = riscv_translate_init; +#endif + /* For now, mark unmigratable: */ + cc->vmsd = &vmstate_riscv_cpu; +} + +static void cpu_register(const RISCVCPUInfo *info) +{ + TypeInfo type_info = { + .name = info->name, + .parent = TYPE_RISCV_CPU, + .instance_size = sizeof(RISCVCPU), + .instance_init = info->initfn, + }; + + type_register(&type_info); +} + +static const TypeInfo riscv_cpu_type_info = { + .name = TYPE_RISCV_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(RISCVCPU), + .instance_init = riscv_cpu_init, + .abstract = false, + .class_size = sizeof(RISCVCPUClass), + .class_init = riscv_cpu_class_init, +}; + +char *riscv_isa_string(RISCVCPU *cpu) +{ + int i; + size_t maxlen = 5 + ctz32(cpu->env.misa); + char *isa_string = g_new0(char, maxlen); + snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS); + for (i = 0; i < sizeof(riscv_exts); i++) { + if (cpu->env.misa & RV(riscv_exts[i])) { + isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a'; + + } + } + return isa_string; +} + +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + const RISCVCPUInfo *info = riscv_cpus; + + while (info->name) { + if (info->bit_widths & TARGET_LONG_BITS) { + (*cpu_fprintf)(f, "%s\n", info->name); + } + info++; + } +} + +static void riscv_cpu_register_types(void) +{ + const RISCVCPUInfo *info = riscv_cpus; + + type_register_static(&riscv_cpu_type_info); + + while (info->name) { + if (info->bit_widths & TARGET_LONG_BITS) { + cpu_register(info); + } + info++; + } +} + +type_init(riscv_cpu_register_types) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h new file mode 100644 index 0000000000..cff02a2857 --- /dev/null +++ b/target/riscv/cpu.h @@ -0,0 +1,296 @@ +/* + * QEMU RISC-V CPU + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef RISCV_CPU_H +#define RISCV_CPU_H + +/* QEMU addressing/paging config */ +#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ +#if defined(TARGET_RISCV64) +#define TARGET_LONG_BITS 64 +#define TARGET_PHYS_ADDR_SPACE_BITS 50 +#define TARGET_VIRT_ADDR_SPACE_BITS 39 +#elif defined(TARGET_RISCV32) +#define TARGET_LONG_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 34 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#endif + +#define TCG_GUEST_DEFAULT_MO 0 + +#define ELF_MACHINE EM_RISCV +#define CPUArchState struct CPURISCVState + +#include "qemu-common.h" +#include "qom/cpu.h" +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +#define TYPE_RISCV_CPU "riscv-cpu" + +#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU +#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX) + +#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any") +#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1") +#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0") +#define TYPE_RISCV_CPU_RV32IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv32imacu-nommu") +#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1") +#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0") +#define TYPE_RISCV_CPU_RV64IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv64imacu-nommu") +#define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") +#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51") +#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34") +#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") + +#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2)) +#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2)) + +#if defined(TARGET_RISCV32) +#define RVXLEN RV32 +#elif defined(TARGET_RISCV64) +#define RVXLEN RV64 +#endif + +#define RV(x) ((target_ulong)1 << (x - 'A')) + +#define RVI RV('I') +#define RVM RV('M') +#define RVA RV('A') +#define RVF RV('F') +#define RVD RV('D') +#define RVC RV('C') +#define RVS RV('S') +#define RVU RV('U') + +/* S extension denotes that Supervisor mode exists, however it is possible + to have a core that support S mode but does not have an MMU and there + is currently no bit in misa to indicate whether an MMU exists or not + so a cpu features bitfield is required */ +enum { + RISCV_FEATURE_MMU +}; + +#define USER_VERSION_2_02_0 0x00020200 +#define PRIV_VERSION_1_09_1 0x00010901 +#define PRIV_VERSION_1_10_0 0x00011000 + +#define TRANSLATE_FAIL 1 +#define TRANSLATE_SUCCESS 0 +#define NB_MMU_MODES 4 +#define MMU_USER_IDX 3 + +#define MAX_RISCV_PMPS (16) + +typedef struct CPURISCVState CPURISCVState; + +#include "pmp.h" + +struct CPURISCVState { + target_ulong gpr[32]; + uint64_t fpr[32]; /* assume both F and D extensions */ + target_ulong pc; + target_ulong load_res; + target_ulong load_val; + + target_ulong frm; + + target_ulong badaddr; + + target_ulong user_ver; + target_ulong priv_ver; + target_ulong misa; + + uint32_t features; + +#ifndef CONFIG_USER_ONLY + target_ulong priv; + target_ulong resetvec; + + target_ulong mhartid; + target_ulong mstatus; + /* + * CAUTION! Unlike the rest of this struct, mip is accessed asynchonously + * by I/O threads and other vCPUs, so hold the iothread mutex before + * operating on it. CPU_INTERRUPT_HARD should be in effect iff this is + * non-zero. Use riscv_cpu_set_local_interrupt. + */ + uint32_t mip; /* allow atomic_read for >= 32-bit hosts */ + target_ulong mie; + target_ulong mideleg; + + target_ulong sptbr; /* until: priv-1.9.1 */ + target_ulong satp; /* since: priv-1.10.0 */ + target_ulong sbadaddr; + target_ulong mbadaddr; + target_ulong medeleg; + + target_ulong stvec; + target_ulong sepc; + target_ulong scause; + + target_ulong mtvec; + target_ulong mepc; + target_ulong mcause; + target_ulong mtval; /* since: priv-1.10.0 */ + + uint32_t mucounteren; + uint32_t mscounteren; + target_ulong scounteren; /* since: priv-1.10.0 */ + target_ulong mcounteren; /* since: priv-1.10.0 */ + + target_ulong sscratch; + target_ulong mscratch; + + /* temporary htif regs */ + uint64_t mfromhost; + uint64_t mtohost; + uint64_t timecmp; + + /* physical memory protection */ + pmp_table_t pmp_state; +#endif + + float_status fp_status; + + /* QEMU */ + CPU_COMMON + + /* Fields from here on are preserved across CPU reset. */ + QEMUTimer *timer; /* Internal timer */ +}; + +#define RISCV_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(RISCVCPUClass, (klass), TYPE_RISCV_CPU) +#define RISCV_CPU(obj) \ + OBJECT_CHECK(RISCVCPU, (obj), TYPE_RISCV_CPU) +#define RISCV_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RISCVCPUClass, (obj), TYPE_RISCV_CPU) + +/** + * RISCVCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A RISCV CPU model. + */ +typedef struct RISCVCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} RISCVCPUClass; + +/** + * RISCVCPU: + * @env: #CPURISCVState + * + * A RISCV CPU. + */ +typedef struct RISCVCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + CPURISCVState env; +} RISCVCPU; + +static inline RISCVCPU *riscv_env_get_cpu(CPURISCVState *env) +{ + return container_of(env, RISCVCPU, env); +} + +static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) +{ + return (env->misa & ext) != 0; +} + +static inline bool riscv_feature(CPURISCVState *env, int feature) +{ + return env->features & (1ULL << feature); +} + +#include "cpu_user.h" +#include "cpu_bits.h" + +extern const char * const riscv_int_regnames[]; +extern const char * const riscv_fpr_regnames[]; +extern const char * const riscv_excp_names[]; +extern const char * const riscv_intr_names[]; + +#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e)) +#define ENV_OFFSET offsetof(RISCVCPU, env) + +void riscv_cpu_do_interrupt(CPUState *cpu); +int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); +int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); +hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); +int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, + int rw, int mmu_idx); + +char *riscv_isa_string(RISCVCPU *cpu); +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); + +#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model) +#define cpu_signal_handler cpu_riscv_signal_handler +#define cpu_list riscv_cpu_list +#define cpu_mmu_index riscv_cpu_mmu_index + +void riscv_set_mode(CPURISCVState *env, target_ulong newpriv); + +void riscv_translate_init(void); +RISCVCPU *cpu_riscv_init(const char *cpu_model); +int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc); +void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, + uint32_t exception, uintptr_t pc); + +target_ulong cpu_riscv_get_fflags(CPURISCVState *env); +void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong); + +#define TB_FLAGS_MMU_MASK 3 +#define TB_FLAGS_FP_ENABLE MSTATUS_FS + +static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; +#ifdef CONFIG_USER_ONLY + *flags = TB_FLAGS_FP_ENABLE; +#else + *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS); +#endif +} + +void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, + target_ulong csrno); +target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno); + +#ifndef CONFIG_USER_ONLY +void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value); +#endif + +#include "exec/cpu-all.h" + +#endif /* RISCV_CPU_H */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h new file mode 100644 index 0000000000..64aa097181 --- /dev/null +++ b/target/riscv/cpu_bits.h @@ -0,0 +1,411 @@ +/* RISC-V ISA constants */ + +#define get_field(reg, mask) (((reg) & \ + (target_ulong)(mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \ + (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \ + (target_ulong)(mask))) + +#define PGSHIFT 12 + +#define FSR_RD_SHIFT 5 +#define FSR_RD (0x7 << FSR_RD_SHIFT) + +#define FPEXC_NX 0x01 +#define FPEXC_UF 0x02 +#define FPEXC_OF 0x04 +#define FPEXC_DZ 0x08 +#define FPEXC_NV 0x10 + +#define FSR_AEXC_SHIFT 0 +#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT) +#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT) +#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT) +#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT) +#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) +#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) + +/* CSR numbers */ +#define CSR_FFLAGS 0x1 +#define CSR_FRM 0x2 +#define CSR_FCSR 0x3 +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_SBADADDR 0x143 +#define CSR_SIP 0x144 +#define CSR_SPTBR 0x180 +#define CSR_SATP 0x180 +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MBADADDR 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MUCOUNTEREN 0x320 +#define CSR_MSCOUNTEREN 0x321 +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f + +/* mstatus bits */ +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_PUM 0x00040000 /* until: priv-1.9.1 */ +#define MSTATUS_SUM 0x00040000 /* since: priv-1.10 */ +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_VM 0x1F000000 /* until: priv-1.9.1 */ +#define MSTATUS_TVM 0x00100000 /* since: priv-1.10 */ +#define MSTATUS_TW 0x20000000 /* since: priv-1.10 */ +#define MSTATUS_TSR 0x40000000 /* since: priv-1.10 */ + +#define MSTATUS64_UXL 0x0000000300000000ULL +#define MSTATUS64_SXL 0x0000000C00000000ULL + +#define MSTATUS32_SD 0x80000000 +#define MSTATUS64_SD 0x8000000000000000ULL + +#if defined(TARGET_RISCV32) +#define MSTATUS_SD MSTATUS32_SD +#elif defined(TARGET_RISCV64) +#define MSTATUS_SD MSTATUS64_SD +#endif + +/* sstatus bits */ +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_PUM 0x00040000 /* until: priv-1.9.1 */ +#define SSTATUS_SUM 0x00040000 /* since: priv-1.10 */ +#define SSTATUS_MXR 0x00080000 + +#define SSTATUS32_SD 0x80000000 +#define SSTATUS64_SD 0x8000000000000000ULL + +#if defined(TARGET_RISCV32) +#define SSTATUS_SD SSTATUS32_SD +#elif defined(TARGET_RISCV64) +#define SSTATUS_SD SSTATUS64_SD +#endif + +/* irqs */ +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP +#define SIP_SEIP MIP_SEIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +/* privileged ISA 1.9.1 VM modes (mstatus.vm) */ +#define VM_1_09_MBARE 0 +#define VM_1_09_MBB 1 +#define VM_1_09_MBBID 2 +#define VM_1_09_SV32 8 +#define VM_1_09_SV39 9 +#define VM_1_09_SV48 10 + +/* privileged ISA 1.10.0 VM modes (satp.mode) */ +#define VM_1_10_MBARE 0 +#define VM_1_10_SV32 1 +#define VM_1_10_SV39 8 +#define VM_1_10_SV48 9 +#define VM_1_10_SV57 10 +#define VM_1_10_SV64 11 + +/* privileged ISA interrupt causes */ +#define IRQ_U_SOFT 0 /* since: priv-1.10 */ +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 /* until: priv-1.9.1 */ +#define IRQ_M_SOFT 3 /* until: priv-1.9.1 */ +#define IRQ_U_TIMER 4 /* since: priv-1.10 */ +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 /* until: priv-1.9.1 */ +#define IRQ_M_TIMER 7 /* until: priv-1.9.1 */ +#define IRQ_U_EXT 8 /* since: priv-1.10 */ +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 /* until: priv-1.9.1 */ +#define IRQ_M_EXT 11 /* until: priv-1.9.1 */ +#define IRQ_X_COP 12 /* non-standard */ + +/* Default addresses */ +#define DEFAULT_RSTVEC 0x00001000 + +/* RV32 satp field masks */ +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7fc00000 +#define SATP32_PPN 0x003fffff + +/* RV64 satp field masks */ +#define SATP64_MODE 0xF000000000000000ULL +#define SATP64_ASID 0x0FFFF00000000000ULL +#define SATP64_PPN 0x00000FFFFFFFFFFFULL + +#if defined(TARGET_RISCV32) +#define SATP_MODE SATP32_MODE +#define SATP_ASID SATP32_ASID +#define SATP_PPN SATP32_PPN +#endif +#if defined(TARGET_RISCV64) +#define SATP_MODE SATP64_MODE +#define SATP_ASID SATP64_ASID +#define SATP_PPN SATP64_PPN +#endif + +/* RISCV Exception Codes */ +#define EXCP_NONE -1 /* not a real RISCV exception code */ +#define RISCV_EXCP_INST_ADDR_MIS 0x0 +#define RISCV_EXCP_INST_ACCESS_FAULT 0x1 +#define RISCV_EXCP_ILLEGAL_INST 0x2 +#define RISCV_EXCP_BREAKPOINT 0x3 +#define RISCV_EXCP_LOAD_ADDR_MIS 0x4 +#define RISCV_EXCP_LOAD_ACCESS_FAULT 0x5 +#define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6 +#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7 +#define RISCV_EXCP_U_ECALL 0x8 /* for convenience, report all + ECALLs as this, handler + fixes */ +#define RISCV_EXCP_S_ECALL 0x9 +#define RISCV_EXCP_H_ECALL 0xa +#define RISCV_EXCP_M_ECALL 0xb +#define RISCV_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */ +#define RISCV_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */ +#define RISCV_EXCP_STORE_PAGE_FAULT 0xf /* since: priv-1.10.0 */ + +#define RISCV_EXCP_INT_FLAG 0x80000000 +#define RISCV_EXCP_INT_MASK 0x7fffffff + +/* page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h new file mode 100644 index 0000000000..c2199610ab --- /dev/null +++ b/target/riscv/cpu_user.h @@ -0,0 +1,13 @@ +#define xRA 1 /* return address (aka link register) */ +#define xSP 2 /* stack pointer */ +#define xGP 3 /* global pointer */ +#define xTP 4 /* thread pointer */ + +#define xA0 10 /* gpr[10-17] are syscall arguments */ +#define xA1 11 +#define xA2 12 +#define xA3 13 +#define xA4 14 +#define xA5 15 +#define xA6 16 +#define xA7 17 /* syscall number goes here */ diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c new file mode 100644 index 0000000000..abbadead5c --- /dev/null +++ b/target/riscv/fpu_helper.c @@ -0,0 +1,373 @@ +/* + * RISC-V FPU Emulation Helpers for QEMU. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include <stdlib.h> +#include "cpu.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + +target_ulong cpu_riscv_get_fflags(CPURISCVState *env) +{ + int soft = get_float_exception_flags(&env->fp_status); + target_ulong hard = 0; + + hard |= (soft & float_flag_inexact) ? FPEXC_NX : 0; + hard |= (soft & float_flag_underflow) ? FPEXC_UF : 0; + hard |= (soft & float_flag_overflow) ? FPEXC_OF : 0; + hard |= (soft & float_flag_divbyzero) ? FPEXC_DZ : 0; + hard |= (soft & float_flag_invalid) ? FPEXC_NV : 0; + + return hard; +} + +void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard) +{ + int soft = 0; + + soft |= (hard & FPEXC_NX) ? float_flag_inexact : 0; + soft |= (hard & FPEXC_UF) ? float_flag_underflow : 0; + soft |= (hard & FPEXC_OF) ? float_flag_overflow : 0; + soft |= (hard & FPEXC_DZ) ? float_flag_divbyzero : 0; + soft |= (hard & FPEXC_NV) ? float_flag_invalid : 0; + + set_float_exception_flags(soft, &env->fp_status); +} + +void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) +{ + int softrm; + + if (rm == 7) { + rm = env->frm; + } + switch (rm) { + case 0: + softrm = float_round_nearest_even; + break; + case 1: + softrm = float_round_to_zero; + break; + case 2: + softrm = float_round_down; + break; + case 3: + softrm = float_round_up; + break; + case 4: + softrm = float_round_ties_away; + break; + default: + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + + set_float_rounding_mode(softrm, &env->fp_status); +} + +uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, 0, &env->fp_status); +} + +uint64_t helper_fmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, 0, &env->fp_status); +} + +uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c, + &env->fp_status); +} + +uint64_t helper_fmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c, + &env->fp_status); +} + +uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, float_muladd_negate_product, + &env->fp_status); +} + +uint64_t helper_fnmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, float_muladd_negate_product, + &env->fp_status); +} + +uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c | + float_muladd_negate_product, &env->fp_status); +} + +uint64_t helper_fnmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c | + float_muladd_negate_product, &env->fp_status); +} + +uint64_t helper_fadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_add(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_sub(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmul_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_mul(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_div(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_minnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmax_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_maxnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t frs1) +{ + return float32_sqrt(frs1, &env->fp_status); +} + +target_ulong helper_fle_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_le(frs1, frs2, &env->fp_status); +} + +target_ulong helper_flt_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_lt(frs1, frs2, &env->fp_status); +} + +target_ulong helper_feq_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float32_eq_quiet(frs1, frs2, &env->fp_status); +} + +target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t frs1) +{ + return float32_to_int32(frs1, &env->fp_status); +} + +target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t frs1) +{ + return (int32_t)float32_to_uint32(frs1, &env->fp_status); +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_l_s(CPURISCVState *env, uint64_t frs1) +{ + return float32_to_int64(frs1, &env->fp_status); +} + +uint64_t helper_fcvt_lu_s(CPURISCVState *env, uint64_t frs1) +{ + return float32_to_uint64(frs1, &env->fp_status); +} +#endif + +uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1) +{ + return int32_to_float32((int32_t)rs1, &env->fp_status); +} + +uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1) +{ + return uint32_to_float32((uint32_t)rs1, &env->fp_status); +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_s_l(CPURISCVState *env, uint64_t rs1) +{ + return int64_to_float32(rs1, &env->fp_status); +} + +uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1) +{ + return uint64_to_float32(rs1, &env->fp_status); +} +#endif + +target_ulong helper_fclass_s(uint64_t frs1) +{ + float32 f = frs1; + bool sign = float32_is_neg(f); + + if (float32_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float32_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float32_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float32_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} + +uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_add(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_sub(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmul_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_mul(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_div(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_minnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_maxnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) +{ + rs1 = float64_to_float32(rs1, &env->fp_status); + return float32_maybe_silence_nan(rs1, &env->fp_status); +} + +uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1) +{ + rs1 = float32_to_float64(rs1, &env->fp_status); + return float64_maybe_silence_nan(rs1, &env->fp_status); +} + +uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1) +{ + return float64_sqrt(frs1, &env->fp_status); +} + +target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_le(frs1, frs2, &env->fp_status); +} + +target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_lt(frs1, frs2, &env->fp_status); +} + +target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_eq_quiet(frs1, frs2, &env->fp_status); +} + +target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1) +{ + return float64_to_int32(frs1, &env->fp_status); +} + +target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1) +{ + return (int32_t)float64_to_uint32(frs1, &env->fp_status); +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1) +{ + return float64_to_int64(frs1, &env->fp_status); +} + +uint64_t helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1) +{ + return float64_to_uint64(frs1, &env->fp_status); +} +#endif + +uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1) +{ + return int32_to_float64((int32_t)rs1, &env->fp_status); +} + +uint64_t helper_fcvt_d_wu(CPURISCVState *env, target_ulong rs1) +{ + return uint32_to_float64((uint32_t)rs1, &env->fp_status); +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_d_l(CPURISCVState *env, uint64_t rs1) +{ + return int64_to_float64(rs1, &env->fp_status); +} + +uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1) +{ + return uint64_to_float64(rs1, &env->fp_status); +} +#endif + +target_ulong helper_fclass_d(uint64_t frs1) +{ + float64 f = frs1; + bool sign = float64_is_neg(f); + + if (float64_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float64_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float64_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float64_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c new file mode 100644 index 0000000000..4f919b6c34 --- /dev/null +++ b/target/riscv/gdbstub.c @@ -0,0 +1,62 @@ +/* + * RISC-V GDB Server Stub + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" +#include "cpu.h" + +int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + if (n < 32) { + return gdb_get_regl(mem_buf, env->gpr[n]); + } else if (n == 32) { + return gdb_get_regl(mem_buf, env->pc); + } else if (n < 65) { + return gdb_get_reg64(mem_buf, env->fpr[n - 33]); + } else if (n < 4096 + 65) { + return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65)); + } + return 0; +} + +int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + if (n == 0) { + /* discard writes to x0 */ + return sizeof(target_ulong); + } else if (n < 32) { + env->gpr[n] = ldtul_p(mem_buf); + return sizeof(target_ulong); + } else if (n == 32) { + env->pc = ldtul_p(mem_buf); + return sizeof(target_ulong); + } else if (n < 65) { + env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */ + return sizeof(uint64_t); + } else if (n < 4096 + 65) { + csr_write_helper(env, ldtul_p(mem_buf), n - 65); + } + return 0; +} diff --git a/target/riscv/helper.c b/target/riscv/helper.c new file mode 100644 index 0000000000..02cbcea2b7 --- /dev/null +++ b/target/riscv/helper.c @@ -0,0 +1,503 @@ +/* + * RISC-V emulation helpers for qemu. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "tcg-op.h" + +#define RISCV_DEBUG_INTERRUPT 0 + +int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) +{ +#ifdef CONFIG_USER_ONLY + return 0; +#else + return env->priv; +#endif +} + +#ifndef CONFIG_USER_ONLY +/* + * Return RISC-V IRQ number if an interrupt should be taken, else -1. + * Used in cpu-exec.c + * + * Adapted from Spike's processor_t::take_interrupt() + */ +static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env) +{ + target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie; + + target_ulong mie = get_field(env->mstatus, MSTATUS_MIE); + target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie); + target_ulong enabled_interrupts = pending_interrupts & + ~env->mideleg & -m_enabled; + + target_ulong sie = get_field(env->mstatus, MSTATUS_SIE); + target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie); + enabled_interrupts |= pending_interrupts & env->mideleg & + -s_enabled; + + if (enabled_interrupts) { + return ctz64(enabled_interrupts); /* since non-zero */ + } else { + return EXCP_NONE; /* indicates no pending interrupt */ + } +} +#endif + +bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +#if !defined(CONFIG_USER_ONLY) + if (interrupt_request & CPU_INTERRUPT_HARD) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + int interruptno = riscv_cpu_hw_interrupts_pending(env); + if (interruptno >= 0) { + cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno; + riscv_cpu_do_interrupt(cs); + return true; + } + } +#endif + return false; +} + +#if !defined(CONFIG_USER_ONLY) + +/* get_physical_address - get the physical address for this virtual address + * + * Do a page table walk to obtain the physical address corresponding to a + * virtual address. Returns 0 if the translation was successful + * + * Adapted from Spike's mmu_t::translate and mmu_t::walk + * + */ +static int get_physical_address(CPURISCVState *env, hwaddr *physical, + int *prot, target_ulong addr, + int access_type, int mmu_idx) +{ + /* NOTE: the env->pc value visible here will not be + * correct, but the value visible to the exception handler + * (riscv_cpu_do_interrupt) is correct */ + + int mode = mmu_idx; + + if (mode == PRV_M && access_type != MMU_INST_FETCH) { + if (get_field(env->mstatus, MSTATUS_MPRV)) { + mode = get_field(env->mstatus, MSTATUS_MPP); + } + } + + if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) { + *physical = addr; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + } + + *prot = 0; + + target_ulong base; + int levels, ptidxbits, ptesize, vm, sum; + int mxr = get_field(env->mstatus, MSTATUS_MXR); + + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + base = get_field(env->satp, SATP_PPN) << PGSHIFT; + sum = get_field(env->mstatus, MSTATUS_SUM); + vm = get_field(env->satp, SATP_MODE); + switch (vm) { + case VM_1_10_SV32: + levels = 2; ptidxbits = 10; ptesize = 4; break; + case VM_1_10_SV39: + levels = 3; ptidxbits = 9; ptesize = 8; break; + case VM_1_10_SV48: + levels = 4; ptidxbits = 9; ptesize = 8; break; + case VM_1_10_SV57: + levels = 5; ptidxbits = 9; ptesize = 8; break; + case VM_1_10_MBARE: + *physical = addr; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + default: + g_assert_not_reached(); + } + } else { + base = env->sptbr << PGSHIFT; + sum = !get_field(env->mstatus, MSTATUS_PUM); + vm = get_field(env->mstatus, MSTATUS_VM); + switch (vm) { + case VM_1_09_SV32: + levels = 2; ptidxbits = 10; ptesize = 4; break; + case VM_1_09_SV39: + levels = 3; ptidxbits = 9; ptesize = 8; break; + case VM_1_09_SV48: + levels = 4; ptidxbits = 9; ptesize = 8; break; + case VM_1_09_MBARE: + *physical = addr; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + default: + g_assert_not_reached(); + } + } + + CPUState *cs = CPU(riscv_env_get_cpu(env)); + int va_bits = PGSHIFT + levels * ptidxbits; + target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; + target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask; + if (masked_msbs != 0 && masked_msbs != mask) { + return TRANSLATE_FAIL; + } + + int ptshift = (levels - 1) * ptidxbits; + int i; + +#if !TCG_OVERSIZED_GUEST +restart: +#endif + for (i = 0; i < levels; i++, ptshift -= ptidxbits) { + target_ulong idx = (addr >> (PGSHIFT + ptshift)) & + ((1 << ptidxbits) - 1); + + /* check that physical address of PTE is legal */ + target_ulong pte_addr = base + idx * ptesize; +#if defined(TARGET_RISCV32) + target_ulong pte = ldl_phys(cs->as, pte_addr); +#elif defined(TARGET_RISCV64) + target_ulong pte = ldq_phys(cs->as, pte_addr); +#endif + target_ulong ppn = pte >> PTE_PPN_SHIFT; + + if (PTE_TABLE(pte)) { /* next level of page table */ + base = ppn << PGSHIFT; + } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) { + break; + } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { + break; + } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) : + access_type == MMU_DATA_LOAD ? !(pte & PTE_R) && + !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) { + break; + } else { + /* if necessary, set accessed and dirty bits. */ + target_ulong updated_pte = pte | PTE_A | + (access_type == MMU_DATA_STORE ? PTE_D : 0); + + /* Page table updates need to be atomic with MTTCG enabled */ + if (updated_pte != pte) { + /* if accessed or dirty bits need updating, and the PTE is + * in RAM, then we do so atomically with a compare and swap. + * if the PTE is in IO space, then it can't be updated. + * if the PTE changed, then we must re-walk the page table + as the PTE is no longer valid */ + MemoryRegion *mr; + hwaddr l = sizeof(target_ulong), addr1; + mr = address_space_translate(cs->as, pte_addr, + &addr1, &l, false); + if (memory_access_is_direct(mr, true)) { + target_ulong *pte_pa = + qemu_map_ram_ptr(mr->ram_block, addr1); +#if TCG_OVERSIZED_GUEST + /* MTTCG is not enabled on oversized TCG guests so + * page table updates do not need to be atomic */ + *pte_pa = pte = updated_pte; +#else + target_ulong old_pte = + atomic_cmpxchg(pte_pa, pte, updated_pte); + if (old_pte != pte) { + goto restart; + } else { + pte = updated_pte; + } +#endif + } else { + /* misconfigured PTE in ROM (AD bits are not preset) or + * PTE is in IO space and can't be updated atomically */ + return TRANSLATE_FAIL; + } + } + + /* for superpage mappings, make a fake leaf PTE for the TLB's + benefit. */ + target_ulong vpn = addr >> PGSHIFT; + *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; + + if ((pte & PTE_R)) { + *prot |= PAGE_READ; + } + if ((pte & PTE_X)) { + *prot |= PAGE_EXEC; + } + /* only add write permission on stores or if the page + is already dirty, so that we don't miss further + page table walks to update the dirty bit */ + if ((pte & PTE_W) && + (access_type == MMU_DATA_STORE || (pte & PTE_D))) { + *prot |= PAGE_WRITE; + } + return TRANSLATE_SUCCESS; + } + } + return TRANSLATE_FAIL; +} + +static void raise_mmu_exception(CPURISCVState *env, target_ulong address, + MMUAccessType access_type) +{ + CPUState *cs = CPU(riscv_env_get_cpu(env)); + int page_fault_exceptions = + (env->priv_ver >= PRIV_VERSION_1_10_0) && + get_field(env->satp, SATP_MODE) != VM_1_10_MBARE; + switch (access_type) { + case MMU_INST_FETCH: + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT; + break; + case MMU_DATA_LOAD: + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT; + break; + case MMU_DATA_STORE: + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + break; + default: + g_assert_not_reached(); + } + env->badaddr = address; +} + +hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + hwaddr phys_addr; + int prot; + int mmu_idx = cpu_mmu_index(&cpu->env, false); + + if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx)) { + return -1; + } + return phys_addr; +} + +void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + switch (access_type) { + case MMU_INST_FETCH: + cs->exception_index = RISCV_EXCP_INST_ADDR_MIS; + break; + case MMU_DATA_LOAD: + cs->exception_index = RISCV_EXCP_LOAD_ADDR_MIS; + break; + case MMU_DATA_STORE: + cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS; + break; + default: + g_assert_not_reached(); + } + env->badaddr = addr; + do_raise_exception_err(env, cs->exception_index, retaddr); +} + +/* called by qemu's softmmu to fill the qemu tlb */ +void tlb_fill(CPUState *cs, target_ulong addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) +{ + int ret; + ret = riscv_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); + if (ret == TRANSLATE_FAIL) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + do_raise_exception_err(env, cs->exception_index, retaddr); + } +} + +#endif + +int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, + int rw, int mmu_idx) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; +#if !defined(CONFIG_USER_ONLY) + hwaddr pa = 0; + int prot; +#endif + int ret = TRANSLATE_FAIL; + + qemu_log_mask(CPU_LOG_MMU, + "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx \ + %d\n", __func__, env->pc, address, rw, mmu_idx); + +#if !defined(CONFIG_USER_ONLY) + ret = get_physical_address(env, &pa, &prot, address, rw, mmu_idx); + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx + " prot %d\n", __func__, address, ret, pa, prot); + if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) { + ret = TRANSLATE_FAIL; + } + if (ret == TRANSLATE_SUCCESS) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); + } else if (ret == TRANSLATE_FAIL) { + raise_mmu_exception(env, address, rw); + } +#else + switch (rw) { + case MMU_INST_FETCH: + cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT; + break; + case MMU_DATA_LOAD: + cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT; + break; + case MMU_DATA_STORE: + cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT; + break; + } +#endif + return ret; +} + +/* + * Handle Traps + * + * Adapted from Spike's processor_t::take_trap. + * + */ +void riscv_cpu_do_interrupt(CPUState *cs) +{ +#if !defined(CONFIG_USER_ONLY) + + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + if (RISCV_DEBUG_INTERRUPT) { + int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK; + if (cs->exception_index & RISCV_EXCP_INT_FLAG) { + qemu_log_mask(LOG_TRACE, "core 0: trap %s, epc 0x" TARGET_FMT_lx, + riscv_intr_names[log_cause], env->pc); + } else { + qemu_log_mask(LOG_TRACE, "core 0: intr %s, epc 0x" TARGET_FMT_lx, + riscv_excp_names[log_cause], env->pc); + } + } + + target_ulong fixed_cause = 0; + if (cs->exception_index & (RISCV_EXCP_INT_FLAG)) { + /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception + index is only 32 bits wide */ + fixed_cause = cs->exception_index & RISCV_EXCP_INT_MASK; + fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1); + } else { + /* fixup User ECALL -> correct priv ECALL */ + if (cs->exception_index == RISCV_EXCP_U_ECALL) { + switch (env->priv) { + case PRV_U: + fixed_cause = RISCV_EXCP_U_ECALL; + break; + case PRV_S: + fixed_cause = RISCV_EXCP_S_ECALL; + break; + case PRV_H: + fixed_cause = RISCV_EXCP_H_ECALL; + break; + case PRV_M: + fixed_cause = RISCV_EXCP_M_ECALL; + break; + } + } else { + fixed_cause = cs->exception_index; + } + } + + target_ulong backup_epc = env->pc; + + target_ulong bit = fixed_cause; + target_ulong deleg = env->medeleg; + + int hasbadaddr = + (fixed_cause == RISCV_EXCP_INST_ADDR_MIS) || + (fixed_cause == RISCV_EXCP_INST_ACCESS_FAULT) || + (fixed_cause == RISCV_EXCP_LOAD_ADDR_MIS) || + (fixed_cause == RISCV_EXCP_STORE_AMO_ADDR_MIS) || + (fixed_cause == RISCV_EXCP_LOAD_ACCESS_FAULT) || + (fixed_cause == RISCV_EXCP_STORE_AMO_ACCESS_FAULT) || + (fixed_cause == RISCV_EXCP_INST_PAGE_FAULT) || + (fixed_cause == RISCV_EXCP_LOAD_PAGE_FAULT) || + (fixed_cause == RISCV_EXCP_STORE_PAGE_FAULT); + + if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) { + deleg = env->mideleg; + bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1)); + } + + if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) { + /* handle the trap in S-mode */ + /* No need to check STVEC for misaligned - lower 2 bits cannot be set */ + env->pc = env->stvec; + env->scause = fixed_cause; + env->sepc = backup_epc; + + if (hasbadaddr) { + if (RISCV_DEBUG_INTERRUPT) { + qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld + ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); + } + env->sbadaddr = env->badaddr; + } + + target_ulong s = env->mstatus; + s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? + get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); + s = set_field(s, MSTATUS_SPP, env->priv); + s = set_field(s, MSTATUS_SIE, 0); + csr_write_helper(env, s, CSR_MSTATUS); + riscv_set_mode(env, PRV_S); + } else { + /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ + env->pc = env->mtvec; + env->mepc = backup_epc; + env->mcause = fixed_cause; + + if (hasbadaddr) { + if (RISCV_DEBUG_INTERRUPT) { + qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld + ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); + } + env->mbadaddr = env->badaddr; + } + + target_ulong s = env->mstatus; + s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? + get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); + s = set_field(s, MSTATUS_MPP, env->priv); + s = set_field(s, MSTATUS_MIE, 0); + csr_write_helper(env, s, CSR_MSTATUS); + riscv_set_mode(env, PRV_M); + } + /* TODO yield load reservation */ +#endif + cs->exception_index = EXCP_NONE; /* mark handled to qemu */ +} diff --git a/target/riscv/helper.h b/target/riscv/helper.h new file mode 100644 index 0000000000..debb22a480 --- /dev/null +++ b/target/riscv/helper.h @@ -0,0 +1,78 @@ +/* Exceptions */ +DEF_HELPER_2(raise_exception, noreturn, env, i32) + +/* Floating Point - rounding mode */ +DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_WG, void, env, i32) + +/* Floating Point - fused */ +DEF_HELPER_FLAGS_4(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) + +/* Floating Point - Single Precision */ +DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64) +#if defined(TARGET_RISCV64) +DEF_HELPER_FLAGS_2(fcvt_l_s, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_lu_s, TCG_CALL_NO_RWG, tl, env, i64) +#endif +DEF_HELPER_FLAGS_2(fcvt_s_w, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl) +#if defined(TARGET_RISCV64) +DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl) +#endif +DEF_HELPER_FLAGS_1(fclass_s, TCG_CALL_NO_RWG_SE, tl, i64) + +/* Floating Point - Double Precision */ +DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64) +#if defined(TARGET_RISCV64) +DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64) +#endif +DEF_HELPER_FLAGS_2(fcvt_d_w, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl) +#if defined(TARGET_RISCV64) +DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl) +#endif +DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) + +/* Special functions */ +DEF_HELPER_3(csrrw, tl, env, tl, tl) +DEF_HELPER_4(csrrs, tl, env, tl, tl, tl) +DEF_HELPER_4(csrrc, tl, env, tl, tl, tl) +#ifndef CONFIG_USER_ONLY +DEF_HELPER_2(sret, tl, env, tl) +DEF_HELPER_2(mret, tl, env, tl) +DEF_HELPER_1(wfi, void, env) +DEF_HELPER_1(tlb_flush, void, env) +#endif diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h new file mode 100644 index 0000000000..58baa1ba1f --- /dev/null +++ b/target/riscv/instmap.h @@ -0,0 +1,364 @@ +/* + * RISC-V emulation for qemu: Instruction decode helpers + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#define MASK_OP_MAJOR(op) (op & 0x7F) +enum { + /* rv32i, rv64i, rv32m */ + OPC_RISC_LUI = (0x37), + OPC_RISC_AUIPC = (0x17), + OPC_RISC_JAL = (0x6F), + OPC_RISC_JALR = (0x67), + OPC_RISC_BRANCH = (0x63), + OPC_RISC_LOAD = (0x03), + OPC_RISC_STORE = (0x23), + OPC_RISC_ARITH_IMM = (0x13), + OPC_RISC_ARITH = (0x33), + OPC_RISC_FENCE = (0x0F), + OPC_RISC_SYSTEM = (0x73), + + /* rv64i, rv64m */ + OPC_RISC_ARITH_IMM_W = (0x1B), + OPC_RISC_ARITH_W = (0x3B), + + /* rv32a, rv64a */ + OPC_RISC_ATOMIC = (0x2F), + + /* floating point */ + OPC_RISC_FP_LOAD = (0x7), + OPC_RISC_FP_STORE = (0x27), + + OPC_RISC_FMADD = (0x43), + OPC_RISC_FMSUB = (0x47), + OPC_RISC_FNMSUB = (0x4B), + OPC_RISC_FNMADD = (0x4F), + + OPC_RISC_FP_ARITH = (0x53), +}; + +#define MASK_OP_ARITH(op) (MASK_OP_MAJOR(op) | (op & ((0x7 << 12) | \ + (0x7F << 25)))) +enum { + OPC_RISC_ADD = OPC_RISC_ARITH | (0x0 << 12) | (0x00 << 25), + OPC_RISC_SUB = OPC_RISC_ARITH | (0x0 << 12) | (0x20 << 25), + OPC_RISC_SLL = OPC_RISC_ARITH | (0x1 << 12) | (0x00 << 25), + OPC_RISC_SLT = OPC_RISC_ARITH | (0x2 << 12) | (0x00 << 25), + OPC_RISC_SLTU = OPC_RISC_ARITH | (0x3 << 12) | (0x00 << 25), + OPC_RISC_XOR = OPC_RISC_ARITH | (0x4 << 12) | (0x00 << 25), + OPC_RISC_SRL = OPC_RISC_ARITH | (0x5 << 12) | (0x00 << 25), + OPC_RISC_SRA = OPC_RISC_ARITH | (0x5 << 12) | (0x20 << 25), + OPC_RISC_OR = OPC_RISC_ARITH | (0x6 << 12) | (0x00 << 25), + OPC_RISC_AND = OPC_RISC_ARITH | (0x7 << 12) | (0x00 << 25), + + /* RV64M */ + OPC_RISC_MUL = OPC_RISC_ARITH | (0x0 << 12) | (0x01 << 25), + OPC_RISC_MULH = OPC_RISC_ARITH | (0x1 << 12) | (0x01 << 25), + OPC_RISC_MULHSU = OPC_RISC_ARITH | (0x2 << 12) | (0x01 << 25), + OPC_RISC_MULHU = OPC_RISC_ARITH | (0x3 << 12) | (0x01 << 25), + + OPC_RISC_DIV = OPC_RISC_ARITH | (0x4 << 12) | (0x01 << 25), + OPC_RISC_DIVU = OPC_RISC_ARITH | (0x5 << 12) | (0x01 << 25), + OPC_RISC_REM = OPC_RISC_ARITH | (0x6 << 12) | (0x01 << 25), + OPC_RISC_REMU = OPC_RISC_ARITH | (0x7 << 12) | (0x01 << 25), +}; + + +#define MASK_OP_ARITH_IMM(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_ADDI = OPC_RISC_ARITH_IMM | (0x0 << 12), + OPC_RISC_SLTI = OPC_RISC_ARITH_IMM | (0x2 << 12), + OPC_RISC_SLTIU = OPC_RISC_ARITH_IMM | (0x3 << 12), + OPC_RISC_XORI = OPC_RISC_ARITH_IMM | (0x4 << 12), + OPC_RISC_ORI = OPC_RISC_ARITH_IMM | (0x6 << 12), + OPC_RISC_ANDI = OPC_RISC_ARITH_IMM | (0x7 << 12), + OPC_RISC_SLLI = OPC_RISC_ARITH_IMM | (0x1 << 12), /* additional part of + IMM */ + OPC_RISC_SHIFT_RIGHT_I = OPC_RISC_ARITH_IMM | (0x5 << 12) /* SRAI, SRLI */ +}; + +#define MASK_OP_BRANCH(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_BEQ = OPC_RISC_BRANCH | (0x0 << 12), + OPC_RISC_BNE = OPC_RISC_BRANCH | (0x1 << 12), + OPC_RISC_BLT = OPC_RISC_BRANCH | (0x4 << 12), + OPC_RISC_BGE = OPC_RISC_BRANCH | (0x5 << 12), + OPC_RISC_BLTU = OPC_RISC_BRANCH | (0x6 << 12), + OPC_RISC_BGEU = OPC_RISC_BRANCH | (0x7 << 12) +}; + +enum { + OPC_RISC_ADDIW = OPC_RISC_ARITH_IMM_W | (0x0 << 12), + OPC_RISC_SLLIW = OPC_RISC_ARITH_IMM_W | (0x1 << 12), /* additional part of + IMM */ + OPC_RISC_SHIFT_RIGHT_IW = OPC_RISC_ARITH_IMM_W | (0x5 << 12) /* SRAI, SRLI + */ +}; + +enum { + OPC_RISC_ADDW = OPC_RISC_ARITH_W | (0x0 << 12) | (0x00 << 25), + OPC_RISC_SUBW = OPC_RISC_ARITH_W | (0x0 << 12) | (0x20 << 25), + OPC_RISC_SLLW = OPC_RISC_ARITH_W | (0x1 << 12) | (0x00 << 25), + OPC_RISC_SRLW = OPC_RISC_ARITH_W | (0x5 << 12) | (0x00 << 25), + OPC_RISC_SRAW = OPC_RISC_ARITH_W | (0x5 << 12) | (0x20 << 25), + + /* RV64M */ + OPC_RISC_MULW = OPC_RISC_ARITH_W | (0x0 << 12) | (0x01 << 25), + OPC_RISC_DIVW = OPC_RISC_ARITH_W | (0x4 << 12) | (0x01 << 25), + OPC_RISC_DIVUW = OPC_RISC_ARITH_W | (0x5 << 12) | (0x01 << 25), + OPC_RISC_REMW = OPC_RISC_ARITH_W | (0x6 << 12) | (0x01 << 25), + OPC_RISC_REMUW = OPC_RISC_ARITH_W | (0x7 << 12) | (0x01 << 25), +}; + +#define MASK_OP_LOAD(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_LB = OPC_RISC_LOAD | (0x0 << 12), + OPC_RISC_LH = OPC_RISC_LOAD | (0x1 << 12), + OPC_RISC_LW = OPC_RISC_LOAD | (0x2 << 12), + OPC_RISC_LD = OPC_RISC_LOAD | (0x3 << 12), + OPC_RISC_LBU = OPC_RISC_LOAD | (0x4 << 12), + OPC_RISC_LHU = OPC_RISC_LOAD | (0x5 << 12), + OPC_RISC_LWU = OPC_RISC_LOAD | (0x6 << 12), +}; + +#define MASK_OP_STORE(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_SB = OPC_RISC_STORE | (0x0 << 12), + OPC_RISC_SH = OPC_RISC_STORE | (0x1 << 12), + OPC_RISC_SW = OPC_RISC_STORE | (0x2 << 12), + OPC_RISC_SD = OPC_RISC_STORE | (0x3 << 12), +}; + +#define MASK_OP_JALR(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +/* no enum since OPC_RISC_JALR is the actual value */ + +#define MASK_OP_ATOMIC(op) \ + (MASK_OP_MAJOR(op) | (op & ((0x7 << 12) | (0x7F << 25)))) +#define MASK_OP_ATOMIC_NO_AQ_RL_SZ(op) \ + (MASK_OP_MAJOR(op) | (op & (0x1F << 27))) + +enum { + OPC_RISC_LR = OPC_RISC_ATOMIC | (0x02 << 27), + OPC_RISC_SC = OPC_RISC_ATOMIC | (0x03 << 27), + OPC_RISC_AMOSWAP = OPC_RISC_ATOMIC | (0x01 << 27), + OPC_RISC_AMOADD = OPC_RISC_ATOMIC | (0x00 << 27), + OPC_RISC_AMOXOR = OPC_RISC_ATOMIC | (0x04 << 27), + OPC_RISC_AMOAND = OPC_RISC_ATOMIC | (0x0C << 27), + OPC_RISC_AMOOR = OPC_RISC_ATOMIC | (0x08 << 27), + OPC_RISC_AMOMIN = OPC_RISC_ATOMIC | (0x10 << 27), + OPC_RISC_AMOMAX = OPC_RISC_ATOMIC | (0x14 << 27), + OPC_RISC_AMOMINU = OPC_RISC_ATOMIC | (0x18 << 27), + OPC_RISC_AMOMAXU = OPC_RISC_ATOMIC | (0x1C << 27), +}; + +#define MASK_OP_SYSTEM(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_ECALL = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_EBREAK = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_ERET = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_MRTS = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_MRTH = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_HRTS = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_WFI = OPC_RISC_SYSTEM | (0x0 << 12), + OPC_RISC_SFENCEVM = OPC_RISC_SYSTEM | (0x0 << 12), + + OPC_RISC_CSRRW = OPC_RISC_SYSTEM | (0x1 << 12), + OPC_RISC_CSRRS = OPC_RISC_SYSTEM | (0x2 << 12), + OPC_RISC_CSRRC = OPC_RISC_SYSTEM | (0x3 << 12), + OPC_RISC_CSRRWI = OPC_RISC_SYSTEM | (0x5 << 12), + OPC_RISC_CSRRSI = OPC_RISC_SYSTEM | (0x6 << 12), + OPC_RISC_CSRRCI = OPC_RISC_SYSTEM | (0x7 << 12), +}; + +#define MASK_OP_FP_LOAD(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_FLW = OPC_RISC_FP_LOAD | (0x2 << 12), + OPC_RISC_FLD = OPC_RISC_FP_LOAD | (0x3 << 12), +}; + +#define MASK_OP_FP_STORE(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) +enum { + OPC_RISC_FSW = OPC_RISC_FP_STORE | (0x2 << 12), + OPC_RISC_FSD = OPC_RISC_FP_STORE | (0x3 << 12), +}; + +#define MASK_OP_FP_FMADD(op) (MASK_OP_MAJOR(op) | (op & (0x3 << 25))) +enum { + OPC_RISC_FMADD_S = OPC_RISC_FMADD | (0x0 << 25), + OPC_RISC_FMADD_D = OPC_RISC_FMADD | (0x1 << 25), +}; + +#define MASK_OP_FP_FMSUB(op) (MASK_OP_MAJOR(op) | (op & (0x3 << 25))) +enum { + OPC_RISC_FMSUB_S = OPC_RISC_FMSUB | (0x0 << 25), + OPC_RISC_FMSUB_D = OPC_RISC_FMSUB | (0x1 << 25), +}; + +#define MASK_OP_FP_FNMADD(op) (MASK_OP_MAJOR(op) | (op & (0x3 << 25))) +enum { + OPC_RISC_FNMADD_S = OPC_RISC_FNMADD | (0x0 << 25), + OPC_RISC_FNMADD_D = OPC_RISC_FNMADD | (0x1 << 25), +}; + +#define MASK_OP_FP_FNMSUB(op) (MASK_OP_MAJOR(op) | (op & (0x3 << 25))) +enum { + OPC_RISC_FNMSUB_S = OPC_RISC_FNMSUB | (0x0 << 25), + OPC_RISC_FNMSUB_D = OPC_RISC_FNMSUB | (0x1 << 25), +}; + +#define MASK_OP_FP_ARITH(op) (MASK_OP_MAJOR(op) | (op & (0x7F << 25))) +enum { + /* float */ + OPC_RISC_FADD_S = OPC_RISC_FP_ARITH | (0x0 << 25), + OPC_RISC_FSUB_S = OPC_RISC_FP_ARITH | (0x4 << 25), + OPC_RISC_FMUL_S = OPC_RISC_FP_ARITH | (0x8 << 25), + OPC_RISC_FDIV_S = OPC_RISC_FP_ARITH | (0xC << 25), + + OPC_RISC_FSGNJ_S = OPC_RISC_FP_ARITH | (0x10 << 25), + OPC_RISC_FSGNJN_S = OPC_RISC_FP_ARITH | (0x10 << 25), + OPC_RISC_FSGNJX_S = OPC_RISC_FP_ARITH | (0x10 << 25), + + OPC_RISC_FMIN_S = OPC_RISC_FP_ARITH | (0x14 << 25), + OPC_RISC_FMAX_S = OPC_RISC_FP_ARITH | (0x14 << 25), + + OPC_RISC_FSQRT_S = OPC_RISC_FP_ARITH | (0x2C << 25), + + OPC_RISC_FEQ_S = OPC_RISC_FP_ARITH | (0x50 << 25), + OPC_RISC_FLT_S = OPC_RISC_FP_ARITH | (0x50 << 25), + OPC_RISC_FLE_S = OPC_RISC_FP_ARITH | (0x50 << 25), + + OPC_RISC_FCVT_W_S = OPC_RISC_FP_ARITH | (0x60 << 25), + OPC_RISC_FCVT_WU_S = OPC_RISC_FP_ARITH | (0x60 << 25), + OPC_RISC_FCVT_L_S = OPC_RISC_FP_ARITH | (0x60 << 25), + OPC_RISC_FCVT_LU_S = OPC_RISC_FP_ARITH | (0x60 << 25), + + OPC_RISC_FCVT_S_W = OPC_RISC_FP_ARITH | (0x68 << 25), + OPC_RISC_FCVT_S_WU = OPC_RISC_FP_ARITH | (0x68 << 25), + OPC_RISC_FCVT_S_L = OPC_RISC_FP_ARITH | (0x68 << 25), + OPC_RISC_FCVT_S_LU = OPC_RISC_FP_ARITH | (0x68 << 25), + + OPC_RISC_FMV_X_S = OPC_RISC_FP_ARITH | (0x70 << 25), + OPC_RISC_FCLASS_S = OPC_RISC_FP_ARITH | (0x70 << 25), + + OPC_RISC_FMV_S_X = OPC_RISC_FP_ARITH | (0x78 << 25), + + /* double */ + OPC_RISC_FADD_D = OPC_RISC_FP_ARITH | (0x1 << 25), + OPC_RISC_FSUB_D = OPC_RISC_FP_ARITH | (0x5 << 25), + OPC_RISC_FMUL_D = OPC_RISC_FP_ARITH | (0x9 << 25), + OPC_RISC_FDIV_D = OPC_RISC_FP_ARITH | (0xD << 25), + + OPC_RISC_FSGNJ_D = OPC_RISC_FP_ARITH | (0x11 << 25), + OPC_RISC_FSGNJN_D = OPC_RISC_FP_ARITH | (0x11 << 25), + OPC_RISC_FSGNJX_D = OPC_RISC_FP_ARITH | (0x11 << 25), + + OPC_RISC_FMIN_D = OPC_RISC_FP_ARITH | (0x15 << 25), + OPC_RISC_FMAX_D = OPC_RISC_FP_ARITH | (0x15 << 25), + + OPC_RISC_FCVT_S_D = OPC_RISC_FP_ARITH | (0x20 << 25), + + OPC_RISC_FCVT_D_S = OPC_RISC_FP_ARITH | (0x21 << 25), + + OPC_RISC_FSQRT_D = OPC_RISC_FP_ARITH | (0x2D << 25), + + OPC_RISC_FEQ_D = OPC_RISC_FP_ARITH | (0x51 << 25), + OPC_RISC_FLT_D = OPC_RISC_FP_ARITH | (0x51 << 25), + OPC_RISC_FLE_D = OPC_RISC_FP_ARITH | (0x51 << 25), + + OPC_RISC_FCVT_W_D = OPC_RISC_FP_ARITH | (0x61 << 25), + OPC_RISC_FCVT_WU_D = OPC_RISC_FP_ARITH | (0x61 << 25), + OPC_RISC_FCVT_L_D = OPC_RISC_FP_ARITH | (0x61 << 25), + OPC_RISC_FCVT_LU_D = OPC_RISC_FP_ARITH | (0x61 << 25), + + OPC_RISC_FCVT_D_W = OPC_RISC_FP_ARITH | (0x69 << 25), + OPC_RISC_FCVT_D_WU = OPC_RISC_FP_ARITH | (0x69 << 25), + OPC_RISC_FCVT_D_L = OPC_RISC_FP_ARITH | (0x69 << 25), + OPC_RISC_FCVT_D_LU = OPC_RISC_FP_ARITH | (0x69 << 25), + + OPC_RISC_FMV_X_D = OPC_RISC_FP_ARITH | (0x71 << 25), + OPC_RISC_FCLASS_D = OPC_RISC_FP_ARITH | (0x71 << 25), + + OPC_RISC_FMV_D_X = OPC_RISC_FP_ARITH | (0x79 << 25), +}; + +#define GET_B_IMM(inst) ((extract32(inst, 8, 4) << 1) \ + | (extract32(inst, 25, 6) << 5) \ + | (extract32(inst, 7, 1) << 11) \ + | (sextract64(inst, 31, 1) << 12)) + +#define GET_STORE_IMM(inst) ((extract32(inst, 7, 5)) \ + | (sextract64(inst, 25, 7) << 5)) + +#define GET_JAL_IMM(inst) ((extract32(inst, 21, 10) << 1) \ + | (extract32(inst, 20, 1) << 11) \ + | (extract32(inst, 12, 8) << 12) \ + | (sextract64(inst, 31, 1) << 20)) + +#define GET_RM(inst) extract32(inst, 12, 3) +#define GET_RS3(inst) extract32(inst, 27, 5) +#define GET_RS1(inst) extract32(inst, 15, 5) +#define GET_RS2(inst) extract32(inst, 20, 5) +#define GET_RD(inst) extract32(inst, 7, 5) +#define GET_IMM(inst) sextract64(inst, 20, 12) + +/* RVC decoding macros */ +#define GET_C_IMM(inst) (extract32(inst, 2, 5) \ + | (sextract64(inst, 12, 1) << 5)) +#define GET_C_ZIMM(inst) (extract32(inst, 2, 5) \ + | (extract32(inst, 12, 1) << 5)) +#define GET_C_ADDI4SPN_IMM(inst) ((extract32(inst, 6, 1) << 2) \ + | (extract32(inst, 5, 1) << 3) \ + | (extract32(inst, 11, 2) << 4) \ + | (extract32(inst, 7, 4) << 6)) +#define GET_C_ADDI16SP_IMM(inst) ((extract32(inst, 6, 1) << 4) \ + | (extract32(inst, 2, 1) << 5) \ + | (extract32(inst, 5, 1) << 6) \ + | (extract32(inst, 3, 2) << 7) \ + | (sextract64(inst, 12, 1) << 9)) +#define GET_C_LWSP_IMM(inst) ((extract32(inst, 4, 3) << 2) \ + | (extract32(inst, 12, 1) << 5) \ + | (extract32(inst, 2, 2) << 6)) +#define GET_C_LDSP_IMM(inst) ((extract32(inst, 5, 2) << 3) \ + | (extract32(inst, 12, 1) << 5) \ + | (extract32(inst, 2, 3) << 6)) +#define GET_C_SWSP_IMM(inst) ((extract32(inst, 9, 4) << 2) \ + | (extract32(inst, 7, 2) << 6)) +#define GET_C_SDSP_IMM(inst) ((extract32(inst, 10, 3) << 3) \ + | (extract32(inst, 7, 3) << 6)) +#define GET_C_LW_IMM(inst) ((extract32(inst, 6, 1) << 2) \ + | (extract32(inst, 10, 3) << 3) \ + | (extract32(inst, 5, 1) << 6)) +#define GET_C_LD_IMM(inst) ((extract32(inst, 10, 3) << 3) \ + | (extract32(inst, 5, 2) << 6)) +#define GET_C_J_IMM(inst) ((extract32(inst, 3, 3) << 1) \ + | (extract32(inst, 11, 1) << 4) \ + | (extract32(inst, 2, 1) << 5) \ + | (extract32(inst, 7, 1) << 6) \ + | (extract32(inst, 6, 1) << 7) \ + | (extract32(inst, 9, 2) << 8) \ + | (extract32(inst, 8, 1) << 10) \ + | (sextract64(inst, 12, 1) << 11)) +#define GET_C_B_IMM(inst) ((extract32(inst, 3, 2) << 1) \ + | (extract32(inst, 10, 2) << 3) \ + | (extract32(inst, 2, 1) << 5) \ + | (extract32(inst, 5, 2) << 6) \ + | (sextract64(inst, 12, 1) << 8)) +#define GET_C_SIMM3(inst) extract32(inst, 10, 3) +#define GET_C_RD(inst) GET_RD(inst) +#define GET_C_RS1(inst) GET_RD(inst) +#define GET_C_RS2(inst) extract32(inst, 2, 5) +#define GET_C_RS1S(inst) (8 + extract32(inst, 7, 3)) +#define GET_C_RS2S(inst) (8 + extract32(inst, 2, 3)) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c new file mode 100644 index 0000000000..e34715df4e --- /dev/null +++ b/target/riscv/op_helper.c @@ -0,0 +1,669 @@ +/* + * RISC-V Emulation Helpers for QEMU. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "qemu/main-loop.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + +#ifndef CONFIG_USER_ONLY + +#if defined(TARGET_RISCV32) +static const char valid_vm_1_09[16] = { + [VM_1_09_MBARE] = 1, + [VM_1_09_SV32] = 1, +}; +static const char valid_vm_1_10[16] = { + [VM_1_10_MBARE] = 1, + [VM_1_10_SV32] = 1 +}; +#elif defined(TARGET_RISCV64) +static const char valid_vm_1_09[16] = { + [VM_1_09_MBARE] = 1, + [VM_1_09_SV39] = 1, + [VM_1_09_SV48] = 1, +}; +static const char valid_vm_1_10[16] = { + [VM_1_10_MBARE] = 1, + [VM_1_10_SV39] = 1, + [VM_1_10_SV48] = 1, + [VM_1_10_SV57] = 1 +}; +#endif + +static int validate_vm(CPURISCVState *env, target_ulong vm) +{ + return (env->priv_ver >= PRIV_VERSION_1_10_0) ? + valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; +} + +#endif + +/* Exceptions processing helpers */ +void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, + uint32_t exception, uintptr_t pc) +{ + CPUState *cs = CPU(riscv_env_get_cpu(env)); + qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); + cs->exception_index = exception; + cpu_loop_exit_restore(cs, pc); +} + +void helper_raise_exception(CPURISCVState *env, uint32_t exception) +{ + do_raise_exception_err(env, exception, 0); +} + +static void validate_mstatus_fs(CPURISCVState *env, uintptr_t ra) +{ +#ifndef CONFIG_USER_ONLY + if (!(env->mstatus & MSTATUS_FS)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra); + } +#endif +} + +/* + * Handle writes to CSRs and any resulting special behavior + * + * Adapted from Spike's processor_t::set_csr + */ +void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, + target_ulong csrno) +{ +#ifndef CONFIG_USER_ONLY + uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP); + uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; +#endif + + switch (csrno) { + case CSR_FFLAGS: + validate_mstatus_fs(env, GETPC()); + cpu_riscv_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT)); + break; + case CSR_FRM: + validate_mstatus_fs(env, GETPC()); + env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT); + break; + case CSR_FCSR: + validate_mstatus_fs(env, GETPC()); + env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT; + cpu_riscv_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT); + break; +#ifndef CONFIG_USER_ONLY + case CSR_MSTATUS: { + target_ulong mstatus = env->mstatus; + target_ulong mask = 0; + target_ulong mpp = get_field(val_to_write, MSTATUS_MPP); + + /* flush tlb on mstatus fields that affect VM */ + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | + MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { + helper_tlb_flush(env); + } + mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | + MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_MPP | MSTATUS_MXR | + (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ? + MSTATUS_VM : 0); + } + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | + MSTATUS_MPRV | MSTATUS_SUM)) { + helper_tlb_flush(env); + } + mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | + MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_MPP | MSTATUS_MXR; + } + + /* silenty discard mstatus.mpp writes for unsupported modes */ + if (mpp == PRV_H || + (!riscv_has_ext(env, RVS) && mpp == PRV_S) || + (!riscv_has_ext(env, RVU) && mpp == PRV_U)) { + mask &= ~MSTATUS_MPP; + } + + mstatus = (mstatus & ~mask) | (val_to_write & mask); + int dirty = (mstatus & MSTATUS_FS) == MSTATUS_FS; + dirty |= (mstatus & MSTATUS_XS) == MSTATUS_XS; + mstatus = set_field(mstatus, MSTATUS_SD, dirty); + env->mstatus = mstatus; + break; + } + case CSR_MIP: { + /* + * Since the writeable bits in MIP are not set asynchrously by the + * CLINT, no additional locking is needed for read-modifiy-write + * CSR operations + */ + qemu_mutex_lock_iothread(); + RISCVCPU *cpu = riscv_env_get_cpu(env); + riscv_set_local_interrupt(cpu, MIP_SSIP, + (val_to_write & MIP_SSIP) != 0); + riscv_set_local_interrupt(cpu, MIP_STIP, + (val_to_write & MIP_STIP) != 0); + /* + * csrs, csrc on mip.SEIP is not decomposable into separate read and + * write steps, so a different implementation is needed + */ + qemu_mutex_unlock_iothread(); + break; + } + case CSR_MIE: { + env->mie = (env->mie & ~all_ints) | + (val_to_write & all_ints); + break; + } + case CSR_MIDELEG: + env->mideleg = (env->mideleg & ~delegable_ints) + | (val_to_write & delegable_ints); + break; + case CSR_MEDELEG: { + target_ulong mask = 0; + mask |= 1ULL << (RISCV_EXCP_INST_ADDR_MIS); + mask |= 1ULL << (RISCV_EXCP_INST_ACCESS_FAULT); + mask |= 1ULL << (RISCV_EXCP_ILLEGAL_INST); + mask |= 1ULL << (RISCV_EXCP_BREAKPOINT); + mask |= 1ULL << (RISCV_EXCP_LOAD_ADDR_MIS); + mask |= 1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT); + mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS); + mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT); + mask |= 1ULL << (RISCV_EXCP_U_ECALL); + mask |= 1ULL << (RISCV_EXCP_S_ECALL); + mask |= 1ULL << (RISCV_EXCP_H_ECALL); + mask |= 1ULL << (RISCV_EXCP_M_ECALL); + mask |= 1ULL << (RISCV_EXCP_INST_PAGE_FAULT); + mask |= 1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT); + mask |= 1ULL << (RISCV_EXCP_STORE_PAGE_FAULT); + env->medeleg = (env->medeleg & ~mask) + | (val_to_write & mask); + break; + } + case CSR_MINSTRET: + qemu_log_mask(LOG_UNIMP, "CSR_MINSTRET: write not implemented"); + goto do_illegal; + case CSR_MCYCLE: + qemu_log_mask(LOG_UNIMP, "CSR_MCYCLE: write not implemented"); + goto do_illegal; + case CSR_MINSTRETH: + qemu_log_mask(LOG_UNIMP, "CSR_MINSTRETH: write not implemented"); + goto do_illegal; + case CSR_MCYCLEH: + qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented"); + goto do_illegal; + case CSR_MUCOUNTEREN: + env->mucounteren = val_to_write; + break; + case CSR_MSCOUNTEREN: + env->mscounteren = val_to_write; + break; + case CSR_SSTATUS: { + target_ulong ms = env->mstatus; + target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE + | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS + | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; + ms = (ms & ~mask) | (val_to_write & mask); + csr_write_helper(env, ms, CSR_MSTATUS); + break; + } + case CSR_SIP: { + qemu_mutex_lock_iothread(); + target_ulong next_mip = (env->mip & ~env->mideleg) + | (val_to_write & env->mideleg); + qemu_mutex_unlock_iothread(); + csr_write_helper(env, next_mip, CSR_MIP); + break; + } + case CSR_SIE: { + target_ulong next_mie = (env->mie & ~env->mideleg) + | (val_to_write & env->mideleg); + csr_write_helper(env, next_mie, CSR_MIE); + break; + } + case CSR_SATP: /* CSR_SPTBR */ { + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + goto do_illegal; + } + if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr)) + { + helper_tlb_flush(env); + env->sptbr = val_to_write & (((target_ulong) + 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); + } + if (env->priv_ver >= PRIV_VERSION_1_10_0 && + validate_vm(env, get_field(val_to_write, SATP_MODE)) && + ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) + { + helper_tlb_flush(env); + env->satp = val_to_write; + } + break; + } + case CSR_SEPC: + env->sepc = val_to_write; + break; + case CSR_STVEC: + if (val_to_write & 1) { + qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); + goto do_illegal; + } + env->stvec = val_to_write >> 2 << 2; + break; + case CSR_SCOUNTEREN: + env->scounteren = val_to_write; + break; + case CSR_SSCRATCH: + env->sscratch = val_to_write; + break; + case CSR_SCAUSE: + env->scause = val_to_write; + break; + case CSR_SBADADDR: + env->sbadaddr = val_to_write; + break; + case CSR_MEPC: + env->mepc = val_to_write; + break; + case CSR_MTVEC: + if (val_to_write & 1) { + qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); + goto do_illegal; + } + env->mtvec = val_to_write >> 2 << 2; + break; + case CSR_MCOUNTEREN: + env->mcounteren = val_to_write; + break; + case CSR_MSCRATCH: + env->mscratch = val_to_write; + break; + case CSR_MCAUSE: + env->mcause = val_to_write; + break; + case CSR_MBADADDR: + env->mbadaddr = val_to_write; + break; + case CSR_MISA: { + qemu_log_mask(LOG_UNIMP, "CSR_MISA: misa writes not supported"); + goto do_illegal; + } + case CSR_PMPCFG0: + case CSR_PMPCFG1: + case CSR_PMPCFG2: + case CSR_PMPCFG3: + pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write); + break; + case CSR_PMPADDR0: + case CSR_PMPADDR1: + case CSR_PMPADDR2: + case CSR_PMPADDR3: + case CSR_PMPADDR4: + case CSR_PMPADDR5: + case CSR_PMPADDR6: + case CSR_PMPADDR7: + case CSR_PMPADDR8: + case CSR_PMPADDR9: + case CSR_PMPADDR10: + case CSR_PMPADDR11: + case CSR_PMPADDR12: + case CSR_PMPADDR13: + case CSR_PMPADDR14: + case CSR_PMPADDR15: + pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write); + break; + do_illegal: +#endif + default: + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } +} + +/* + * Handle reads to CSRs and any resulting special behavior + * + * Adapted from Spike's processor_t::get_csr + */ +target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) +{ +#ifndef CONFIG_USER_ONLY + target_ulong ctr_en = env->priv == PRV_U ? env->mucounteren : + env->priv == PRV_S ? env->mscounteren : -1U; +#else + target_ulong ctr_en = -1; +#endif + target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1; + + if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) { + if (ctr_ok) { + return 0; + } + } +#if defined(TARGET_RISCV32) + if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) { + if (ctr_ok) { + return 0; + } + } +#endif + if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { + return 0; + } +#if defined(TARGET_RISCV32) + if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { + return 0; + } +#endif + if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) { + return 0; + } + + switch (csrno) { + case CSR_FFLAGS: + validate_mstatus_fs(env, GETPC()); + return cpu_riscv_get_fflags(env); + case CSR_FRM: + validate_mstatus_fs(env, GETPC()); + return env->frm; + case CSR_FCSR: + validate_mstatus_fs(env, GETPC()); + return (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT) + | (env->frm << FSR_RD_SHIFT); + /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */ +#ifdef CONFIG_USER_ONLY + case CSR_TIME: + return cpu_get_host_ticks(); +#if defined(TARGET_RISCV32) + case CSR_TIMEH: + return cpu_get_host_ticks() >> 32; +#endif +#endif + case CSR_INSTRET: + case CSR_CYCLE: + if (ctr_ok) { + return cpu_get_host_ticks(); + } + break; +#if defined(TARGET_RISCV32) + case CSR_INSTRETH: + case CSR_CYCLEH: + if (ctr_ok) { + return cpu_get_host_ticks() >> 32; + } + break; +#endif +#ifndef CONFIG_USER_ONLY + case CSR_MINSTRET: + case CSR_MCYCLE: + return cpu_get_host_ticks(); + case CSR_MINSTRETH: + case CSR_MCYCLEH: +#if defined(TARGET_RISCV32) + return cpu_get_host_ticks() >> 32; +#endif + break; + case CSR_MUCOUNTEREN: + return env->mucounteren; + case CSR_MSCOUNTEREN: + return env->mscounteren; + case CSR_SSTATUS: { + target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE + | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS + | SSTATUS_SUM | SSTATUS_SD; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + mask |= SSTATUS_MXR; + } + return env->mstatus & mask; + } + case CSR_SIP: { + qemu_mutex_lock_iothread(); + target_ulong tmp = env->mip & env->mideleg; + qemu_mutex_unlock_iothread(); + return tmp; + } + case CSR_SIE: + return env->mie & env->mideleg; + case CSR_SEPC: + return env->sepc; + case CSR_SBADADDR: + return env->sbadaddr; + case CSR_STVEC: + return env->stvec; + case CSR_SCOUNTEREN: + return env->scounteren; + case CSR_SCAUSE: + return env->scause; + case CSR_SPTBR: + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + return env->satp; + } else { + return env->sptbr; + } + case CSR_SSCRATCH: + return env->sscratch; + case CSR_MSTATUS: + return env->mstatus; + case CSR_MIP: { + qemu_mutex_lock_iothread(); + target_ulong tmp = env->mip; + qemu_mutex_unlock_iothread(); + return tmp; + } + case CSR_MIE: + return env->mie; + case CSR_MEPC: + return env->mepc; + case CSR_MSCRATCH: + return env->mscratch; + case CSR_MCAUSE: + return env->mcause; + case CSR_MBADADDR: + return env->mbadaddr; + case CSR_MISA: + return env->misa; + case CSR_MARCHID: + return 0; /* as spike does */ + case CSR_MIMPID: + return 0; /* as spike does */ + case CSR_MVENDORID: + return 0; /* as spike does */ + case CSR_MHARTID: + return env->mhartid; + case CSR_MTVEC: + return env->mtvec; + case CSR_MCOUNTEREN: + return env->mcounteren; + case CSR_MEDELEG: + return env->medeleg; + case CSR_MIDELEG: + return env->mideleg; + case CSR_PMPCFG0: + case CSR_PMPCFG1: + case CSR_PMPCFG2: + case CSR_PMPCFG3: + return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); + case CSR_PMPADDR0: + case CSR_PMPADDR1: + case CSR_PMPADDR2: + case CSR_PMPADDR3: + case CSR_PMPADDR4: + case CSR_PMPADDR5: + case CSR_PMPADDR6: + case CSR_PMPADDR7: + case CSR_PMPADDR8: + case CSR_PMPADDR9: + case CSR_PMPADDR10: + case CSR_PMPADDR11: + case CSR_PMPADDR12: + case CSR_PMPADDR13: + case CSR_PMPADDR14: + case CSR_PMPADDR15: + return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); +#endif + } + /* used by e.g. MTIME read */ + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); +} + +/* + * Check that CSR access is allowed. + * + * Adapted from Spike's decode.h:validate_csr + */ +static void validate_csr(CPURISCVState *env, uint64_t which, + uint64_t write, uintptr_t ra) +{ +#ifndef CONFIG_USER_ONLY + unsigned csr_priv = get_field((which), 0x300); + unsigned csr_read_only = get_field((which), 0xC00) == 3; + if (((write) && csr_read_only) || (env->priv < csr_priv)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra); + } +#endif +} + +target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, + target_ulong csr) +{ + validate_csr(env, csr, 1, GETPC()); + uint64_t csr_backup = csr_read_helper(env, csr); + csr_write_helper(env, src, csr); + return csr_backup; +} + +target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, + target_ulong csr, target_ulong rs1_pass) +{ + validate_csr(env, csr, rs1_pass != 0, GETPC()); + uint64_t csr_backup = csr_read_helper(env, csr); + if (rs1_pass != 0) { + csr_write_helper(env, src | csr_backup, csr); + } + return csr_backup; +} + +target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, + target_ulong csr, target_ulong rs1_pass) +{ + validate_csr(env, csr, rs1_pass != 0, GETPC()); + uint64_t csr_backup = csr_read_helper(env, csr); + if (rs1_pass != 0) { + csr_write_helper(env, (~src) & csr_backup, csr); + } + return csr_backup; +} + +#ifndef CONFIG_USER_ONLY + +/* iothread_mutex must be held */ +void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value) +{ + target_ulong old_mip = cpu->env.mip; + cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0); + + if (cpu->env.mip && !old_mip) { + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } else if (!cpu->env.mip && old_mip) { + cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } +} + +void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) +{ + if (newpriv > PRV_M) { + g_assert_not_reached(); + } + if (newpriv == PRV_H) { + newpriv = PRV_U; + } + /* tlb_flush is unnecessary as mode is contained in mmu_idx */ + env->priv = newpriv; +} + +target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) +{ + if (!(env->priv >= PRV_S)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + + target_ulong retpc = env->sepc; + if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { + do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + } + + target_ulong mstatus = env->mstatus; + target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP); + mstatus = set_field(mstatus, + env->priv_ver >= PRIV_VERSION_1_10_0 ? + MSTATUS_SIE : MSTATUS_UIE << prev_priv, + get_field(mstatus, MSTATUS_SPIE)); + mstatus = set_field(mstatus, MSTATUS_SPIE, 0); + mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + riscv_set_mode(env, prev_priv); + csr_write_helper(env, mstatus, CSR_MSTATUS); + + return retpc; +} + +target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) +{ + if (!(env->priv >= PRV_M)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + + target_ulong retpc = env->mepc; + if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { + do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + } + + target_ulong mstatus = env->mstatus; + target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + mstatus = set_field(mstatus, + env->priv_ver >= PRIV_VERSION_1_10_0 ? + MSTATUS_MIE : MSTATUS_UIE << prev_priv, + get_field(mstatus, MSTATUS_MPIE)); + mstatus = set_field(mstatus, MSTATUS_MPIE, 0); + mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); + riscv_set_mode(env, prev_priv); + csr_write_helper(env, mstatus, CSR_MSTATUS); + + return retpc; +} + + +void helper_wfi(CPURISCVState *env) +{ + CPUState *cs = CPU(riscv_env_get_cpu(env)); + + cs->halted = 1; + cs->exception_index = EXCP_HLT; + cpu_loop_exit(cs); +} + +void helper_tlb_flush(CPURISCVState *env) +{ + RISCVCPU *cpu = riscv_env_get_cpu(env); + CPUState *cs = CPU(cpu); + tlb_flush(cs); +} + +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c new file mode 100644 index 0000000000..f432f3b759 --- /dev/null +++ b/target/riscv/pmp.c @@ -0,0 +1,380 @@ +/* + * QEMU RISC-V PMP (Physical Memory Protection) + * + * Author: Daire McNamara, daire.mcnamara@emdalo.com + * Ivan Griffin, ivan.griffin@emdalo.com + * + * This provides a RISC-V Physical Memory Protection implementation + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* + * PMP (Physical Memory Protection) is as-of-yet unused and needs testing. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" + +#ifndef CONFIG_USER_ONLY + +#define RISCV_DEBUG_PMP 0 +#define PMP_DEBUG(fmt, ...) \ + do { \ + if (RISCV_DEBUG_PMP) { \ + qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ + } \ + } while (0) + +static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, + uint8_t val); +static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); +static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); + +/* + * Accessor method to extract address matching type 'a field' from cfg reg + */ +static inline uint8_t pmp_get_a_field(uint8_t cfg) +{ + uint8_t a = cfg >> 3; + return a & 0x3; +} + +/* + * Check whether a PMP is locked or not. + */ +static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) +{ + + if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) { + return 1; + } + + /* Top PMP has no 'next' to check */ + if ((pmp_index + 1u) >= MAX_RISCV_PMPS) { + return 0; + } + + /* In TOR mode, need to check the lock bit of the next pmp + * (if there is a next) + */ + const uint8_t a_field = + pmp_get_a_field(env->pmp_state.pmp[pmp_index + 1].cfg_reg); + if ((env->pmp_state.pmp[pmp_index + 1u].cfg_reg & PMP_LOCK) && + (PMP_AMATCH_TOR == a_field)) { + return 1; + } + + return 0; +} + +/* + * Count the number of active rules. + */ +static inline uint32_t pmp_get_num_rules(CPURISCVState *env) +{ + return env->pmp_state.num_rules; +} + +/* + * Accessor to get the cfg reg for a specific PMP/HART + */ +static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) +{ + if (pmp_index < MAX_RISCV_PMPS) { + return env->pmp_state.pmp[pmp_index].cfg_reg; + } + + return 0; +} + + +/* + * Accessor to set the cfg reg for a specific PMP/HART + * Bounds checks and relevant lock bit. + */ +static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) +{ + if (pmp_index < MAX_RISCV_PMPS) { + if (!pmp_is_locked(env, pmp_index)) { + env->pmp_state.pmp[pmp_index].cfg_reg = val; + pmp_update_rule(env, pmp_index); + } else { + PMP_DEBUG("ignoring write - locked"); + } + } else { + PMP_DEBUG("ignoring write - out of bounds"); + } +} + +static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) +{ + /* + aaaa...aaa0 8-byte NAPOT range + aaaa...aa01 16-byte NAPOT range + aaaa...a011 32-byte NAPOT range + ... + aa01...1111 2^XLEN-byte NAPOT range + a011...1111 2^(XLEN+1)-byte NAPOT range + 0111...1111 2^(XLEN+2)-byte NAPOT range + 1111...1111 Reserved + */ + if (a == -1) { + *sa = 0u; + *ea = -1; + return; + } else { + target_ulong t1 = ctz64(~a); + target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3; + target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; + *sa = base; + *ea = base + range; + } +} + + +/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' + * end address values. + * This function is called relatively infrequently whereas the check that + * an address is within a pmp rule is called often, so optimise that one + */ +static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) +{ + int i; + + env->pmp_state.num_rules = 0; + + uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; + target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; + target_ulong prev_addr = 0u; + target_ulong sa = 0u; + target_ulong ea = 0u; + + if (pmp_index >= 1u) { + prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg; + } + + switch (pmp_get_a_field(this_cfg)) { + case PMP_AMATCH_OFF: + sa = 0u; + ea = -1; + break; + + case PMP_AMATCH_TOR: + sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (this_addr << 2) - 1u; + break; + + case PMP_AMATCH_NA4: + sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (this_addr + 4u) - 1u; + break; + + case PMP_AMATCH_NAPOT: + pmp_decode_napot(this_addr, &sa, &ea); + break; + + default: + sa = 0u; + ea = 0u; + break; + } + + env->pmp_state.addr[pmp_index].sa = sa; + env->pmp_state.addr[pmp_index].ea = ea; + + for (i = 0; i < MAX_RISCV_PMPS; i++) { + const uint8_t a_field = + pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); + if (PMP_AMATCH_OFF != a_field) { + env->pmp_state.num_rules++; + } + } +} + +static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) +{ + int result = 0; + + if ((addr >= env->pmp_state.addr[pmp_index].sa) + && (addr <= env->pmp_state.addr[pmp_index].ea)) { + result = 1; + } else { + result = 0; + } + + return result; +} + + +/* + * Public Interface + */ + +/* + * Check if the address has required RWX privs to complete desired operation + */ +bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, + target_ulong size, pmp_priv_t privs) +{ + int i = 0; + int ret = -1; + target_ulong s = 0; + target_ulong e = 0; + pmp_priv_t allowed_privs = 0; + + /* Short cut if no rules */ + if (0 == pmp_get_num_rules(env)) { + return true; + } + + /* 1.10 draft priv spec states there is an implicit order + from low to high */ + for (i = 0; i < MAX_RISCV_PMPS; i++) { + s = pmp_is_in_range(env, i, addr); + e = pmp_is_in_range(env, i, addr + size); + + /* partially inside */ + if ((s + e) == 1) { + PMP_DEBUG("pmp violation - access is partially inside"); + ret = 0; + break; + } + + /* fully inside */ + const uint8_t a_field = + pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); + if ((s + e) == 2) { + if (PMP_AMATCH_OFF == a_field) { + return 1; + } + + allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC; + if ((env->priv != PRV_M) || pmp_is_locked(env, i)) { + allowed_privs &= env->pmp_state.pmp[i].cfg_reg; + } + + if ((privs & allowed_privs) == privs) { + ret = 1; + break; + } else { + ret = 0; + break; + } + } + } + + /* No rule matched */ + if (ret == -1) { + if (env->priv == PRV_M) { + ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an + * M-Mode access, the access succeeds */ + } else { + ret = 0; /* Other modes are not allowed to succeed if they don't + * match a rule, but there are rules. We've checked for + * no rule earlier in this function. */ + } + } + + return ret == 1 ? true : false; +} + + +/* + * Handle a write to a pmpcfg CSP + */ +void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, + target_ulong val) +{ + int i; + uint8_t cfg_val; + + PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx, + env->mhartid, reg_index, val); + + if ((reg_index & 1) && (sizeof(target_ulong) == 8)) { + PMP_DEBUG("ignoring write - incorrect address"); + return; + } + + for (i = 0; i < sizeof(target_ulong); i++) { + cfg_val = (val >> 8 * i) & 0xff; + pmp_write_cfg(env, (reg_index * sizeof(target_ulong)) + i, + cfg_val); + } +} + + +/* + * Handle a read from a pmpcfg CSP + */ +target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index) +{ + int i; + target_ulong cfg_val = 0; + uint8_t val = 0; + + for (i = 0; i < sizeof(target_ulong); i++) { + val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i); + cfg_val |= (val << (i * 8)); + } + + PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx, + env->mhartid, reg_index, cfg_val); + + return cfg_val; +} + + +/* + * Handle a write to a pmpaddr CSP + */ +void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, + target_ulong val) +{ + PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx, + env->mhartid, addr_index, val); + + if (addr_index < MAX_RISCV_PMPS) { + if (!pmp_is_locked(env, addr_index)) { + env->pmp_state.pmp[addr_index].addr_reg = val; + pmp_update_rule(env, addr_index); + } else { + PMP_DEBUG("ignoring write - locked"); + } + } else { + PMP_DEBUG("ignoring write - out of bounds"); + } +} + + +/* + * Handle a read from a pmpaddr CSP + */ +target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) +{ + PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx, + env->mhartid, addr_index, + env->pmp_state.pmp[addr_index].addr_reg); + if (addr_index < MAX_RISCV_PMPS) { + return env->pmp_state.pmp[addr_index].addr_reg; + } else { + PMP_DEBUG("ignoring read - out of bounds"); + return 0; + } +} + +#endif diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h new file mode 100644 index 0000000000..e3953c885f --- /dev/null +++ b/target/riscv/pmp.h @@ -0,0 +1,64 @@ +/* + * QEMU RISC-V PMP (Physical Memory Protection) + * + * Author: Daire McNamara, daire.mcnamara@emdalo.com + * Ivan Griffin, ivan.griffin@emdalo.com + * + * This provides a RISC-V Physical Memory Protection interface + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _RISCV_PMP_H_ +#define _RISCV_PMP_H_ + +typedef enum { + PMP_READ = 1 << 0, + PMP_WRITE = 1 << 1, + PMP_EXEC = 1 << 2, + PMP_LOCK = 1 << 7 +} pmp_priv_t; + +typedef enum { + PMP_AMATCH_OFF, /* Null (off) */ + PMP_AMATCH_TOR, /* Top of Range */ + PMP_AMATCH_NA4, /* Naturally aligned four-byte region */ + PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */ +} pmp_am_t; + +typedef struct { + target_ulong addr_reg; + uint8_t cfg_reg; +} pmp_entry_t; + +typedef struct { + target_ulong sa; + target_ulong ea; +} pmp_addr_t; + +typedef struct { + pmp_entry_t pmp[MAX_RISCV_PMPS]; + pmp_addr_t addr[MAX_RISCV_PMPS]; + uint32_t num_rules; +} pmp_table_t; + +void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, + target_ulong val); +target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index); +void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, + target_ulong val); +target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); +bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, + target_ulong size, pmp_priv_t priv); + +#endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c new file mode 100644 index 0000000000..808eab7f50 --- /dev/null +++ b/target/riscv/translate.c @@ -0,0 +1,1978 @@ +/* + * RISC-V emulation for qemu: main translation routines. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "tcg-op.h" +#include "disas/disas.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +#include "exec/log.h" + +#include "instmap.h" + +/* global register indices */ +static TCGv cpu_gpr[32], cpu_pc; +static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ +static TCGv load_res; +static TCGv load_val; + +#include "exec/gen-icount.h" + +typedef struct DisasContext { + struct TranslationBlock *tb; + target_ulong pc; + target_ulong next_pc; + uint32_t opcode; + uint32_t flags; + uint32_t mem_idx; + int singlestep_enabled; + int bstate; + /* Remember the rounding mode encoded in the previous fp instruction, + which we have already installed into env->fp_status. Or -1 for + no previous fp instruction. Note that we exit the TB when writing + to any system register, which includes CSR_FRM, so we do not have + to reset this known value. */ + int frm; +} DisasContext; + +enum { + BS_NONE = 0, /* When seen outside of translation while loop, indicates + need to exit tb due to end of page. */ + BS_STOP = 1, /* Need to exit tb for syscall, sret, etc. */ + BS_BRANCH = 2, /* Need to exit tb for branch, jal, etc. */ +}; + +/* convert riscv funct3 to qemu memop for load/store */ +static const int tcg_memop_lookup[8] = { + [0 ... 7] = -1, + [0] = MO_SB, + [1] = MO_TESW, + [2] = MO_TESL, + [4] = MO_UB, + [5] = MO_TEUW, +#ifdef TARGET_RISCV64 + [3] = MO_TEQ, + [6] = MO_TEUL, +#endif +}; + +#ifdef TARGET_RISCV64 +#define CASE_OP_32_64(X) case X: case glue(X, W) +#else +#define CASE_OP_32_64(X) case X +#endif + +static void generate_exception(DisasContext *ctx, int excp) +{ + tcg_gen_movi_tl(cpu_pc, ctx->pc); + TCGv_i32 helper_tmp = tcg_const_i32(excp); + gen_helper_raise_exception(cpu_env, helper_tmp); + tcg_temp_free_i32(helper_tmp); + ctx->bstate = BS_BRANCH; +} + +static void generate_exception_mbadaddr(DisasContext *ctx, int excp) +{ + tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); + TCGv_i32 helper_tmp = tcg_const_i32(excp); + gen_helper_raise_exception(cpu_env, helper_tmp); + tcg_temp_free_i32(helper_tmp); + ctx->bstate = BS_BRANCH; +} + +static void gen_exception_debug(void) +{ + TCGv_i32 helper_tmp = tcg_const_i32(EXCP_DEBUG); + gen_helper_raise_exception(cpu_env, helper_tmp); + tcg_temp_free_i32(helper_tmp); +} + +static void gen_exception_illegal(DisasContext *ctx) +{ + generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); +} + +static void gen_exception_inst_addr_mis(DisasContext *ctx) +{ + generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS); +} + +static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) +{ + if (unlikely(ctx->singlestep_enabled)) { + return false; + } + +#ifndef CONFIG_USER_ONLY + return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); +#else + return true; +#endif +} + +static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +{ + if (use_goto_tb(ctx, dest)) { + /* chaining is only allowed when the jump is to the same page */ + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + } else { + tcg_gen_movi_tl(cpu_pc, dest); + if (ctx->singlestep_enabled) { + gen_exception_debug(); + } else { + tcg_gen_exit_tb(0); + } + } +} + +/* Wrapper for getting reg values - need to check of reg is zero since + * cpu_gpr[0] is not actually allocated + */ +static inline void gen_get_gpr(TCGv t, int reg_num) +{ + if (reg_num == 0) { + tcg_gen_movi_tl(t, 0); + } else { + tcg_gen_mov_tl(t, cpu_gpr[reg_num]); + } +} + +/* Wrapper for setting reg values - need to check of reg is zero since + * cpu_gpr[0] is not actually allocated. this is more for safety purposes, + * since we usually avoid calling the OP_TYPE_gen function if we see a write to + * $zero + */ +static inline void gen_set_gpr(int reg_num_dst, TCGv t) +{ + if (reg_num_dst != 0) { + tcg_gen_mov_tl(cpu_gpr[reg_num_dst], t); + } +} + +static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv rl = tcg_temp_new(); + TCGv rh = tcg_temp_new(); + + tcg_gen_mulu2_tl(rl, rh, arg1, arg2); + /* fix up for one negative */ + tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1); + tcg_gen_and_tl(rl, rl, arg2); + tcg_gen_sub_tl(ret, rh, rl); + + tcg_temp_free(rl); + tcg_temp_free(rh); +} + +static void gen_fsgnj(DisasContext *ctx, uint32_t rd, uint32_t rs1, + uint32_t rs2, int rm, uint64_t min) +{ + switch (rm) { + case 0: /* fsgnj */ + if (rs1 == rs2) { /* FMOV */ + tcg_gen_mov_i64(cpu_fpr[rd], cpu_fpr[rs1]); + } else { + tcg_gen_deposit_i64(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1], + 0, min == INT32_MIN ? 31 : 63); + } + break; + case 1: /* fsgnjn */ + if (rs1 == rs2) { /* FNEG */ + tcg_gen_xori_i64(cpu_fpr[rd], cpu_fpr[rs1], min); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, cpu_fpr[rs2]); + tcg_gen_deposit_i64(cpu_fpr[rd], t0, cpu_fpr[rs1], + 0, min == INT32_MIN ? 31 : 63); + tcg_temp_free_i64(t0); + } + break; + case 2: /* fsgnjx */ + if (rs1 == rs2) { /* FABS */ + tcg_gen_andi_i64(cpu_fpr[rd], cpu_fpr[rs1], ~min); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(t0, cpu_fpr[rs2], min); + tcg_gen_xor_i64(cpu_fpr[rd], cpu_fpr[rs1], t0); + tcg_temp_free_i64(t0); + } + break; + default: + gen_exception_illegal(ctx); + } +} + +static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, + int rs2) +{ + TCGv source1, source2, cond1, cond2, zeroreg, resultopt1; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + gen_get_gpr(source1, rs1); + gen_get_gpr(source2, rs2); + + switch (opc) { + CASE_OP_32_64(OPC_RISC_ADD): + tcg_gen_add_tl(source1, source1, source2); + break; + CASE_OP_32_64(OPC_RISC_SUB): + tcg_gen_sub_tl(source1, source1, source2); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_SLLW: + tcg_gen_andi_tl(source2, source2, 0x1F); + tcg_gen_shl_tl(source1, source1, source2); + break; +#endif + case OPC_RISC_SLL: + tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); + tcg_gen_shl_tl(source1, source1, source2); + break; + case OPC_RISC_SLT: + tcg_gen_setcond_tl(TCG_COND_LT, source1, source1, source2); + break; + case OPC_RISC_SLTU: + tcg_gen_setcond_tl(TCG_COND_LTU, source1, source1, source2); + break; + case OPC_RISC_XOR: + tcg_gen_xor_tl(source1, source1, source2); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_SRLW: + /* clear upper 32 */ + tcg_gen_ext32u_tl(source1, source1); + tcg_gen_andi_tl(source2, source2, 0x1F); + tcg_gen_shr_tl(source1, source1, source2); + break; +#endif + case OPC_RISC_SRL: + tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); + tcg_gen_shr_tl(source1, source1, source2); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_SRAW: + /* first, trick to get it to act like working on 32 bits (get rid of + upper 32, sign extend to fill space) */ + tcg_gen_ext32s_tl(source1, source1); + tcg_gen_andi_tl(source2, source2, 0x1F); + tcg_gen_sar_tl(source1, source1, source2); + break; + /* fall through to SRA */ +#endif + case OPC_RISC_SRA: + tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); + tcg_gen_sar_tl(source1, source1, source2); + break; + case OPC_RISC_OR: + tcg_gen_or_tl(source1, source1, source2); + break; + case OPC_RISC_AND: + tcg_gen_and_tl(source1, source1, source2); + break; + CASE_OP_32_64(OPC_RISC_MUL): + tcg_gen_mul_tl(source1, source1, source2); + break; + case OPC_RISC_MULH: + tcg_gen_muls2_tl(source2, source1, source1, source2); + break; + case OPC_RISC_MULHSU: + gen_mulhsu(source1, source1, source2); + break; + case OPC_RISC_MULHU: + tcg_gen_mulu2_tl(source2, source1, source1, source2); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_DIVW: + tcg_gen_ext32s_tl(source1, source1); + tcg_gen_ext32s_tl(source2, source2); + /* fall through to DIV */ +#endif + case OPC_RISC_DIV: + /* Handle by altering args to tcg_gen_div to produce req'd results: + * For overflow: want source1 in source1 and 1 in source2 + * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */ + cond1 = tcg_temp_new(); + cond2 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); + + tcg_gen_movi_tl(resultopt1, (target_ulong)-1); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L)); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, + ((target_ulong)1) << (TARGET_LONG_BITS - 1)); + tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */ + /* if div by zero, set source1 to -1, otherwise don't change */ + tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1, + resultopt1); + /* if overflow or div by zero, set source2 to 1, else don't change */ + tcg_gen_or_tl(cond1, cond1, cond2); + tcg_gen_movi_tl(resultopt1, (target_ulong)1); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, + resultopt1); + tcg_gen_div_tl(source1, source1, source2); + + tcg_temp_free(cond1); + tcg_temp_free(cond2); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_DIVUW: + tcg_gen_ext32u_tl(source1, source1); + tcg_gen_ext32u_tl(source2, source2); + /* fall through to DIVU */ +#endif + case OPC_RISC_DIVU: + cond1 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); + + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); + tcg_gen_movi_tl(resultopt1, (target_ulong)-1); + tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1, + resultopt1); + tcg_gen_movi_tl(resultopt1, (target_ulong)1); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, + resultopt1); + tcg_gen_divu_tl(source1, source1, source2); + + tcg_temp_free(cond1); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_REMW: + tcg_gen_ext32s_tl(source1, source1); + tcg_gen_ext32s_tl(source2, source2); + /* fall through to REM */ +#endif + case OPC_RISC_REM: + cond1 = tcg_temp_new(); + cond2 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); + + tcg_gen_movi_tl(resultopt1, 1L); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, + (target_ulong)1 << (TARGET_LONG_BITS - 1)); + tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */ + /* if overflow or div by zero, set source2 to 1, else don't change */ + tcg_gen_or_tl(cond2, cond1, cond2); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2, + resultopt1); + tcg_gen_rem_tl(resultopt1, source1, source2); + /* if div by zero, just return the original dividend */ + tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1, + source1); + + tcg_temp_free(cond1); + tcg_temp_free(cond2); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_REMUW: + tcg_gen_ext32u_tl(source1, source1); + tcg_gen_ext32u_tl(source2, source2); + /* fall through to REMU */ +#endif + case OPC_RISC_REMU: + cond1 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); + + tcg_gen_movi_tl(resultopt1, (target_ulong)1); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, + resultopt1); + tcg_gen_remu_tl(resultopt1, source1, source2); + /* if div by zero, just return the original dividend */ + tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1, + source1); + + tcg_temp_free(cond1); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); + break; + default: + gen_exception_illegal(ctx); + return; + } + + if (opc & 0x8) { /* sign extend for W instructions */ + tcg_gen_ext32s_tl(source1, source1); + } + + gen_set_gpr(rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); +} + +static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd, + int rs1, target_long imm) +{ + TCGv source1 = tcg_temp_new(); + int shift_len = TARGET_LONG_BITS; + int shift_a; + + gen_get_gpr(source1, rs1); + + switch (opc) { + case OPC_RISC_ADDI: +#if defined(TARGET_RISCV64) + case OPC_RISC_ADDIW: +#endif + tcg_gen_addi_tl(source1, source1, imm); + break; + case OPC_RISC_SLTI: + tcg_gen_setcondi_tl(TCG_COND_LT, source1, source1, imm); + break; + case OPC_RISC_SLTIU: + tcg_gen_setcondi_tl(TCG_COND_LTU, source1, source1, imm); + break; + case OPC_RISC_XORI: + tcg_gen_xori_tl(source1, source1, imm); + break; + case OPC_RISC_ORI: + tcg_gen_ori_tl(source1, source1, imm); + break; + case OPC_RISC_ANDI: + tcg_gen_andi_tl(source1, source1, imm); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_SLLIW: + shift_len = 32; + /* FALLTHRU */ +#endif + case OPC_RISC_SLLI: + if (imm >= shift_len) { + goto do_illegal; + } + tcg_gen_shli_tl(source1, source1, imm); + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_SHIFT_RIGHT_IW: + shift_len = 32; + /* FALLTHRU */ +#endif + case OPC_RISC_SHIFT_RIGHT_I: + /* differentiate on IMM */ + shift_a = imm & 0x400; + imm &= 0x3ff; + if (imm >= shift_len) { + goto do_illegal; + } + if (imm != 0) { + if (shift_a) { + /* SRAI[W] */ + tcg_gen_sextract_tl(source1, source1, imm, shift_len - imm); + } else { + /* SRLI[W] */ + tcg_gen_extract_tl(source1, source1, imm, shift_len - imm); + } + /* No further sign-extension needed for W instructions. */ + opc &= ~0x8; + } + break; + default: + do_illegal: + gen_exception_illegal(ctx); + return; + } + + if (opc & 0x8) { /* sign-extend for W instructions */ + tcg_gen_ext32s_tl(source1, source1); + } + + gen_set_gpr(rd, source1); + tcg_temp_free(source1); +} + +static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, + target_ulong imm) +{ + target_ulong next_pc; + + /* check misaligned: */ + next_pc = ctx->pc + imm; + if (!riscv_has_ext(env, RVC)) { + if ((next_pc & 0x3) != 0) { + gen_exception_inst_addr_mis(ctx); + return; + } + } + if (rd != 0) { + tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc); + } + + gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */ + ctx->bstate = BS_BRANCH; +} + +static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, + int rd, int rs1, target_long imm) +{ + /* no chaining with JALR */ + TCGLabel *misaligned = NULL; + TCGv t0 = tcg_temp_new(); + + switch (opc) { + case OPC_RISC_JALR: + gen_get_gpr(cpu_pc, rs1); + tcg_gen_addi_tl(cpu_pc, cpu_pc, imm); + tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); + + if (!riscv_has_ext(env, RVC)) { + misaligned = gen_new_label(); + tcg_gen_andi_tl(t0, cpu_pc, 0x2); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); + } + + if (rd != 0) { + tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc); + } + tcg_gen_exit_tb(0); + + if (misaligned) { + gen_set_label(misaligned); + gen_exception_inst_addr_mis(ctx); + } + ctx->bstate = BS_BRANCH; + break; + + default: + gen_exception_illegal(ctx); + break; + } + tcg_temp_free(t0); +} + +static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, + int rs1, int rs2, target_long bimm) +{ + TCGLabel *l = gen_new_label(); + TCGv source1, source2; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + gen_get_gpr(source1, rs1); + gen_get_gpr(source2, rs2); + + switch (opc) { + case OPC_RISC_BEQ: + tcg_gen_brcond_tl(TCG_COND_EQ, source1, source2, l); + break; + case OPC_RISC_BNE: + tcg_gen_brcond_tl(TCG_COND_NE, source1, source2, l); + break; + case OPC_RISC_BLT: + tcg_gen_brcond_tl(TCG_COND_LT, source1, source2, l); + break; + case OPC_RISC_BGE: + tcg_gen_brcond_tl(TCG_COND_GE, source1, source2, l); + break; + case OPC_RISC_BLTU: + tcg_gen_brcond_tl(TCG_COND_LTU, source1, source2, l); + break; + case OPC_RISC_BGEU: + tcg_gen_brcond_tl(TCG_COND_GEU, source1, source2, l); + break; + default: + gen_exception_illegal(ctx); + return; + } + tcg_temp_free(source1); + tcg_temp_free(source2); + + gen_goto_tb(ctx, 1, ctx->next_pc); + gen_set_label(l); /* branch taken */ + if (!riscv_has_ext(env, RVC) && ((ctx->pc + bimm) & 0x3)) { + /* misaligned */ + gen_exception_inst_addr_mis(ctx); + } else { + gen_goto_tb(ctx, 0, ctx->pc + bimm); + } + ctx->bstate = BS_BRANCH; +} + +static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, + target_long imm) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + gen_get_gpr(t0, rs1); + tcg_gen_addi_tl(t0, t0, imm); + int memop = tcg_memop_lookup[(opc >> 12) & 0x7]; + + if (memop < 0) { + gen_exception_illegal(ctx); + return; + } + + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, memop); + gen_set_gpr(rd, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2, + target_long imm) +{ + TCGv t0 = tcg_temp_new(); + TCGv dat = tcg_temp_new(); + gen_get_gpr(t0, rs1); + tcg_gen_addi_tl(t0, t0, imm); + gen_get_gpr(dat, rs2); + int memop = tcg_memop_lookup[(opc >> 12) & 0x7]; + + if (memop < 0) { + gen_exception_illegal(ctx); + return; + } + + tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx, memop); + tcg_temp_free(t0); + tcg_temp_free(dat); +} + +static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, + int rs1, target_long imm) +{ + TCGv t0; + + if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + gen_exception_illegal(ctx); + return; + } + + t0 = tcg_temp_new(); + gen_get_gpr(t0, rs1); + tcg_gen_addi_tl(t0, t0, imm); + + switch (opc) { + case OPC_RISC_FLW: + tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL); + /* RISC-V requires NaN-boxing of narrower width floating point values */ + tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL); + break; + case OPC_RISC_FLD: + tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ); + break; + default: + gen_exception_illegal(ctx); + break; + } + tcg_temp_free(t0); +} + +static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, + int rs2, target_long imm) +{ + TCGv t0; + + if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + gen_exception_illegal(ctx); + return; + } + + t0 = tcg_temp_new(); + gen_get_gpr(t0, rs1); + tcg_gen_addi_tl(t0, t0, imm); + + switch (opc) { + case OPC_RISC_FSW: + tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL); + break; + case OPC_RISC_FSD: + tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ); + break; + default: + gen_exception_illegal(ctx); + break; + } + + tcg_temp_free(t0); +} + +static void gen_atomic(DisasContext *ctx, uint32_t opc, + int rd, int rs1, int rs2) +{ + TCGv src1, src2, dat; + TCGLabel *l1, *l2; + TCGMemOp mop; + TCGCond cond; + bool aq, rl; + + /* Extract the size of the atomic operation. */ + switch (extract32(opc, 12, 3)) { + case 2: /* 32-bit */ + mop = MO_ALIGN | MO_TESL; + break; +#if defined(TARGET_RISCV64) + case 3: /* 64-bit */ + mop = MO_ALIGN | MO_TEQ; + break; +#endif + default: + gen_exception_illegal(ctx); + return; + } + rl = extract32(opc, 25, 1); + aq = extract32(opc, 26, 1); + + src1 = tcg_temp_new(); + src2 = tcg_temp_new(); + + switch (MASK_OP_ATOMIC_NO_AQ_RL_SZ(opc)) { + case OPC_RISC_LR: + /* Put addr in load_res, data in load_val. */ + gen_get_gpr(src1, rs1); + if (rl) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop); + if (aq) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + tcg_gen_mov_tl(load_res, src1); + gen_set_gpr(rd, load_val); + break; + + case OPC_RISC_SC: + l1 = gen_new_label(); + l2 = gen_new_label(); + dat = tcg_temp_new(); + + gen_get_gpr(src1, rs1); + tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); + + gen_get_gpr(src2, rs2); + /* Note that the TCG atomic primitives are SC, + so we can ignore AQ/RL along this path. */ + tcg_gen_atomic_cmpxchg_tl(src1, load_res, load_val, src2, + ctx->mem_idx, mop); + tcg_gen_setcond_tl(TCG_COND_NE, dat, src1, load_val); + gen_set_gpr(rd, dat); + tcg_gen_br(l2); + + gen_set_label(l1); + /* Address comparion failure. However, we still need to + provide the memory barrier implied by AQ/RL. */ + tcg_gen_mb(TCG_MO_ALL + aq * TCG_BAR_LDAQ + rl * TCG_BAR_STRL); + tcg_gen_movi_tl(dat, 1); + gen_set_gpr(rd, dat); + + gen_set_label(l2); + tcg_temp_free(dat); + break; + + case OPC_RISC_AMOSWAP: + /* Note that the TCG atomic primitives are SC, + so we can ignore AQ/RL along this path. */ + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_xchg_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOADD: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_add_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOXOR: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_xor_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOAND: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_and_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOOR: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_or_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + + case OPC_RISC_AMOMIN: + cond = TCG_COND_LT; + goto do_minmax; + case OPC_RISC_AMOMAX: + cond = TCG_COND_GT; + goto do_minmax; + case OPC_RISC_AMOMINU: + cond = TCG_COND_LTU; + goto do_minmax; + case OPC_RISC_AMOMAXU: + cond = TCG_COND_GTU; + goto do_minmax; + do_minmax: + /* Handle the RL barrier. The AQ barrier is handled along the + parallel path by the SC atomic cmpxchg. On the serial path, + of course, barriers do not matter. */ + if (rl) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + if (tb_cflags(ctx->tb) & CF_PARALLEL) { + l1 = gen_new_label(); + gen_set_label(l1); + } else { + l1 = NULL; + } + + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + if ((mop & MO_SSIZE) == MO_SL) { + /* Sign-extend the register comparison input. */ + tcg_gen_ext32s_tl(src2, src2); + } + dat = tcg_temp_local_new(); + tcg_gen_qemu_ld_tl(dat, src1, ctx->mem_idx, mop); + tcg_gen_movcond_tl(cond, src2, dat, src2, dat, src2); + + if (tb_cflags(ctx->tb) & CF_PARALLEL) { + /* Parallel context. Make this operation atomic by verifying + that the memory didn't change while we computed the result. */ + tcg_gen_atomic_cmpxchg_tl(src2, src1, dat, src2, ctx->mem_idx, mop); + + /* If the cmpxchg failed, retry. */ + /* ??? There is an assumption here that this will eventually + succeed, such that we don't live-lock. This is not unlike + a similar loop that the compiler would generate for e.g. + __atomic_fetch_and_xor, so don't worry about it. */ + tcg_gen_brcond_tl(TCG_COND_NE, dat, src2, l1); + } else { + /* Serial context. Directly store the result. */ + tcg_gen_qemu_st_tl(src2, src1, ctx->mem_idx, mop); + } + gen_set_gpr(rd, dat); + tcg_temp_free(dat); + break; + + default: + gen_exception_illegal(ctx); + break; + } + + tcg_temp_free(src1); + tcg_temp_free(src2); +} + +static void gen_set_rm(DisasContext *ctx, int rm) +{ + TCGv_i32 t0; + + if (ctx->frm == rm) { + return; + } + ctx->frm = rm; + t0 = tcg_const_i32(rm); + gen_helper_set_rounding_mode(cpu_env, t0); + tcg_temp_free_i32(t0); +} + +static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + switch (opc) { + case OPC_RISC_FMADD_S: + gen_set_rm(ctx, rm); + gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + case OPC_RISC_FMADD_D: + gen_set_rm(ctx, rm); + gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + default: + gen_exception_illegal(ctx); + break; + } +} + +static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + switch (opc) { + case OPC_RISC_FMSUB_S: + gen_set_rm(ctx, rm); + gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + case OPC_RISC_FMSUB_D: + gen_set_rm(ctx, rm); + gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + default: + gen_exception_illegal(ctx); + break; + } +} + +static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + switch (opc) { + case OPC_RISC_FNMSUB_S: + gen_set_rm(ctx, rm); + gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + case OPC_RISC_FNMSUB_D: + gen_set_rm(ctx, rm); + gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + default: + gen_exception_illegal(ctx); + break; + } +} + +static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + switch (opc) { + case OPC_RISC_FNMADD_S: + gen_set_rm(ctx, rm); + gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + case OPC_RISC_FNMADD_D: + gen_set_rm(ctx, rm); + gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2], cpu_fpr[rs3]); + break; + default: + gen_exception_illegal(ctx); + break; + } +} + +static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rm) +{ + TCGv t0 = NULL; + + if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + goto do_illegal; + } + + switch (opc) { + case OPC_RISC_FADD_S: + gen_set_rm(ctx, rm); + gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FSUB_S: + gen_set_rm(ctx, rm); + gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FMUL_S: + gen_set_rm(ctx, rm); + gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FDIV_S: + gen_set_rm(ctx, rm); + gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FSQRT_S: + gen_set_rm(ctx, rm); + gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); + break; + case OPC_RISC_FSGNJ_S: + gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN); + break; + + case OPC_RISC_FMIN_S: + /* also handles: OPC_RISC_FMAX_S */ + switch (rm) { + case 0x0: + gen_helper_fmin_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case 0x1: + gen_helper_fmax_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + default: + goto do_illegal; + } + break; + + case OPC_RISC_FEQ_S: + /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */ + t0 = tcg_temp_new(); + switch (rm) { + case 0x0: + gen_helper_fle_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case 0x1: + gen_helper_flt_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case 0x2: + gen_helper_feq_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + default: + goto do_illegal; + } + gen_set_gpr(rd, t0); + tcg_temp_free(t0); + break; + + case OPC_RISC_FCVT_W_S: + /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */ + t0 = tcg_temp_new(); + switch (rs2) { + case 0: /* FCVT_W_S */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_w_s(t0, cpu_env, cpu_fpr[rs1]); + break; + case 1: /* FCVT_WU_S */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_wu_s(t0, cpu_env, cpu_fpr[rs1]); + break; +#if defined(TARGET_RISCV64) + case 2: /* FCVT_L_S */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_l_s(t0, cpu_env, cpu_fpr[rs1]); + break; + case 3: /* FCVT_LU_S */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_lu_s(t0, cpu_env, cpu_fpr[rs1]); + break; +#endif + default: + goto do_illegal; + } + gen_set_gpr(rd, t0); + tcg_temp_free(t0); + break; + + case OPC_RISC_FCVT_S_W: + /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */ + t0 = tcg_temp_new(); + gen_get_gpr(t0, rs1); + switch (rs2) { + case 0: /* FCVT_S_W */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_s_w(cpu_fpr[rd], cpu_env, t0); + break; + case 1: /* FCVT_S_WU */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_s_wu(cpu_fpr[rd], cpu_env, t0); + break; +#if defined(TARGET_RISCV64) + case 2: /* FCVT_S_L */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_s_l(cpu_fpr[rd], cpu_env, t0); + break; + case 3: /* FCVT_S_LU */ + gen_set_rm(ctx, rm); + gen_helper_fcvt_s_lu(cpu_fpr[rd], cpu_env, t0); + break; +#endif + default: + goto do_illegal; + } + tcg_temp_free(t0); + break; + + case OPC_RISC_FMV_X_S: + /* also OPC_RISC_FCLASS_S */ + t0 = tcg_temp_new(); + switch (rm) { + case 0: /* FMV */ +#if defined(TARGET_RISCV64) + tcg_gen_ext32s_tl(t0, cpu_fpr[rs1]); +#else + tcg_gen_extrl_i64_i32(t0, cpu_fpr[rs1]); +#endif + break; + case 1: + gen_helper_fclass_s(t0, cpu_fpr[rs1]); + break; + default: + goto do_illegal; + } + gen_set_gpr(rd, t0); + tcg_temp_free(t0); + break; + + case OPC_RISC_FMV_S_X: + t0 = tcg_temp_new(); + gen_get_gpr(t0, rs1); +#if defined(TARGET_RISCV64) + tcg_gen_mov_i64(cpu_fpr[rd], t0); +#else + tcg_gen_extu_i32_i64(cpu_fpr[rd], t0); +#endif + tcg_temp_free(t0); + break; + + /* double */ + case OPC_RISC_FADD_D: + gen_set_rm(ctx, rm); + gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FSUB_D: + gen_set_rm(ctx, rm); + gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FMUL_D: + gen_set_rm(ctx, rm); + gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FDIV_D: + gen_set_rm(ctx, rm); + gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case OPC_RISC_FSQRT_D: + gen_set_rm(ctx, rm); + gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); + break; + case OPC_RISC_FSGNJ_D: + gen_fsgnj(ctx, rd, rs1, rs2, rm, INT64_MIN); + break; + + case OPC_RISC_FMIN_D: + /* also OPC_RISC_FMAX_D */ + switch (rm) { + case 0: + gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case 1: + gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + default: + goto do_illegal; + } + break; + + case OPC_RISC_FCVT_S_D: + switch (rs2) { + case 1: + gen_set_rm(ctx, rm); + gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); + break; + default: + goto do_illegal; + } + break; + + case OPC_RISC_FCVT_D_S: + switch (rs2) { + case 0: + gen_set_rm(ctx, rm); + gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); + break; + default: + goto do_illegal; + } + break; + + case OPC_RISC_FEQ_D: + /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */ + t0 = tcg_temp_new(); + switch (rm) { + case 0: + gen_helper_fle_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case 1: + gen_helper_flt_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + case 2: + gen_helper_feq_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + break; + default: + goto do_illegal; + } + gen_set_gpr(rd, t0); + tcg_temp_free(t0); + break; + + case OPC_RISC_FCVT_W_D: + /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */ + t0 = tcg_temp_new(); + switch (rs2) { + case 0: + gen_set_rm(ctx, rm); + gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[rs1]); + break; + case 1: + gen_set_rm(ctx, rm); + gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[rs1]); + break; +#if defined(TARGET_RISCV64) + case 2: + gen_set_rm(ctx, rm); + gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[rs1]); + break; + case 3: + gen_set_rm(ctx, rm); + gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[rs1]); + break; +#endif + default: + goto do_illegal; + } + gen_set_gpr(rd, t0); + tcg_temp_free(t0); + break; + + case OPC_RISC_FCVT_D_W: + /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */ + t0 = tcg_temp_new(); + gen_get_gpr(t0, rs1); + switch (rs2) { + case 0: + gen_set_rm(ctx, rm); + gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, t0); + break; + case 1: + gen_set_rm(ctx, rm); + gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, t0); + break; +#if defined(TARGET_RISCV64) + case 2: + gen_set_rm(ctx, rm); + gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, t0); + break; + case 3: + gen_set_rm(ctx, rm); + gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, t0); + break; +#endif + default: + goto do_illegal; + } + tcg_temp_free(t0); + break; + +#if defined(TARGET_RISCV64) + case OPC_RISC_FMV_X_D: + /* also OPC_RISC_FCLASS_D */ + switch (rm) { + case 0: /* FMV */ + gen_set_gpr(rd, cpu_fpr[rs1]); + break; + case 1: + t0 = tcg_temp_new(); + gen_helper_fclass_d(t0, cpu_fpr[rs1]); + gen_set_gpr(rd, t0); + tcg_temp_free(t0); + break; + default: + goto do_illegal; + } + break; + + case OPC_RISC_FMV_D_X: + t0 = tcg_temp_new(); + gen_get_gpr(t0, rs1); + tcg_gen_mov_tl(cpu_fpr[rd], t0); + tcg_temp_free(t0); + break; +#endif + + default: + do_illegal: + if (t0) { + tcg_temp_free(t0); + } + gen_exception_illegal(ctx); + break; + } +} + +static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, + int rd, int rs1, int csr) +{ + TCGv source1, csr_store, dest, rs1_pass, imm_rs1; + source1 = tcg_temp_new(); + csr_store = tcg_temp_new(); + dest = tcg_temp_new(); + rs1_pass = tcg_temp_new(); + imm_rs1 = tcg_temp_new(); + gen_get_gpr(source1, rs1); + tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(rs1_pass, rs1); + tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */ + +#ifndef CONFIG_USER_ONLY + /* Extract funct7 value and check whether it matches SFENCE.VMA */ + if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) { + /* sfence.vma */ + /* TODO: handle ASID specific fences */ + gen_helper_tlb_flush(cpu_env); + return; + } +#endif + + switch (opc) { + case OPC_RISC_ECALL: + switch (csr) { + case 0x0: /* ECALL */ + /* always generates U-level ECALL, fixed in do_interrupt handler */ + generate_exception(ctx, RISCV_EXCP_U_ECALL); + tcg_gen_exit_tb(0); /* no chaining */ + ctx->bstate = BS_BRANCH; + break; + case 0x1: /* EBREAK */ + generate_exception(ctx, RISCV_EXCP_BREAKPOINT); + tcg_gen_exit_tb(0); /* no chaining */ + ctx->bstate = BS_BRANCH; + break; +#ifndef CONFIG_USER_ONLY + case 0x002: /* URET */ + gen_exception_illegal(ctx); + break; + case 0x102: /* SRET */ + if (riscv_has_ext(env, RVS)) { + gen_helper_sret(cpu_pc, cpu_env, cpu_pc); + tcg_gen_exit_tb(0); /* no chaining */ + ctx->bstate = BS_BRANCH; + } else { + gen_exception_illegal(ctx); + } + break; + case 0x202: /* HRET */ + gen_exception_illegal(ctx); + break; + case 0x302: /* MRET */ + gen_helper_mret(cpu_pc, cpu_env, cpu_pc); + tcg_gen_exit_tb(0); /* no chaining */ + ctx->bstate = BS_BRANCH; + break; + case 0x7b2: /* DRET */ + gen_exception_illegal(ctx); + break; + case 0x105: /* WFI */ + tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + gen_helper_wfi(cpu_env); + break; + case 0x104: /* SFENCE.VM */ + gen_helper_tlb_flush(cpu_env); + break; +#endif + default: + gen_exception_illegal(ctx); + break; + } + break; + default: + tcg_gen_movi_tl(imm_rs1, rs1); + switch (opc) { + case OPC_RISC_CSRRW: + gen_helper_csrrw(dest, cpu_env, source1, csr_store); + break; + case OPC_RISC_CSRRS: + gen_helper_csrrs(dest, cpu_env, source1, csr_store, rs1_pass); + break; + case OPC_RISC_CSRRC: + gen_helper_csrrc(dest, cpu_env, source1, csr_store, rs1_pass); + break; + case OPC_RISC_CSRRWI: + gen_helper_csrrw(dest, cpu_env, imm_rs1, csr_store); + break; + case OPC_RISC_CSRRSI: + gen_helper_csrrs(dest, cpu_env, imm_rs1, csr_store, rs1_pass); + break; + case OPC_RISC_CSRRCI: + gen_helper_csrrc(dest, cpu_env, imm_rs1, csr_store, rs1_pass); + break; + default: + gen_exception_illegal(ctx); + return; + } + gen_set_gpr(rd, dest); + /* end tb since we may be changing priv modes, to get mmu_index right */ + tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + tcg_gen_exit_tb(0); /* no chaining */ + ctx->bstate = BS_BRANCH; + break; + } + tcg_temp_free(source1); + tcg_temp_free(csr_store); + tcg_temp_free(dest); + tcg_temp_free(rs1_pass); + tcg_temp_free(imm_rs1); +} + +static void decode_RV32_64C0(DisasContext *ctx) +{ + uint8_t funct3 = extract32(ctx->opcode, 13, 3); + uint8_t rd_rs2 = GET_C_RS2S(ctx->opcode); + uint8_t rs1s = GET_C_RS1S(ctx->opcode); + + switch (funct3) { + case 0: + /* illegal */ + if (ctx->opcode == 0) { + gen_exception_illegal(ctx); + } else { + /* C.ADDI4SPN -> addi rd', x2, zimm[9:2]*/ + gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs2, 2, + GET_C_ADDI4SPN_IMM(ctx->opcode)); + } + break; + case 1: + /* C.FLD -> fld rd', offset[7:3](rs1')*/ + gen_fp_load(ctx, OPC_RISC_FLD, rd_rs2, rs1s, + GET_C_LD_IMM(ctx->opcode)); + /* C.LQ(RV128) */ + break; + case 2: + /* C.LW -> lw rd', offset[6:2](rs1') */ + gen_load(ctx, OPC_RISC_LW, rd_rs2, rs1s, + GET_C_LW_IMM(ctx->opcode)); + break; + case 3: +#if defined(TARGET_RISCV64) + /* C.LD(RV64/128) -> ld rd', offset[7:3](rs1')*/ + gen_load(ctx, OPC_RISC_LD, rd_rs2, rs1s, + GET_C_LD_IMM(ctx->opcode)); +#else + /* C.FLW (RV32) -> flw rd', offset[6:2](rs1')*/ + gen_fp_load(ctx, OPC_RISC_FLW, rd_rs2, rs1s, + GET_C_LW_IMM(ctx->opcode)); +#endif + break; + case 4: + /* reserved */ + gen_exception_illegal(ctx); + break; + case 5: + /* C.FSD(RV32/64) -> fsd rs2', offset[7:3](rs1') */ + gen_fp_store(ctx, OPC_RISC_FSD, rs1s, rd_rs2, + GET_C_LD_IMM(ctx->opcode)); + /* C.SQ (RV128) */ + break; + case 6: + /* C.SW -> sw rs2', offset[6:2](rs1')*/ + gen_store(ctx, OPC_RISC_SW, rs1s, rd_rs2, + GET_C_LW_IMM(ctx->opcode)); + break; + case 7: +#if defined(TARGET_RISCV64) + /* C.SD (RV64/128) -> sd rs2', offset[7:3](rs1')*/ + gen_store(ctx, OPC_RISC_SD, rs1s, rd_rs2, + GET_C_LD_IMM(ctx->opcode)); +#else + /* C.FSW (RV32) -> fsw rs2', offset[6:2](rs1')*/ + gen_fp_store(ctx, OPC_RISC_FSW, rs1s, rd_rs2, + GET_C_LW_IMM(ctx->opcode)); +#endif + break; + } +} + +static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) +{ + uint8_t funct3 = extract32(ctx->opcode, 13, 3); + uint8_t rd_rs1 = GET_C_RS1(ctx->opcode); + uint8_t rs1s, rs2s; + uint8_t funct2; + + switch (funct3) { + case 0: + /* C.ADDI -> addi rd, rd, nzimm[5:0] */ + gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, rd_rs1, + GET_C_IMM(ctx->opcode)); + break; + case 1: +#if defined(TARGET_RISCV64) + /* C.ADDIW (RV64/128) -> addiw rd, rd, imm[5:0]*/ + gen_arith_imm(ctx, OPC_RISC_ADDIW, rd_rs1, rd_rs1, + GET_C_IMM(ctx->opcode)); +#else + /* C.JAL(RV32) -> jal x1, offset[11:1] */ + gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode)); +#endif + break; + case 2: + /* C.LI -> addi rd, x0, imm[5:0]*/ + gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, 0, GET_C_IMM(ctx->opcode)); + break; + case 3: + if (rd_rs1 == 2) { + /* C.ADDI16SP -> addi x2, x2, nzimm[9:4]*/ + gen_arith_imm(ctx, OPC_RISC_ADDI, 2, 2, + GET_C_ADDI16SP_IMM(ctx->opcode)); + } else if (rd_rs1 != 0) { + /* C.LUI (rs1/rd =/= {0,2}) -> lui rd, nzimm[17:12]*/ + tcg_gen_movi_tl(cpu_gpr[rd_rs1], + GET_C_IMM(ctx->opcode) << 12); + } + break; + case 4: + funct2 = extract32(ctx->opcode, 10, 2); + rs1s = GET_C_RS1S(ctx->opcode); + switch (funct2) { + case 0: /* C.SRLI(RV32) -> srli rd', rd', shamt[5:0] */ + gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s, + GET_C_ZIMM(ctx->opcode)); + /* C.SRLI64(RV128) */ + break; + case 1: + /* C.SRAI -> srai rd', rd', shamt[5:0]*/ + gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s, + GET_C_ZIMM(ctx->opcode) | 0x400); + /* C.SRAI64(RV128) */ + break; + case 2: + /* C.ANDI -> andi rd', rd', imm[5:0]*/ + gen_arith_imm(ctx, OPC_RISC_ANDI, rs1s, rs1s, + GET_C_IMM(ctx->opcode)); + break; + case 3: + funct2 = extract32(ctx->opcode, 5, 2); + rs2s = GET_C_RS2S(ctx->opcode); + switch (funct2) { + case 0: + /* C.SUB -> sub rd', rd', rs2' */ + if (extract32(ctx->opcode, 12, 1) == 0) { + gen_arith(ctx, OPC_RISC_SUB, rs1s, rs1s, rs2s); + } +#if defined(TARGET_RISCV64) + else { + gen_arith(ctx, OPC_RISC_SUBW, rs1s, rs1s, rs2s); + } +#endif + break; + case 1: + /* C.XOR -> xor rs1', rs1', rs2' */ + if (extract32(ctx->opcode, 12, 1) == 0) { + gen_arith(ctx, OPC_RISC_XOR, rs1s, rs1s, rs2s); + } +#if defined(TARGET_RISCV64) + else { + /* C.ADDW (RV64/128) */ + gen_arith(ctx, OPC_RISC_ADDW, rs1s, rs1s, rs2s); + } +#endif + break; + case 2: + /* C.OR -> or rs1', rs1', rs2' */ + gen_arith(ctx, OPC_RISC_OR, rs1s, rs1s, rs2s); + break; + case 3: + /* C.AND -> and rs1', rs1', rs2' */ + gen_arith(ctx, OPC_RISC_AND, rs1s, rs1s, rs2s); + break; + } + break; + } + break; + case 5: + /* C.J -> jal x0, offset[11:1]*/ + gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode)); + break; + case 6: + /* C.BEQZ -> beq rs1', x0, offset[8:1]*/ + rs1s = GET_C_RS1S(ctx->opcode); + gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); + break; + case 7: + /* C.BNEZ -> bne rs1', x0, offset[8:1]*/ + rs1s = GET_C_RS1S(ctx->opcode); + gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); + break; + } +} + +static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) +{ + uint8_t rd, rs2; + uint8_t funct3 = extract32(ctx->opcode, 13, 3); + + + rd = GET_RD(ctx->opcode); + + switch (funct3) { + case 0: /* C.SLLI -> slli rd, rd, shamt[5:0] + C.SLLI64 -> */ + gen_arith_imm(ctx, OPC_RISC_SLLI, rd, rd, GET_C_ZIMM(ctx->opcode)); + break; + case 1: /* C.FLDSP(RV32/64DC) -> fld rd, offset[8:3](x2) */ + gen_fp_load(ctx, OPC_RISC_FLD, rd, 2, GET_C_LDSP_IMM(ctx->opcode)); + break; + case 2: /* C.LWSP -> lw rd, offset[7:2](x2) */ + gen_load(ctx, OPC_RISC_LW, rd, 2, GET_C_LWSP_IMM(ctx->opcode)); + break; + case 3: +#if defined(TARGET_RISCV64) + /* C.LDSP(RVC64) -> ld rd, offset[8:3](x2) */ + gen_load(ctx, OPC_RISC_LD, rd, 2, GET_C_LDSP_IMM(ctx->opcode)); +#else + /* C.FLWSP(RV32FC) -> flw rd, offset[7:2](x2) */ + gen_fp_load(ctx, OPC_RISC_FLW, rd, 2, GET_C_LWSP_IMM(ctx->opcode)); +#endif + break; + case 4: + rs2 = GET_C_RS2(ctx->opcode); + + if (extract32(ctx->opcode, 12, 1) == 0) { + if (rs2 == 0) { + /* C.JR -> jalr x0, rs1, 0*/ + gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0); + } else { + /* C.MV -> add rd, x0, rs2 */ + gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2); + } + } else { + if (rd == 0) { + /* C.EBREAK -> ebreak*/ + gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1); + } else { + if (rs2 == 0) { + /* C.JALR -> jalr x1, rs1, 0*/ + gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0); + } else { + /* C.ADD -> add rd, rd, rs2 */ + gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2); + } + } + } + break; + case 5: + /* C.FSDSP -> fsd rs2, offset[8:3](x2)*/ + gen_fp_store(ctx, OPC_RISC_FSD, 2, GET_C_RS2(ctx->opcode), + GET_C_SDSP_IMM(ctx->opcode)); + /* C.SQSP */ + break; + case 6: /* C.SWSP -> sw rs2, offset[7:2](x2)*/ + gen_store(ctx, OPC_RISC_SW, 2, GET_C_RS2(ctx->opcode), + GET_C_SWSP_IMM(ctx->opcode)); + break; + case 7: +#if defined(TARGET_RISCV64) + /* C.SDSP(Rv64/128) -> sd rs2, offset[8:3](x2)*/ + gen_store(ctx, OPC_RISC_SD, 2, GET_C_RS2(ctx->opcode), + GET_C_SDSP_IMM(ctx->opcode)); +#else + /* C.FSWSP(RV32) -> fsw rs2, offset[7:2](x2) */ + gen_fp_store(ctx, OPC_RISC_FSW, 2, GET_C_RS2(ctx->opcode), + GET_C_SWSP_IMM(ctx->opcode)); +#endif + break; + } +} + +static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) +{ + uint8_t op = extract32(ctx->opcode, 0, 2); + + switch (op) { + case 0: + decode_RV32_64C0(ctx); + break; + case 1: + decode_RV32_64C1(env, ctx); + break; + case 2: + decode_RV32_64C2(env, ctx); + break; + } +} + +static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) +{ + int rs1; + int rs2; + int rd; + uint32_t op; + target_long imm; + + /* We do not do misaligned address check here: the address should never be + * misaligned at this point. Instructions that set PC must do the check, + * since epc must be the address of the instruction that caused us to + * perform the misaligned instruction fetch */ + + op = MASK_OP_MAJOR(ctx->opcode); + rs1 = GET_RS1(ctx->opcode); + rs2 = GET_RS2(ctx->opcode); + rd = GET_RD(ctx->opcode); + imm = GET_IMM(ctx->opcode); + + switch (op) { + case OPC_RISC_LUI: + if (rd == 0) { + break; /* NOP */ + } + tcg_gen_movi_tl(cpu_gpr[rd], sextract64(ctx->opcode, 12, 20) << 12); + break; + case OPC_RISC_AUIPC: + if (rd == 0) { + break; /* NOP */ + } + tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) + + ctx->pc); + break; + case OPC_RISC_JAL: + imm = GET_JAL_IMM(ctx->opcode); + gen_jal(env, ctx, rd, imm); + break; + case OPC_RISC_JALR: + gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); + break; + case OPC_RISC_BRANCH: + gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, + GET_B_IMM(ctx->opcode)); + break; + case OPC_RISC_LOAD: + gen_load(ctx, MASK_OP_LOAD(ctx->opcode), rd, rs1, imm); + break; + case OPC_RISC_STORE: + gen_store(ctx, MASK_OP_STORE(ctx->opcode), rs1, rs2, + GET_STORE_IMM(ctx->opcode)); + break; + case OPC_RISC_ARITH_IMM: +#if defined(TARGET_RISCV64) + case OPC_RISC_ARITH_IMM_W: +#endif + if (rd == 0) { + break; /* NOP */ + } + gen_arith_imm(ctx, MASK_OP_ARITH_IMM(ctx->opcode), rd, rs1, imm); + break; + case OPC_RISC_ARITH: +#if defined(TARGET_RISCV64) + case OPC_RISC_ARITH_W: +#endif + if (rd == 0) { + break; /* NOP */ + } + gen_arith(ctx, MASK_OP_ARITH(ctx->opcode), rd, rs1, rs2); + break; + case OPC_RISC_FP_LOAD: + gen_fp_load(ctx, MASK_OP_FP_LOAD(ctx->opcode), rd, rs1, imm); + break; + case OPC_RISC_FP_STORE: + gen_fp_store(ctx, MASK_OP_FP_STORE(ctx->opcode), rs1, rs2, + GET_STORE_IMM(ctx->opcode)); + break; + case OPC_RISC_ATOMIC: + gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2); + break; + case OPC_RISC_FMADD: + gen_fp_fmadd(ctx, MASK_OP_FP_FMADD(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FMSUB: + gen_fp_fmsub(ctx, MASK_OP_FP_FMSUB(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FNMSUB: + gen_fp_fnmsub(ctx, MASK_OP_FP_FNMSUB(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FNMADD: + gen_fp_fnmadd(ctx, MASK_OP_FP_FNMADD(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FP_ARITH: + gen_fp_arith(ctx, MASK_OP_FP_ARITH(ctx->opcode), rd, rs1, rs2, + GET_RM(ctx->opcode)); + break; + case OPC_RISC_FENCE: +#ifndef CONFIG_USER_ONLY + if (ctx->opcode & 0x1000) { + /* FENCE_I is a no-op in QEMU, + * however we need to end the translation block */ + tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + tcg_gen_exit_tb(0); + ctx->bstate = BS_BRANCH; + } else { + /* FENCE is a full memory barrier. */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + } +#endif + break; + case OPC_RISC_SYSTEM: + gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, + (ctx->opcode & 0xFFF00000) >> 20); + break; + default: + gen_exception_illegal(ctx); + break; + } +} + +static void decode_opc(CPURISCVState *env, DisasContext *ctx) +{ + /* check for compressed insn */ + if (extract32(ctx->opcode, 0, 2) != 3) { + if (!riscv_has_ext(env, RVC)) { + gen_exception_illegal(ctx); + } else { + ctx->next_pc = ctx->pc + 2; + decode_RV32_64C(env, ctx); + } + } else { + ctx->next_pc = ctx->pc + 4; + decode_RV32_64G(env, ctx); + } +} + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + CPURISCVState *env = cs->env_ptr; + DisasContext ctx; + target_ulong pc_start; + target_ulong next_page_start; + int num_insns; + int max_insns; + pc_start = tb->pc; + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + ctx.pc = pc_start; + + /* once we have GDB, the rest of the translate.c implementation should be + ready for singlestep */ + ctx.singlestep_enabled = cs->singlestep_enabled; + + ctx.tb = tb; + ctx.bstate = BS_NONE; + ctx.flags = tb->flags; + ctx.mem_idx = tb->flags & TB_FLAGS_MMU_MASK; + ctx.frm = -1; /* unknown rounding mode */ + + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + gen_tb_start(tb); + + while (ctx.bstate == BS_NONE) { + tcg_gen_insn_start(ctx.pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + tcg_gen_movi_tl(cpu_pc, ctx.pc); + ctx.bstate = BS_BRANCH; + gen_exception_debug(); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.pc += 4; + goto done_generating; + } + + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + ctx.opcode = cpu_ldl_code(env, ctx.pc); + decode_opc(env, &ctx); + ctx.pc = ctx.next_pc; + + if (cs->singlestep_enabled) { + break; + } + if (ctx.pc >= next_page_start) { + break; + } + if (tcg_op_buf_full()) { + break; + } + if (num_insns >= max_insns) { + break; + } + if (singlestep) { + break; + } + + } + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + switch (ctx.bstate) { + case BS_STOP: + gen_goto_tb(&ctx, 0, ctx.pc); + break; + case BS_NONE: /* handle end of page - DO NOT CHAIN. See gen_goto_tb. */ + tcg_gen_movi_tl(cpu_pc, ctx.pc); + if (cs->singlestep_enabled) { + gen_exception_debug(); + } else { + tcg_gen_exit_tb(0); + } + break; + case BS_BRANCH: /* ops using BS_BRANCH generate own exit seq */ + default: + break; + } +done_generating: + gen_tb_end(tb, num_insns); + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) + && qemu_log_in_addr_range(pc_start)) { + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + log_target_disas(cs, pc_start, ctx.pc - pc_start); + qemu_log("\n"); + } +#endif +} + +void riscv_translate_init(void) +{ + int i; + + /* cpu_gpr[0] is a placeholder for the zero register. Do not use it. */ + /* Use the gen_set_gpr and gen_get_gpr helper functions when accessing */ + /* registers, unless you specifically block reads/writes to reg 0 */ + cpu_gpr[0] = NULL; + + for (i = 1; i < 32; i++) { + cpu_gpr[i] = tcg_global_mem_new(cpu_env, + offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]); + } + + for (i = 0; i < 32; i++) { + cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]); + } + + cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc"); + load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res), + "load_res"); + load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val), + "load_val"); +} |