aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS11
-rw-r--r--arch_init.c2
-rwxr-xr-xconfigure13
-rw-r--r--cpus.c6
-rw-r--r--default-configs/riscv32-linux-user.mak1
-rw-r--r--default-configs/riscv32-softmmu.mak4
-rw-r--r--default-configs/riscv64-linux-user.mak1
-rw-r--r--default-configs/riscv64-softmmu.mak4
-rw-r--r--disas.c2
-rw-r--r--disas/Makefile.objs1
-rw-r--r--disas/riscv.c3048
-rw-r--r--fpu/softfloat-specialize.h7
-rw-r--r--hw/core/loader.c18
-rw-r--r--hw/riscv/Makefile.objs11
-rw-r--r--hw/riscv/riscv_hart.c89
-rw-r--r--hw/riscv/riscv_htif.c258
-rw-r--r--hw/riscv/sifive_clint.c254
-rw-r--r--hw/riscv/sifive_e.c234
-rw-r--r--hw/riscv/sifive_plic.c505
-rw-r--r--hw/riscv/sifive_prci.c89
-rw-r--r--hw/riscv/sifive_test.c93
-rw-r--r--hw/riscv/sifive_u.c339
-rw-r--r--hw/riscv/sifive_uart.c176
-rw-r--r--hw/riscv/spike.c376
-rw-r--r--hw/riscv/virt.c420
-rw-r--r--include/disas/bfd.h2
-rw-r--r--include/elf.h2
-rw-r--r--include/hw/elf_ops.h34
-rw-r--r--include/hw/loader.h17
-rw-r--r--include/hw/riscv/riscv_hart.h39
-rw-r--r--include/hw/riscv/riscv_htif.h61
-rw-r--r--include/hw/riscv/sifive_clint.h50
-rw-r--r--include/hw/riscv/sifive_e.h79
-rw-r--r--include/hw/riscv/sifive_plic.h85
-rw-r--r--include/hw/riscv/sifive_prci.h37
-rw-r--r--include/hw/riscv/sifive_test.h42
-rw-r--r--include/hw/riscv/sifive_u.h69
-rw-r--r--include/hw/riscv/sifive_uart.h71
-rw-r--r--include/hw/riscv/spike.h53
-rw-r--r--include/hw/riscv/virt.h74
-rw-r--r--include/sysemu/arch_init.h1
-rw-r--r--linux-user/elfload.c22
-rw-r--r--linux-user/main.c99
-rw-r--r--linux-user/riscv/syscall_nr.h287
-rw-r--r--linux-user/riscv/target_cpu.h18
-rw-r--r--linux-user/riscv/target_elf.h14
-rw-r--r--linux-user/riscv/target_signal.h23
-rw-r--r--linux-user/riscv/target_structs.h46
-rw-r--r--linux-user/riscv/target_syscall.h56
-rw-r--r--linux-user/riscv/termbits.h222
-rw-r--r--linux-user/signal.c203
-rw-r--r--linux-user/syscall.c2
-rw-r--r--linux-user/syscall_defs.h13
-rw-r--r--qapi/misc.json17
-rwxr-xr-xscripts/qemu-binfmt-conf.sh13
-rw-r--r--target/riscv/Makefile.objs1
-rw-r--r--target/riscv/cpu.c432
-rw-r--r--target/riscv/cpu.h296
-rw-r--r--target/riscv/cpu_bits.h411
-rw-r--r--target/riscv/cpu_user.h13
-rw-r--r--target/riscv/fpu_helper.c373
-rw-r--r--target/riscv/gdbstub.c62
-rw-r--r--target/riscv/helper.c503
-rw-r--r--target/riscv/helper.h78
-rw-r--r--target/riscv/instmap.h364
-rw-r--r--target/riscv/op_helper.c669
-rw-r--r--target/riscv/pmp.c380
-rw-r--r--target/riscv/pmp.h64
-rw-r--r--target/riscv/translate.c1978
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)
diff --git a/configure b/configure
index 6f3921c02a..f74e1f3b7c 100755
--- a/configure
+++ b/configure
@@ -6797,6 +6797,16 @@ case "$target_name" in
echo "TARGET_ABI32=y" >> $config_target_mak
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
;;
+ riscv32)
+ TARGET_BASE_ARCH=riscv
+ TARGET_ABI_DIR=riscv
+ mttcg=yes
+ ;;
+ riscv64)
+ TARGET_BASE_ARCH=riscv
+ TARGET_ABI_DIR=riscv
+ mttcg=yes
+ ;;
sh4|sh4eb)
TARGET_ARCH=sh4
bflt="yes"
@@ -6966,6 +6976,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
ppc*)
disas_config "PPC"
;;
+ riscv)
+ disas_config "RISCV"
+ ;;
s390*)
disas_config "S390"
;;
diff --git a/cpus.c b/cpus.c
index 9bcff7d63c..865cffd025 100644
--- a/cpus.c
+++ b/cpus.c
@@ -2081,6 +2081,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
#elif defined(TARGET_SPARC)
SPARCCPU *sparc_cpu = SPARC_CPU(cpu);
CPUSPARCState *env = &sparc_cpu->env;
+#elif defined(TARGET_RISCV)
+ RISCVCPU *riscv_cpu = RISCV_CPU(cpu);
+ CPURISCVState *env = &riscv_cpu->env;
#elif defined(TARGET_MIPS)
MIPSCPU *mips_cpu = MIPS_CPU(cpu);
CPUMIPSState *env = &mips_cpu->env;
@@ -2120,6 +2123,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
#elif defined(TARGET_S390X)
info->value->arch = CPU_INFO_ARCH_S390;
info->value->u.s390.cpu_state = env->cpu_state;
+#elif defined(TARGET_RISCV)
+ info->value->arch = CPU_INFO_ARCH_RISCV;
+ info->value->u.riscv.pc = env->pc;
#else
info->value->arch = CPU_INFO_ARCH_OTHER;
#endif
diff --git a/default-configs/riscv32-linux-user.mak b/default-configs/riscv32-linux-user.mak
new file mode 100644
index 0000000000..865b362f5a
--- /dev/null
+++ b/default-configs/riscv32-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for riscv-linux-user
diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak
new file mode 100644
index 0000000000..f9e742120c
--- /dev/null
+++ b/default-configs/riscv32-softmmu.mak
@@ -0,0 +1,4 @@
+# Default configuration for riscv-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_VIRTIO=y
diff --git a/default-configs/riscv64-linux-user.mak b/default-configs/riscv64-linux-user.mak
new file mode 100644
index 0000000000..865b362f5a
--- /dev/null
+++ b/default-configs/riscv64-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for riscv-linux-user
diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak
new file mode 100644
index 0000000000..f9e742120c
--- /dev/null
+++ b/default-configs/riscv64-softmmu.mak
@@ -0,0 +1,4 @@
+# Default configuration for riscv-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_VIRTIO=y
diff --git a/disas.c b/disas.c
index d4ad1089ef..5325b7e6be 100644
--- a/disas.c
+++ b/disas.c
@@ -522,6 +522,8 @@ void disas(FILE *out, void *code, unsigned long size)
# ifdef _ARCH_PPC64
s.info.cap_mode = CS_MODE_64;
# endif
+#elif defined(__riscv__)
+ print_insn = print_insn_riscv;
#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
print_insn = print_insn_arm_a64;
s.info.cap_arch = CS_ARCH_ARM64;
diff --git a/disas/Makefile.objs b/disas/Makefile.objs
index 53556f8f5a..213be2fab2 100644
--- a/disas/Makefile.objs
+++ b/disas/Makefile.objs
@@ -17,6 +17,7 @@ common-obj-$(CONFIG_MIPS_DIS) += mips.o
common-obj-$(CONFIG_NIOS2_DIS) += nios2.o
common-obj-$(CONFIG_MOXIE_DIS) += moxie.o
common-obj-$(CONFIG_PPC_DIS) += ppc.o
+common-obj-$(CONFIG_RISCV_DIS) += riscv.o
common-obj-$(CONFIG_S390_DIS) += s390.o
common-obj-$(CONFIG_SH4_DIS) += sh4.o
common-obj-$(CONFIG_SPARC_DIS) += sparc.o
diff --git a/disas/riscv.c b/disas/riscv.c
new file mode 100644
index 0000000000..3c17501120
--- /dev/null
+++ b/disas/riscv.c
@@ -0,0 +1,3048 @@
+/*
+ * QEMU RISC-V Disassembler
+ *
+ * Copyright (c) 2016-2017 Michael Clark <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");
+}