Discussion:
[PATCH v1 01/21] RISC-V Maintainers
(too old to reply)
Michael Clark
2018-01-03 00:44:05 UTC
Permalink
Add Michael Clark, Sagar Karandikar and Bastian Koppelmann as
RISC-V Maintainers.

Signed-off-by: Michael Clark <***@sifive.com>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 73a5555..09a1314 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -209,6 +209,16 @@ F: hw/ppc/
F: include/hw/ppc/
F: disas/ppc.c

+RISC-V
+M: Michael Clark <***@sifive.com>
+M: Sagar Karandikar <***@eecs.berkeley.edu>
+M: Bastian Koppelmann <***@mail.uni-paderborn.de>
+S: Maintained
+F: target/riscv/
+F: hw/riscv/
+F: include/hw/riscv/
+F: disas/riscv.c
+
S390
M: Richard Henderson <***@twiddle.net>
M: Alexander Graf <***@suse.de>
--
2.7.0
Michael Clark
2018-01-03 00:44:06 UTC
Permalink
Define RISC-V ELF machine EM_RISCV 243

Signed-off-by: Michael Clark <***@sifive.com>
---
include/elf.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/include/elf.h b/include/elf.h
index e8a515c..8e457fc 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -112,6 +112,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.
--
2.7.0
Richard Henderson
2018-01-03 05:30:42 UTC
Permalink
Post by Michael Clark
Define RISC-V ELF machine EM_RISCV 243
---
include/elf.h | 2 ++
1 file changed, 2 insertions(+)
Reviewed-by: Richard Henderson <***@linaro.org>


r~
Alistair Francis
2018-01-09 21:33:41 UTC
Permalink
Post by Michael Clark
Define RISC-V ELF machine EM_RISCV 243
Reviewed-by: Alistair Francis <***@xilinx.com>

Alistair
Post by Michael Clark
---
include/elf.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/elf.h b/include/elf.h
index e8a515c..8e457fc 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -112,6 +112,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.
--
2.7.0
Michael Clark
2018-01-03 00:44:07 UTC
Permalink
Add CPU state header, CPU definitions and initialization routines

Signed-off-by: Michael Clark <***@sifive.com>
---
target/riscv/cpu.c | 338 +++++++++++++++++++++++++++++++++++++++
target/riscv/cpu.h | 363 ++++++++++++++++++++++++++++++++++++++++++
target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1112 insertions(+)
create mode 100644 target/riscv/cpu.c
create mode 100644 target/riscv/cpu.h
create mode 100644 target/riscv/cpu_bits.h

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
new file mode 100644
index 0000000..ee4c4c2
--- /dev/null
+++ b/target/riscv/cpu.c
@@ -0,0 +1,338 @@
+/*
+ * QEMU RISC-V CPU
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#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 */
+
+typedef struct RISCVCPUInfo {
+ const char *name;
+ void (*initfn)(Object *obj);
+} RISCVCPUInfo;
+
+#ifdef CONFIG_USER_ONLY
+static void riscv_any_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ env->misa = RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU;
+ env->misa_mask = env->misa;
+ env->user_ver = USER_VERSION_2_02_0;
+ env->priv_ver = PRIV_VERSION_1_10_0;
+}
+#else
+static void riscv_imafdcsu_priv1_9_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ env->misa = RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU;
+ env->misa_mask = env->misa;
+ env->user_ver = USER_VERSION_2_02_0;
+ env->priv_ver = PRIV_VERSION_1_09_1;
+}
+
+static void riscv_imafdcsu_priv1_10_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ env->misa = RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU;
+ env->misa_mask = env->misa;
+ env->user_ver = USER_VERSION_2_02_0;
+ env->priv_ver = PRIV_VERSION_1_10_0;
+}
+
+static void riscv_imacu_priv1_10_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ env->misa = RVXLEN | RVI | RVM | RVA | RVC | RVU;
+ env->misa_mask = env->misa;
+ env->user_ver = USER_VERSION_2_02_0;
+ env->priv_ver = PRIV_VERSION_1_10_0;
+}
+
+static void riscv_imac_priv1_10_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ env->misa = RVXLEN | RVI | RVM | RVA | RVC;
+ env->misa_mask = env->misa;
+ env->user_ver = USER_VERSION_2_02_0;
+ env->priv_ver = PRIV_VERSION_1_10_0;
+}
+#endif
+
+static const RISCVCPUInfo riscv_cpus[] = {
+#ifdef CONFIG_USER_ONLY
+ { TYPE_RISCV_CPU_ANY, riscv_any_cpu_init },
+#else
+ { TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_09, riscv_imafdcsu_priv1_9_cpu_init },
+ { TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10, riscv_imafdcsu_priv1_10_cpu_init },
+ { TYPE_RISCV_CPU_IMACU_PRIV_1_10, riscv_imacu_priv1_10_cpu_init },
+ { TYPE_RISCV_CPU_IMAC_PRIV_1_10, riscv_imac_priv1_10_cpu_init },
+#endif
+ { 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 inline void set_feature(CPURISCVState *env, int feature)
+{
+ env->features |= 1ULL << feature;
+}
+
+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;
+}
+
+#ifdef CONFIG_USER_ONLY
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return 0;
+}
+#else
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return cs->interrupt_request & CPU_INTERRUPT_HARD;
+}
+#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
+ tlb_flush(cs);
+ env->priv = PRV_M;
+ env->mtvec = DEFAULT_MTVEC;
+#endif
+ env->pc = DEFAULT_RSTVEC;
+ 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);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (env->misa & RVM) {
+ set_feature(env, RISCV_FEATURE_RVM);
+ }
+ if (env->misa & RVA) {
+ set_feature(env, RISCV_FEATURE_RVA);
+ }
+ if (env->misa & RVF) {
+ set_feature(env, RISCV_FEATURE_RVF);
+ }
+ if (env->misa & RVD) {
+ set_feature(env, RISCV_FEATURE_RVD);
+ }
+ if (env->misa & RVC) {
+ set_feature(env, RISCV_FEATURE_RVC);
+ }
+
+ 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_unassigned_access = riscv_cpu_unassigned_access;
+ 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 = g_strdup(info->name),
+ .parent = TYPE_RISCV_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = info->initfn,
+ };
+
+ type_register(&type_info);
+ g_free((void *)type_info.name);
+}
+
+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)
+{
+ size_t len = 5 + ctz32(cpu->env.misa);
+ char *isa_string = g_new(char, len);
+ isa_string[0] = '\0';
+#if defined(TARGET_RISCV32)
+ strncat(isa_string, "rv32", len);
+#elif defined(TARGET_RISCV64)
+ strncat(isa_string, "rv64", len);
+#endif
+ if (cpu->env.misa & RVI) {
+ strncat(isa_string, "i", len);
+ }
+ if (cpu->env.misa & RVM) {
+ strncat(isa_string, "m", len);
+ }
+ if (cpu->env.misa & RVA) {
+ strncat(isa_string, "a", len);
+ }
+ if (cpu->env.misa & RVF) {
+ strncat(isa_string, "f", len);
+ }
+ if (cpu->env.misa & RVD) {
+ strncat(isa_string, "d", len);
+ }
+ if (cpu->env.misa & RVC) {
+ strncat(isa_string, "c", len);
+ }
+ return isa_string;
+}
+
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ const RISCVCPUInfo *info = riscv_cpus;
+
+ while (info->name) {
+ (*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) {
+ 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 0000000..0480127
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,363 @@
+#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 TARGET_HAS_ICE 1
+#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 DEBUG_OP */
+/* #define RISCV_DEBUG_PRINT */
+
+#define TYPE_RISCV_CPU "riscv"
+#define TYPE_RISCV_CPU_ANY "riscv-any"
+#define TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_09 "riscv-imafdcsu-priv1.9"
+#define TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10 "riscv-imafdcsu-priv1.10"
+#define TYPE_RISCV_CPU_IMACU_PRIV_1_10 "riscv-imacu-priv1.10"
+#define TYPE_RISCV_CPU_IMAC_PRIV_1_10 "riscv-imac-priv1.10"
+
+#define RISCV_CPU_TYPE_PREFIX TYPE_RISCV_CPU "-"
+#define RISCV_CPU_TYPE_NAME(name) (RISCV_CPU_TYPE_PREFIX name)
+
+#if defined(TARGET_RISCV32)
+#define RVXLEN ((target_ulong)1 << (TARGET_LONG_BITS - 2))
+#elif defined(TARGET_RISCV64)
+#define RVXLEN ((target_ulong)2 << (TARGET_LONG_BITS - 2))
+#endif
+#define RV(x) (1L << (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')
+
+#define USER_VERSION_2_02_0 0x00020200
+#define PRIV_VERSION_1_09_1 0x00010901
+#define PRIV_VERSION_1_10_0 0x00011000
+
+/* 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 TRANSLATE_FAIL 1
+#define TRANSLATE_SUCCESS 0
+#define NB_MMU_MODES 4
+#define MMU_USER_IDX 3
+
+#define SSIP_IRQ (env->irq[0])
+#define STIP_IRQ (env->irq[1])
+#define MSIP_IRQ (env->irq[2])
+#define MTIP_IRQ (env->irq[3])
+#define HTIF_IRQ (env->irq[4])
+#define SEIP_IRQ (env->irq[5])
+#define MEIP_IRQ (env->irq[6])
+
+#define MAX_RISCV_IRQ (8)
+#define MAX_RISCV_PMPS (16)
+
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+typedef 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 frm;
+ target_ulong fstatus;
+ target_ulong fflags;
+
+ target_ulong badaddr;
+
+ uint32_t mucounteren;
+
+ target_ulong user_ver;
+ target_ulong priv_ver;
+ target_ulong misa_mask;
+ target_ulong misa;
+
+#ifdef CONFIG_USER_ONLY
+ uint32_t amoinsn;
+ target_long amoaddr;
+ target_long amotest;
+#else
+ target_ulong priv;
+
+ target_ulong mhartid;
+ target_ulong mstatus;
+ target_ulong mip;
+ 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 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;
+
+ /* Internal CPU feature flags. */
+ uint64_t features;
+
+ /* QEMU */
+ CPU_COMMON
+
+ /* Fields from here on are preserved across CPU reset. */
+ void *irq[8];
+ QEMUTimer *timer; /* Internal timer */
+} CPURISCVState;
+
+#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);
+}
+
+enum riscv_features {
+ RISCV_FEATURE_RVM,
+ RISCV_FEATURE_RVA,
+ RISCV_FEATURE_RVF,
+ RISCV_FEATURE_RVD,
+ RISCV_FEATURE_RVC,
+};
+
+static inline int riscv_feature(CPURISCVState *env, int feature)
+{
+ return (env->features & (1ULL << feature)) != 0;
+}
+
+#include "cpu_user.h"
+#include "cpu_bits.h"
+
+#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
+#define ENV_OFFSET offsetof(RISCVCPU, env)
+
+void riscv_cpu_do_interrupt(CPUState *cpu);
+void riscv_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
+ int flags);
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+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);
+void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, int mmu_idx,
+ uintptr_t retaddr);
+#if !defined(CONFIG_USER_ONLY)
+void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write,
+ bool is_exec, int unused, unsigned size);
+#endif
+
+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
+
+void set_privilege(CPURISCVState *env, target_ulong newpriv);
+unsigned int softfloat_flags_to_riscv(unsigned int flag);
+uint_fast16_t float32_classify(uint32_t a, float_status *status);
+uint_fast16_t float64_classify(uint64_t a, float_status *status);
+
+/*
+ * Compute mmu index
+ * Adapted from Spike's mmu_t::translate
+ */
+#ifdef CONFIG_USER_ONLY
+static inline int cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+ return 0;
+}
+#else
+static inline int cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+ target_ulong mode = env->priv;
+ if (!ifetch) {
+ if (get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ }
+ return mode;
+}
+#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 inline int cpu_riscv_hw_interrupts_pending(CPURISCVState *env)
+{
+ target_ulong pending_interrupts = 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) {
+ target_ulong counted = ctz64(enabled_interrupts); /* since non-zero */
+ if (counted == IRQ_X_HOST) {
+ /* we're handing it to the cpu now, so get rid of the qemu irq */
+ qemu_irq_lower(HTIF_IRQ);
+ } else if (counted == IRQ_M_TIMER) {
+ /* we're handing it to the cpu now, so get rid of the qemu irq */
+ qemu_irq_lower(MTIP_IRQ);
+ } else if (counted == IRQ_S_TIMER || counted == IRQ_H_TIMER) {
+ /* don't lower irq here */
+ }
+ return counted;
+ } else {
+ return EXCP_NONE; /* indicates no pending interrupt */
+ }
+}
+#endif
+
+#include "exec/cpu-all.h"
+
+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);
+
+/* hw/riscv/sifive_clint.c - supplies instret by approximating */
+uint64_t cpu_riscv_read_instret(CPURISCVState *env);
+uint64_t cpu_riscv_read_rtc(void);
+
+int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
+ int mmu_idx);
+#if !defined(CONFIG_USER_ONLY)
+hwaddr cpu_riscv_translate_address(CPURISCVState *env, target_ulong address,
+ int rw);
+#endif
+
+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;
+ *flags = 0; /* necessary to avoid compiler warning */
+}
+
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+ target_ulong csrno);
+target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
+
+void validate_csr(CPURISCVState *env, uint64_t which, uint64_t write);
+
+#endif /* RISCV_CPU_H */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
new file mode 100644
index 0000000..f9698a9
--- /dev/null
+++ b/target/riscv/cpu_bits.h
@@ -0,0 +1,411 @@
+/* Below taken from Spike's decode.h and encoding.h.
+ * Using these directly drastically simplifies updating to new versions of the
+ * RISC-V privileged specification */
+
+#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 FP_RD_NE 0
+#define FP_RD_0 1
+#define FP_RD_DN 2
+#define FP_RD_UP 3
+#define FP_RD_NMM 4
+
+#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)
+
+#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
+
+#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 0x0000000300000000
+#define MSTATUS64_SXL 0x0000000C00000000
+
+#define MSTATUS32_SD 0x80000000
+#define MSTATUS64_SD 0x8000000000000000
+
+#if defined(TARGET_RISCV32)
+#define MSTATUS_SD MSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define MSTATUS_SD MSTATUS64_SD
+#endif
+
+#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 0x8000000000000000
+
+#if defined(TARGET_RISCV32)
+#define SSTATUS_SD SSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define SSTATUS_SD SSTATUS64_SD
+#endif
+
+#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 */
+#define IRQ_X_HOST 13 /* non-standard */
+
+/* Default addresses */
+#define DEFAULT_RSTVEC 0x00001000
+#define DEFAULT_NMIVEC 0x00001004
+#define DEFAULT_MTVEC 0x00001010
+#define CONFIG_STRING_ADDR 0x0000100C
+#define EXT_IO_BASE 0x40000000
+#define DRAM_BASE 0x80000000
+
+/* RV32 satp field masks */
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7fc00000
+#define SATP32_PPN 0x003fffff
+
+/* RV64 satp field masks */
+#define SATP64_MODE 0xF000000000000000
+#define SATP64_ASID 0x0FFFF00000000000
+#define SATP64_PPN 0x00000FFFFFFFFFFF
+
+#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
+
+/* breakpoint control fields */
+#define BPCONTROL_X 0x00000001
+#define BPCONTROL_W 0x00000002
+#define BPCONTROL_R 0x00000004
+#define BPCONTROL_U 0x00000008
+#define BPCONTROL_S 0x00000010
+#define BPCONTROL_H 0x00000020
+#define BPCONTROL_M 0x00000040
+#define BPCONTROL_BPMATCH 0x00000780
+#define BPCONTROL_BPAMASKMAX 0x0F80000000000000
+#define BPCONTROL_TDRTYPE 0xF000000000000000
+
+/* 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)
+/* end Spike decode.h, encoding.h section */
--
2.7.0
Richard Henderson
2018-01-03 05:21:27 UTC
Permalink
Post by Michael Clark
+#ifdef CONFIG_USER_ONLY
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return 0;
+}
+#else
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return cs->interrupt_request & CPU_INTERRUPT_HARD;
+}
+#endif
There's no need to conditionalize this.
Post by Michael Clark
+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
+ tlb_flush(cs);
Flush is now generic. Remove it from here.
Post by Michael Clark
+static void riscv_cpu_realize(DeviceState *dev, Error **errp)
+{
+ CPUState *cs = CPU(dev);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (env->misa & RVM) {
+ set_feature(env, RISCV_FEATURE_RVM);
+ }
What's the point of replicating this information?
Post by Michael Clark
+static void cpu_register(const RISCVCPUInfo *info)
+{
+ TypeInfo type_info = {
+ .name = g_strdup(info->name),
+ .parent = TYPE_RISCV_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = info->initfn,
+ };
+
+ type_register(&type_info);
+ g_free((void *)type_info.name);
+}
I think type_register does its own strdup; you don't need to do your own.
Post by Michael Clark
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
new file mode 100644
index 0000000..0480127
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,363 @@
+#ifndef RISCV_CPU_H
Header comment and license?
Post by Michael Clark
+#define TARGET_HAS_ICE 1
What's this for?
Post by Michael Clark
+#define RV(x) (1L << (x - 'A'))
L is useless since the type of long is variable. Either U or ULL.
Post by Michael Clark
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+typedef struct CPURISCVState {
Duplicate typedef.
Post by Michael Clark
+ target_ulong gpr[32];
+ uint64_t fpr[32]; /* assume both F and D extensions */
+ target_ulong pc;
+ target_ulong load_res;
+
+ target_ulong frm;
+ target_ulong fstatus;
+ target_ulong fflags;
+
+ target_ulong badaddr;
+
+ uint32_t mucounteren;
+
+ target_ulong user_ver;
+ target_ulong priv_ver;
+ target_ulong misa_mask;
+ target_ulong misa;
+
+#ifdef CONFIG_USER_ONLY
+ uint32_t amoinsn;
+ target_long amoaddr;
+ target_long amotest;
+#else
+ target_ulong priv;
+
+ target_ulong mhartid;
+ target_ulong mstatus;
+ target_ulong mip;
+ 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 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;
+
+ /* Internal CPU feature flags. */
+ uint64_t features;
+
+ /* QEMU */
+ CPU_COMMON
+
+ /* Fields from here on are preserved across CPU reset. */
+ void *irq[8];
+ QEMUTimer *timer; /* Internal timer */
FWIW, other targets have moved this timer to RISCVCPU struct.
Post by Michael Clark
+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;
+ *flags = 0; /* necessary to avoid compiler warning */
Remove the comment -- the assignment is necessary full stop.
Post by Michael Clark
+#define MSTATUS64_UXL 0x0000000300000000
+#define MSTATUS64_SXL 0x0000000C00000000
64-bit constants must use ULL. Otherwise builds from a 32-bit host will fail.
There are lots more instances within this file.


r~
Michael Clark
2018-01-03 22:30:55 UTC
Permalink
On Wed, Jan 3, 2018 at 6:21 PM, Richard Henderson <
Post by Richard Henderson
Post by Michael Clark
+#ifdef CONFIG_USER_ONLY
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return 0;
+}
+#else
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return cs->interrupt_request & CPU_INTERRUPT_HARD;
+}
+#endif
There's no need to conditionalize this.
Got it. Will be in the next spin.
Post by Richard Henderson
Post by Michael Clark
+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
+ tlb_flush(cs);
Flush is now generic. Remove it from here.
OK.
Post by Richard Henderson
+static void riscv_cpu_realize(DeviceState *dev, Error **errp)
Post by Michael Clark
+{
+ CPUState *cs = CPU(dev);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (env->misa & RVM) {
+ set_feature(env, RISCV_FEATURE_RVM);
+ }
What's the point of replicating this information?
This is inherited code. I noticed this too. In this version they are
actually in sync with each other, which they weren't several weeks ago :-D

It may well be that the features flags pre-date the addition of the 'misa'
register in the privilege spec.

This will take a bit of re-work as a reasonable amount of code uses the
FEATURE flags vs misa.

Are you happy for this to be a pending work item? I don't like it either
and eventually want to fix, and already did some work to sync it with
'misa', but it's not a critical issue.
Post by Richard Henderson
+static void cpu_register(const RISCVCPUInfo *info)
Post by Michael Clark
+{
+ TypeInfo type_info = {
+ .name = g_strdup(info->name),
+ .parent = TYPE_RISCV_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = info->initfn,
+ };
+
+ type_register(&type_info);
+ g_free((void *)type_info.name);
+}
I think type_register does its own strdup; you don't need to do your own.
Got it.
Post by Richard Henderson
Post by Michael Clark
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
new file mode 100644
index 0000000..0480127
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,363 @@
+#ifndef RISCV_CPU_H
Header comment and license?
Post by Michael Clark
+#define TARGET_HAS_ICE 1
What's this for?
It's redundant. Inherited code. Looks like it came from nios2. Will remove.
Post by Richard Henderson
Post by Michael Clark
+#define RV(x) (1L << (x - 'A'))
L is useless since the type of long is variable. Either U or ULL.
Post by Michael Clark
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+typedef struct CPURISCVState {
Duplicate typedef.
Got it.
Post by Richard Henderson
Post by Michael Clark
+ target_ulong gpr[32];
+ uint64_t fpr[32]; /* assume both F and D extensions */
+ target_ulong pc;
+ target_ulong load_res;
+
+ target_ulong frm;
+ target_ulong fstatus;
+ target_ulong fflags;
+
+ target_ulong badaddr;
+
+ uint32_t mucounteren;
+
+ target_ulong user_ver;
+ target_ulong priv_ver;
+ target_ulong misa_mask;
+ target_ulong misa;
+
+#ifdef CONFIG_USER_ONLY
+ uint32_t amoinsn;
+ target_long amoaddr;
+ target_long amotest;
+#else
+ target_ulong priv;
+
+ target_ulong mhartid;
+ target_ulong mstatus;
+ target_ulong mip;
+ 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 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;
+
+ /* Internal CPU feature flags. */
+ uint64_t features;
+
+ /* QEMU */
+ CPU_COMMON
+
+ /* Fields from here on are preserved across CPU reset. */
+ void *irq[8];
+ QEMUTimer *timer; /* Internal timer */
FWIW, other targets have moved this timer to RISCVCPU struct.
I'll look into this. It seems like it should be a simple change and easy to
include in the next spin.
Post by Richard Henderson
+static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
Post by Michael Clark
+ target_ulong *cs_base, uint32_t
*flags)
Post by Michael Clark
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = 0; /* necessary to avoid compiler warning */
Remove the comment -- the assignment is necessary full stop.
Post by Michael Clark
+#define MSTATUS64_UXL 0x0000000300000000
+#define MSTATUS64_SXL 0x0000000C00000000
64-bit constants must use ULL. Otherwise builds from a 32-bit host will fail.
There are lots more instances within this file.
Got it. Will fix these in the next spin.

Thanks!
Michael Clark
2018-01-08 06:55:06 UTC
Permalink
FYI,

I've been working on the patch review comments in order of the patches and
am focusing on cleanup up the cpu and helpers.

OK [PATCH v1 0001/0021] RISC-V Maintainers
OK [PATCH v1 0002/0021] RISC-V ELF Machine Definition
INPROGRESS [PATCH v1 0003/0021] RISC-V CPU Core Definition
DONE [PATCH v1 0004/0021] RISC-V Disassembler
INPROGRESS [PATCH v1 0005/0021] RISC-V CPU Helpers

Changelog

- Remove redundant NULL terminators from disassembler register name arrays
- Change disassembler register name arrays to const
- Refine disassembler internal function names
- Update dates in disassembler copyright message
- Remove #ifdef CONFIG_USER_ONLY version of cpu_has_work
- Use ULL suffix on 64-bit constants so that builds on 32-bit hosts will
work
- Move riscv_cpu_mmu_index from cpu.h to helper.c
- Move riscv_cpu_hw_interrupts_pending from cpu.h to helper.c
- Remove redundant TARGET_HAS_ICE from cpu.h
- Use qemu_irq instead of void* for irq definition in cpu.h
- Remove duplicate typedef from struct CPURISCVState
- Remove redundant g_strdup from cpu_register
- Remove redundant tlb_flush from riscv_cpu_reset
- Remove redundant mode calculation from get_physical_address
- Remove redundant debug mode printf and dcsr comment
- Remove redundant clearing of MSB for bare physical addresses
- Use g_assert_not_reached for invalid mode in get_physical_address
- Use g_assert_not_reached for unreachable permission checks in
get_physical_address
- Use g_assert_not_reached for unreachable access type in
raise_mmu_exception
- Return misalign exception instead of aborting for misalined fetches
- Move exception defines from cpu.h to cpu_bits.h
- Remove redundant breakpoint control definitions from cpu_bits.h
- Implement riscv_cpu_unassigned_access exception handling
- Log and raise exceptions for unimplemented CSRs

Here is my development tree:

- https://github.com/michaeljclark/riscv-qemu/tree/qemu-devel

I've got individual commit entries for each change to make the deltas
easier to review and will archive this branch before squashing for the v2
spin:

- https://github.com/michaeljclark/riscv-qemu/commits/qemu-devel

Hopefully, I'll have a new spin relatively soon... I'm making good progress
on getting target/riscv clean enough for re-submission...

Michael.
Post by Michael Clark
On Wed, Jan 3, 2018 at 6:21 PM, Richard Henderson <
Post by Richard Henderson
Post by Michael Clark
+#ifdef CONFIG_USER_ONLY
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return 0;
+}
+#else
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+ return cs->interrupt_request & CPU_INTERRUPT_HARD;
+}
+#endif
There's no need to conditionalize this.
Got it. Will be in the next spin.
Post by Richard Henderson
Post by Michael Clark
+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
+ tlb_flush(cs);
Flush is now generic. Remove it from here.
OK.
Post by Richard Henderson
+static void riscv_cpu_realize(DeviceState *dev, Error **errp)
Post by Michael Clark
+{
+ CPUState *cs = CPU(dev);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (env->misa & RVM) {
+ set_feature(env, RISCV_FEATURE_RVM);
+ }
What's the point of replicating this information?
This is inherited code. I noticed this too. In this version they are
actually in sync with each other, which they weren't several weeks ago :-D
It may well be that the features flags pre-date the addition of the 'misa'
register in the privilege spec.
This will take a bit of re-work as a reasonable amount of code uses the
FEATURE flags vs misa.
Are you happy for this to be a pending work item? I don't like it either
and eventually want to fix, and already did some work to sync it with
'misa', but it's not a critical issue.
Post by Richard Henderson
+static void cpu_register(const RISCVCPUInfo *info)
Post by Michael Clark
+{
+ TypeInfo type_info = {
+ .name = g_strdup(info->name),
+ .parent = TYPE_RISCV_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = info->initfn,
+ };
+
+ type_register(&type_info);
+ g_free((void *)type_info.name);
+}
I think type_register does its own strdup; you don't need to do your own.
Got it.
Post by Richard Henderson
Post by Michael Clark
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
new file mode 100644
index 0000000..0480127
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,363 @@
+#ifndef RISCV_CPU_H
Header comment and license?
Post by Michael Clark
+#define TARGET_HAS_ICE 1
What's this for?
It's redundant. Inherited code. Looks like it came from nios2. Will remove.
Post by Richard Henderson
Post by Michael Clark
+#define RV(x) (1L << (x - 'A'))
L is useless since the type of long is variable. Either U or ULL.
Post by Michael Clark
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+typedef struct CPURISCVState {
Duplicate typedef.
Got it.
Post by Richard Henderson
Post by Michael Clark
+ target_ulong gpr[32];
+ uint64_t fpr[32]; /* assume both F and D extensions */
+ target_ulong pc;
+ target_ulong load_res;
+
+ target_ulong frm;
+ target_ulong fstatus;
+ target_ulong fflags;
+
+ target_ulong badaddr;
+
+ uint32_t mucounteren;
+
+ target_ulong user_ver;
+ target_ulong priv_ver;
+ target_ulong misa_mask;
+ target_ulong misa;
+
+#ifdef CONFIG_USER_ONLY
+ uint32_t amoinsn;
+ target_long amoaddr;
+ target_long amotest;
+#else
+ target_ulong priv;
+
+ target_ulong mhartid;
+ target_ulong mstatus;
+ target_ulong mip;
+ 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 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;
+
+ /* Internal CPU feature flags. */
+ uint64_t features;
+
+ /* QEMU */
+ CPU_COMMON
+
+ /* Fields from here on are preserved across CPU reset. */
+ void *irq[8];
+ QEMUTimer *timer; /* Internal timer */
FWIW, other targets have moved this timer to RISCVCPU struct.
I'll look into this. It seems like it should be a simple change and easy
to include in the next spin.
Post by Richard Henderson
+static inline void cpu_get_tb_cpu_state(CPURISCVState *env,
target_ulong *pc,
Post by Michael Clark
+ target_ulong *cs_base,
uint32_t *flags)
Post by Michael Clark
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = 0; /* necessary to avoid compiler warning */
Remove the comment -- the assignment is necessary full stop.
Post by Michael Clark
+#define MSTATUS64_UXL 0x0000000300000000
+#define MSTATUS64_SXL 0x0000000C00000000
64-bit constants must use ULL. Otherwise builds from a 32-bit host will fail.
There are lots more instances within this file.
Got it. Will fix these in the next spin.
Thanks!
Antony Pavlov
2018-01-04 06:47:59 UTC
Permalink
On Wed, 3 Jan 2018 13:44:07 +1300
Post by Michael Clark
Add CPU state header, CPU definitions and initialization routines
---
target/riscv/cpu.c | 338 +++++++++++++++++++++++++++++++++++++++
target/riscv/cpu.h | 363 ++++++++++++++++++++++++++++++++++++++++++
target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1112 insertions(+)
create mode 100644 target/riscv/cpu.c
create mode 100644 target/riscv/cpu.h
create mode 100644 target/riscv/cpu_bits.h
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
...
Post by Michael Clark
+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
+ tlb_flush(cs);
+ env->priv = PRV_M;
+ env->mtvec = DEFAULT_MTVEC;
+#endif
+ env->pc = DEFAULT_RSTVEC;
The RISC-V Privileged Architecture Manual v1.10 states that

The pc is set to an implementation-defined reset vector.

But hard-coded DEFAULT_RSTVEC leaves no chance for changing reset vector.

Can we add a mechanism for changing reset vector?

E.g. there is the "reset-hivecs" property in the ARM emulation code
so SoC-specific code can change reset vector.
Post by Michael Clark
+ 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);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (env->misa & RVM) {
+ set_feature(env, RISCV_FEATURE_RVM);
+ }
+ if (env->misa & RVA) {
+ set_feature(env, RISCV_FEATURE_RVA);
+ }
+ if (env->misa & RVF) {
+ set_feature(env, RISCV_FEATURE_RVF);
+ }
+ if (env->misa & RVD) {
+ set_feature(env, RISCV_FEATURE_RVD);
+ }
+ if (env->misa & RVC) {
+ set_feature(env, RISCV_FEATURE_RVC);
+ }
+
+ 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_unassigned_access = riscv_cpu_unassigned_access;
+ 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 = g_strdup(info->name),
+ .parent = TYPE_RISCV_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = info->initfn,
+ };
+
+ type_register(&type_info);
+ g_free((void *)type_info.name);
+}
+
+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)
+{
+ size_t len = 5 + ctz32(cpu->env.misa);
+ char *isa_string = g_new(char, len);
+ isa_string[0] = '\0';
+#if defined(TARGET_RISCV32)
+ strncat(isa_string, "rv32", len);
+#elif defined(TARGET_RISCV64)
+ strncat(isa_string, "rv64", len);
+#endif
+ if (cpu->env.misa & RVI) {
+ strncat(isa_string, "i", len);
+ }
+ if (cpu->env.misa & RVM) {
+ strncat(isa_string, "m", len);
+ }
+ if (cpu->env.misa & RVA) {
+ strncat(isa_string, "a", len);
+ }
+ if (cpu->env.misa & RVF) {
+ strncat(isa_string, "f", len);
+ }
+ if (cpu->env.misa & RVD) {
+ strncat(isa_string, "d", len);
+ }
+ if (cpu->env.misa & RVC) {
+ strncat(isa_string, "c", len);
+ }
+ return isa_string;
+}
+
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ const RISCVCPUInfo *info = riscv_cpus;
+
+ while (info->name) {
+ (*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) {
+ 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 0000000..0480127
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,363 @@
+#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 TARGET_HAS_ICE 1
+#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 DEBUG_OP */
+/* #define RISCV_DEBUG_PRINT */
+
+#define TYPE_RISCV_CPU "riscv"
+#define TYPE_RISCV_CPU_ANY "riscv-any"
+#define TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_09 "riscv-imafdcsu-priv1.9"
+#define TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10 "riscv-imafdcsu-priv1.10"
+#define TYPE_RISCV_CPU_IMACU_PRIV_1_10 "riscv-imacu-priv1.10"
+#define TYPE_RISCV_CPU_IMAC_PRIV_1_10 "riscv-imac-priv1.10"
+
+#define RISCV_CPU_TYPE_PREFIX TYPE_RISCV_CPU "-"
+#define RISCV_CPU_TYPE_NAME(name) (RISCV_CPU_TYPE_PREFIX name)
+
+#if defined(TARGET_RISCV32)
+#define RVXLEN ((target_ulong)1 << (TARGET_LONG_BITS - 2))
+#elif defined(TARGET_RISCV64)
+#define RVXLEN ((target_ulong)2 << (TARGET_LONG_BITS - 2))
+#endif
+#define RV(x) (1L << (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')
+
+#define USER_VERSION_2_02_0 0x00020200
+#define PRIV_VERSION_1_09_1 0x00010901
+#define PRIV_VERSION_1_10_0 0x00011000
+
+/* 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 TRANSLATE_FAIL 1
+#define TRANSLATE_SUCCESS 0
+#define NB_MMU_MODES 4
+#define MMU_USER_IDX 3
+
+#define SSIP_IRQ (env->irq[0])
+#define STIP_IRQ (env->irq[1])
+#define MSIP_IRQ (env->irq[2])
+#define MTIP_IRQ (env->irq[3])
+#define HTIF_IRQ (env->irq[4])
+#define SEIP_IRQ (env->irq[5])
+#define MEIP_IRQ (env->irq[6])
+
+#define MAX_RISCV_IRQ (8)
+#define MAX_RISCV_PMPS (16)
+
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+typedef 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 frm;
+ target_ulong fstatus;
+ target_ulong fflags;
+
+ target_ulong badaddr;
+
+ uint32_t mucounteren;
+
+ target_ulong user_ver;
+ target_ulong priv_ver;
+ target_ulong misa_mask;
+ target_ulong misa;
+
+#ifdef CONFIG_USER_ONLY
+ uint32_t amoinsn;
+ target_long amoaddr;
+ target_long amotest;
+#else
+ target_ulong priv;
+
+ target_ulong mhartid;
+ target_ulong mstatus;
+ target_ulong mip;
+ 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 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;
+
+ /* Internal CPU feature flags. */
+ uint64_t features;
+
+ /* QEMU */
+ CPU_COMMON
+
+ /* Fields from here on are preserved across CPU reset. */
+ void *irq[8];
+ QEMUTimer *timer; /* Internal timer */
+} CPURISCVState;
+
+#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)
+
+/**
+ *
+ * A RISCV CPU model.
+ */
+typedef struct RISCVCPUClass {
+ /*< private >*/
+ CPUClass parent_class;
+ /*< public >*/
+ DeviceRealize parent_realize;
+ void (*parent_reset)(CPUState *cpu);
+} RISCVCPUClass;
+
+/**
+ *
+ * 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);
+}
+
+enum riscv_features {
+ RISCV_FEATURE_RVM,
+ RISCV_FEATURE_RVA,
+ RISCV_FEATURE_RVF,
+ RISCV_FEATURE_RVD,
+ RISCV_FEATURE_RVC,
+};
+
+static inline int riscv_feature(CPURISCVState *env, int feature)
+{
+ return (env->features & (1ULL << feature)) != 0;
+}
+
+#include "cpu_user.h"
+#include "cpu_bits.h"
+
+#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
+#define ENV_OFFSET offsetof(RISCVCPU, env)
+
+void riscv_cpu_do_interrupt(CPUState *cpu);
+void riscv_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
+ int flags);
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+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);
+void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, int mmu_idx,
+ uintptr_t retaddr);
+#if !defined(CONFIG_USER_ONLY)
+void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write,
+ bool is_exec, int unused, unsigned size);
+#endif
+
+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
+
+void set_privilege(CPURISCVState *env, target_ulong newpriv);
+unsigned int softfloat_flags_to_riscv(unsigned int flag);
+uint_fast16_t float32_classify(uint32_t a, float_status *status);
+uint_fast16_t float64_classify(uint64_t a, float_status *status);
+
+/*
+ * Compute mmu index
+ * Adapted from Spike's mmu_t::translate
+ */
+#ifdef CONFIG_USER_ONLY
+static inline int cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+ return 0;
+}
+#else
+static inline int cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+ target_ulong mode = env->priv;
+ if (!ifetch) {
+ if (get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ }
+ return mode;
+}
+#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 inline int cpu_riscv_hw_interrupts_pending(CPURISCVState *env)
+{
+ target_ulong pending_interrupts = 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) {
+ target_ulong counted = ctz64(enabled_interrupts); /* since non-zero */
+ if (counted == IRQ_X_HOST) {
+ /* we're handing it to the cpu now, so get rid of the qemu irq */
+ qemu_irq_lower(HTIF_IRQ);
+ } else if (counted == IRQ_M_TIMER) {
+ /* we're handing it to the cpu now, so get rid of the qemu irq */
+ qemu_irq_lower(MTIP_IRQ);
+ } else if (counted == IRQ_S_TIMER || counted == IRQ_H_TIMER) {
+ /* don't lower irq here */
+ }
+ return counted;
+ } else {
+ return EXCP_NONE; /* indicates no pending interrupt */
+ }
+}
+#endif
+
+#include "exec/cpu-all.h"
+
+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);
+
+/* hw/riscv/sifive_clint.c - supplies instret by approximating */
+uint64_t cpu_riscv_read_instret(CPURISCVState *env);
+uint64_t cpu_riscv_read_rtc(void);
+
+int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
+ int mmu_idx);
+#if !defined(CONFIG_USER_ONLY)
+hwaddr cpu_riscv_translate_address(CPURISCVState *env, target_ulong address,
+ int rw);
+#endif
+
+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;
+ *flags = 0; /* necessary to avoid compiler warning */
+}
+
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+ target_ulong csrno);
+target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
+
+void validate_csr(CPURISCVState *env, uint64_t which, uint64_t write);
+
+#endif /* RISCV_CPU_H */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
new file mode 100644
index 0000000..f9698a9
--- /dev/null
+++ b/target/riscv/cpu_bits.h
@@ -0,0 +1,411 @@
+/* Below taken from Spike's decode.h and encoding.h.
+ * Using these directly drastically simplifies updating to new versions of the
+ * RISC-V privileged specification */
+
+#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 FP_RD_NE 0
+#define FP_RD_0 1
+#define FP_RD_DN 2
+#define FP_RD_UP 3
+#define FP_RD_NMM 4
+
+#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)
+
+#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
+
+#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 0x0000000300000000
+#define MSTATUS64_SXL 0x0000000C00000000
+
+#define MSTATUS32_SD 0x80000000
+#define MSTATUS64_SD 0x8000000000000000
+
+#if defined(TARGET_RISCV32)
+#define MSTATUS_SD MSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define MSTATUS_SD MSTATUS64_SD
+#endif
+
+#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 0x8000000000000000
+
+#if defined(TARGET_RISCV32)
+#define SSTATUS_SD SSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define SSTATUS_SD SSTATUS64_SD
+#endif
+
+#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 */
+#define IRQ_X_HOST 13 /* non-standard */
+
+/* Default addresses */
+#define DEFAULT_RSTVEC 0x00001000
+#define DEFAULT_NMIVEC 0x00001004
+#define DEFAULT_MTVEC 0x00001010
+#define CONFIG_STRING_ADDR 0x0000100C
+#define EXT_IO_BASE 0x40000000
+#define DRAM_BASE 0x80000000
+
+/* RV32 satp field masks */
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7fc00000
+#define SATP32_PPN 0x003fffff
+
+/* RV64 satp field masks */
+#define SATP64_MODE 0xF000000000000000
+#define SATP64_ASID 0x0FFFF00000000000
+#define SATP64_PPN 0x00000FFFFFFFFFFF
+
+#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
+
+/* breakpoint control fields */
+#define BPCONTROL_X 0x00000001
+#define BPCONTROL_W 0x00000002
+#define BPCONTROL_R 0x00000004
+#define BPCONTROL_U 0x00000008
+#define BPCONTROL_S 0x00000010
+#define BPCONTROL_H 0x00000020
+#define BPCONTROL_M 0x00000040
+#define BPCONTROL_BPMATCH 0x00000780
+#define BPCONTROL_BPAMASKMAX 0x0F80000000000000
+#define BPCONTROL_TDRTYPE 0xF000000000000000
+
+/* 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)
+/* end Spike decode.h, encoding.h section */
--
2.7.0
--
Best regards,
  Antony Pavlov
Michael Clark
2018-01-04 07:33:57 UTC
Permalink
Post by Antony Pavlov
On Wed, 3 Jan 2018 13:44:07 +1300
Post by Michael Clark
Add CPU state header, CPU definitions and initialization routines
---
target/riscv/cpu.c | 338 +++++++++++++++++++++++++++++++++++++++
target/riscv/cpu.h | 363 ++++++++++++++++++++++++++++++
++++++++++++
Post by Michael Clark
target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
++++++++++++++++++
Post by Michael Clark
3 files changed, 1112 insertions(+)
create mode 100644 target/riscv/cpu.c
create mode 100644 target/riscv/cpu.h
create mode 100644 target/riscv/cpu_bits.h
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
...
Post by Michael Clark
+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
+ tlb_flush(cs);
+ env->priv = PRV_M;
+ env->mtvec = DEFAULT_MTVEC;
+#endif
+ env->pc = DEFAULT_RSTVEC;
The RISC-V Privileged Architecture Manual v1.10 states that
The pc is set to an implementation-defined reset vector.
But hard-coded DEFAULT_RSTVEC leaves no chance for changing reset vector.
Can we add a mechanism for changing reset vector?
That can be added very easily at some point when necessary.

All 5 RISC-V machines in the QEMU port currently have their emulated Mask
ROMs at 0x1000 so its not necessary until we add a machine that needs a
different value. I certainly wouldn't reject a patch that adds that
functionality if we had a machine with a different reset vector, although
given we have 5 machines using the same vector, it may remain a sensible
default. I would think twice about adding a property that no machines sets,
or duplicate code and have all machines set their reset vector even when
they are all the same? Shall we add the functionality when we need it?

I'd categorise this as a feature request. #define DEFAULT_RSTVEC 0x00001000
is the "implementation-defined reset vector"

Folk on the RISC-V mailing list are actually seeking guidance on the blanks
in the RISC-V specification so it may be that a de-facto standard emerges
for some of these "implementation defined" blanks, in which case it may
become part of a platform spec (vs the ISA spec).

E.g. there is the "reset-hivecs" property in the ARM emulation code
Post by Antony Pavlov
so SoC-specific code can change reset vector.
Post by Michael Clark
+ 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)
Post by Michael Clark
+{
+#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);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (env->misa & RVM) {
+ set_feature(env, RISCV_FEATURE_RVM);
+ }
+ if (env->misa & RVA) {
+ set_feature(env, RISCV_FEATURE_RVA);
+ }
+ if (env->misa & RVF) {
+ set_feature(env, RISCV_FEATURE_RVF);
+ }
+ if (env->misa & RVD) {
+ set_feature(env, RISCV_FEATURE_RVD);
+ }
+ if (env->misa & RVC) {
+ set_feature(env, RISCV_FEATURE_RVC);
+ }
+
+ 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_unassigned_access = riscv_cpu_unassigned_access;
+ 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 = g_strdup(info->name),
+ .parent = TYPE_RISCV_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = info->initfn,
+ };
+
+ type_register(&type_info);
+ g_free((void *)type_info.name);
+}
+
+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)
+{
+ size_t len = 5 + ctz32(cpu->env.misa);
+ char *isa_string = g_new(char, len);
+ isa_string[0] = '\0';
+#if defined(TARGET_RISCV32)
+ strncat(isa_string, "rv32", len);
+#elif defined(TARGET_RISCV64)
+ strncat(isa_string, "rv64", len);
+#endif
+ if (cpu->env.misa & RVI) {
+ strncat(isa_string, "i", len);
+ }
+ if (cpu->env.misa & RVM) {
+ strncat(isa_string, "m", len);
+ }
+ if (cpu->env.misa & RVA) {
+ strncat(isa_string, "a", len);
+ }
+ if (cpu->env.misa & RVF) {
+ strncat(isa_string, "f", len);
+ }
+ if (cpu->env.misa & RVD) {
+ strncat(isa_string, "d", len);
+ }
+ if (cpu->env.misa & RVC) {
+ strncat(isa_string, "c", len);
+ }
+ return isa_string;
+}
+
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ const RISCVCPUInfo *info = riscv_cpus;
+
+ while (info->name) {
+ (*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) {
+ 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 0000000..0480127
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,363 @@
+#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 TARGET_HAS_ICE 1
+#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 DEBUG_OP */
+/* #define RISCV_DEBUG_PRINT */
+
+#define TYPE_RISCV_CPU "riscv"
+#define TYPE_RISCV_CPU_ANY "riscv-any"
+#define TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_09 "riscv-imafdcsu-priv1.9"
+#define TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10 "riscv-imafdcsu-priv1.10"
+#define TYPE_RISCV_CPU_IMACU_PRIV_1_10 "riscv-imacu-priv1.10"
+#define TYPE_RISCV_CPU_IMAC_PRIV_1_10 "riscv-imac-priv1.10"
+
+#define RISCV_CPU_TYPE_PREFIX TYPE_RISCV_CPU "-"
+#define RISCV_CPU_TYPE_NAME(name) (RISCV_CPU_TYPE_PREFIX name)
+
+#if defined(TARGET_RISCV32)
+#define RVXLEN ((target_ulong)1 << (TARGET_LONG_BITS - 2))
+#elif defined(TARGET_RISCV64)
+#define RVXLEN ((target_ulong)2 << (TARGET_LONG_BITS - 2))
+#endif
+#define RV(x) (1L << (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')
+
+#define USER_VERSION_2_02_0 0x00020200
+#define PRIV_VERSION_1_09_1 0x00010901
+#define PRIV_VERSION_1_10_0 0x00011000
+
+/* RISCV Exception Codes */
+#define EXCP_NONE -1 /* not a real RISCV
exception code */
Post by Michael Clark
+#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
Post by Michael Clark
+ ECALLs as this,
handler
Post by Michael Clark
+ 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 TRANSLATE_FAIL 1
+#define TRANSLATE_SUCCESS 0
+#define NB_MMU_MODES 4
+#define MMU_USER_IDX 3
+
+#define SSIP_IRQ (env->irq[0])
+#define STIP_IRQ (env->irq[1])
+#define MSIP_IRQ (env->irq[2])
+#define MTIP_IRQ (env->irq[3])
+#define HTIF_IRQ (env->irq[4])
+#define SEIP_IRQ (env->irq[5])
+#define MEIP_IRQ (env->irq[6])
+
+#define MAX_RISCV_IRQ (8)
+#define MAX_RISCV_PMPS (16)
+
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+typedef 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 frm;
+ target_ulong fstatus;
+ target_ulong fflags;
+
+ target_ulong badaddr;
+
+ uint32_t mucounteren;
+
+ target_ulong user_ver;
+ target_ulong priv_ver;
+ target_ulong misa_mask;
+ target_ulong misa;
+
+#ifdef CONFIG_USER_ONLY
+ uint32_t amoinsn;
+ target_long amoaddr;
+ target_long amotest;
+#else
+ target_ulong priv;
+
+ target_ulong mhartid;
+ target_ulong mstatus;
+ target_ulong mip;
+ 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 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;
+
+ /* Internal CPU feature flags. */
+ uint64_t features;
+
+ /* QEMU */
+ CPU_COMMON
+
+ /* Fields from here on are preserved across CPU reset. */
+ void *irq[8];
+ QEMUTimer *timer; /* Internal timer */
+} CPURISCVState;
+
+#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)
+
+/**
+ *
+ * A RISCV CPU model.
+ */
+typedef struct RISCVCPUClass {
+ /*< private >*/
+ CPUClass parent_class;
+ /*< public >*/
+ DeviceRealize parent_realize;
+ void (*parent_reset)(CPUState *cpu);
+} RISCVCPUClass;
+
+/**
+ *
+ * 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);
+}
+
+enum riscv_features {
+ RISCV_FEATURE_RVM,
+ RISCV_FEATURE_RVA,
+ RISCV_FEATURE_RVF,
+ RISCV_FEATURE_RVD,
+ RISCV_FEATURE_RVC,
+};
+
+static inline int riscv_feature(CPURISCVState *env, int feature)
+{
+ return (env->features & (1ULL << feature)) != 0;
+}
+
+#include "cpu_user.h"
+#include "cpu_bits.h"
+
+#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
+#define ENV_OFFSET offsetof(RISCVCPU, env)
+
+void riscv_cpu_do_interrupt(CPUState *cpu);
+void riscv_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function
cpu_fprintf,
Post by Michael Clark
+ int flags);
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+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);
+void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, int
mmu_idx,
Post by Michael Clark
+ uintptr_t retaddr);
+#if !defined(CONFIG_USER_ONLY)
+void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool
is_write,
Post by Michael Clark
+ bool is_exec, int unused, unsigned size);
+#endif
+
+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
+
+void set_privilege(CPURISCVState *env, target_ulong newpriv);
+unsigned int softfloat_flags_to_riscv(unsigned int flag);
+uint_fast16_t float32_classify(uint32_t a, float_status *status);
+uint_fast16_t float64_classify(uint64_t a, float_status *status);
+
+/*
+ * Compute mmu index
+ * Adapted from Spike's mmu_t::translate
+ */
+#ifdef CONFIG_USER_ONLY
+static inline int cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+ return 0;
+}
+#else
+static inline int cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+ target_ulong mode = env->priv;
+ if (!ifetch) {
+ if (get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ }
+ return mode;
+}
+#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 inline int cpu_riscv_hw_interrupts_pending(CPURISCVState *env)
+{
+ target_ulong pending_interrupts = 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);
Post by Michael Clark
+ 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);
Post by Michael Clark
+ enabled_interrupts |= pending_interrupts & env->mideleg &
+ -s_enabled;
+
+ if (enabled_interrupts) {
+ target_ulong counted = ctz64(enabled_interrupts); /* since
non-zero */
Post by Michael Clark
+ if (counted == IRQ_X_HOST) {
+ /* we're handing it to the cpu now, so get rid of the qemu
irq */
Post by Michael Clark
+ qemu_irq_lower(HTIF_IRQ);
+ } else if (counted == IRQ_M_TIMER) {
+ /* we're handing it to the cpu now, so get rid of the qemu
irq */
Post by Michael Clark
+ qemu_irq_lower(MTIP_IRQ);
+ } else if (counted == IRQ_S_TIMER || counted == IRQ_H_TIMER) {
+ /* don't lower irq here */
+ }
+ return counted;
+ } else {
+ return EXCP_NONE; /* indicates no pending interrupt */
+ }
+}
+#endif
+
+#include "exec/cpu-all.h"
+
+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);
Post by Michael Clark
+
+/* hw/riscv/sifive_clint.c - supplies instret by approximating */
+uint64_t cpu_riscv_read_instret(CPURISCVState *env);
+uint64_t cpu_riscv_read_rtc(void);
+
+int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
+ int mmu_idx);
+#if !defined(CONFIG_USER_ONLY)
+hwaddr cpu_riscv_translate_address(CPURISCVState *env, target_ulong
address,
Post by Michael Clark
+ int rw);
+#endif
+
+static inline void cpu_get_tb_cpu_state(CPURISCVState *env,
target_ulong *pc,
Post by Michael Clark
+ target_ulong *cs_base, uint32_t
*flags)
Post by Michael Clark
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = 0; /* necessary to avoid compiler warning */
+}
+
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+ target_ulong csrno);
+target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
+
+void validate_csr(CPURISCVState *env, uint64_t which, uint64_t write);
+
+#endif /* RISCV_CPU_H */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
new file mode 100644
index 0000000..f9698a9
--- /dev/null
+++ b/target/riscv/cpu_bits.h
@@ -0,0 +1,411 @@
+/* Below taken from Spike's decode.h and encoding.h.
+ * Using these directly drastically simplifies updating to new versions
of the
Post by Michael Clark
+ * RISC-V privileged specification */
+
+#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 FP_RD_NE 0
+#define FP_RD_0 1
+#define FP_RD_DN 2
+#define FP_RD_UP 3
+#define FP_RD_NMM 4
+
+#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)
+
+#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
+
+#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 0x0000000300000000
+#define MSTATUS64_SXL 0x0000000C00000000
+
+#define MSTATUS32_SD 0x80000000
+#define MSTATUS64_SD 0x8000000000000000
+
+#if defined(TARGET_RISCV32)
+#define MSTATUS_SD MSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define MSTATUS_SD MSTATUS64_SD
+#endif
+
+#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 0x8000000000000000
+
+#if defined(TARGET_RISCV32)
+#define SSTATUS_SD SSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define SSTATUS_SD SSTATUS64_SD
+#endif
+
+#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 */
+#define IRQ_X_HOST 13 /* non-standard */
+
+/* Default addresses */
+#define DEFAULT_RSTVEC 0x00001000
+#define DEFAULT_NMIVEC 0x00001004
+#define DEFAULT_MTVEC 0x00001010
+#define CONFIG_STRING_ADDR 0x0000100C
+#define EXT_IO_BASE 0x40000000
+#define DRAM_BASE 0x80000000
+
+/* RV32 satp field masks */
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7fc00000
+#define SATP32_PPN 0x003fffff
+
+/* RV64 satp field masks */
+#define SATP64_MODE 0xF000000000000000
+#define SATP64_ASID 0x0FFFF00000000000
+#define SATP64_PPN 0x00000FFFFFFFFFFF
+
+#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
+
+/* breakpoint control fields */
+#define BPCONTROL_X 0x00000001
+#define BPCONTROL_W 0x00000002
+#define BPCONTROL_R 0x00000004
+#define BPCONTROL_U 0x00000008
+#define BPCONTROL_S 0x00000010
+#define BPCONTROL_H 0x00000020
+#define BPCONTROL_M 0x00000040
+#define BPCONTROL_BPMATCH 0x00000780
+#define BPCONTROL_BPAMASKMAX 0x0F80000000000000
+#define BPCONTROL_TDRTYPE 0xF000000000000000
+
+/* 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)
Post by Michael Clark
+/* end Spike decode.h, encoding.h section */
--
2.7.0
--
Best regards,
Antony Pavlov
Antony Pavlov
2018-01-04 17:53:53 UTC
Permalink
On Thu, 4 Jan 2018 20:33:57 +1300
Post by Michael Clark
Post by Antony Pavlov
On Wed, 3 Jan 2018 13:44:07 +1300
Post by Michael Clark
Add CPU state header, CPU definitions and initialization routines
---
target/riscv/cpu.c | 338 +++++++++++++++++++++++++++++++++++++++
target/riscv/cpu.h | 363 ++++++++++++++++++++++++++++++
++++++++++++
Post by Michael Clark
target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
++++++++++++++++++
Post by Michael Clark
3 files changed, 1112 insertions(+)
create mode 100644 target/riscv/cpu.c
create mode 100644 target/riscv/cpu.h
create mode 100644 target/riscv/cpu_bits.h
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
...
Post by Michael Clark
+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
+ tlb_flush(cs);
+ env->priv = PRV_M;
+ env->mtvec = DEFAULT_MTVEC;
+#endif
+ env->pc = DEFAULT_RSTVEC;
The RISC-V Privileged Architecture Manual v1.10 states that
The pc is set to an implementation-defined reset vector.
But hard-coded DEFAULT_RSTVEC leaves no chance for changing reset vector.
Can we add a mechanism for changing reset vector?
That can be added very easily at some point when necessary.
All 5 RISC-V machines in the QEMU port currently have their emulated Mask
ROMs at 0x1000 so its not necessary until we add a machine that needs a
different value. I certainly wouldn't reject a patch that adds that
functionality if we had a machine with a different reset vector, although
given we have 5 machines using the same vector, it may remain a sensible
default. I would think twice about adding a property that no machines sets,
or duplicate code and have all machines set their reset vector even when
they are all the same? Shall we add the functionality when we need it?
Actually it is me who needs this functionality.

At the moment my board code needs this dirty hack:

https://github.com/miet-riscv-workgroup/riscv-qemu/commit/bfc8221d89b9bb828f3742f17eb89d8513a75aae#diff-429448b1b26e0bc4256cc290758c0ab5
Post by Michael Clark
I'd categorise this as a feature request. #define DEFAULT_RSTVEC 0x00001000
is the "implementation-defined reset vector"
Folk on the RISC-V mailing list are actually seeking guidance on the blanks
in the RISC-V specification so it may be that a de-facto standard emerges
for some of these "implementation defined" blanks, in which case it may
become part of a platform spec (vs the ISA spec).
E.g. there is the "reset-hivecs" property in the ARM emulation code
Post by Antony Pavlov
so SoC-specific code can change reset vector.
--
Best regards,
  Antony Pavlov
Michael Clark
2018-01-05 05:59:51 UTC
Permalink
Post by Antony Pavlov
On Thu, 4 Jan 2018 20:33:57 +1300
Post by Michael Clark
Post by Antony Pavlov
On Wed, 3 Jan 2018 13:44:07 +1300
Post by Michael Clark
Add CPU state header, CPU definitions and initialization routines
---
target/riscv/cpu.c | 338
+++++++++++++++++++++++++++++++++++++++
Post by Michael Clark
Post by Antony Pavlov
Post by Michael Clark
target/riscv/cpu.h | 363 ++++++++++++++++++++++++++++++
++++++++++++
Post by Michael Clark
target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
++++++++++++++++++
Post by Michael Clark
3 files changed, 1112 insertions(+)
create mode 100644 target/riscv/cpu.c
create mode 100644 target/riscv/cpu.h
create mode 100644 target/riscv/cpu_bits.h
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
...
Post by Michael Clark
+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
+ tlb_flush(cs);
+ env->priv = PRV_M;
+ env->mtvec = DEFAULT_MTVEC;
+#endif
+ env->pc = DEFAULT_RSTVEC;
The RISC-V Privileged Architecture Manual v1.10 states that
The pc is set to an implementation-defined reset vector.
But hard-coded DEFAULT_RSTVEC leaves no chance for changing reset
vector.
Post by Michael Clark
Post by Antony Pavlov
Can we add a mechanism for changing reset vector?
That can be added very easily at some point when necessary.
All 5 RISC-V machines in the QEMU port currently have their emulated Mask
ROMs at 0x1000 so its not necessary until we add a machine that needs a
different value. I certainly wouldn't reject a patch that adds that
functionality if we had a machine with a different reset vector, although
given we have 5 machines using the same vector, it may remain a sensible
default. I would think twice about adding a property that no machines
sets,
Post by Michael Clark
or duplicate code and have all machines set their reset vector even when
they are all the same? Shall we add the functionality when we need it?
Actually it is me who needs this functionality.
https://github.com/miet-riscv-workgroup/riscv-qemu/commit/bfc8221d89b9bb828f3742f17eb89d8513a75aae#diff-429448b1b26e0bc4256cc290758c0ab5
Okay, we can add a property. It’s also possible to register a cpu_reset
callback and set the pc there, within the machine.
Post by Antony Pavlov
Post by Michael Clark
I'd categorise this as a feature request. #define DEFAULT_RSTVEC
0x00001000
Post by Michael Clark
is the "implementation-defined reset vector"
Folk on the RISC-V mailing list are actually seeking guidance on the
blanks
Post by Michael Clark
in the RISC-V specification so it may be that a de-facto standard emerges
for some of these "implementation defined" blanks, in which case it may
become part of a platform spec (vs the ISA spec).
E.g. there is the "reset-hivecs" property in the ARM emulation code
Post by Antony Pavlov
so SoC-specific code can change reset vector.
--
Best regards,
Antony Pavlov
Michael Clark
2018-03-03 01:41:15 UTC
Permalink
Hi Antony,

As of v8 of the RISC-V QEMU target patch series, you can now define the
reset vector in your CPU initializer:

https://github.com/riscv/riscv-qemu/blob/qemu-upstream-v8/target/riscv/cpu.c#L110-L168

Michael.
Post by Antony Pavlov
On Thu, 4 Jan 2018 20:33:57 +1300
Post by Michael Clark
Post by Antony Pavlov
On Wed, 3 Jan 2018 13:44:07 +1300
Post by Michael Clark
Add CPU state header, CPU definitions and initialization routines
---
target/riscv/cpu.c | 338 ++++++++++++++++++++++++++++++
+++++++++
Post by Michael Clark
Post by Antony Pavlov
Post by Michael Clark
target/riscv/cpu.h | 363 ++++++++++++++++++++++++++++++
++++++++++++
Post by Michael Clark
target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
++++++++++++++++++
Post by Michael Clark
3 files changed, 1112 insertions(+)
create mode 100644 target/riscv/cpu.c
create mode 100644 target/riscv/cpu.h
create mode 100644 target/riscv/cpu_bits.h
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
...
Post by Michael Clark
+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
+ tlb_flush(cs);
+ env->priv = PRV_M;
+ env->mtvec = DEFAULT_MTVEC;
+#endif
+ env->pc = DEFAULT_RSTVEC;
The RISC-V Privileged Architecture Manual v1.10 states that
The pc is set to an implementation-defined reset vector.
But hard-coded DEFAULT_RSTVEC leaves no chance for changing reset
vector.
Post by Michael Clark
Post by Antony Pavlov
Can we add a mechanism for changing reset vector?
That can be added very easily at some point when necessary.
All 5 RISC-V machines in the QEMU port currently have their emulated Mask
ROMs at 0x1000 so its not necessary until we add a machine that needs a
different value. I certainly wouldn't reject a patch that adds that
functionality if we had a machine with a different reset vector, although
given we have 5 machines using the same vector, it may remain a sensible
default. I would think twice about adding a property that no machines
sets,
Post by Michael Clark
or duplicate code and have all machines set their reset vector even when
they are all the same? Shall we add the functionality when we need it?
Actually it is me who needs this functionality.
https://github.com/miet-riscv-workgroup/riscv-qemu/commit/
bfc8221d89b9bb828f3742f17eb89d8513a75aae#diff-
429448b1b26e0bc4256cc290758c0ab5
Post by Michael Clark
I'd categorise this as a feature request. #define DEFAULT_RSTVEC
0x00001000
Post by Michael Clark
is the "implementation-defined reset vector"
Folk on the RISC-V mailing list are actually seeking guidance on the
blanks
Post by Michael Clark
in the RISC-V specification so it may be that a de-facto standard emerges
for some of these "implementation defined" blanks, in which case it may
become part of a platform spec (vs the ISA spec).
E.g. there is the "reset-hivecs" property in the ARM emulation code
Post by Antony Pavlov
so SoC-specific code can change reset vector.
--
Best regards,
Antony Pavlov
Michael Clark
2018-01-03 00:44:09 UTC
Permalink
Privileged control and status register helpers and page fault handling.

Signed-off-by: Michael Clark <***@sifive.com>
---
target/riscv/helper.c | 494 +++++++++++++++++++++++++++++++++
target/riscv/helper.h | 78 ++++++
target/riscv/op_helper.c | 707 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1279 insertions(+)
create mode 100644 target/riscv/helper.c
create mode 100644 target/riscv/helper.h
create mode 100644 target/riscv/op_helper.c

diff --git a/target/riscv/helper.c b/target/riscv/helper.c
new file mode 100644
index 0000000..34da7dd
--- /dev/null
+++ b/target/riscv/helper.c
@@ -0,0 +1,494 @@
+/*
+ * RISC-V emulation helpers for qemu.
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+
+/*#define RISCV_DEBUG_INTERRUPT */
+
+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 = cpu_riscv_hw_interrupts_pending(env);
+ if (interruptno + 1) {
+ cs->exception_index = 0x70000000U | 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 address,
+ 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 */
+
+ *prot = 0;
+ CPUState *cs = CPU(riscv_env_get_cpu(env));
+
+ target_ulong mode = env->priv;
+ if (access_type != MMU_INST_FETCH) {
+ if (get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ }
+
+ /* check to make sure that mmu_idx and mode that we get matches */
+ if (unlikely(mode != mmu_idx)) {
+ fprintf(stderr, "MODE: mmu_idx mismatch\n");
+ exit(1);
+ }
+
+ if (mode == PRV_M) {
+ target_ulong msb_mask = /*0x7FFFFFFFFFFFFFFF; */
+ (((target_ulong)2) << (TARGET_LONG_BITS - 1)) - 1;
+ *physical = address & msb_mask;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return TRANSLATE_SUCCESS;
+ }
+
+ target_ulong addr = address;
+ 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;
+ default:
+ printf("unsupported SATP_MODE value\n");
+ exit(1);
+ }
+ } 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;
+ default:
+ printf("unsupported MSTATUS_VM value\n");
+ exit(1);
+ }
+ }
+
+ 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;
+ 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;
+ target_ulong pte = ldq_phys(cs->as, pte_addr);
+ 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 {
+ /* set accessed and possibly dirty bits.
+ we only put it in the TLB if it has the right stuff */
+ stq_phys(cs->as, pte_addr, ldq_phys(cs->as, pte_addr) | PTE_A |
+ ((access_type == MMU_DATA_STORE) * PTE_D));
+
+ /* 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;
+
+ /* we do not give all prots indicated by the PTE
+ * this is because future accesses need to do things like set the
+ * dirty bit on the PTE
+ *
+ * at this point, we assume that protection checks have occurred */
+ if (mode == PRV_S) {
+ if ((pte & PTE_X) && access_type == MMU_INST_FETCH) {
+ *prot |= PAGE_EXEC;
+ } else if ((pte & PTE_W) && access_type == MMU_DATA_STORE) {
+ *prot |= PAGE_WRITE;
+ } else if ((pte & PTE_R) && access_type == MMU_DATA_LOAD) {
+ *prot |= PAGE_READ;
+ } else {
+ printf("err in translation prots");
+ exit(1);
+ }
+ } else {
+ if ((pte & PTE_X) && access_type == MMU_INST_FETCH) {
+ *prot |= PAGE_EXEC;
+ } else if ((pte & PTE_W) && access_type == MMU_DATA_STORE) {
+ *prot |= PAGE_WRITE;
+ } else if ((pte & PTE_R) && access_type == MMU_DATA_LOAD) {
+ *prot |= PAGE_READ;
+ } else {
+ printf("err in translation prots");
+ exit(1);
+ }
+ }
+ 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;
+ int exception = 0;
+ if (access_type == MMU_INST_FETCH) { /* inst access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_STORE) { /* store access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_LOAD) { /* load access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT;
+ env->badaddr = address;
+ } else {
+ fprintf(stderr, "FAIL: invalid access_type\n");
+ exit(1);
+ }
+ cs->exception_index = exception;
+}
+
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ hwaddr phys_addr;
+ int prot;
+ int mem_idx = cpu_mmu_index(&cpu->env, false);
+
+ if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mem_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;
+ if (access_type == MMU_INST_FETCH) {
+ fprintf(stderr, "unaligned inst fetch not handled here. should not "
+ "trigger\n");
+ exit(1);
+ } else if (access_type == MMU_DATA_STORE) {
+ cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS;
+ env->badaddr = addr;
+ } else if (access_type == MMU_DATA_LOAD) {
+ cs->exception_index = RISCV_EXCP_LOAD_ADDR_MIS;
+ env->badaddr = addr;
+ } else {
+ fprintf(stderr, "Invalid MMUAccessType\n");
+ exit(1);
+ }
+ 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, MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr)
+{
+ int ret;
+ ret = riscv_cpu_handle_mmu_fault(cs, addr, 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);
+ }
+}
+
+void riscv_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write,
+ bool is_exec, int unused, unsigned size)
+{
+ printf("unassigned address not implemented for riscv\n");
+ printf("are you trying to fetch instructions from an MMIO page?\n");
+ printf("unassigned Address: %016" PRIx64 "\n", addr);
+ exit(1);
+}
+
+#endif
+
+int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
+ int access_type, 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 " access_type %d mmu_idx \
+ %d\n", __func__, env->pc, address, access_type, mmu_idx);
+
+#if !defined(CONFIG_USER_ONLY)
+ ret = get_physical_address(env, &pa, &prot, address, access_type,
+ 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 << access_type)) {
+ 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, access_type);
+ }
+#else
+ cs->exception_index = QEMU_USER_EXCP_FAULT;
+#endif
+ return ret;
+}
+
+#ifdef RISCV_DEBUG_INTERRUPT
+static const char * const riscv_excp_names[16] = {
+ "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"
+};
+
+static const char * const riscv_interrupt_names[14] = {
+ "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"
+};
+#endif /* RISCV_DEBUG_INTERRUPT */
+
+/*
+ * 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;
+
+ #ifdef RISCV_DEBUG_INTERRUPT
+ if (cs->exception_index & 0x70000000) {
+ fprintf(stderr, "core 0: exception trap_%s, epc 0x" TARGET_FMT_lx "\n"
+ , riscv_interrupt_names[cs->exception_index & 0x0fffffff],
+ env->pc);
+ } else {
+ fprintf(stderr, "core 0: exception trap_%s, epc 0x" TARGET_FMT_lx "\n"
+ , riscv_excp_names[cs->exception_index], env->pc);
+ }
+ #endif
+
+ if (cs->exception_index == RISCV_EXCP_BREAKPOINT) {
+ fprintf(stderr, "debug mode not implemented\n");
+ }
+
+ /* skip dcsr cause check */
+
+ target_ulong fixed_cause = 0;
+ if (cs->exception_index & (0x70000000)) {
+ /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception
+ index is only 32 bits wide */
+ fixed_cause = cs->exception_index & 0x0FFFFFFF;
+ 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) {
+ #ifdef RISCV_DEBUG_INTERRUPT
+ fprintf(stderr, "core %d: badaddr 0x" TARGET_FMT_lx "\n",
+ env->mhartid, env->badaddr);
+ #endif
+ env->sbadaddr = env->badaddr;
+ }
+
+ target_ulong s = env->mstatus;
+ s = set_field(s, MSTATUS_SPIE, 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);
+ set_privilege(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) {
+ #ifdef RISCV_DEBUG_INTERRUPT
+ fprintf(stderr, "core %d: badaddr 0x" TARGET_FMT_lx "\n",
+ env->mhartid, env->badaddr);
+ #endif
+ env->mbadaddr = env->badaddr;
+ }
+
+ target_ulong s = env->mstatus;
+ s = set_field(s, MSTATUS_MPIE, 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);
+ set_privilege(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 0000000..60f04a2
--- /dev/null
+++ b/target/riscv/helper.h
@@ -0,0 +1,78 @@
+/* Exceptions */
+DEF_HELPER_2(raise_exception, noreturn, env, i32)
+DEF_HELPER_1(raise_exception_debug, noreturn, env)
+DEF_HELPER_3(raise_exception_mbadaddr, noreturn, env, i32, tl)
+
+/* Floating Point - fused */
+DEF_HELPER_FLAGS_5(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fnmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fnmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fnmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_5(fnmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
+
+/* Floating Point - Single Precision */
+DEF_HELPER_FLAGS_4(fadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, 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_3(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64, 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_3(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_3(fcvt_l_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_lu_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+#endif
+DEF_HELPER_FLAGS_3(fcvt_s_w, TCG_CALL_NO_RWG, i64, env, tl, i64)
+DEF_HELPER_FLAGS_3(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_3(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, i64, i64)
+#endif
+DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG, tl, env, i64)
+
+/* Floating Point - Double Precision */
+DEF_HELPER_FLAGS_4(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, 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_3(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64, 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_3(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_3(fcvt_l_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_lu_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+#endif
+DEF_HELPER_FLAGS_3(fcvt_d_w, TCG_CALL_NO_RWG, i64, env, tl, i64)
+DEF_HELPER_FLAGS_3(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_3(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, i64, i64)
+#endif
+DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG, tl, env, 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)
+DEF_HELPER_1(fence_i, void, env)
+#endif
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
new file mode 100644
index 0000000..c9bad0a
--- /dev/null
+++ b/target/riscv/op_helper.c
@@ -0,0 +1,707 @@
+/*
+ * RISC-V Emulation Helpers for QEMU.
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "qemu/error-report.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 */
+inline 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);
+}
+
+void helper_raise_exception_debug(CPURISCVState *env)
+{
+ do_raise_exception_err(env, EXCP_DEBUG, 0);
+}
+
+void helper_raise_exception_mbadaddr(CPURISCVState *env, uint32_t exception,
+ target_ulong bad_pc) {
+ env->badaddr = bad_pc;
+ do_raise_exception_err(env, exception, 0);
+}
+
+/*
+ * Handle writes to CSRs and any resulting special behavior
+ *
+ * Adapted from Spike's processor_t::set_csr
+ */
+inline void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+ target_ulong csrno)
+{
+ #ifdef RISCV_DEBUG_PRINT
+ fprintf(stderr, "Write CSR reg: 0x" TARGET_FMT_lx "\n", csrno);
+ fprintf(stderr, "Write CSR val: 0x" TARGET_FMT_lx "\n", val_to_write);
+ #endif
+
+#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:
+#ifndef CONFIG_USER_ONLY
+ env->mstatus |= MSTATUS_FS | MSTATUS64_SD;
+#endif
+ env->fflags = val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT);
+ break;
+ case CSR_FRM:
+#ifndef CONFIG_USER_ONLY
+ env->mstatus |= MSTATUS_FS | MSTATUS64_SD;
+#endif
+ env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT);
+ break;
+ case CSR_FCSR:
+#ifndef CONFIG_USER_ONLY
+ env->mstatus |= MSTATUS_FS | MSTATUS64_SD;
+#endif
+ env->fflags = (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT;
+ env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT;
+ break;
+#ifndef CONFIG_USER_ONLY
+ case CSR_MSTATUS: {
+ target_ulong mstatus = env->mstatus;
+ target_ulong mask = 0;
+ 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;
+ }
+ mstatus = (mstatus & ~mask) | (val_to_write & mask);
+ int dirty = (mstatus & MSTATUS_FS) == MSTATUS_FS;
+ dirty |= (mstatus & MSTATUS_XS) == MSTATUS_XS;
+ mstatus = set_field(mstatus, MSTATUS64_SD, dirty);
+ env->mstatus = mstatus;
+ break;
+ }
+ case CSR_MIP: {
+ target_ulong mask = MIP_SSIP | MIP_STIP | MIP_SEIP;
+ env->mip = (env->mip & ~mask) |
+ (val_to_write & mask);
+ qemu_mutex_lock_iothread();
+ if (env->mip & MIP_SSIP) {
+ qemu_irq_raise(SSIP_IRQ);
+ } else {
+ qemu_irq_lower(SSIP_IRQ);
+ }
+ if (env->mip & MIP_STIP) {
+ qemu_irq_raise(STIP_IRQ);
+ } else {
+ qemu_irq_lower(STIP_IRQ);
+ }
+ if (env->mip & MIP_SEIP) {
+ qemu_irq_raise(SEIP_IRQ);
+ } else {
+ qemu_irq_lower(SEIP_IRQ);
+ }
+ 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:
+ error_report("CSR_MINSTRET: write not implemented");
+ exit(1);
+ break;
+ case CSR_MCYCLE:
+ error_report("CSR_MCYCLE: write not implemented");
+ exit(1);
+ break;
+ case CSR_MINSTRETH:
+ error_report("CSR_MINSTRETH: write not implemented");
+ exit(1);
+ break;
+ case CSR_MCYCLEH:
+ error_report("CSR_MCYCLEH: write not implemented");
+ exit(1);
+ break;
+ 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: {
+ target_ulong next_mip = (env->mip & ~env->mideleg)
+ | (val_to_write & env->mideleg);
+ 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 (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) {
+ error_report("CSR_STVEC: vectored interrupts not implemented");
+ exit(1);
+ }
+ 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) {
+ error_report("CSR_MTVEC: vectored interrupts not implemented");
+ exit(1);
+ }
+ 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: {
+ if (!(val_to_write & (1L << ('F' - 'A')))) {
+ val_to_write &= ~(1L << ('D' - 'A'));
+ }
+
+ /* allow MAFDC bits in MISA to be modified */
+ target_ulong mask = 0;
+ mask |= 1L << ('M' - 'A');
+ mask |= 1L << ('A' - 'A');
+ mask |= 1L << ('F' - 'A');
+ mask |= 1L << ('D' - 'A');
+ mask |= 1L << ('C' - 'A');
+ mask &= env->misa_mask;
+
+ env->misa = (val_to_write & mask) | (env->misa & ~mask);
+ break;
+ }
+ case CSR_TSELECT:
+ /* TSELECT is hardwired in this implementation */
+ break;
+ case CSR_TDATA1:
+ error_report("CSR_TDATA1: write not implemented");
+ exit(1);
+ break;
+ case CSR_TDATA2:
+ error_report("CSR_TDATA2: write not implemented");
+ exit(1);
+ break;
+ case CSR_DCSR:
+ error_report("CSR_DCSR: write not implementedn");
+ exit(1);
+ break;
+ 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;
+#endif
+ default:
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
+ }
+}
+
+/*
+ * Handle reads to CSRs and any resulting special behavior
+ *
+ * Adapted from Spike's processor_t::get_csr
+ */
+inline target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno)
+{
+ #ifdef RISCV_DEBUG_PRINT
+ fprintf(stderr, "READ CSR 0x%x\n", csrno);
+ #endif
+#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 = env->mucounteren;
+#endif
+ target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1;
+
+ if (ctr_ok) {
+ if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) {
+ return 0;
+ }
+#if defined(TARGET_RISCV32)
+ if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) {
+ 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:
+ return env->fflags;
+ case CSR_FRM:
+ return env->frm;
+ case CSR_FCSR:
+ return env->fflags << FSR_AEXC_SHIFT |
+ env->frm << FSR_RD_SHIFT;
+#ifdef CONFIG_USER_ONLY
+ case CSR_TIME:
+ case CSR_CYCLE:
+ case CSR_INSTRET:
+ return (target_ulong)cpu_get_host_ticks();
+ case CSR_TIMEH:
+ case CSR_CYCLEH:
+ case CSR_INSTRETH:
+#if defined(TARGET_RISCV32)
+ return (target_ulong)(cpu_get_host_ticks() >> 32);
+#endif
+ break;
+#endif
+#ifndef CONFIG_USER_ONLY
+ case CSR_TIME:
+ return cpu_riscv_read_rtc();
+ case CSR_TIMEH:
+ return (target_ulong)(cpu_riscv_read_rtc() >> 32);
+ case CSR_INSTRET:
+ case CSR_CYCLE:
+ if (ctr_ok) {
+ return cpu_riscv_read_instret(env);
+ }
+ break;
+ case CSR_MINSTRET:
+ case CSR_MCYCLE:
+ return cpu_riscv_read_instret(env);
+ case CSR_MINSTRETH:
+ case CSR_MCYCLEH:
+#if defined(TARGET_RISCV32)
+ return cpu_riscv_read_instret(env) >> 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:
+ return env->mip & env->mideleg;
+ 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:
+ return env->mip;
+ 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_TSELECT:
+ /* indicate only usable in debug mode (which we don't support) */
+ return 1L << (TARGET_LONG_BITS - 5);
+ case CSR_TDATA1:
+ printf("CSR_TDATA1 read not implemented.\n");
+ exit(1);
+ break;
+ case CSR_TDATA2:
+ printf("CSR_TDATA2 read not implemented.\n");
+ exit(1);
+ break;
+ case CSR_TDATA3:
+ printf("CSR_TDATA3 read not implemented.\n");
+ exit(1);
+ break;
+ case CSR_DCSR:
+ printf("CSR_DCSR read not implemented.\n");
+ exit(1);
+ break;
+ 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 */
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
+ return 0;
+}
+
+/*
+ * Check that CSR access is allowed.
+ *
+ * Adapted from Spike's decode.h:validate_csr
+ */
+void validate_csr(CPURISCVState *env, uint64_t which, uint64_t write)
+{
+#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, env->pc);
+ }
+#endif
+}
+
+target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
+ target_ulong csr)
+{
+ validate_csr(env, csr, 1);
+ 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);
+ 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);
+ 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
+
+void set_privilege(CPURISCVState *env, target_ulong newpriv)
+{
+ if (!(newpriv <= PRV_M)) {
+ printf("INVALID PRIV SET\n");
+ exit(1);
+ }
+ if (newpriv == PRV_H) {
+ newpriv = PRV_U;
+ }
+ helper_tlb_flush(env);
+ env->priv = newpriv;
+}
+
+target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+ if (!(env->priv >= PRV_S)) {
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
+ }
+
+ target_ulong retpc = env->sepc;
+ if (!riscv_feature(env, RISCV_FEATURE_RVC) && (retpc & 0x3)) {
+ helper_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS);
+ }
+
+ target_ulong mstatus = env->mstatus;
+ target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP);
+ mstatus = set_field(mstatus, MSTATUS_UIE << prev_priv,
+ get_field(mstatus, MSTATUS_SPIE));
+ mstatus = set_field(mstatus, MSTATUS_SPIE, 0);
+ mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
+ set_privilege(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)) {
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
+ }
+
+ target_ulong retpc = env->mepc;
+ if (!riscv_feature(env, RISCV_FEATURE_RVC) && (retpc & 0x3)) {
+ helper_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS);
+ }
+
+ target_ulong mstatus = env->mstatus;
+ target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
+ mstatus = set_field(mstatus, MSTATUS_UIE << prev_priv,
+ get_field(mstatus, MSTATUS_MPIE));
+ mstatus = set_field(mstatus, MSTATUS_MPIE, 0);
+ mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
+ set_privilege(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_fence_i(CPURISCVState *env)
+{
+ RISCVCPU *cpu = riscv_env_get_cpu(env);
+ CPUState *cs = CPU(cpu);
+ /* Flush QEMU's TLB */
+ tlb_flush(cs);
+ /* ARM port seems to not know if this is okay inside a TB
+ But we need to do it */
+ tb_flush(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 */
--
2.7.0
Richard Henderson
2018-01-03 07:12:27 UTC
Permalink
Post by Michael Clark
+ target_ulong mode = env->priv;
+ if (access_type != MMU_INST_FETCH) {
+ if (get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ }
This is replicating cpu_mmu_index.
Therefore you should be relying on mmu_idx.
Post by Michael Clark
+ /* check to make sure that mmu_idx and mode that we get matches */
+ if (unlikely(mode != mmu_idx)) {
+ fprintf(stderr, "MODE: mmu_idx mismatch\n");
+ exit(1);
+ }
As in the opposite of this.
Post by Michael Clark
+
+ if (mode == PRV_M) {
+ target_ulong msb_mask = /*0x7FFFFFFFFFFFFFFF; */
+ (((target_ulong)2) << (TARGET_LONG_BITS - 1)) - 1;
+ *physical = address & msb_mask;
Or perhaps extract64(address, 0, TARGET_LONG_BITS - 1)?
Post by Michael Clark
+ 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) {
+ levels = 2; ptidxbits = 10; ptesize = 4; break;
+ levels = 3; ptidxbits = 9; ptesize = 8; break;
+ levels = 4; ptidxbits = 9; ptesize = 8; break;
+ levels = 5; ptidxbits = 9; ptesize = 8; break;
+ printf("unsupported SATP_MODE value\n");
+ exit(1);
Just qemu_log_mask with LOG_UNIMP or LOG_GUEST_ERROR, and then return
TRANSLATE_FAIL. Printing to stdout and exiting isn't kosher. Lots more
occurrences within this file.
Post by Michael Clark
+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;
+ int exception = 0;
+ if (access_type == MMU_INST_FETCH) { /* inst access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_STORE) { /* store access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_LOAD) { /* load access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT;
+ env->badaddr = address;
+ } else {
+ fprintf(stderr, "FAIL: invalid access_type\n");
+ exit(1);
Switch with a default: g_assert_not_reached(), since access_type is not
controlled by the guest.
Post by Michael Clark
+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;
+ if (access_type == MMU_INST_FETCH) {
+ fprintf(stderr, "unaligned inst fetch not handled here. should not "
+ "trigger\n");
+ exit(1);
No exit. Do something logical.
Post by Michael Clark
+ } else if (access_type == MMU_DATA_STORE) {
+ cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS;
+ env->badaddr = addr;
Why does STORE imply AMO? Why can't a normal store trigger an unaligned trap?
Post by Michael Clark
+ fprintf(stderr, "Invalid MMUAccessType\n");
+ exit(1);
I'll stop pointing these out, but there need to be zero instances of exit
within the backend.
Post by Michael Clark
+void riscv_cpu_do_interrupt(CPUState *cs)
+{
+#if !defined(CONFIG_USER_ONLY)
+
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ #ifdef RISCV_DEBUG_INTERRUPT
+ if (cs->exception_index & 0x70000000) {
+ fprintf(stderr, "core 0: exception trap_%s, epc 0x" TARGET_FMT_lx "\n"
+ , riscv_interrupt_names[cs->exception_index & 0x0fffffff],
+ env->pc);
+ } else {
+ fprintf(stderr, "core 0: exception trap_%s, epc 0x" TARGET_FMT_lx "\n"
+ , riscv_excp_names[cs->exception_index], env->pc);
+ }
+ #endif
+
+ if (cs->exception_index == RISCV_EXCP_BREAKPOINT) {
+ fprintf(stderr, "debug mode not implemented\n");
+ }
+
+ /* skip dcsr cause check */
+
+ target_ulong fixed_cause = 0;
+ if (cs->exception_index & (0x70000000)) {
+ /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception
+ index is only 32 bits wide */
+ fixed_cause = cs->exception_index & 0x0FFFFFFF;
+ 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) {
+ fixed_cause = RISCV_EXCP_U_ECALL;
+ break;
+ fixed_cause = RISCV_EXCP_S_ECALL;
+ break;
+ fixed_cause = RISCV_EXCP_H_ECALL;
+ break;
+ 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) {
+ #ifdef RISCV_DEBUG_INTERRUPT
+ fprintf(stderr, "core %d: badaddr 0x" TARGET_FMT_lx "\n",
+ env->mhartid, env->badaddr);
+ #endif
+ env->sbadaddr = env->badaddr;
+ }
+
+ target_ulong s = env->mstatus;
+ s = set_field(s, MSTATUS_SPIE, 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);
+ set_privilege(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) {
+ #ifdef RISCV_DEBUG_INTERRUPT
+ fprintf(stderr, "core %d: badaddr 0x" TARGET_FMT_lx "\n",
+ env->mhartid, env->badaddr);
+ #endif
+ env->mbadaddr = env->badaddr;
+ }
+
+ target_ulong s = env->mstatus;
+ s = set_field(s, MSTATUS_MPIE, 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);
+ set_privilege(env, PRV_M);
+ }
+ /* TODO yield load reservation */
+#endif
+ cs->exception_index = EXCP_NONE; /* mark handled to qemu */
+}
Marking handled is done generically. Why do you need to do it here?
Post by Michael Clark
+/* Floating Point - fused */
+DEF_HELPER_FLAGS_5(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64)
Ideally these would go in with the patch that adds the helpers, so they're
easier to validate. However, I suppose it doesn't really matter.
Post by Michael Clark
+void helper_raise_exception_mbadaddr(CPURISCVState *env, uint32_t exception,
+ target_ulong bad_pc) {
Brace on next line.
Post by Michael Clark
+ #ifdef RISCV_DEBUG_PRINT
+ fprintf(stderr, "Write CSR reg: 0x" TARGET_FMT_lx "\n", csrno);
+ fprintf(stderr, "Write CSR val: 0x" TARGET_FMT_lx "\n", val_to_write);
+ #endif
Drop the debugging prints. Perhaps use the tracing infrastructure?
Post by Michael Clark
+ case CSR_MISA: {
+ if (!(val_to_write & (1L << ('F' - 'A')))) {
+ val_to_write &= ~(1L << ('D' - 'A'));
+ }
+
+ /* allow MAFDC bits in MISA to be modified */
+ target_ulong mask = 0;
+ mask |= 1L << ('M' - 'A');
+ mask |= 1L << ('A' - 'A');
+ mask |= 1L << ('F' - 'A');
+ mask |= 1L << ('D' - 'A');
+ mask |= 1L << ('C' - 'A');
+ mask &= env->misa_mask;
+
+ env->misa = (val_to_write & mask) | (env->misa & ~mask);
Does this not affect the set of instructions that are allowable? If so, you'd
want something like

new_misa = (val_to_write & mask) | (env->misa & ~mask);
if (env->misa != new_misa) {
env->misa = new_misa;
tb_flush(CPU(riscv_env_get_cpu(env)));
}

so that we start with all new translations, which would then check the new
value of misa, and would then raise INST_ADDR_MIS (or not).
Post by Michael Clark
+inline target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno)
Why mark such large functions inline?
Post by Michael Clark
+void set_privilege(CPURISCVState *env, target_ulong newpriv)
+{
+ if (!(newpriv <= PRV_M)) {
+ printf("INVALID PRIV SET\n");
+ exit(1);
+ }
+ if (newpriv == PRV_H) {
+ newpriv = PRV_U;
+ }
+ helper_tlb_flush(env);
Why flush? Doesn't this just switch to a different mmu_idx?
Post by Michael Clark
+void helper_fence_i(CPURISCVState *env)
+{
+ RISCVCPU *cpu = riscv_env_get_cpu(env);
+ CPUState *cs = CPU(cpu);
+ /* Flush QEMU's TLB */
+ tlb_flush(cs);
+ /* ARM port seems to not know if this is okay inside a TB
+ But we need to do it */
+ tb_flush(cs);
+}
You should not require either flush.
This insn can be implemented in qemu as a nop.


r~
Michael Clark
2018-01-03 22:59:23 UTC
Permalink
On Wed, Jan 3, 2018 at 8:12 PM, Richard Henderson <
Post by Richard Henderson
Post by Michael Clark
+ target_ulong mode = env->priv;
+ if (access_type != MMU_INST_FETCH) {
+ if (get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ }
This is replicating cpu_mmu_index.
Therefore you should be relying on mmu_idx.
Post by Michael Clark
+ /* check to make sure that mmu_idx and mode that we get matches */
+ if (unlikely(mode != mmu_idx)) {
+ fprintf(stderr, "MODE: mmu_idx mismatch\n");
+ exit(1);
+ }
As in the opposite of this.
OK. cpu_mmu_index has already translated the mode into mmu_idx for us so we
can eliminate the redundant mode fetch, check and error message.

Essentially we should trust mmu_idx returned from cpu_mmu_index, so this
statement should never trigger.

Will include in the next spin.
Post by Richard Henderson
Post by Michael Clark
+
+ if (mode == PRV_M) {
+ target_ulong msb_mask = /*0x7FFFFFFFFFFFFFFF; */
+ (((target_ulong)2) << (TARGET_LONG_BITS - 1)) - 1;
+ *physical = address & msb_mask;
Or perhaps extract64(address, 0, TARGET_LONG_BITS - 1)?
Post by Michael Clark
+ 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) {
+ levels = 2; ptidxbits = 10; ptesize = 4; break;
+ levels = 3; ptidxbits = 9; ptesize = 8; break;
+ levels = 4; ptidxbits = 9; ptesize = 8; break;
+ levels = 5; ptidxbits = 9; ptesize = 8; break;
+ printf("unsupported SATP_MODE value\n");
+ exit(1);
Just qemu_log_mask with LOG_UNIMP or LOG_GUEST_ERROR, and then return
TRANSLATE_FAIL. Printing to stdout and exiting isn't kosher. Lots more
occurrences within this file.
Understand. I had aleady converted several printfs to error_report.

I wasn't sure which logging API to use. There are also quite a lot of uses
of grep -r error_report target.

I'll grep -r for printf and change to qemu_log_mask with appropriate level.

I see exit(1) called in quite a few of the other ports too. I was wondering
at the time if there is a canonical error_abort API?

Will try to improve things in the next spin.
Post by Richard Henderson
+static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
Post by Michael Clark
+ 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;
+ int exception = 0;
+ if (access_type == MMU_INST_FETCH) { /* inst access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_STORE) { /* store access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_
FAULT;
Post by Michael Clark
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_LOAD) { /* load access */
+ exception = page_fault_exceptions ?
+ RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT;
+ env->badaddr = address;
+ } else {
+ fprintf(stderr, "FAIL: invalid access_type\n");
+ exit(1);
Switch with a default: g_assert_not_reached(), since access_type is not
controlled by the guest.
Post by Michael Clark
+void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, int
mmu_idx,
Post by Michael Clark
+ uintptr_t retaddr)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ if (access_type == MMU_INST_FETCH) {
+ fprintf(stderr, "unaligned inst fetch not handled here. should
not "
Post by Michael Clark
+ "trigger\n");
+ exit(1);
No exit. Do something logical.
Got it. Assertion.
Post by Richard Henderson
Post by Michael Clark
+ } else if (access_type == MMU_DATA_STORE) {
+ cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS;
+ env->badaddr = addr;
Why does STORE imply AMO? Why can't a normal store trigger an unaligned trap?
It's STORE or AMO. Here are the exception causes from the RISC-V Privileged
ISA Specification:

# Machine Cause Register faults (mcause), interrupt bit clear
cause misaligned_fetch 0 "Instruction address misaligned"
cause fault_fetch 1 "Instruction access fault"
cause illegal_instruction 2 "Illegal instruction"
cause breakpoint 3 "Breakpoint"
cause misaligned_load 4 "Load address misaligned"
cause fault_load 5 "Load access fault"
cause misaligned_store 6 "Store/AMO address misaligned"
cause fault_store 7 "Store/AMO access fault"
cause user_ecall 8 "Environment call from U-mode"
cause supervisor_ecall 9 "Environment call from S-mode"
cause hypervisor_ecall 10 "Environment call from H-mode"
cause machine_ecall 11 "Environment call from M-mode"
cause exec_page_fault 12 "Instruction page fault"
cause load_page_fault 13 "Load page fault"
cause store_page_fault 15 "Store/AMO page fault"
Post by Richard Henderson
Post by Michael Clark
+ fprintf(stderr, "Invalid MMUAccessType\n");
+ exit(1);
I'll stop pointing these out, but there need to be zero instances of exit
within the backend.
I totally agree. Inherited. However it seems there are lots of calls to
exit(1) in many other targets. Not that that is an excuse.
Post by Richard Henderson
Post by Michael Clark
+void riscv_cpu_do_interrupt(CPUState *cs)
+{
+#if !defined(CONFIG_USER_ONLY)
+
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ #ifdef RISCV_DEBUG_INTERRUPT
+ if (cs->exception_index & 0x70000000) {
+ fprintf(stderr, "core 0: exception trap_%s, epc 0x"
TARGET_FMT_lx "\n"
Post by Michael Clark
+ , riscv_interrupt_names[cs->exception_index &
0x0fffffff],
Post by Michael Clark
+ env->pc);
+ } else {
+ fprintf(stderr, "core 0: exception trap_%s, epc 0x"
TARGET_FMT_lx "\n"
Post by Michael Clark
+ , riscv_excp_names[cs->exception_index], env->pc);
+ }
+ #endif
+
+ if (cs->exception_index == RISCV_EXCP_BREAKPOINT) {
+ fprintf(stderr, "debug mode not implemented\n");
+ }
+
+ /* skip dcsr cause check */
+
+ target_ulong fixed_cause = 0;
+ if (cs->exception_index & (0x70000000)) {
+ /* hacky for now. the MSB (bit 63) indicates interrupt but
cs->exception
Post by Michael Clark
+ index is only 32 bits wide */
+ fixed_cause = cs->exception_index & 0x0FFFFFFF;
+ 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) {
+ fixed_cause = RISCV_EXCP_U_ECALL;
+ break;
+ fixed_cause = RISCV_EXCP_S_ECALL;
+ break;
+ fixed_cause = RISCV_EXCP_H_ECALL;
+ break;
+ 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 */
Post by Michael Clark
+ env->pc = env->stvec;
+ env->scause = fixed_cause;
+ env->sepc = backup_epc;
+
+ if (hasbadaddr) {
+ #ifdef RISCV_DEBUG_INTERRUPT
+ fprintf(stderr, "core %d: badaddr 0x" TARGET_FMT_lx "\n",
+ env->mhartid, env->badaddr);
+ #endif
+ env->sbadaddr = env->badaddr;
+ }
+
+ target_ulong s = env->mstatus;
+ s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_UIE <<
env->priv));
Post by Michael Clark
+ s = set_field(s, MSTATUS_SPP, env->priv);
+ s = set_field(s, MSTATUS_SIE, 0);
+ csr_write_helper(env, s, CSR_MSTATUS);
+ set_privilege(env, PRV_S);
+ } else {
+ /* No need to check MTVEC for misaligned - lower 2 bits cannot
be set */
Post by Michael Clark
+ env->pc = env->mtvec;
+ env->mepc = backup_epc;
+ env->mcause = fixed_cause;
+
+ if (hasbadaddr) {
+ #ifdef RISCV_DEBUG_INTERRUPT
+ fprintf(stderr, "core %d: badaddr 0x" TARGET_FMT_lx "\n",
+ env->mhartid, env->badaddr);
+ #endif
+ env->mbadaddr = env->badaddr;
+ }
+
+ target_ulong s = env->mstatus;
+ s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_UIE <<
env->priv));
Post by Michael Clark
+ s = set_field(s, MSTATUS_MPP, env->priv);
+ s = set_field(s, MSTATUS_MIE, 0);
+ csr_write_helper(env, s, CSR_MSTATUS);
+ set_privilege(env, PRV_M);
+ }
+ /* TODO yield load reservation */
+#endif
+ cs->exception_index = EXCP_NONE; /* mark handled to qemu */
+}
Marking handled is done generically. Why do you need to do it here?
Nothing i guess. I'll remove and re-test...
Post by Richard Henderson
Post by Michael Clark
+/* Floating Point - fused */
+DEF_HELPER_FLAGS_5(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64,
i64)
Ideally these would go in with the patch that adds the helpers, so they're
easier to validate. However, I suppose it doesn't really matter.
Post by Michael Clark
+void helper_raise_exception_mbadaddr(CPURISCVState *env, uint32_t
exception,
Post by Michael Clark
+ target_ulong bad_pc) {
Brace on next line.
Post by Michael Clark
+ #ifdef RISCV_DEBUG_PRINT
+ fprintf(stderr, "Write CSR reg: 0x" TARGET_FMT_lx "\n", csrno);
+ fprintf(stderr, "Write CSR val: 0x" TARGET_FMT_lx "\n",
val_to_write);
Post by Michael Clark
+ #endif
Drop the debugging prints. Perhaps use the tracing infrastructure?
Agree. That will take some time...
Post by Richard Henderson
Post by Michael Clark
+ case CSR_MISA: {
+ if (!(val_to_write & (1L << ('F' - 'A')))) {
+ val_to_write &= ~(1L << ('D' - 'A'));
+ }
+
+ /* allow MAFDC bits in MISA to be modified */
+ target_ulong mask = 0;
+ mask |= 1L << ('M' - 'A');
+ mask |= 1L << ('A' - 'A');
+ mask |= 1L << ('F' - 'A');
+ mask |= 1L << ('D' - 'A');
+ mask |= 1L << ('C' - 'A');
+ mask &= env->misa_mask;
+
+ env->misa = (val_to_write & mask) | (env->misa & ~mask);
Does this not affect the set of instructions that are allowable? If so, you'd
want something like
new_misa = (val_to_write & mask) | (env->misa & ~mask);
if (env->misa != new_misa) {
env->misa = new_misa;
tb_flush(CPU(riscv_env_get_cpu(env)));
}
so that we start with all new translations, which would then check the new
value of misa, and would then raise INST_ADDR_MIS (or not).
Post by Michael Clark
+inline target_ulong csr_read_helper(CPURISCVState *env, target_ulong
csrno)
Why mark such large functions inline?
Inherited. I'd outlined some occurences.

It seems we need to do quite a bit of work on the cpu core.
Post by Richard Henderson
+void set_privilege(CPURISCVState *env, target_ulong newpriv)
Post by Michael Clark
+{
+ if (!(newpriv <= PRV_M)) {
+ printf("INVALID PRIV SET\n");
+ exit(1);
+ }
+ if (newpriv == PRV_H) {
+ newpriv = PRV_U;
+ }
+ helper_tlb_flush(env);
Why flush? Doesn't this just switch to a different mmu_idx?
Sagar or Bastien might be able to answer that.
Post by Richard Henderson
Post by Michael Clark
+void helper_fence_i(CPURISCVState *env)
+{
+ RISCVCPU *cpu = riscv_env_get_cpu(env);
+ CPUState *cs = CPU(cpu);
+ /* Flush QEMU's TLB */
+ tlb_flush(cs);
+ /* ARM port seems to not know if this is okay inside a TB
+ But we need to do it */
+ tb_flush(cs);
+}
You should not require either flush.
This insn can be implemented in qemu as a nop.
So self modifying code with no advice is fine? I guess that is required to
support x86. Will include in the next spin.

This may take some time... When is code freeze for 2.12?
Richard Henderson
2018-01-03 23:25:12 UTC
Permalink
I see exit(1) called in quite a few of the other ports too. I was wondering at
the time if there is a canonical error_abort API?
Yes, but they're wrong too. Lots of that is old code in less maintained targets.

The only time errors should exit are when parsing options for startup. Even
then new code should use qapi/error.h, propagating the error back to generic
code. (This is where your canonical error_abort API is located.)

Once running, guest errors should continue as best as we can. Either ignoring
the action or raising an exception are usually the right thing. The guest --
and even more importantly a guest running without supervisor -- should not be
able to force the hypervisor to shutdown.

Asserting for logic errors that are fully within the hypervisor are permitted.
It should be taken as written that any such assertion actually triggering is a
bug to be fixed.

We prefer g_assert_not_reached() over assert(false) or abort() for protecting
code paths that should not be reachable. I do not use the other g_assert*
functions myself, though other parts of qemu do.


r~
Stefan O'Rear
2018-01-10 10:35:52 UTC
Permalink
On Tue, Jan 2, 2018 at 11:12 PM, Richard Henderson
Post by Richard Henderson
Post by Michael Clark
+ case CSR_MISA: {
+ if (!(val_to_write & (1L << ('F' - 'A')))) {
+ val_to_write &= ~(1L << ('D' - 'A'));
+ }
+
+ /* allow MAFDC bits in MISA to be modified */
+ target_ulong mask = 0;
+ mask |= 1L << ('M' - 'A');
+ mask |= 1L << ('A' - 'A');
+ mask |= 1L << ('F' - 'A');
+ mask |= 1L << ('D' - 'A');
+ mask |= 1L << ('C' - 'A');
+ mask &= env->misa_mask;
+
+ env->misa = (val_to_write & mask) | (env->misa & ~mask);
Does this not affect the set of instructions that are allowable? If so, you'd
want something like
new_misa = (val_to_write & mask) | (env->misa & ~mask);
if (env->misa != new_misa) {
env->misa = new_misa;
tb_flush(CPU(riscv_env_get_cpu(env)));
}
so that we start with all new translations, which would then check the new
value of misa, and would then raise INST_ADDR_MIS (or not).
This does not seem quite right. misa can legally differ between
cores/threads, but tb_flush is a global operation. The way this is
supposed to work is that the relevant misa bits are extracted into
tb_flags:

static inline void cpu_riscv_set_tb_flags(CPURISCVState *env)
{
env->tb_flags = 0;
if (env->misa & MISA_A) {
env->tb_flags |= RISCV_TF_MISA_A;
}

if (env->misa & MISA_D) {
env->tb_flags |= RISCV_TF_MISA_D;
}

if (env->misa & MISA_F) {
env->tb_flags |= RISCV_TF_MISA_F;
}

if (env->misa & MISA_M) {
env->tb_flags |= RISCV_TF_MISA_M;
}

if (env->misa & MISA_C) {
env->tb_flags |= RISCV_TF_MISA_C;
}

env->tb_flags |= cpu_mmu_index(env, true) << RISCV_TF_IAT_SHIFT;
env->tb_flags |= cpu_mmu_index(env, false) << RISCV_TF_DAT_SHIFT;
}

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;
*flags = env->tb_flags;
}

but this code appears to be missing in the tree submitted for upstreaming?

-s
Richard Henderson
2018-01-10 17:04:19 UTC
Permalink
Post by Stefan O'Rear
On Tue, Jan 2, 2018 at 11:12 PM, Richard Henderson
Post by Richard Henderson
Post by Michael Clark
+ case CSR_MISA: {
+ if (!(val_to_write & (1L << ('F' - 'A')))) {
+ val_to_write &= ~(1L << ('D' - 'A'));
+ }
+
+ /* allow MAFDC bits in MISA to be modified */
+ target_ulong mask = 0;
+ mask |= 1L << ('M' - 'A');
+ mask |= 1L << ('A' - 'A');
+ mask |= 1L << ('F' - 'A');
+ mask |= 1L << ('D' - 'A');
+ mask |= 1L << ('C' - 'A');
+ mask &= env->misa_mask;
+
+ env->misa = (val_to_write & mask) | (env->misa & ~mask);
Does this not affect the set of instructions that are allowable? If so, you'd
want something like
new_misa = (val_to_write & mask) | (env->misa & ~mask);
if (env->misa != new_misa) {
env->misa = new_misa;
tb_flush(CPU(riscv_env_get_cpu(env)));
}
so that we start with all new translations, which would then check the new
value of misa, and would then raise INST_ADDR_MIS (or not).
This does not seem quite right. misa can legally differ between
cores/threads, but tb_flush is a global operation. The way this is
supposed to work is that the relevant misa bits are extracted into
static inline void cpu_riscv_set_tb_flags(CPURISCVState *env)
{
env->tb_flags = 0;
if (env->misa & MISA_A) {
env->tb_flags |= RISCV_TF_MISA_A;
}
if (env->misa & MISA_D) {
env->tb_flags |= RISCV_TF_MISA_D;
}
if (env->misa & MISA_F) {
env->tb_flags |= RISCV_TF_MISA_F;
}
if (env->misa & MISA_M) {
env->tb_flags |= RISCV_TF_MISA_M;
}
if (env->misa & MISA_C) {
env->tb_flags |= RISCV_TF_MISA_C;
}
env->tb_flags |= cpu_mmu_index(env, true) << RISCV_TF_IAT_SHIFT;
env->tb_flags |= cpu_mmu_index(env, false) << RISCV_TF_DAT_SHIFT;
}
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;
*flags = env->tb_flags;
}
but this code appears to be missing in the tree submitted for upstreaming?
Ah hah. Yes, this is another completely valid way to accomplish this.

I am also glad that you are thinking about the computational overhead of
cpu_get_tb_cpu_state. With lookup_and_goto_ptr, it is in the hot path of
indirect branching.


r~
Christoph Hellwig
2018-01-08 14:28:50 UTC
Permalink
Post by Michael Clark
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (get_field(env->satp, SATP_MODE) == VM_1_09_MBARE) {
+ mode = PRV_M;
+ }
+ } else {
+ if (get_field(env->mstatus, MSTATUS_VM) == VM_1_10_MBARE) {
+ mode = PRV_M;
+ }
+ }
This mixes up VM_1_09_MBARE and VM_1_10_MBARE, but they evaluate to
the same value anyway.

And as Richard said just rely on the mmu_idx from cpu_mmu_index. I
actually already did the change to remove it in a patch for a new riscv
CSR I developed and can thus confirm it works fine.
This should use CSR_SATP. In fact even if you want to keep 1.9.1 support
I would highly recommend to remove the CSR_SPTBR define and only use
CSR_SATP in code, with sptbr limited to comments to avoid confusion.
Michael Clark
2018-01-03 00:44:10 UTC
Permalink
Helper routines for FPU instructions and NaN definitions.

Signed-off-by: Michael Clark <***@sifive.com>
---
fpu/softfloat-specialize.h | 7 +-
target/riscv/fpu_helper.c | 591 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 595 insertions(+), 3 deletions(-)
create mode 100644 target/riscv/fpu_helper.c

diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index de2c5d5..49ee578 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));
@@ -189,7 +190,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/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
new file mode 100644
index 0000000..ada985f
--- /dev/null
+++ b/target/riscv/fpu_helper.c
@@ -0,0 +1,591 @@
+/*
+ * RISC-V FPU Emulation Helpers for QEMU.
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/helper-proto.h"
+
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
+ float_round_nearest_even,
+ float_round_to_zero,
+ float_round_down,
+ float_round_up,
+ float_round_ties_away
+};
+
+/* obtain rm value to use in computation
+ * as the last step, convert rm codes to what the softfloat library expects
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({ \
+if (rm == 7) { \
+ rm = env->frm; \
+} \
+if (rm > 4) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+} \
+ieee_rm[rm]; })
+
+#ifndef CONFIG_USER_ONLY
+#define require_fp if (!(env->mstatus & MSTATUS_FS)) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+}
+#else
+#define require_fp /* nop */
+#endif
+
+/* convert softfloat library flag numbers to RISC-V */
+unsigned int softfloat_flags_to_riscv(unsigned int flags)
+{
+ int rv_flags = 0;
+ rv_flags |= (flags & float_flag_inexact) ? 1 : 0;
+ rv_flags |= (flags & float_flag_underflow) ? 2 : 0;
+ rv_flags |= (flags & float_flag_overflow) ? 4 : 0;
+ rv_flags |= (flags & float_flag_divbyzero) ? 8 : 0;
+ rv_flags |= (flags & float_flag_invalid) ? 16 : 0;
+ return rv_flags;
+}
+
+/* adapted from Spike's decode.h:set_fp_exceptions */
+#define set_fp_exceptions() do { \
+ env->fflags |= softfloat_flags_to_riscv(get_float_exception_flags(\
+ &env->fp_status)); \
+ set_float_exception_flags(0, &env->fp_status); \
+} while (0)
+
+uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1, frs2, frs3, 0, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_muladd(frs1, frs2, frs3, 0, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0,
+ &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_muladd(frs1, frs2, frs3 ^ (uint64_t)INT64_MIN, 0,
+ &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, frs3, 0,
+ &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fnmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_muladd(frs1 ^ (uint64_t)INT64_MIN, frs2, frs3, 0,
+ &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2,
+ frs3 ^ (uint32_t)INT32_MIN, 0, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fnmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_muladd(frs1 ^ (uint64_t)INT64_MIN, frs2,
+ frs3 ^ (uint64_t)INT64_MIN, 0, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_add(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_sub(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmul_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_mul(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_div(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_minnum(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmax_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_maxnum(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_sqrt(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_fle_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_le(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_flt_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_lt(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_feq_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_eq_quiet(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_to_int32(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = (int32_t)float32_to_uint32(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_l_s(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_to_int64(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fcvt_lu_s(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_to_uint64(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+#endif
+
+uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = int32_to_float32((int32_t)rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+
+uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = uint32_to_float32((uint32_t)rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_s_l(CPURISCVState *env, uint64_t rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = int64_to_float32(rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+
+uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = uint64_to_float32(rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+#endif
+
+/* adapted from spike */
+#define isNaNF32UI(ui) (0xFF000000 < (uint32_t)((uint_fast32_t)ui << 1))
+#define signF32UI(a) ((bool)((uint32_t)a >> 31))
+#define expF32UI(a) ((int_fast16_t)(a >> 23) & 0xFF)
+#define fracF32UI(a) (a & 0x007FFFFF)
+
+union ui32_f32 { uint32_t ui; uint32_t f; };
+
+uint_fast16_t float32_classify(uint32_t a, float_status *status)
+{
+ union ui32_f32 uA;
+ uint_fast32_t uiA;
+
+ uA.f = a;
+ uiA = uA.ui;
+
+ uint_fast16_t infOrNaN = expF32UI(uiA) == 0xFF;
+ uint_fast16_t subnormalOrZero = expF32UI(uiA) == 0;
+ bool sign = signF32UI(uiA);
+
+ return
+ (sign && infOrNaN && fracF32UI(uiA) == 0) << 0 |
+ (sign && !infOrNaN && !subnormalOrZero) << 1 |
+ (sign && subnormalOrZero && fracF32UI(uiA)) << 2 |
+ (sign && subnormalOrZero && fracF32UI(uiA) == 0) << 3 |
+ (!sign && infOrNaN && fracF32UI(uiA) == 0) << 7 |
+ (!sign && !infOrNaN && !subnormalOrZero) << 6 |
+ (!sign && subnormalOrZero && fracF32UI(uiA)) << 5 |
+ (!sign && subnormalOrZero && fracF32UI(uiA) == 0) << 4 |
+ (isNaNF32UI(uiA) && float32_is_signaling_nan(uiA, status)) << 8 |
+ (isNaNF32UI(uiA) && !float32_is_signaling_nan(uiA, status)) << 9;
+}
+
+target_ulong helper_fclass_s(CPURISCVState *env, uint64_t frs1)
+{
+ require_fp;
+ frs1 = float32_classify(frs1, &env->fp_status);
+ return frs1;
+}
+
+uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_add(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_sub(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmul_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_mul(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_div(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float64_minnum(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float64_maxnum(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = float64_to_float32(rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+
+uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = float32_to_float64(rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+
+uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_sqrt(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float64_le(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float64_lt(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float64_eq_quiet(frs1, frs2, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = (int64_t)((int32_t)float64_to_int32(frs1, &env->fp_status));
+ set_fp_exceptions();
+ return frs1;
+}
+
+target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = (int64_t)((int32_t)float64_to_uint32(frs1, &env->fp_status));
+ set_fp_exceptions();
+ return frs1;
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_to_int64(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+
+uint64_t helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float64_to_uint64(frs1, &env->fp_status);
+ set_fp_exceptions();
+ return frs1;
+}
+#endif
+
+uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1, uint64_t rm)
+{
+ require_fp;
+ uint64_t res;
+ set_float_rounding_mode(RM, &env->fp_status);
+ res = int32_to_float64((int32_t)rs1, &env->fp_status);
+ set_fp_exceptions();
+ return res;
+}
+
+uint64_t helper_fcvt_d_wu(CPURISCVState *env, target_ulong rs1, uint64_t rm)
+{
+ require_fp;
+ uint64_t res;
+ set_float_rounding_mode(RM, &env->fp_status);
+ res = uint32_to_float64((uint32_t)rs1, &env->fp_status);
+ set_fp_exceptions();
+ return res;
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_d_l(CPURISCVState *env, uint64_t rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = int64_to_float64(rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+
+uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ rs1 = uint64_to_float64(rs1, &env->fp_status);
+ set_fp_exceptions();
+ return rs1;
+}
+#endif
+
+/* adapted from spike */
+#define isNaNF64UI(ui) (UINT64_C(0xFFE0000000000000) \
+ < (uint64_t)((uint_fast64_t)ui << 1))
+#define signF64UI(a) ((bool)((uint64_t) a >> 63))
+#define expF64UI(a) ((int_fast16_t)(a >> 52) & 0x7FF)
+#define fracF64UI(a) (a & UINT64_C(0x000FFFFFFFFFFFFF))
+
+union ui64_f64 { uint64_t ui; uint64_t f; };
+
+uint_fast16_t float64_classify(uint64_t a, float_status *status)
+{
+ union ui64_f64 uA;
+ uint_fast64_t uiA;
+
+ uA.f = a;
+ uiA = uA.ui;
+
+ uint_fast16_t infOrNaN = expF64UI(uiA) == 0x7FF;
+ uint_fast16_t subnormalOrZero = expF64UI(uiA) == 0;
+ bool sign = signF64UI(uiA);
+
+ return
+ (sign && infOrNaN && fracF64UI(uiA) == 0) << 0 |
+ (sign && !infOrNaN && !subnormalOrZero) << 1 |
+ (sign && subnormalOrZero && fracF64UI(uiA)) << 2 |
+ (sign && subnormalOrZero && fracF64UI(uiA) == 0) << 3 |
+ (!sign && infOrNaN && fracF64UI(uiA) == 0) << 7 |
+ (!sign && !infOrNaN && !subnormalOrZero) << 6 |
+ (!sign && subnormalOrZero && fracF64UI(uiA)) << 5 |
+ (!sign && subnormalOrZero && fracF64UI(uiA) == 0) << 4 |
+ (isNaNF64UI(uiA) && float64_is_signaling_nan(uiA, status)) << 8 |
+ (isNaNF64UI(uiA) && !float64_is_signaling_nan(uiA, status)) << 9;
+}
+
+target_ulong helper_fclass_d(CPURISCVState *env, uint64_t frs1)
+{
+ require_fp;
+ frs1 = float64_classify(frs1, &env->fp_status);
+ return frs1;
+}
--
2.7.0
Richard Henderson
2018-01-03 20:10:56 UTC
Permalink
Post by Michael Clark
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
static const.
Post by Michael Clark
+/* obtain rm value to use in computation
+ * as the last step, convert rm codes to what the softfloat library expects
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({ \
+if (rm == 7) { \
+ rm = env->frm; \
+} \
+if (rm > 4) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+} \
+ieee_rm[rm]; })
Please use inline functions and not these macros.

Probably this applies to some of the helpers that I've already reviewed, but
you're going to need to use an exception raising function that also performs an
unwind (usually via cpu_loop_exit_restore). The GETPC() that feeds the unwind
must be placed in the outer-most helper (including inlines).
Post by Michael Clark
+#ifndef CONFIG_USER_ONLY
+#define require_fp if (!(env->mstatus & MSTATUS_FS)) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+}
If you included MSTATUS_FS in cpu_get_tb_cpu_state flags, then you could be
checking for this at translation time instead of at run time.
Post by Michael Clark
+/* convert softfloat library flag numbers to RISC-V */
+unsigned int softfloat_flags_to_riscv(unsigned int flags)
+{
+ int rv_flags = 0;
+ rv_flags |= (flags & float_flag_inexact) ? 1 : 0;
+ rv_flags |= (flags & float_flag_underflow) ? 2 : 0;
+ rv_flags |= (flags & float_flag_overflow) ? 4 : 0;
+ rv_flags |= (flags & float_flag_divbyzero) ? 8 : 0;
+ rv_flags |= (flags & float_flag_invalid) ? 16 : 0;
FPEXC_NX et al.
Post by Michael Clark
+/* adapted from Spike's decode.h:set_fp_exceptions */
+#define set_fp_exceptions() do { \
+ env->fflags |= softfloat_flags_to_riscv(get_float_exception_flags(\
+ &env->fp_status)); \
+ set_float_exception_flags(0, &env->fp_status); \
+} while (0)
inline function. Usually written as

int flags = get_float_exception_flags(&env->fp_status);
if (flags) {
set_float_exception_flags(0, &env->fp_status);
env->fflags |= softfloat_flags_to_riscv(flags);
}

since we really do expect exceptions to be exceptional.
Post by Michael Clark
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0,
+ &env->fp_status);
Given that RISC-V always returns a default NaN, you obviously do not care about
the sign of a NaN result. Therefore you should use float_muladd_negate_c as
the fourth argument here and not perform the sign flip manually.
Post by Michael Clark
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, frs3, 0,
+ &env->fp_status);
float_muladd_negate_product.
Post by Michael Clark
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2,
+ frs3 ^ (uint32_t)INT32_MIN, 0, &env->fp_status);
float_muladd_negate_c | float_muladd_negate_product
Post by Michael Clark
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_minnum(frs1, frs2, &env->fp_status);
If you want minnum and not min, riscv-spec-v2.2.pdf could use some more verbage
to specify that.
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF32UI(ui) (0xFF000000 < (uint32_t)((uint_fast32_t)ui << 1))
float32_is_any_nan
Post by Michael Clark
+#define signF32UI(a) ((bool)((uint32_t)a >> 31))
float32_is_neg
Post by Michael Clark
+#define expF32UI(a) ((int_fast16_t)(a >> 23) & 0xFF)
+#define fracF32UI(a) (a & 0x007FFFFF)
Either float32_is_infinity or float32_is_zero_or_denormal.
Post by Michael Clark
+union ui32_f32 { uint32_t ui; uint32_t f; };
This is just silly.
Post by Michael Clark
+uint_fast16_t float32_classify(uint32_t a, float_status *status)
Please drop *_fast*_t for just "int".
Post by Michael Clark
+ return
+ (sign && infOrNaN && fracF32UI(uiA) == 0) << 0 |
+ (sign && !infOrNaN && !subnormalOrZero) << 1 |
+ (sign && subnormalOrZero && fracF32UI(uiA)) << 2 |
+ (sign && subnormalOrZero && fracF32UI(uiA) == 0) << 3 |
+ (!sign && infOrNaN && fracF32UI(uiA) == 0) << 7 |
+ (!sign && !infOrNaN && !subnormalOrZero) << 6 |
+ (!sign && subnormalOrZero && fracF32UI(uiA)) << 5 |
+ (!sign && subnormalOrZero && fracF32UI(uiA) == 0) << 4 |
+ (isNaNF32UI(uiA) && float32_is_signaling_nan(uiA, status)) << 8 |
+ (isNaNF32UI(uiA) && !float32_is_signaling_nan(uiA, status)) << 9;
These conditions are all exclusive. You do not need to compute all of them.

if (float32_is_any_nan(f)) {
if (float32_is_signaling_nan(f, status)) {
return 1 << 8;
} else {
return 1 << 9;
}
}
if (float32_is_neg(f)) {
if (float32_is_infinity(f)) {
return 1 << 0;
} else if (float32_is_zero(f)) {
return 1 << 3;
} else if (float32_is_zero_or_denormal(f)) {
return 1 << 2;
} else {
return 1 << 1;
}
} else {
...
}
Post by Michael Clark
+ frs1 = (int64_t)((int32_t)float64_to_int32(frs1, &env->fp_status));
Double cast is pointless, as are the extra parenthesis.
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF64UI(ui) (UINT64_C(0xFFE0000000000000) \
+ < (uint64_t)((uint_fast64_t)ui << 1))
+#define signF64UI(a) ((bool)((uint64_t) a >> 63))
+#define expF64UI(a) ((int_fast16_t)(a >> 52) & 0x7FF)
+#define fracF64UI(a) (a & UINT64_C(0x000FFFFFFFFFFFFF))
+
+union ui64_f64 { uint64_t ui; uint64_t f; };
+
+uint_fast16_t float64_classify(uint64_t a, float_status *status)
Likewise.


r~
Michael Clark
2018-01-23 21:37:44 UTC
Permalink
On Wed, Jan 3, 2018 at 12:10 PM, Richard Henderson <
Post by Richard Henderson
Post by Michael Clark
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
static const.
Done.
Post by Richard Henderson
+/* obtain rm value to use in computation
Post by Michael Clark
+ * as the last step, convert rm codes to what the softfloat library
expects
Post by Michael Clark
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({ \
+if (rm == 7) { \
+ rm = env->frm; \
+} \
+if (rm > 4) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+} \
+ieee_rm[rm]; })
Please use inline functions and not these macros.
Done.

In fact the previous code would, with normal control flow, dereference
ieee_rm[rm] with an out of bounds round mode so assuming
helper_raise_exception does a longjmp, i've inserted g_assert_not_reached()
after the exception. An analyser could detect that ieee_rm has an out of
bounds access assuming normal control flow. e.g.

static inline void set_fp_round_mode(CPURISCVState *env, uint64_t rm)
{
if (rm == 7) {
rm = env->frm;
} else if (rm > 4) {
helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
g_assert_not_reached();
}
set_float_rounding_mode(ieee_rm[rm], &env->fp_status);
}
Post by Richard Henderson
Probably this applies to some of the helpers that I've already reviewed, but
you're going to need to use an exception raising function that also performs an
unwind (usually via cpu_loop_exit_restore). The GETPC() that feeds the unwind
must be placed in the outer-most helper (including inlines).
Post by Michael Clark
+#ifndef CONFIG_USER_ONLY
+#define require_fp if (!(env->mstatus & MSTATUS_FS)) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+}
If you included MSTATUS_FS in cpu_get_tb_cpu_state flags, then you could be
checking for this at translation time instead of at run time.
I'll keep a note of this but I might attempt this later. We have some other
changes in this area with repsect to cpu_mmu_index.

Are translations not implicitly indexed by cpu_mmu_index? i.e. do we also
need to add cpu_mmu_index to cpu_get_tb_cpu_state flags to prevent code
translated for one value of mmu_index running in code with another value of
mmu_index (in the case of riscv, we currently return processor mode /
privilege level in cpu_mmu_index).
Post by Richard Henderson
Post by Michael Clark
+/* convert softfloat library flag numbers to RISC-V */
+unsigned int softfloat_flags_to_riscv(unsigned int flags)
+{
+ int rv_flags = 0;
+ rv_flags |= (flags & float_flag_inexact) ? 1 : 0;
+ rv_flags |= (flags & float_flag_underflow) ? 2 : 0;
+ rv_flags |= (flags & float_flag_overflow) ? 4 : 0;
+ rv_flags |= (flags & float_flag_divbyzero) ? 8 : 0;
+ rv_flags |= (flags & float_flag_invalid) ? 16 : 0;
FPEXC_NX et al.
Post by Michael Clark
+/* adapted from Spike's decode.h:set_fp_exceptions */
+#define set_fp_exceptions() do { \
+ env->fflags |= softfloat_flags_to_riscv(get_float_exception_flags(\
+ &env->fp_status)); \
+ set_float_exception_flags(0, &env->fp_status); \
+} while (0)
inline function. Usually written as
int flags = get_float_exception_flags(&env->fp_status);
if (flags) {
set_float_exception_flags(0, &env->fp_status);
env->fflags |= softfloat_flags_to_riscv(flags);
}
since we really do expect exceptions to be exceptional.
Done.
Post by Richard Henderson
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0,
+ &env->fp_status);
Given that RISC-V always returns a default NaN, you obviously do not care about
the sign of a NaN result. Therefore you should use float_muladd_negate_c as
the fourth argument here and not perform the sign flip manually.
Now working on feedback from here on...

Here is the WIP changelog for the v4 spin...

v4

- Move code to set round mode into set_fp_round_mode function
- Convert set_fp_exceptions from a macro to an inline function
- Convert round mode helper into an inline function
- Make fpu_helper ieee_rm array static const
- Include cpu_mmu_index in cpu_get_tb_cpu_state flags
- Eliminate MPRV influence on mmu_index
- Remove unrecoverable do_unassigned_access function
- Only update PTE accessed and dirty bits if necessary
- Remove unnecessary tlb_flush in set_mode as mode is in mmu_idx
- Remove buggy support for misa writes. misa writes are optional
and are not implemented in any known hardware
- Always set PTE read or execute permissions during page walk
- Reorder helper function declarations to match order in helper.c
- Remove redundant variable declaration in get_physical_address
- Remove duplicated code from get_physical_address
- Use mmu_idx instead of mem_idx in riscv_cpu_get_phys_page_debug
- Use IEEE NaNs instead of default NaNs

The changes being added to the v3 baseline and we'll rebase before we spin
v4 in a week or two...

- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v3
Post by Richard Henderson
Post by Michael Clark
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, frs3, 0,
+ &env->fp_status);
float_muladd_negate_product.
Post by Michael Clark
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2,
+ frs3 ^ (uint32_t)INT32_MIN, 0,
&env->fp_status);
float_muladd_negate_c | float_muladd_negate_product
Post by Michael Clark
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_minnum(frs1, frs2, &env->fp_status);
If you want minnum and not min, riscv-spec-v2.2.pdf could use some more verbage
to specify that.
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF32UI(ui) (0xFF000000 < (uint32_t)((uint_fast32_t)ui << 1))
float32_is_any_nan
Post by Michael Clark
+#define signF32UI(a) ((bool)((uint32_t)a >> 31))
float32_is_neg
Post by Michael Clark
+#define expF32UI(a) ((int_fast16_t)(a >> 23) & 0xFF)
+#define fracF32UI(a) (a & 0x007FFFFF)
Either float32_is_infinity or float32_is_zero_or_denormal.
Post by Michael Clark
+union ui32_f32 { uint32_t ui; uint32_t f; };
This is just silly.
Post by Michael Clark
+uint_fast16_t float32_classify(uint32_t a, float_status *status)
Please drop *_fast*_t for just "int".
Post by Michael Clark
+ return
+ (sign && infOrNaN && fracF32UI(uiA) == 0) << 0 |
+ (sign && !infOrNaN && !subnormalOrZero) << 1 |
+ (sign && subnormalOrZero && fracF32UI(uiA)) << 2 |
+ (sign && subnormalOrZero && fracF32UI(uiA) == 0) << 3 |
+ (!sign && infOrNaN && fracF32UI(uiA) == 0) << 7 |
+ (!sign && !infOrNaN && !subnormalOrZero) << 6 |
+ (!sign && subnormalOrZero && fracF32UI(uiA)) << 5 |
+ (!sign && subnormalOrZero && fracF32UI(uiA) == 0) << 4 |
+ (isNaNF32UI(uiA) && float32_is_signaling_nan(uiA, status)) <<
8 |
Post by Michael Clark
+ (isNaNF32UI(uiA) && !float32_is_signaling_nan(uiA, status)) <<
9;
These conditions are all exclusive. You do not need to compute all of them.
if (float32_is_any_nan(f)) {
if (float32_is_signaling_nan(f, status)) {
return 1 << 8;
} else {
return 1 << 9;
}
}
if (float32_is_neg(f)) {
if (float32_is_infinity(f)) {
return 1 << 0;
} else if (float32_is_zero(f)) {
return 1 << 3;
} else if (float32_is_zero_or_denormal(f)) {
return 1 << 2;
} else {
return 1 << 1;
}
} else {
...
}
Post by Michael Clark
+ frs1 = (int64_t)((int32_t)float64_to_int32(frs1, &env->fp_status));
Double cast is pointless, as are the extra parenthesis.
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF64UI(ui) (UINT64_C(0xFFE0000000000000) \
+ < (uint64_t)((uint_fast64_t)ui << 1))
+#define signF64UI(a) ((bool)((uint64_t) a >> 63))
+#define expF64UI(a) ((int_fast16_t)(a >> 52) & 0x7FF)
+#define fracF64UI(a) (a & UINT64_C(0x000FFFFFFFFFFFFF))
+
+union ui64_f64 { uint64_t ui; uint64_t f; };
+
+uint_fast16_t float64_classify(uint64_t a, float_status *status)
Likewise.
r~
Richard Henderson
2018-01-24 00:01:13 UTC
Permalink
Post by Michael Clark
On Wed, Jan 3, 2018 at 12:10 PM, Richard Henderson
Post by Michael Clark
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
static const.
Done. 
Post by Michael Clark
+/* obtain rm value to use in computation
+ * as the last step, convert rm codes to what the softfloat library expects
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({                                             \
+if (rm == 7) {                                            \
+    rm = env->frm;                               \
+}                                                         \
+if (rm > 4) {                                             \
+    helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+}                                                         \
+ieee_rm[rm]; })
Please use inline functions and not these macros.
Done.
In fact the previous code would, with normal control flow, dereference
ieee_rm[rm] with an out of bounds round mode so assuming helper_raise_exception
does a longjmp, i've inserted g_assert_not_reached() after the exception. An
analyser could detect that ieee_rm has an out of bounds access assuming normal
control flow. e.g.
static inline void set_fp_round_mode(CPURISCVState *env, uint64_t rm)
{
    if (rm == 7) {
        rm = env->frm;
    } else if (rm > 4) {
        helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
        g_assert_not_reached();
Yes, raise_exception exits via longjmp. This is a good change.
Post by Michael Clark
Are translations not implicitly indexed by cpu_mmu_index? i.e. do we also need
to add cpu_mmu_index to cpu_get_tb_cpu_state flags to prevent code translated
for one value of mmu_index running in code with another value of mmu_index (in
the case of riscv, we currently return processor mode / privilege level in
cpu_mmu_index).
No, there is no implicit indexing. That's why I've mentioned exactly this at
least twice in review so far.

There are two ways to do this correctly. One is to add all the bits that
specify processor state (e.g. Alpha stores pal_code bit and supervisor bit).
Another is to actually encode the mmu_idx into the flags (e.g. ARM).


r~
Michael Clark
2018-01-24 01:31:49 UTC
Permalink
On Tue, Jan 23, 2018 at 4:01 PM, Richard Henderson <
Post by Michael Clark
Post by Michael Clark
On Wed, Jan 3, 2018 at 12:10 PM, Richard Henderson
Post by Michael Clark
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
static const.
Done.
Post by Michael Clark
+/* obtain rm value to use in computation
+ * as the last step, convert rm codes to what the softfloat
library expects
Post by Michael Clark
Post by Michael Clark
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({ \
+if (rm == 7) { \
+ rm = env->frm; \
+} \
+if (rm > 4) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+} \
+ieee_rm[rm]; })
Please use inline functions and not these macros.
Done.
In fact the previous code would, with normal control flow, dereference
ieee_rm[rm] with an out of bounds round mode so assuming
helper_raise_exception
Post by Michael Clark
does a longjmp, i've inserted g_assert_not_reached() after the
exception. An
Post by Michael Clark
analyser could detect that ieee_rm has an out of bounds access assuming
normal
Post by Michael Clark
control flow. e.g.
static inline void set_fp_round_mode(CPURISCVState *env, uint64_t rm)
{
if (rm == 7) {
rm = env->frm;
} else if (rm > 4) {
helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST);
g_assert_not_reached();
Yes, raise_exception exits via longjmp. This is a good change.
Post by Michael Clark
Are translations not implicitly indexed by cpu_mmu_index? i.e. do we
also need
Post by Michael Clark
to add cpu_mmu_index to cpu_get_tb_cpu_state flags to prevent code
translated
Post by Michael Clark
for one value of mmu_index running in code with another value of
mmu_index (in
Post by Michael Clark
the case of riscv, we currently return processor mode / privilege level
in
Post by Michael Clark
cpu_mmu_index).
No, there is no implicit indexing. That's why I've mentioned exactly this at
least twice in review so far.
There are two ways to do this correctly. One is to add all the bits that
specify processor state (e.g. Alpha stores pal_code bit and supervisor bit).
Another is to actually encode the mmu_idx into the flags (e.g. ARM).
I reviewed Stefan's patches. Stefan has some code that encoded processor
flags in mmu_index however it removed some well-tested code paths and I was
hesitant to make such a large change to get_physical_address at this point
in time. I read Stefan's code and used some fragments from it but was
relatively cautious with regard to changing relatively well tested code
paths. I tried to make the most minimal change for the sake of correctness.

For the meantime we've greatly simplified cpu_mmu_index to just return the
processor mode as well as adding the processor mode to cpu_get_tb_cpu_state
flags. cpu_mmu_index previously returned a permutation of env->priv
(containing processer mode), mstatus.MPP (previous mode) and mstatus.MPRV.
When MPRV is set, M mode loads and stores operate as per the mode in
mstatus.MPP and the previous cpu_mmu_index function returned the mode the
processor should do loads and stores as, not the current mode. This is
problematic as mstatus.MPRV can be altered from user code via register
values so there was a potential to re-enter a pre-existing trace with a
different mmu_index. I believe we have eliminated that issue.

These two changes should fix the issue and we've performed testing with
these patches:

-
https://github.com/michaeljclark/riscv-qemu/commit/82012aef90e5c4500f926523b3b2ff621b0cd512
-
https://github.com/michaeljclark/riscv-qemu/commit/abdb897a4a607d00cfce577ac37ca6119004658f

There is a possibility to further improve this code and potentially avoid
TLB flushes, by encoding more state in cpu_mmu_index
and cpu_get_tb_cpu_state flags. I would say that mstatus.SUM flag is
altered frequently in Linux's copy_to/from_user and is likely to result in
decent performance improvement if we can eliminate the TLB flush when the
SUM bit is changed (it is in concept similar to SMAP on x86). mstatus.MPRV
is used by the monitor for handling misaligned loads and stores which
should be relatively rare (the occasional use of structs with
__attribute__((packed))). Eliminating the tlb_flush on mstatus.SUM changes
likely will have the most positive performance impact

We have increased performance somewhat already (~2.5X faster on dd
if=/dev/zero of=/dev/null bs=1 count=100000). We eliminated a tlb_flush on
privilege level changes as this is included (now solely) in cpu_mmu_index,
and any helper that changes privilege level terminates translation so
mmu_index should now always be consistent. Possibly not quite as fast as
Stefan's original patch but an improvement nevertheless. The changes are
somewhat smaller.

The code is in a state where the mmu_index invariant is now very easy to
reason about. Any helper that changes mstatus or priv level terminates
traces [1] and cpu_mmu_index is included in cpu_get_tb_cpu_state flags.
Stefan may very well propose some incremental changes to eliminate some
more TLB flushes.

[1]
https://github.com/riscv/riscv-qemu/blob/ef7332fc693d386f572169b4011a84cfc5c02ac0/target/riscv/translate.c#L1420

During the process, we also found some bugs in the accessed/dirty bit
handling in get_physical_address. i.e. when the accessed/dirty bits don't
change, we now elide the store. This allows various use cases such as PTE
entries in ROM. We coalesced some of the flag
setting in get_physical_address based on Stefan's patch
(PAGE_READ+PAGE_EXEC) which likely reduces the number of TLB misses, but we
don't set PAGE_WRITE unless the access_type is MMU_DATA_STORE. The previous
code would only set the TLB prot flag for the specific access type. We set
PAGE_READ+PAGE_EXEC based on the PTE flags for load and fetch but we don't
set PAGE_WRITE on loads or fetches because we want a TLB miss for
subsequent stores so we don't miss updating the dirty bit if the first
access on a RW page is a load or fetch. I saw code in i386 that does
essentially the same thing.

We still need to make PTE updates atomic but given we only have multiplexed
SMP, it should not be too much of an issue right now.
Richard Henderson
2018-01-24 16:16:34 UTC
Permalink
Post by Michael Clark
For the meantime we've greatly simplified cpu_mmu_index to just return the
processor mode as well as adding the processor mode to cpu_get_tb_cpu_state
flags. cpu_mmu_index previously returned a permutation of env->priv (containing
processer mode), mstatus.MPP (previous mode) and mstatus.MPRV. When MPRV is
set, M mode loads and stores operate as per the mode in mstatus.MPP and the
previous cpu_mmu_index function returned the mode the processor should do loads
and stores as, not the current mode. This is problematic as mstatus.MPRV can be
altered from user code via register values so there was a potential to re-enter
a pre-existing trace with a different mmu_index. I believe we have eliminated
that issue.
These two changes should fix the issue and we've performed testing with these
-
https://github.com/michaeljclark/riscv-qemu/commit/82012aef90e5c4500f926523b3b2ff621b0cd512
- https://github.com/michaeljclark/riscv-qemu/commit/abdb897a4a607d00cfce577ac37ca6119004658f
I had been concerned, because solely within the context of these patches I
didn't see the additional flushing being added. However, on checking out the
branch I see that they are all there on changing mstatus.

No need for it immediately, but as an incremental improvement you can use
targeted flushing. E.g. when only MPRV changes, only PRV_M's mmu_idx is
invalidated; PRV_S and PRV_U are unaffected. For this, use tlb_flush_by_mmuidx.
Post by Michael Clark
During the process, we also found some bugs in the accessed/dirty bit handling
in get_physical_address. i.e. when the accessed/dirty bits don't change, we now
elide the store. This allows various use cases such as PTE entries in ROM. We
coalesced some of the flag setting in get_physical_address based on Stefan's
patch (PAGE_READ+PAGE_EXEC) which likely reduces the number of TLB misses, but
we don't set PAGE_WRITE unless the access_type is MMU_DATA_STORE. The previous
code would only set the TLB prot flag for the specific access type. We set
PAGE_READ+PAGE_EXEC based on the PTE flags for load and fetch but we don't set
PAGE_WRITE on loads or fetches because we want a TLB miss for subsequent stores
so we don't miss updating the dirty bit if the first access on a RW page is a
load or fetch. I saw code in i386 that does essentially the same thing.
You should still set PAGE_WRITE for MMU_DATA_LOAD if the dirty bit is already
set. Consider:

Addresses A and B alias in the TLB. Since our tlb implementation is of
necessity trivial, an access to B will remove A from the table. Assuming both
pages are initially dirty, the access sequence

load A
load B
store B

will fault 3 times with your code, but only twice if you consider the dirty bit
right away.
Post by Michael Clark
We still need to make PTE updates atomic but given we only have multiplexed
SMP, it should not be too much of an issue right now.
I'm sure that enabling multi-threading will be high on the list of post-merge
improvements. There's certainly a lot of bang to be had there.


r~
Michael Clark
2018-01-24 17:35:49 UTC
Permalink
On Wed, Jan 24, 2018 at 8:16 AM, Richard Henderson <
Post by Michael Clark
Post by Michael Clark
For the meantime we've greatly simplified cpu_mmu_index to just return
the
Post by Michael Clark
processor mode as well as adding the processor mode to
cpu_get_tb_cpu_state
Post by Michael Clark
flags. cpu_mmu_index previously returned a permutation of env->priv
(containing
Post by Michael Clark
processer mode), mstatus.MPP (previous mode) and mstatus.MPRV. When MPRV
is
Post by Michael Clark
set, M mode loads and stores operate as per the mode in mstatus.MPP and
the
Post by Michael Clark
previous cpu_mmu_index function returned the mode the processor should
do loads
Post by Michael Clark
and stores as, not the current mode. This is problematic as mstatus.MPRV
can be
Post by Michael Clark
altered from user code via register values so there was a potential to
re-enter
Post by Michael Clark
a pre-existing trace with a different mmu_index. I believe we have
eliminated
Post by Michael Clark
that issue.
These two changes should fix the issue and we've performed testing with
these
Post by Michael Clark
-
https://github.com/michaeljclark/riscv-qemu/commit/
82012aef90e5c4500f926523b3b2ff621b0cd512
Post by Michael Clark
- https://github.com/michaeljclark/riscv-qemu/commit/
abdb897a4a607d00cfce577ac37ca6119004658f
I had been concerned, because solely within the context of these patches I
didn't see the additional flushing being added. However, on checking out the
branch I see that they are all there on changing mstatus.
No need for it immediately, but as an incremental improvement you can use
targeted flushing. E.g. when only MPRV changes, only PRV_M's mmu_idx is
invalidated; PRV_S and PRV_U are unaffected. For this, use
tlb_flush_by_mmuidx.
Right. I saw those APIs. I experimented with a patch that did this but
didn't see a noticeable performance improvement so didn't pursue it.
Post by Michael Clark
Post by Michael Clark
During the process, we also found some bugs in the accessed/dirty bit
handling
Post by Michael Clark
in get_physical_address. i.e. when the accessed/dirty bits don't change,
we now
Post by Michael Clark
elide the store. This allows various use cases such as PTE entries in
ROM. We
Post by Michael Clark
coalesced some of the flag setting in get_physical_address based on
Stefan's
Post by Michael Clark
patch (PAGE_READ+PAGE_EXEC) which likely reduces the number of TLB
misses, but
Post by Michael Clark
we don't set PAGE_WRITE unless the access_type is MMU_DATA_STORE. The
previous
Post by Michael Clark
code would only set the TLB prot flag for the specific access type. We
set
Post by Michael Clark
PAGE_READ+PAGE_EXEC based on the PTE flags for load and fetch but we
don't set
Post by Michael Clark
PAGE_WRITE on loads or fetches because we want a TLB miss for subsequent
stores
Post by Michael Clark
so we don't miss updating the dirty bit if the first access on a RW page
is a
Post by Michael Clark
load or fetch. I saw code in i386 that does essentially the same thing.
You should still set PAGE_WRITE for MMU_DATA_LOAD if the dirty bit is already
Good spotting. I'll fix this case right now...
Post by Michael Clark
Addresses A and B alias in the TLB. Since our tlb implementation is of
necessity trivial, an access to B will remove A from the table. Assuming both
pages are initially dirty, the access sequence
load A
load B
store B
will fault 3 times with your code, but only twice if you consider the dirty bit
right away.
Post by Michael Clark
We still need to make PTE updates atomic but given we only have
multiplexed
Post by Michael Clark
SMP, it should not be too much of an issue right now.
I'm sure that enabling multi-threading will be high on the list of post-merge
improvements. There's certainly a lot of bang to be had there.
Agree.
Michael Clark
2018-01-23 23:15:09 UTC
Permalink
On Wed, Jan 3, 2018 at 12:10 PM, Richard Henderson <
Post by Richard Henderson
Post by Michael Clark
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
static const.
Post by Michael Clark
+/* obtain rm value to use in computation
+ * as the last step, convert rm codes to what the softfloat library
expects
Post by Michael Clark
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({ \
+if (rm == 7) { \
+ rm = env->frm; \
+} \
+if (rm > 4) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+} \
+ieee_rm[rm]; })
Please use inline functions and not these macros.
Probably this applies to some of the helpers that I've already reviewed, but
you're going to need to use an exception raising function that also performs an
unwind (usually via cpu_loop_exit_restore). The GETPC() that feeds the unwind
must be placed in the outer-most helper (including inlines).
Post by Michael Clark
+#ifndef CONFIG_USER_ONLY
+#define require_fp if (!(env->mstatus & MSTATUS_FS)) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+}
If you included MSTATUS_FS in cpu_get_tb_cpu_state flags, then you could be
checking for this at translation time instead of at run time.
Post by Michael Clark
+/* convert softfloat library flag numbers to RISC-V */
+unsigned int softfloat_flags_to_riscv(unsigned int flags)
+{
+ int rv_flags = 0;
+ rv_flags |= (flags & float_flag_inexact) ? 1 : 0;
+ rv_flags |= (flags & float_flag_underflow) ? 2 : 0;
+ rv_flags |= (flags & float_flag_overflow) ? 4 : 0;
+ rv_flags |= (flags & float_flag_divbyzero) ? 8 : 0;
+ rv_flags |= (flags & float_flag_invalid) ? 16 : 0;
FPEXC_NX et al.
Post by Michael Clark
+/* adapted from Spike's decode.h:set_fp_exceptions */
+#define set_fp_exceptions() do { \
+ env->fflags |= softfloat_flags_to_riscv(get_float_exception_flags(\
+ &env->fp_status)); \
+ set_float_exception_flags(0, &env->fp_status); \
+} while (0)
inline function. Usually written as
int flags = get_float_exception_flags(&env->fp_status);
if (flags) {
set_float_exception_flags(0, &env->fp_status);
env->fflags |= softfloat_flags_to_riscv(flags);
}
since we really do expect exceptions to be exceptional.
Post by Michael Clark
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0,
+ &env->fp_status);
Given that RISC-V always returns a default NaN, you obviously do not care about
the sign of a NaN result. Therefore you should use float_muladd_negate_c as
the fourth argument here and not perform the sign flip manually.
We do care about the sign of NaN results.

Jim Wilson spotted this bug and removed a call to set_default_nan_mode

https://github.com/riscv/riscv-qemu/commit/4223d89b0c5c671332d66bcd649db5c6f46559f5
Post by Richard Henderson
Post by Michael Clark
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, frs3, 0,
+ &env->fp_status);
float_muladd_negate_product.
Post by Michael Clark
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2,
+ frs3 ^ (uint32_t)INT32_MIN, 0,
&env->fp_status);
float_muladd_negate_c | float_muladd_negate_product
Post by Michael Clark
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ require_fp;
+ frs1 = float32_minnum(frs1, frs2, &env->fp_status);
If you want minnum and not min, riscv-spec-v2.2.pdf could use some more verbage
to specify that.
We'll look at Spike (riscv-isa-sim)... Spike has the correct behavior.
Post by Richard Henderson
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF32UI(ui) (0xFF000000 < (uint32_t)((uint_fast32_t)ui << 1))
float32_is_any_nan
Post by Michael Clark
+#define signF32UI(a) ((bool)((uint32_t)a >> 31))
float32_is_neg
Post by Michael Clark
+#define expF32UI(a) ((int_fast16_t)(a >> 23) & 0xFF)
+#define fracF32UI(a) (a & 0x007FFFFF)
Either float32_is_infinity or float32_is_zero_or_denormal.
Post by Michael Clark
+union ui32_f32 { uint32_t ui; uint32_t f; };
This is just silly.
Post by Michael Clark
+uint_fast16_t float32_classify(uint32_t a, float_status *status)
Please drop *_fast*_t for just "int".
Post by Michael Clark
+ return
+ (sign && infOrNaN && fracF32UI(uiA) == 0) << 0 |
+ (sign && !infOrNaN && !subnormalOrZero) << 1 |
+ (sign && subnormalOrZero && fracF32UI(uiA)) << 2 |
+ (sign && subnormalOrZero && fracF32UI(uiA) == 0) << 3 |
+ (!sign && infOrNaN && fracF32UI(uiA) == 0) << 7 |
+ (!sign && !infOrNaN && !subnormalOrZero) << 6 |
+ (!sign && subnormalOrZero && fracF32UI(uiA)) << 5 |
+ (!sign && subnormalOrZero && fracF32UI(uiA) == 0) << 4 |
+ (isNaNF32UI(uiA) && float32_is_signaling_nan(uiA, status)) <<
8 |
Post by Michael Clark
+ (isNaNF32UI(uiA) && !float32_is_signaling_nan(uiA, status)) <<
9;
These conditions are all exclusive. You do not need to compute all of them.
if (float32_is_any_nan(f)) {
if (float32_is_signaling_nan(f, status)) {
return 1 << 8;
} else {
return 1 << 9;
}
}
if (float32_is_neg(f)) {
if (float32_is_infinity(f)) {
return 1 << 0;
} else if (float32_is_zero(f)) {
return 1 << 3;
} else if (float32_is_zero_or_denormal(f)) {
return 1 << 2;
} else {
return 1 << 1;
}
} else {
...
}
Post by Michael Clark
+ frs1 = (int64_t)((int32_t)float64_to_int32(frs1, &env->fp_status));
Double cast is pointless, as are the extra parenthesis.
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF64UI(ui) (UINT64_C(0xFFE0000000000000) \
+ < (uint64_t)((uint_fast64_t)ui << 1))
+#define signF64UI(a) ((bool)((uint64_t) a >> 63))
+#define expF64UI(a) ((int_fast16_t)(a >> 52) & 0x7FF)
+#define fracF64UI(a) (a & UINT64_C(0x000FFFFFFFFFFFFF))
+
+union ui64_f64 { uint64_t ui; uint64_t f; };
+
+uint_fast16_t float64_classify(uint64_t a, float_status *status)
Likewise.
r~
Michael Clark
2018-01-23 23:35:52 UTC
Permalink
Post by Michael Clark
On Wed, Jan 3, 2018 at 12:10 PM, Richard Henderson <
Post by Richard Henderson
Post by Michael Clark
+/* convert RISC-V rounding mode to IEEE library numbers */
+unsigned int ieee_rm[] = {
static const.
Post by Michael Clark
+/* obtain rm value to use in computation
+ * as the last step, convert rm codes to what the softfloat library
expects
Post by Michael Clark
+ * Adapted from Spike's decode.h:RM
+ */
+#define RM ({ \
+if (rm == 7) { \
+ rm = env->frm; \
+} \
+if (rm > 4) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+} \
+ieee_rm[rm]; })
Please use inline functions and not these macros.
Probably this applies to some of the helpers that I've already reviewed, but
you're going to need to use an exception raising function that also performs an
unwind (usually via cpu_loop_exit_restore). The GETPC() that feeds the unwind
must be placed in the outer-most helper (including inlines).
Post by Michael Clark
+#ifndef CONFIG_USER_ONLY
+#define require_fp if (!(env->mstatus & MSTATUS_FS)) { \
+ helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \
+}
If you included MSTATUS_FS in cpu_get_tb_cpu_state flags, then you could be
checking for this at translation time instead of at run time.
Post by Michael Clark
+/* convert softfloat library flag numbers to RISC-V */
+unsigned int softfloat_flags_to_riscv(unsigned int flags)
+{
+ int rv_flags = 0;
+ rv_flags |= (flags & float_flag_inexact) ? 1 : 0;
+ rv_flags |= (flags & float_flag_underflow) ? 2 : 0;
+ rv_flags |= (flags & float_flag_overflow) ? 4 : 0;
+ rv_flags |= (flags & float_flag_divbyzero) ? 8 : 0;
+ rv_flags |= (flags & float_flag_invalid) ? 16 : 0;
FPEXC_NX et al.
Post by Michael Clark
+/* adapted from Spike's decode.h:set_fp_exceptions */
+#define set_fp_exceptions() do { \
+ env->fflags |= softfloat_flags_to_riscv(get_f
loat_exception_flags(\
Post by Michael Clark
+ &env->fp_status)); \
+ set_float_exception_flags(0, &env->fp_status); \
+} while (0)
inline function. Usually written as
int flags = get_float_exception_flags(&env->fp_status);
if (flags) {
set_float_exception_flags(0, &env->fp_status);
env->fflags |= softfloat_flags_to_riscv(flags);
}
since we really do expect exceptions to be exceptional.
Post by Michael Clark
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0,
+ &env->fp_status);
Given that RISC-V always returns a default NaN, you obviously do not care about
the sign of a NaN result. Therefore you should use float_muladd_negate_c as
the fourth argument here and not perform the sign flip manually.
We do care about the sign of NaN results.
Jim Wilson spotted this bug and removed a call to set_default_nan_mode
https://github.com/riscv/riscv-qemu/commit/4223d89b0c5c671332d66bcd649db5
c6f46559f5
Post by Richard Henderson
Post by Michael Clark
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, frs3, 0,
+ &env->fp_status);
float_muladd_negate_product.
Post by Michael Clark
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2,
Post by Michael Clark
+ uint64_t frs3, uint64_t rm)
+{
+ require_fp;
+ set_float_rounding_mode(RM, &env->fp_status);
+ frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2,
+ frs3 ^ (uint32_t)INT32_MIN, 0,
&env->fp_status);
float_muladd_negate_c | float_muladd_negate_product
Post by Michael Clark
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t
frs2)
Post by Michael Clark
+{
+ require_fp;
+ frs1 = float32_minnum(frs1, frs2, &env->fp_status);
If you want minnum and not min, riscv-spec-v2.2.pdf could use some more verbage
to specify that.
We'll look at Spike (riscv-isa-sim)... Spike has the correct behavior.
We want minimum number (minnum). It's been added to the draft spec and will
be in riscv-spec-v2.3.pdf

Section 8.6 in the draft spec says:

"
Floating-point minimum-number and maximum-number instructions FMIN.S and
FMAX.S write, respectively, the smaller or larger of rs1 and rs2 to rd. For
the purposes of these instructions only, the value -0:0 is considered to be
less than the value +0:0. If both inputs are NaNs, the result is the
canonical NaN. If only one operand is a NaN, the result is the non-NaN
operand. Signaling NaN inputs raise the invalid operation exception, even
when the result is not NaN.
"
Post by Michael Clark
+/* adapted from spike */
Post by Richard Henderson
Post by Michael Clark
+#define isNaNF32UI(ui) (0xFF000000 < (uint32_t)((uint_fast32_t)ui <<
1))
float32_is_any_nan
Post by Michael Clark
+#define signF32UI(a) ((bool)((uint32_t)a >> 31))
float32_is_neg
Post by Michael Clark
+#define expF32UI(a) ((int_fast16_t)(a >> 23) & 0xFF)
+#define fracF32UI(a) (a & 0x007FFFFF)
Either float32_is_infinity or float32_is_zero_or_denormal.
Post by Michael Clark
+union ui32_f32 { uint32_t ui; uint32_t f; };
This is just silly.
Post by Michael Clark
+uint_fast16_t float32_classify(uint32_t a, float_status *status)
Please drop *_fast*_t for just "int".
Post by Michael Clark
+ return
+ (sign && infOrNaN && fracF32UI(uiA) == 0) << 0 |
+ (sign && !infOrNaN && !subnormalOrZero) << 1 |
+ (sign && subnormalOrZero && fracF32UI(uiA)) << 2 |
+ (sign && subnormalOrZero && fracF32UI(uiA) == 0) << 3 |
+ (!sign && infOrNaN && fracF32UI(uiA) == 0) << 7 |
+ (!sign && !infOrNaN && !subnormalOrZero) << 6 |
+ (!sign && subnormalOrZero && fracF32UI(uiA)) << 5 |
+ (!sign && subnormalOrZero && fracF32UI(uiA) == 0) << 4 |
+ (isNaNF32UI(uiA) && float32_is_signaling_nan(uiA, status)) <<
8 |
Post by Michael Clark
+ (isNaNF32UI(uiA) && !float32_is_signaling_nan(uiA, status)) <<
9;
These conditions are all exclusive. You do not need to compute all of them.
if (float32_is_any_nan(f)) {
if (float32_is_signaling_nan(f, status)) {
return 1 << 8;
} else {
return 1 << 9;
}
}
if (float32_is_neg(f)) {
if (float32_is_infinity(f)) {
return 1 << 0;
} else if (float32_is_zero(f)) {
return 1 << 3;
} else if (float32_is_zero_or_denormal(f)) {
return 1 << 2;
} else {
return 1 << 1;
}
} else {
...
}
Post by Michael Clark
+ frs1 = (int64_t)((int32_t)float64_to_int32(frs1,
&env->fp_status));
Double cast is pointless, as are the extra parenthesis.
Post by Michael Clark
+/* adapted from spike */
+#define isNaNF64UI(ui) (UINT64_C(0xFFE0000000000000) \
+ < (uint64_t)((uint_fast64_t)ui << 1))
+#define signF64UI(a) ((bool)((uint64_t) a >> 63))
+#define expF64UI(a) ((int_fast16_t)(a >> 52) & 0x7FF)
+#define fracF64UI(a) (a & UINT64_C(0x000FFFFFFFFFFFFF))
+
+union ui64_f64 { uint64_t ui; uint64_t f; };
+
+uint_fast16_t float64_classify(uint64_t a, float_status *status)
Likewise.
r~
Jim Wilson
2018-01-24 00:03:11 UTC
Permalink
Post by Michael Clark
We want minimum number (minnum). It's been added to the draft spec and will
be in riscv-spec-v2.3.pdf
In the preface of the draft, it says
• Defined the signed-zero behavior of FMIN.fmt and FMAX.fmt, and
changed their behavior on
signaling-NaN inputs to conform to the minimumNumber and maximumNumber
operations
in the proposed IEEE 754-201x specification.

But if qemu doesn't have minimumNumber support yet, then yes minNum is correct.

This is discussed a bit here
https://github.com/riscv/riscv-isa-manual/issues/65

Jim
Richard Henderson
2018-01-24 00:15:47 UTC
Permalink
Post by Richard Henderson
Post by Michael Clark
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3, uint64_t rm)
+{
+    require_fp;
+    set_float_rounding_mode(RM, &env->fp_status);
+    frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0,
+                          &env->fp_status);
Given that RISC-V always returns a default NaN, you obviously do not care about
the sign of a NaN result.  Therefore you should use float_muladd_negate_c as
the fourth argument here and not perform the sign flip manually.
We do care about the sign of NaN results.
Jim Wilson spotted this bug and removed a call to set_default_nan_mode
https://github.com/riscv/riscv-qemu/commit/4223d89b0c5c671332d66bcd649db5c6f46559f5
Ok. Now it depends on what result you care about for madd specifically.

If, like x86 and Power, fmsub returns the (silenced) original input NaN, you
want the float_muladd_* flags.

If, like ARM, fmsub returns the (silenced) negated input NaN, then you do need
to change sign externally. If this is the case, please use float32_chs instead
of open-coding it with xor.


r~
Jim Wilson
2018-01-24 18:58:53 UTC
Permalink
On Tue, Jan 23, 2018 at 4:15 PM, Richard Henderson
Post by Richard Henderson
Ok. Now it depends on what result you care about for madd specifically.
If, like x86 and Power, fmsub returns the (silenced) original input NaN, you
want the float_muladd_* flags.
If, like ARM, fmsub returns the (silenced) negated input NaN, then you do need
to change sign externally. If this is the case, please use float32_chs instead
of open-coding it with xor.
The ISA spec is a little ambiguous here. There is text that says we
multiply, optionally negate the result, and then add or subtract the
addend. However, this is followed by a sentence that gives equations,
and for fnmsub it says -rs1*rs2+rs3. If we assume C semantics, then
they are negating rs1 not the multiply result. This could potentially
give a different result if rs2 is a +qNaN and rs1/rs3 are not NaNs.
qemu is implementing what the equation says, not what the text says.
The definitive source would be the Spike simulator, and it agrees with
qemu and the equations. The ISA spec does not specify what happens
when one of the operands is a NaN, except to mention that operations
comply with the IEEE 754 2008 standard.

I think that qemu is correct here, and that you want to use float32_chs.

Although, looking at this again, I see another statement in a
different place that says:

Except when otherwise stated, if the result of a floating-point
operation is NaN, it is the canonical
NaN. The canonical NaN has a positive sign and all significand bits
clear except the MSB, a.k.a.
the quiet bit. For single-precision floating-point, this corresponds
to the pattern 0x7fc00000.

So it sounds like maybe we do want default NaN support. It appears
that spike is using default NaNs. Unfortunately, enabling default NaN
support causes gcc and glibc testsuite failures which complicates
upstreaming glibc support, as they won't accept it unless we get
failures below a certain number. Also, not having hardware to compare
against makes it impossible to determine if the simulator is doing
exactly what the hardware does. This could take a little time to sort
out.

Jim
Richard Henderson
2018-01-24 23:47:39 UTC
Permalink
Post by Jim Wilson
I think that qemu is correct here, and that you want to use float32_chs.
Ok.
Post by Jim Wilson
Although, looking at this again, I see another statement in a
Except when otherwise stated, if the result of a floating-point operation is
NaN, it is the canonical NaN. The canonical NaN has a positive sign and all
significand bits clear except the MSB, a.k.a. the quiet bit. For
single-precision floating-point, this corresponds to the pattern
0x7fc00000.
Yes, I had read this before as well. I had assumed that your patch constituted
an intended change to this text.
Post by Jim Wilson
This could take a little time to sort out.
Ok. I don't see this as a blocking issue for merging.


r~
Jim Wilson
2018-01-29 20:33:19 UTC
Permalink
On Wed, Jan 24, 2018 at 3:47 PM, Richard Henderson
Post by Richard Henderson
Post by Jim Wilson
Although, looking at this again, I see another statement in a
Except when otherwise stated, if the result of a floating-point operation is
NaN, it is the canonical NaN. The canonical NaN has a positive sign and all
significand bits clear except the MSB, a.k.a. the quiet bit. For
single-precision floating-point, this corresponds to the pattern
0x7fc00000.
Yes, I had read this before as well. I had assumed that your patch constituted
an intended change to this text.
Post by Jim Wilson
This could take a little time to sort out.
Ok. I don't see this as a blocking issue for merging.
So after looking at this a bit more, I've come to the conclusion that
my patch to remove the default/canonical nan support from RISC-V qemu
was wrong. We will have to fix this on the gcc/glibc side.

Michael, please revert my change
https://github.com/riscv/riscv-qemu/commit/4223d89b0c5c671332d66bcd649db5c6f46559f5

Jim
Michael Clark
2018-02-02 05:26:36 UTC
Permalink
Post by Jim Wilson
On Wed, Jan 24, 2018 at 3:47 PM, Richard Henderson
Post by Richard Henderson
Post by Jim Wilson
Although, looking at this again, I see another statement in a
Except when otherwise stated, if the result of a floating-point
operation is
Post by Richard Henderson
Post by Jim Wilson
NaN, it is the canonical NaN. The canonical NaN has a positive sign and
all
Post by Richard Henderson
Post by Jim Wilson
significand bits clear except the MSB, a.k.a. the quiet bit. For
single-precision floating-point, this corresponds to the pattern
0x7fc00000.
Yes, I had read this before as well. I had assumed that your patch
constituted
Post by Richard Henderson
an intended change to this text.
Post by Jim Wilson
This could take a little time to sort out.
Ok. I don't see this as a blocking issue for merging.
So after looking at this a bit more, I've come to the conclusion that
my patch to remove the default/canonical nan support from RISC-V qemu
was wrong. We will have to fix this on the gcc/glibc side.
Michael, please revert my change
https://github.com/riscv/riscv-qemu/commit/
4223d89b0c5c671332d66bcd649db5c6f46559f5
Done.
Michael Clark
2018-01-03 00:44:11 UTC
Permalink
GDB Register read and write routines.

Signed-off-by: Michael Clark <***@sifive.com>
---
target/riscv/gdbstub.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 target/riscv/gdbstub.c

diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
new file mode 100644
index 0000000..12d1d9f
--- /dev/null
+++ b/target/riscv/gdbstub.c
@@ -0,0 +1,59 @@
+/*
+ * RISC-V GDB Server Stub
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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)
+{
+ /* TODO proper x0 handling */
+ 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]);
+ }
+ return 0;
+}
+
+int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+ /* TODO proper x0 handling */
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ 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);
+ }
+ return 0;
+}
--
2.7.0
Richard Henderson
2018-01-03 20:25:10 UTC
Permalink
Post by Michael Clark
GDB Register read and write routines.
---
target/riscv/gdbstub.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 target/riscv/gdbstub.c
Reviewed-by: Richard Henderson <***@linaro.org>

r~
Michael Clark
2018-01-03 00:44:14 UTC
Permalink
Implementation of linux user emulation for RISC-V.

Signed-off-by: Michael Clark <***@sifive.com>
---
linux-user/elfload.c | 22 +++
linux-user/main.c | 130 ++++++++++++++++-
linux-user/riscv/syscall_nr.h | 275 +++++++++++++++++++++++++++++++++++
linux-user/riscv/target_cpu.h | 18 +++
linux-user/riscv/target_signal.h | 23 +++
linux-user/riscv/target_structs.h | 46 ++++++
linux-user/riscv/target_syscall.h | 56 ++++++++
linux-user/riscv/termbits.h | 220 ++++++++++++++++++++++++++++
linux-user/signal.c | 260 ++++++++++++++++++++++++++++++----
linux-user/syscall.c | 2 +
linux-user/syscall_defs.h | 13 +-
target/riscv/cpu_user.h | 29 ++++
target/riscv/user_atomic.c | 291 ++++++++++++++++++++++++++++++++++++++
target/riscv/user_syscall.c | 40 ++++++
14 files changed, 1387 insertions(+), 38 deletions(-)
create mode 100644 linux-user/riscv/syscall_nr.h
create mode 100644 linux-user/riscv/target_cpu.h
create mode 100644 linux-user/riscv/target_signal.h
create mode 100644 linux-user/riscv/target_structs.h
create mode 100644 linux-user/riscv/target_syscall.h
create mode 100644 linux-user/riscv/termbits.h
create mode 100644 target/riscv/cpu_user.h
create mode 100644 target/riscv/user_atomic.c
create mode 100644 target/riscv/user_syscall.c

diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 20f3d8c..178af56 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1272,6 +1272,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 71696ed..8900141 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -227,7 +227,7 @@ void cpu_loop(CPUX86State *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);

- switch(trapnr) {
+ switch (trapnr) {
case 0x80:
/* linux syscall from int $0x80 */
ret = do_syscall(env,
@@ -585,7 +585,7 @@ void cpu_loop(CPUARMState *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);

- switch(trapnr) {
+ switch (trapnr) {
case EXCP_UDEF:
case EXCP_NOCP:
case EXCP_INVSTATE:
@@ -1379,7 +1379,7 @@ void cpu_loop(CPUPPCState *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);

- switch(trapnr) {
+ switch (trapnr) {
case POWERPC_EXCP_NONE:
/* Just go on */
break;
@@ -2251,7 +2251,7 @@ void cpu_loop(CPUMIPSState *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);

- switch(trapnr) {
+ switch (trapnr) {
case EXCP_SYSCALL:
env->active_tc.PC += 4;
# ifdef TARGET_ABI_MIPSO32
@@ -2957,7 +2957,7 @@ void cpu_loop(CPUM68KState *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);

- switch(trapnr) {
+ switch (trapnr) {
case EXCP_ILLEGAL:
{
if (ts->sim_syscalls) {
@@ -3640,6 +3640,121 @@ 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);
+
+ signum = 0;
+ sigcode = 0;
+ sigaddr = 0;
+
+ switch (trapnr) {
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case RISCV_EXCP_U_ECALL:
+ env->pc += 4;
+ if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) {
+ /* kernel-assisted AMO not suitable for do_syscall */
+ start_exclusive();
+ ret = riscv_flush_icache_syscall(env,
+ env->gpr[xA7],
+ env->gpr[xA0],
+ env->gpr[xA1],
+ env->gpr[xA2],
+ env->gpr[xA3]);
+ end_exclusive();
+ } 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 QEMU_USER_EXCP_ATOMIC:
+ start_exclusive();
+ switch (riscv_cpu_do_usermode_amo(cs)) {
+ case RISCV_AMO_OK:
+ env->pc += 4;
+ break;
+ case RISCV_AMO_BADADDR:
+ signum = TARGET_SIGSEGV;
+ sigcode = TARGET_SEGV_MAPERR;
+ sigaddr = env->badaddr;
+ break;
+ case RISCV_AMO_BADINSN:
+ default:
+ signum = TARGET_SIGILL;
+ sigcode = TARGET_ILL_ILLOPC;
+ }
+ end_exclusive();
+ 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 QEMU_USER_EXCP_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)
@@ -4802,6 +4917,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 0000000..bd164ef
--- /dev/null
+++ b/linux-user/riscv/syscall_nr.h
@@ -0,0 +1,275 @@
+/*
+ * 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_syscalls (__NR_finit_module + 1)
diff --git a/linux-user/riscv/target_cpu.h b/linux-user/riscv/target_cpu.h
new file mode 100644
index 0000000..c5549b1
--- /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_signal.h b/linux-user/riscv/target_signal.h
new file mode 100644
index 0000000..ce77f75
--- /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 0000000..4f0462c
--- /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 0000000..d4e109a
--- /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 0000000..ec49dbf
--- /dev/null
+++ b/linux-user/riscv/termbits.h
@@ -0,0 +1,220 @@
+/* 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_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 dae14d4..c928238 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -145,8 +145,9 @@ void host_to_target_sigset(target_sigset_t *d, const sigset_t *s)
int i;

host_to_target_sigset_internal(&d1, s);
- for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
d->sig[i] = tswapal(d1.sig[i]);
+ }
}

static void target_to_host_sigset_internal(sigset_t *d,
@@ -166,8 +167,9 @@ void target_to_host_sigset(sigset_t *d, const target_sigset_t *s)
target_sigset_t s1;
int i;

- for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
s1.sig[i] = tswapal(s->sig[i]);
+ }
target_to_host_sigset_internal(d, &s1);
}

@@ -186,8 +188,9 @@ void target_to_host_old_sigset(sigset_t *sigset,
int i;

d.sig[0] = *old_sigset;
- for(i = 1;i < TARGET_NSIG_WORDS; i++)
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
d.sig[i] = 0;
+ }
target_to_host_sigset(sigset, &d);
}

@@ -474,11 +477,11 @@ void signal_init(void)
int host_sig;

/* generate signal conversion tables */
- for(i = 1; i < _NSIG; i++) {
+ for (i = 1; i < _NSIG; i++) {
if (host_to_target_signal_table[i] == 0)
host_to_target_signal_table[i] = i;
}
- for(i = 1; i < _NSIG; i++) {
+ for (i = 1; i < _NSIG; i++) {
j = host_to_target_signal_table[i];
target_to_host_signal_table[j] = i;
}
@@ -493,7 +496,7 @@ void signal_init(void)
sigfillset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = host_signal_handler;
- for(i = 1; i <= TARGET_NSIG; i++) {
+ for (i = 1; i <= TARGET_NSIG; i++) {
host_sig = target_to_host_signal(i);
sigaction(host_sig, NULL, &oact);
if (oact.sa_sigaction == (void *)SIG_IGN) {
@@ -535,6 +538,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 +551,8 @@ static void force_sigsegv(int oldsig)
}
#endif

+#endif
+
/* abort execution with signal */
static void QEMU_NORETURN dump_core_and_abort(int target_sig)
{
@@ -1129,7 +1135,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0],
frame_addr + offsetof(struct sigframe, fpstate));

- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}

@@ -1210,7 +1216,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,
setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env,
set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate));

- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}

@@ -1348,7 +1354,7 @@ long do_sigreturn(CPUX86State *env)
goto badframe;
/* set blocked signals */
__get_user(target_set.sig[0], &frame->sc.oldmask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}

@@ -1957,7 +1963,7 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc,
/* Write terminating magic word */
__put_user(0, regspace);

- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &uc->tuc_sigmask.sig[i]);
}
}
@@ -1977,7 +1983,7 @@ static void setup_frame_v1(int usig, struct target_sigaction *ka,

setup_sigcontext(&frame->sc, regs, set->sig[0]);

- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}

@@ -2054,7 +2060,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka,
memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack));

setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]);
- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}

@@ -2168,7 +2174,7 @@ static long do_sigreturn_v1(CPUARMState *env)
}

__get_user(set.sig[0], &frame->sc.oldmask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(set.sig[i], &frame->extramask[i - 1]);
}

@@ -2731,7 +2737,7 @@ long do_sigreturn(CPUSPARCState *env)
* the races which exist anyways.
*/
__get_user(set.sig[0], &sf->info.si_mask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(set.sig[i], &sf->extramask[i - 1]);
}

@@ -3232,7 +3238,7 @@ static void setup_frame(int sig, struct target_sigaction * ka,

setup_sigcontext(regs, &frame->sf_sc);

- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->sf_mask.sig[i]);
}

@@ -3276,7 +3282,7 @@ long do_sigreturn(CPUMIPSState *regs)
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;

- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->sf_mask.sig[i]);
}

@@ -3337,7 +3343,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,

setup_sigcontext(env, &frame->rs_uc.tuc_mcontext);

- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
}

@@ -3632,7 +3638,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,
&frame->uc.tuc_stack.ss_size);
setup_sigcontext(&frame->uc.tuc_mcontext,
regs, set->sig[0]);
- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}

@@ -3682,7 +3688,7 @@ long do_sigreturn(CPUSH4State *regs)
}

__get_user(target_set.sig[0], &frame->sc.oldmask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}

@@ -3869,7 +3875,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
/* Save the mask. */
__put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask);

- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}

@@ -3936,7 +3942,7 @@ long do_sigreturn(CPUMBState *env)

/* Restore blocked signals */
__get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&set, &target_set);
@@ -4065,7 +4071,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
/* Save the mask. */
__put_user(set->sig[0], &frame->sc.oldmask);

- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}

@@ -4108,7 +4114,7 @@ long do_sigreturn(CPUCRISState *env)

/* Restore blocked signals */
__get_user(target_set.sig[0], &frame->sc.oldmask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&set, &target_set);
@@ -5385,7 +5391,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,
__put_user(h2g (&rt_sf->uc.tuc_mcontext),
&rt_sf->uc.tuc_regs);
#endif
- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]);
}

@@ -5678,7 +5684,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,

setup_sigcontext(&frame->sc, env, set->sig[0]);

- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}

@@ -5856,7 +5862,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,
if (err)
goto give_sigsegv;

- for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}

@@ -5903,7 +5909,7 @@ long do_sigreturn(CPUM68KState *env)

__get_user(target_set.sig[0], &frame->sc.sc_mask);

- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}

@@ -6378,6 +6384,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 {
@@ -6669,7 +6872,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 11c9116..5e54889 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8434,9 +8434,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 bec3680..e62112a 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/target/riscv/cpu_user.h b/target/riscv/cpu_user.h
new file mode 100644
index 0000000..1c687f4
--- /dev/null
+++ b/target/riscv/cpu_user.h
@@ -0,0 +1,29 @@
+/* Return codes for riscv_cpu_do_userspace_amo */
+#define RISCV_AMO_OK 0
+#define RISCV_AMO_BADINSN 1
+#define RISCV_AMO_BADADDR 2
+
+/* not RISC-V exception codes - this is for qemu user-mode */
+#define QEMU_USER_EXCP_ATOMIC 0xc
+#define QEMU_USER_EXCP_FAULT 0xd
+
+#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 */
+
+#ifdef CONFIG_USER_ONLY
+int riscv_cpu_do_usermode_amo(CPUState *cs);
+
+target_long riscv_flush_icache_syscall(CPURISCVState *env, int num,
+ target_long cmd, target_long arg1, target_long arg2, target_long arg3);
+#endif
diff --git a/target/riscv/user_atomic.c b/target/riscv/user_atomic.c
new file mode 100644
index 0000000..b1646d7
--- /dev/null
+++ b/target/riscv/user_atomic.c
@@ -0,0 +1,291 @@
+/*
+ * RISC-V user-mode atomic memory ops
+ *
+ * Copyright (c) 2016 Alex Suykov <***@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+#ifdef CONFIG_USER_ONLY
+
+#include "qemu.h"
+
+/* The code in this file runs outside of cpu_loop and may not raise
+ any exceptions. Instead, it should return one of RISCV_AMO_* consts.
+
+ See comments around cpu_list_mutex in linux-user/main.c
+ on why exclusive memory ops are done this way.
+
+ Some other arches put AMO handling right into main.c,
+ but for RISC-V there's just too many ops to handle and too much code,
+ so it's all here instead. */
+
+#define BITFIELD(src, end, start) \
+ (((src) >> start) & ((1 << (end - start + 1)) - 1))
+
+#define ENV CPURISCVState *env
+
+/* Load-Reserve: rd = [rs1], creating reservation for [rs1]. */
+
+static int rv_do_lr(ENV, int rd, int rs1, int rs2, int width)
+{
+ int64_t val64;
+ int32_t val32;
+ target_long val;
+ int fault;
+
+ target_long addr = env->gpr[rs1];
+
+ if (rs2) {
+ return RISCV_AMO_BADINSN;
+ }
+
+ switch (width) {
+ case /* 010 */ 2:
+ fault = get_user_s32(val32, addr);
+ val = val32;
+ break;
+ case /* 011 */ 3:
+ fault = get_user_s64(val64, addr);
+ val = val64;
+ break;
+ default:
+ return RISCV_AMO_BADINSN;
+ }
+
+ if (fault) {
+ return RISCV_AMO_BADADDR;
+ }
+
+ if (rd) {
+ env->gpr[rd] = val;
+ }
+
+ env->amoaddr = addr;
+ env->amotest = val;
+
+ return RISCV_AMO_OK;
+}
+
+/* Store-Conditional: [rs1] = rs2, rd = 0 if reservation is intact,
+ otherwise rd = 1. */
+
+static int rv_do_sc(ENV, int rd, int rs1, int rs2, int width)
+{
+ int64_t val64;
+ int32_t val32;
+ target_long val;
+ int fault;
+
+ target_long addr = env->gpr[rs1];
+ target_long resaddr = env->amoaddr;
+ target_long restest = env->amotest;
+
+ if (addr != resaddr) {
+ goto fail; /* no reservation for this address */
+ }
+
+ /* Load and test */
+ switch (width) {
+ case /* 010 */ 2:
+ fault = get_user_s32(val32, addr);
+ val = val32;
+ break;
+ case /* 011 */ 3:
+ fault = get_user_s64(val64, addr);
+ val = val64;
+ break;
+ default:
+ return RISCV_AMO_BADINSN;
+ }
+
+ if (fault) {
+ return RISCV_AMO_BADADDR;
+ }
+ if (val != restest) {
+ goto fail;
+ }
+
+ /* Store */
+ val = env->gpr[rs2];
+ switch (width) {
+ case /* 010 */ 2:
+ val32 = val;
+ fault = put_user_s32(val32, addr);
+ break;
+ case /* 011 */ 3:
+ val64 = val;
+ fault = put_user_s64(val64, addr);
+ break;
+ default:
+ return RISCV_AMO_BADINSN; /* should not happen */
+ }
+
+ if (fault) {
+ return RISCV_AMO_BADADDR;
+ }
+
+ if (rd) {
+ env->gpr[rd] = 0;
+ }
+ return RISCV_AMO_OK;
+
+fail:
+ if (rd) {
+ env->gpr[rd] = 1;
+ }
+ return RISCV_AMO_OK;
+}
+
+/* Tricky signed-unsigned minmaxes */
+#define DEFMINMAX(type, name, ret) \
+ static inline type name(type a, type b) { return ret; }
+DEFMINMAX(target_long, rv_min, a < b ? a : b);
+DEFMINMAX(target_long, rv_max, a > b ? a : b);
+DEFMINMAX(target_ulong, rv_minu, a < b ? a : b);
+DEFMINMAX(target_ulong, rv_maxu, a > b ? a : b);
+
+/* Atomic memory ops: [rs1] = rd = [rs1] op rs2;
+ amoswap, amoadd, amoxor, amoor, amomin, amomax, amominu, amomaxu. */
+
+static int rv_do_amo(ENV, int func, int rd, int rs1, int rs2, int width)
+{
+ int64_t val64;
+ int32_t val32;
+ int fault = 0;
+ target_long addr = env->gpr[rs1];
+ target_long arg = env->gpr[rs2];
+
+ target_long val; /* read/written */
+
+ /* Load, but do not report BADADDR yet */
+ switch (width) {
+ case /* 010 */ 2:
+ fault = get_user_s32(val32, addr);
+ val = val32;
+ break;
+ case /* 011 */ 3:
+ fault = get_user_s64(val64, addr);
+ val = val64;
+ break;
+ default:
+ return RISCV_AMO_BADINSN;
+ }
+
+ target_long vrd = val;
+
+ switch (func) {
+ case /* 00001 */ 0x01:
+ val = arg;
+ break;
+ case /* 00000 */ 0x00:
+ val += arg;
+ break;
+ case /* 00100 */ 0x04:
+ val ^= arg;
+ break;
+ case /* 01100 */ 0x0C:
+ val &= arg;
+ break;
+ case /* 01000 */ 0x08:
+ val |= arg;
+ break;
+ case /* 10000 */ 0x10:
+ val = rv_min(val, arg);
+ break;
+ case /* 10100 */ 0x14:
+ val = rv_max(val, arg);
+ break;
+ case /* 11000 */ 0x18:
+ val = rv_minu(val, arg);
+ break;
+ case /* 11100 */ 0x1C:
+ val = rv_maxu(val, arg);
+ break;
+ default:
+ return RISCV_AMO_BADINSN;
+ }
+
+ /* No BADINSN during decoding, ok to report BADADDR */
+ if (fault) {
+ return RISCV_AMO_BADADDR;
+ }
+ /* No BADINSN on decoding and no BADADDR on read, ok to write rd */
+ if (rd) {
+ env->gpr[rd] = vrd;
+ }
+
+ switch (width) {
+ case /* 010 */ 2:
+ val32 = val;
+ fault = put_user_s32(val32, addr);
+ break;
+ case /* 011 */ 3:
+ val64 = val;
+ fault = put_user_s64(val64, addr);
+ break;
+ default:
+ return RISCV_AMO_BADINSN; /* should not happen */
+ }
+
+ if (fault) {
+ return RISCV_AMO_BADADDR;
+ }
+
+ return RISCV_AMO_OK;
+}
+
+int riscv_cpu_do_usermode_amo(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ uint64_t insn = env->amoinsn;
+ /* Major opcode must always be 0101111 AMO here */
+ if (BITFIELD(insn, 6, 0) != /* 0101111 */ 0x2F) {
+ return RISCV_AMO_BADINSN;
+ }
+ env->amoinsn = 0; /* clear amo request, just in case */
+
+ int func = BITFIELD(insn, 31, 27);
+ int rd = BITFIELD(insn, 11, 7);
+ int width = BITFIELD(insn, 14, 12);
+ int rs1 = BITFIELD(insn, 19, 15);
+ int rs2 = BITFIELD(insn, 24, 20);
+
+ int ret;
+
+ switch (func) {
+ case /* 00010 */ 2:
+ ret = rv_do_lr(env, rd, rs1, rs2, width);
+ break;
+ case /* 00011 */ 3:
+ ret = rv_do_sc(env, rd, rs1, rs2, width);
+ break;
+ default:
+ ret = rv_do_amo(env, func, rd, rs1, rs2, width);
+ break;
+ }
+
+ if (ret == RISCV_AMO_BADADDR) {
+ env->badaddr = rs1;
+ }
+
+ return ret;
+}
+
+#endif
diff --git a/target/riscv/user_syscall.c b/target/riscv/user_syscall.c
new file mode 100644
index 0000000..2cbab94
--- /dev/null
+++ b/target/riscv/user_syscall.c
@@ -0,0 +1,40 @@
+/*
+ * RISC-V Architecture-specific Syscalls for linux-user mode
+ *
+ * Copyright (c) 2016 Alex Suykov <***@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+#ifdef CONFIG_USER_ONLY
+
+#include "qemu.h"
+
+target_long riscv_flush_icache_syscall(CPURISCVState *env, int num,
+ target_long cmd, target_long arg1, target_long arg2, target_long arg3)
+{
+ CPUState *cs = ENV_GET_CPU(env);
+
+ trace_guest_user_syscall(cs, num, cmd, arg1, arg2, arg3, 0, 0, 0, 0);
+
+ tlb_flush(cs);
+ tb_flush(cs);
+
+ return 0;
+}
+
+#endif
--
2.7.0
Richard Henderson
2018-01-03 23:47:04 UTC
Permalink
Post by Michael Clark
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 20f3d8c..178af56 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1272,6 +1272,28 @@ static inline void init_thread(struct target_pt_regs *regs,
#endif /* TARGET_TILEGX */
+#ifdef TARGET_RISCV
+
+#define ELF_START_MMAP 0x80000000
For riscv64 too? Surely closer to ((TASK_SIZE / 3) * 2).
Post by Michael Clark
diff --git a/linux-user/main.c b/linux-user/main.c
index 71696ed..8900141 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -227,7 +227,7 @@ void cpu_loop(CPUX86State *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);
- switch(trapnr) {
+ switch (trapnr) {
Even though the formatting is wrong, don't change unrelated code.
Post by Michael Clark
+ signum = gdb_handlesig(cs, TARGET_SIGTRAP);
+ sigcode = TARGET_TRAP_BRKPT;
+ break;
+ EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n",
+ trapnr);
+ exit(EXIT_FAILURE);
You will need to handle the generic EXCP_ATOMIC as well.
Though of course you won't see that until you use tcg_gen_atomic_*.


r~
Michael Clark
2018-01-05 06:51:05 UTC
Permalink
On Thu, Jan 4, 2018 at 12:47 PM, Richard Henderson <
Post by Michael Clark
Post by Michael Clark
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 20f3d8c..178af56 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1272,6 +1272,28 @@ static inline void init_thread(struct
target_pt_regs *regs,
Post by Michael Clark
#endif /* TARGET_TILEGX */
+#ifdef TARGET_RISCV
+
+#define ELF_START_MMAP 0x80000000
For riscv64 too? Surely closer to ((TASK_SIZE / 3) * 2).
Post by Michael Clark
diff --git a/linux-user/main.c b/linux-user/main.c
index 71696ed..8900141 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -227,7 +227,7 @@ void cpu_loop(CPUX86State *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);
- switch(trapnr) {
+ switch (trapnr) {
Even though the formatting is wrong, don't change unrelated code.
I didn't intend for this to happen. False assumption that only the RISC-V
code was broken. Will attend to this in the next spin.
Post by Michael Clark
Post by Michael Clark
+ signum = gdb_handlesig(cs, TARGET_SIGTRAP);
+ sigcode = TARGET_TRAP_BRKPT;
+ break;
+ EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x -
aborting\n",
Post by Michael Clark
+ trapnr);
+ exit(EXIT_FAILURE);
You will need to handle the generic EXCP_ATOMIC as well.
Though of course you won't see that until you use tcg_gen_atomic_*.
Michael Clark
2018-01-03 00:44:18 UTC
Permalink
The PLIC (Platform Level Interrupt Controller) device provides a
parameterizable interrupt controller based on SiFive's PLIC specification.

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/sifive_plic.c | 558 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_plic.h | 91 +++++++
2 files changed, 649 insertions(+)
create mode 100644 hw/riscv/sifive_plic.c
create mode 100644 include/hw/riscv/sifive_plic.h

diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
new file mode 100644
index 0000000..771dce0
--- /dev/null
+++ b/hw/riscv/sifive_plic.c
@@ -0,0 +1,558 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a parameterizable interrupt controller based on SiFive's PLIC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.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 */
+
+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);
+ }
+}
+
+#if defined RISCV_DEBUG_PLIC
+
+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 */
+ printf("pending : ");
+ for (i = plic->bitfield_words - 1; i >= 0; i--) {
+ printf("%08x", plic->pending[i]);
+ }
+ printf("\n");
+
+ /* pending */
+ printf("claimed : ");
+ for (i = plic->bitfield_words - 1; i >= 0; i--) {
+ printf("%08x", plic->claimed[i]);
+ }
+ printf("\n");
+
+ for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+ printf("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--) {
+ printf("%08x", plic->enable[addrid * plic->bitfield_words + i]);
+ }
+ printf("\n");
+ }
+}
+
+#endif
+
+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;
+ int count = 0;
+ if (!env) {
+ continue;
+ }
+ if (sifive_plic_num_irqs_pending(plic, addrid) > 0) {
+ switch (mode) {
+ case PLICMode_M:
+ if ((env->mip & MIP_MEIP) == 0) {
+ env->mip |= MIP_MEIP;
+ count++;
+ #if defined RISCV_DEBUG_PLIC
+ printf("sifive_plic_update: RAISE hart%d-%c\n",
+ hartid, mode_to_char(mode));
+ #endif
+ }
+ break;
+ case PLICMode_S:
+ if ((env->mip & MIP_SEIP) == 0) {
+ env->mip |= MIP_SEIP;
+ count++;
+ #if defined RISCV_DEBUG_PLIC
+ printf("sifive_plic_update: RAISE hart%d-%c\n",
+ hartid, mode_to_char(mode));
+ #endif
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (mode) {
+ case PLICMode_M:
+ if (env->mip & MIP_MEIP) {
+ env->mip &= ~MIP_MEIP;
+ #if defined RISCV_DEBUG_PLIC
+ printf("sifive_plic_update: LOWER hart%d-%c\n",
+ hartid, mode_to_char(mode));
+ #endif
+ }
+ break;
+ case PLICMode_S:
+ if (env->mip & MIP_SEIP) {
+ env->mip &= ~MIP_SEIP;
+ #if defined RISCV_DEBUG_PLIC
+ printf("sifive_plic_update: LOWER hart%d-%c\n",
+ hartid, mode_to_char(mode));
+ #endif
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (count > 0) {
+ cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
+ }
+ }
+
+ #if defined RISCV_DEBUG_PLIC
+ sifive_plic_print_state(plic);
+ #endif
+}
+
+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;
+}
+
+/* CPU wants to read rtc or timecmp register */
+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 defined RISCV_DEBUG_PLIC
+ printf("plic: read priority: irq=%d priority=%d\n",
+ irq, plic->source_priority[irq]);
+ #endif
+ 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 defined RISCV_DEBUG_PLIC
+ printf("plic: read pending: word=%d value=%d\n",
+ word, plic->pending[word]);
+ #endif
+ 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 defined RISCV_DEBUG_PLIC
+ printf("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]);
+ #endif
+ 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 defined RISCV_DEBUG_PLIC
+ printf("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]);
+ #endif
+ return plic->target_priority[addrid];
+ } else if (contextid == 4) {
+ uint32_t value = sifive_plic_claim(plic, addrid);
+ #if defined RISCV_DEBUG_PLIC
+ printf("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);
+ #endif
+ return value;
+ }
+ }
+
+err:
+ error_report("plic: invalid register read: %08x", (uint32_t)addr);
+ return 0;
+}
+
+/* CPU wrote to rtc or timecmp register */
+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 defined RISCV_DEBUG_PLIC
+ printf("plic: write priority: irq=%d priority=%d\n",
+ irq, plic->source_priority[irq]);
+ #endif
+ 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 defined RISCV_DEBUG_PLIC
+ printf("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]);
+ #endif
+ 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 defined RISCV_DEBUG_PLIC
+ printf("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]);
+ #endif
+ if (value <= plic->num_priorities) {
+ plic->target_priority[addrid] = value;
+ sifive_plic_update(plic);
+ }
+ return;
+ } else if (contextid == 4) {
+ #if defined RISCV_DEBUG_PLIC
+ printf("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);
+ #endif
+ 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 defined RISCV_DEBUG_PLIC
+ printf("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
+ #endif
+ sifive_plic_set_pending(plic, irq, level > 0);
+ sifive_plic_update(plic);
+}
+
+static void sifive_plic_realize(DeviceState *dev, Error **errp)
+{
+ SiFivePLICState *plic = SIFIVE_PLIC(dev);
+ int i;
+
+ memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
+ TYPE_SIFIVE_PLIC, plic->aperture_size);
+ parse_hart_config(plic);
+ qemu_mutex_init(&plic->lock);
+ plic->bitfield_words = (plic->num_sources + 31) >> 5;
+ plic->source_priority = g_new0(uint32_t, plic->num_sources);
+ plic->target_priority = g_new(uint32_t, plic->num_addrs);
+ plic->pending = g_new0(uint32_t, plic->bitfield_words);
+ plic->claimed = g_new0(uint32_t, plic->bitfield_words);
+ plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
+ plic->irqs = g_new0(qemu_irq, plic->num_sources + 1);
+ for (i = 0; i <= plic->num_sources; i++) {
+ plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i);
+ }
+}
+
+static void sifive_plic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = sifive_plic_properties;
+ dc->realize = sifive_plic_realize;
+}
+
+static const TypeInfo sifive_plic_info = {
+ .name = TYPE_SIFIVE_PLIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePLICState),
+ .class_init = sifive_plic_class_init,
+};
+
+static void sifive_plic_register_types(void)
+{
+ type_register_static(&sifive_plic_info);
+}
+
+type_init(sifive_plic_register_types)
+
+/*
+ * Create PLIC device.
+ */
+DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+ uint32_t num_sources, uint32_t num_priorities,
+ uint32_t priority_base, uint32_t pending_base,
+ uint32_t enable_base, uint32_t enable_stride,
+ uint32_t context_base, uint32_t context_stride,
+ uint32_t aperture_size)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC);
+ assert(enable_stride == (enable_stride & -enable_stride));
+ assert(context_stride == (context_stride & -context_stride));
+ qdev_prop_set_string(dev, "hart-config", hart_config);
+ qdev_prop_set_uint32(dev, "num-sources", num_sources);
+ qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
+ qdev_prop_set_uint32(dev, "priority-base", priority_base);
+ qdev_prop_set_uint32(dev, "pending-base", pending_base);
+ qdev_prop_set_uint32(dev, "enable-base", enable_base);
+ qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
+ qdev_prop_set_uint32(dev, "context-base", context_base);
+ qdev_prop_set_uint32(dev, "context-stride", context_stride);
+ qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h
new file mode 100644
index 0000000..e1be499
--- /dev/null
+++ b/include/hw/riscv/sifive_plic.h
@@ -0,0 +1,91 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a RISC-V PLIC device
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
+
--
2.7.0
Michael Clark
2018-01-03 00:44:08 UTC
Permalink
The RISC-V disassembler has no dependencies outside of the 'disas'
directory so it can be applied independently. The majority of the
disassembler is machine-generated from instruction set metadata:

- https://github.com/michaeljclark/riscv-meta

Expected checkpatch errors for consistency and brevity reasons:

ERROR: line over 90 characters
ERROR: trailing statements should be on next line
ERROR: space prohibited between function name and open parenthesis '('
Signed-off-by: Michael Clark <***@sifive.com>
---
disas.c | 2 +
disas/Makefile.objs | 1 +
disas/riscv.c | 2966 +++++++++++++++++++++++++++++++++++++++++++++++++++
include/disas/bfd.h | 2 +
4 files changed, 2971 insertions(+)
create mode 100644 disas/riscv.c

diff --git a/disas.c b/disas.c
index d4ad108..5325b7e 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 194648f..95c64cf 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 0000000..126d352
--- /dev/null
+++ b/disas/riscv.c
@@ -0,0 +1,2966 @@
+/*
+ * QEMU RISC-V Disassembler
+ *
+ * Copyright (c) 2016-2017 Michael Clark <***@mac.com>
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 *name;
+ const rv_codec codec;
+ const char *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[] = {
+ "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",
+ NULL
+};
+
+static const char *rv_freg_name_sym[] = {
+ "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",
+ NULL
+};
+
+/* 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 rv_opcode decode_inst_op(rv_inst inst, rv_isa isa)
+{
+ 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;
+ }
+ return 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_rv_instype(rv_decode *dec, rv_inst 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 constraint_check(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++;
+ }
+}
+
+/* decode instruction to pseudo-instruction */
+
+static void decode_pseudo_inst(rv_decode *dec)
+{
+ const rv_comp_data *comp_data = opcode_data[dec->op].pseudo;
+ if (!comp_data) {
+ return;
+ }
+ while (comp_data->constraints) {
+ if (constraint_check(dec, comp_data->constraints)) {
+ dec->op = comp_data->op;
+ dec->codec = opcode_data[dec->op].codec;
+ return;
+ }
+ comp_data++;
+ }
+}
+
+/* decompress instruction */
+
+static void decompress_inst_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 decompress_inst_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 decompress_inst_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;
+ }
+}
+
+/* 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;
+ dec.op = decode_inst_op(inst, isa);
+ decode_rv_instype(&dec, inst);
+ switch (isa) {
+ case rv32: decompress_inst_rv32(&dec); break;
+ case rv64: decompress_inst_rv64(&dec); break;
+ case rv128: decompress_inst_rv128(&dec); break;
+ }
+ decode_pseudo_inst(&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);
+ }
+ }
+
+ /* TODO - need access to TARGET_RISCV32 or TARGET_RISCV64 */
+ disasm_inst(buf, sizeof(buf), isa, memaddr, inst);
+
+ (*info->fprintf_func)(info->stream, "%s", buf);
+
+ return len;
+}
+
+int print_insn_riscv32(bfd_vma memaddr, struct disassemble_info *info)
+{
+ return print_insn_riscv(memaddr, info, rv32);
+}
+
+int print_insn_riscv64(bfd_vma memaddr, struct disassemble_info *info)
+{
+ return print_insn_riscv(memaddr, info, rv64);
+}
diff --git a/include/disas/bfd.h b/include/disas/bfd.h
index 46c7ec3..ea3714a 100644
--- a/include/disas/bfd.h
+++ b/include/disas/bfd.h
@@ -428,6 +428,8 @@ int print_insn_ia64 (bfd_vma, disassemble_info*);
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_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. */
--
2.7.0
Richard Henderson
2018-01-03 05:30:06 UTC
Permalink
Post by Michael Clark
+static const char *rv_ireg_name_sym[] = {
+ "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",
+ NULL
+};
static const char * const

But maybe even better as

static const char rv_ireg_name_sym[32][4]

and without the useless NULL.

Otherwise,

Reviewed-by: Richard Henderson <***@linaro.org>


r~
Michael Clark
2018-01-03 22:12:04 UTC
Permalink
On Wed, Jan 3, 2018 at 6:30 PM, Richard Henderson <
Post by Michael Clark
Post by Michael Clark
+static const char *rv_ireg_name_sym[] = {
+ "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",
+ NULL
+};
static const char * const
OK.
Post by Michael Clark
But maybe even better as
static const char rv_ireg_name_sym[32][4]
Got it, but it would need to be [32][5] to make room for the NULL
terminator on zero.
Post by Michael Clark
and without the useless NULL.
Yes. they are redundant.
Post by Michael Clark
Otherwise,
Thanks.

These changes will be in the next spin of the patchset.
Michael Clark
2018-01-03 00:44:15 UTC
Permalink
HTIF (Host Target Interface) provides console emulation for QEMU. HTIF
allows identical copies of BBL (Berkeley Boot Loader) and linux to run
on both Spike and QEMU. BBL provides HTIF console access via the
SBI (Supervisor Binary Interface) and the linux kernel SBI console.

The HTIF interface reads the ELF kernel and locates the 'tohost' and
'fromhost' symbols which it uses for guest to host console MMIO.

The HTIT chardev implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/riscv_elf.c | 244 ++++++++++++++++++++++++++
hw/riscv/riscv_htif.c | 399 ++++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/riscv_elf.h | 69 ++++++++
include/hw/riscv/riscv_htif.h | 62 +++++++
4 files changed, 774 insertions(+)
create mode 100644 hw/riscv/riscv_elf.c
create mode 100644 hw/riscv/riscv_htif.c
create mode 100644 include/hw/riscv/riscv_elf.h
create mode 100644 include/hw/riscv/riscv_htif.h

diff --git a/hw/riscv/riscv_elf.c b/hw/riscv/riscv_elf.c
new file mode 100644
index 0000000..355a5a1
--- /dev/null
+++ b/hw/riscv/riscv_elf.c
@@ -0,0 +1,244 @@
+/*
+ * elf.c - A simple package for manipulating symbol tables in elf binaries.
+ *
+ * Taken from
+ * https://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15213-f03/www/
+ * ftrace/elf.c
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glib.h>
+
+#include "hw/riscv/riscv_elf.h"
+
+/*
+ * elf_open - Map a binary into the address space and extract the
+ * locations of the static and dynamic symbol tables and their string
+ * tables. Return this information in a Elf object file handle that will
+ * be passed to all of the other elf functions.
+ */
+Elf_obj64 *elf_open64(const char *filename)
+{
+ int i, fd;
+ struct stat sbuf;
+ Elf_obj64 *ep;
+ Elf64_Shdr *shdr;
+
+ ep = g_new(Elf_obj64, 1);
+
+ /* Do some consistency checks on the binary */
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Can't open \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ if (fstat(fd, &sbuf) == -1) {
+ fprintf(stderr, "Can't stat \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ if (sbuf.st_size < sizeof(Elf64_Ehdr)) {
+ fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename);
+ exit(1);
+ }
+
+ /* It looks OK, so map the Elf binary into our address space */
+ ep->mlen = sbuf.st_size;
+ ep->maddr = mmap(NULL, ep->mlen, PROT_READ, MAP_SHARED, fd, 0);
+ if (ep->maddr == (void *)-1) {
+ fprintf(stderr, "Can't mmap \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ close(fd);
+
+ /* The Elf binary begins with the Elf header */
+ ep->ehdr = ep->maddr;
+
+ /* check we have a 64-bit little-endian RISC-V ELF object */
+ if (ep->ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
+ ep->ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
+ ep->ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
+ ep->ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
+ ep->ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
+ ep->ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
+ ep->ehdr->e_machine != EM_RISCV)
+ {
+ fprintf(stderr, "\"%s\" is not a 64-bit RISC-V ELF object\n", filename);
+ exit(1);
+ }
+
+ /*
+ * Find the static and dynamic symbol tables and their string
+ * tables in the the mapped binary. The sh_link field in symbol
+ * table section headers gives the section index of the string
+ * table for that symbol table.
+ */
+ shdr = (Elf64_Shdr *)(ep->maddr + ep->ehdr->e_shoff);
+ for (i = 0; i < ep->ehdr->e_shnum; i++) {
+ if (shdr[i].sh_type == SHT_SYMTAB) { /* Static symbol table */
+ ep->symtab = (Elf64_Sym *)(ep->maddr + shdr[i].sh_offset);
+ ep->symtab_end = (Elf64_Sym *)((char *)ep->symtab +
+ shdr[i].sh_size);
+ ep->strtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset);
+ }
+ if (shdr[i].sh_type == SHT_DYNSYM) { /* Dynamic symbol table */
+ ep->dsymtab = (Elf64_Sym *)(ep->maddr + shdr[i].sh_offset);
+ ep->dsymtab_end = (Elf64_Sym *)((char *)ep->dsymtab +
+ shdr[i].sh_size);
+ ep->dstrtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset);
+ }
+ }
+ return ep;
+}
+
+Elf_obj32 *elf_open32(const char *filename)
+{
+ int i, fd;
+ struct stat sbuf;
+ Elf_obj32 *ep;
+ Elf32_Shdr *shdr;
+
+ ep = g_new(Elf_obj32, 1);
+
+ /* Do some consistency checks on the binary */
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Can't open \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ if (fstat(fd, &sbuf) == -1) {
+ fprintf(stderr, "Can't stat \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ if (sbuf.st_size < sizeof(Elf32_Ehdr)) {
+ fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename);
+ exit(1);
+ }
+
+ /* It looks OK, so map the Elf binary into our address space */
+ ep->mlen = sbuf.st_size;
+ ep->maddr = mmap(NULL, ep->mlen, PROT_READ, MAP_SHARED, fd, 0);
+ if (ep->maddr == (void *)-1) {
+ fprintf(stderr, "Can't mmap \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ close(fd);
+
+ /* The Elf binary begins with the Elf header */
+ ep->ehdr = ep->maddr;
+
+ /* check we have a 32-bit little-endian RISC-V ELF object */
+ if (ep->ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
+ ep->ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
+ ep->ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
+ ep->ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
+ ep->ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
+ ep->ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
+ ep->ehdr->e_machine != EM_RISCV)
+ {
+ fprintf(stderr, "\"%s\" is not a 32-bit RISC-V ELF object\n", filename);
+ exit(1);
+ }
+
+ /*
+ * Find the static and dynamic symbol tables and their string
+ * tables in the the mapped binary. The sh_link field in symbol
+ * table section headers gives the section index of the string
+ * table for that symbol table.
+ */
+ shdr = (Elf32_Shdr *)(ep->maddr + ep->ehdr->e_shoff);
+ for (i = 0; i < ep->ehdr->e_shnum; i++) {
+ if (shdr[i].sh_type == SHT_SYMTAB) { /* Static symbol table */
+ ep->symtab = (Elf32_Sym *)(ep->maddr + shdr[i].sh_offset);
+ ep->symtab_end = (Elf32_Sym *)((char *)ep->symtab +
+ shdr[i].sh_size);
+ ep->strtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset);
+ }
+ if (shdr[i].sh_type == SHT_DYNSYM) { /* Dynamic symbol table */
+ ep->dsymtab = (Elf32_Sym *)(ep->maddr + shdr[i].sh_offset);
+ ep->dsymtab_end = (Elf32_Sym *)((char *)ep->dsymtab +
+ shdr[i].sh_size);
+ ep->dstrtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset);
+ }
+ }
+ return ep;
+}
+
+/*
+ * elf_close - Free up the resources of an elf object
+ */
+void elf_close64(Elf_obj64 *ep)
+{
+ if (munmap(ep->maddr, ep->mlen) < 0) {
+ perror("munmap");
+ exit(1);
+ }
+ free(ep);
+}
+
+void elf_close32(Elf_obj32 *ep)
+{
+ if (munmap(ep->maddr, ep->mlen) < 0) {
+ perror("munmap");
+ exit(1);
+ }
+ free(ep);
+}
+
+/*
+ * elf_symname - Return ASCII name of a static symbol
+ */
+char *elf_symname64(Elf_obj64 *ep, Elf64_Sym *sym)
+{
+ return &ep->strtab[sym->st_name];
+}
+
+char *elf_symname32(Elf_obj32 *ep, Elf32_Sym *sym)
+{
+ return &ep->strtab[sym->st_name];
+}
+
+/*
+ * elf_firstsym - Return ptr to first symbol in static symbol table
+ */
+Elf64_Sym *elf_firstsym64(Elf_obj64 *ep)
+{
+ return ep->symtab;
+}
+
+Elf32_Sym *elf_firstsym32(Elf_obj32 *ep)
+{
+ return ep->symtab;
+}
+
+
+/*
+ * elf_nextsym - Return ptr to next symbol in static symbol table,
+ * or NULL if no more symbols.
+ */
+Elf64_Sym *elf_nextsym64(Elf_obj64 *ep, Elf64_Sym *sym)
+{
+ sym++;
+ if (sym < ep->symtab_end) {
+ return sym;
+ } else {
+ return NULL;
+ }
+}
+
+Elf32_Sym *elf_nextsym32(Elf_obj32 *ep, Elf32_Sym *sym)
+{
+ sym++;
+ if (sym < ep->symtab_end) {
+ return sym;
+ } else {
+ return NULL;
+ }
+}
diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c
new file mode 100644
index 0000000..f4272e9
--- /dev/null
+++ b/hw/riscv/riscv_htif.c
@@ -0,0 +1,399 @@
+/*
+ * QEMU RISC-V Host Target Interface (HTIF) Emulation
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ * 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.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.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"
+#include "hw/riscv/riscv_elf.h"
+
+#define ENABLE_CHARDEV
+/*#define DEBUG_CHARDEV */
+/*#define DEBUG_HTIF */
+
+#ifdef ENABLE_CHARDEV
+/*
+ * 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)
+{
+ if (size != 1) {
+ return;
+ }
+
+ HTIFState *htifstate = opaque;
+
+ #ifdef DEBUG_CHARDEV
+ if (htifstate->env->mfromhost != 0x0) {
+ fprintf(stderr, "recv handler: fromhost was not ready to \
+ accept input\n");
+ fprintf(stderr, "recv handler: prev value was: %016lx\n",
+ htifstate->env->mfromhost);
+ }
+ #endif
+
+ uint64_t val_written = htifstate->pending_read;
+ uint64_t resp = 0x100 | *buf;
+
+ htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+ qemu_irq_raise(htifstate->irq);
+}
+
+/*
+ * 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)
+{
+ #ifdef DEBUG_CHARDEV
+ fprintf(stderr, "GOT EVENT: %d\n", event);
+ #endif
+}
+
+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;
+}
+#endif
+
+static void dma_strcopy(HTIFState *htifstate, char *str, hwaddr phys_addr)
+{
+ int i = 0;
+ void *base_copy_addr = htifstate->main_mem_ram_ptr + phys_addr;
+ while (*(str + i)) {
+ stb_p((void *)(base_copy_addr + i), *(str + i));
+ i++;
+ }
+ stb_p((void *)(base_copy_addr + i), 0); /* store null term */
+}
+
+static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
+{
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "TOHOST WRITE WITH val 0x%016lx\n", val_written);
+ #endif
+
+ uint8_t device = val_written >> 56;
+ uint8_t cmd = val_written >> 48;
+ uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
+
+ uint64_t addr = payload >> 8;
+ hwaddr real_addr = (hwaddr)addr;
+ uint8_t what = payload & 0xFF;
+ int resp;
+
+ resp = 0; /* stop gcc complaining */
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "mtohost write:\n-device: %d\n-cmd: %d\n-what: %02lx\n\
+ -payload: %016lx\n", device, cmd, payload & 0xFF, payload);
+ #endif
+
+ /*
+ * 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, only test pass/fail support */
+ if (cmd == 0x0) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "frontend syscall handler\n");
+ #endif
+ if (payload & 0x1) {
+ /* test result */
+ if (payload >> 1) {
+ printf("*** FAILED *** (exitcode = %016" PRIx64 ")\n",
+ payload >> 1);
+ } else {
+ printf("TEST PASSED\n");
+ }
+ exit(payload >> 1);
+ }
+ fprintf(stderr, "pk syscall proxy not supported\n");
+ } else if (cmd == 0xFF) {
+ /* use what */
+ if (what == 0xFF) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering name\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"syscall_proxy", real_addr);
+ } else if (what == 0x0) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering syscall cmd\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"syscall", real_addr);
+ } else {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering end of cmds list\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"", real_addr);
+ }
+ resp = 0x1; /* write to indicate device name placed */
+ } else {
+ fprintf(stderr, "HTIF device %d: UNKNOWN COMMAND\n", device);
+ exit(1);
+ }
+ } 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) {
+ #ifdef ENABLE_CHARDEV
+ qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
+ #endif
+ resp = 0x100 | (uint8_t)payload;
+ } else if (cmd == 0xFF) {
+ /* use what */
+ if (what == 0xFF) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering name\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"bcd", real_addr);
+ } else if (what == 0x0) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering read cmd\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"read", real_addr);
+ } else if (what == 0x1) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering write cmd\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"write", real_addr);
+ } else {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering end of cmds list\n");
+ #endif
+ dma_strcopy(htifstate, (char *)"", real_addr);
+ }
+ resp = 0x1; /* write to indicate device name placed */
+ } else {
+ fprintf(stderr, "HTIF device %d: UNKNOWN COMMAND\n", device);
+ exit(1);
+ }
+ /* all other devices */
+ } else if (device == 0x2 && cmd == 0xFF && what == 0xFF) {
+ #ifdef DEBUG_HTIF
+ fprintf(stderr, "registering no device as last\n");
+ #endif
+ stb_p((void *)(htifstate->main_mem_ram_ptr + real_addr), 0);
+ resp = 0x1; /* write to indicate device name placed */
+ } else {
+ fprintf(stderr, "HTIF UNKNOWN DEVICE OR COMMAND!\n");
+ fprintf(stderr, "device: %d\ncmd: %d\nwhat: %02" PRIx64 "\n"
+ "payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload);
+ exit(1);
+ }
+ /*
+ * - 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 */
+ if (htifstate->env->mfromhost != 0) {
+ /* raise HTIF interrupt */
+ qemu_irq_raise(htifstate->irq);
+ }
+}
+
+#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 {
+ printf("Invalid htif register address %016" PRIx64 "\n",
+ (uint64_t)addr);
+ exit(1);
+ }
+}
+
+/* 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;
+ if (htifstate->env->mfromhost == 0x0) {
+ qemu_irq_lower(htifstate->irq);
+ }
+ htifstate->fromhost_inprogress = 0;
+ } else {
+ printf("Invalid htif register address %016" PRIx64 "\n",
+ (uint64_t)addr);
+ exit(1);
+ }
+}
+
+static const MemoryRegionOps htif_mm_ops = {
+ .read = htif_mm_read,
+ .write = htif_mm_write,
+};
+
+HTIFState *htif_mm_init(MemoryRegion *address_space,
+ const char *kernel_filename, qemu_irq irq, MemoryRegion *main_mem,
+ CPURISCVState *env, Chardev *chr)
+{
+ uint64_t fromhost_addr = 0, tohost_addr = 0;
+
+ /* get fromhost/tohost addresses from the ELF, as spike/fesvr do */
+ if (kernel_filename) {
+#if defined(TARGET_RISCV64)
+ Elf_obj64 *e = elf_open64(kernel_filename);
+#else
+ Elf_obj32 *e = elf_open32(kernel_filename);
+#endif
+
+ const char *fromhost = "fromhost";
+ const char *tohost = "tohost";
+
+#if defined(TARGET_RISCV64)
+ Elf64_Sym *curr_sym = elf_firstsym64(e);
+#else
+ Elf32_Sym *curr_sym = elf_firstsym32(e);
+#endif
+ while (curr_sym) {
+#if defined(TARGET_RISCV64)
+ char *symname = elf_symname64(e, curr_sym);
+#else
+ char *symname = elf_symname32(e, curr_sym);
+#endif
+
+ if (strcmp(fromhost, symname) == 0) {
+ /* get fromhost addr */
+ fromhost_addr = curr_sym->st_value;
+ if (curr_sym->st_size != 8) {
+ error_report("HTIF fromhost must be 8 bytes");
+ exit(1);
+ }
+ } else if (strcmp(tohost, symname) == 0) {
+ /* get tohost addr */
+ tohost_addr = curr_sym->st_value;
+ if (curr_sym->st_size != 8) {
+ error_report("HTIF tohost must be 8 bytes");
+ exit(1);
+ }
+ }
+#if defined(TARGET_RISCV64)
+ curr_sym = elf_nextsym64(e, curr_sym);
+#else
+ curr_sym = elf_nextsym32(e, curr_sym);
+#endif
+ }
+
+#if defined(TARGET_RISCV64)
+ elf_close64(e);
+#else
+ elf_close32(e);
+#endif
+ }
+
+ 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->irq = irq;
+ 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;
+#ifdef ENABLE_CHARDEV
+ 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);
+#endif
+ if (base) {
+ memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
+ TYPE_HTIF_UART, size);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ }
+
+ return s;
+}
diff --git a/include/hw/riscv/riscv_elf.h b/include/hw/riscv/riscv_elf.h
new file mode 100644
index 0000000..5771829
--- /dev/null
+++ b/include/hw/riscv/riscv_elf.h
@@ -0,0 +1,69 @@
+/*
+ * elf.h - A package for manipulating Elf binaries
+ *
+ * Taken from:
+ * https://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15213-f03/www/ftrace/
+ * elf.h
+ *
+ */
+
+#ifndef ELF_H
+#define ELF_H
+
+#include <stdint.h>
+#include <elf.h>
+
+/*
+ * This is a handle that is created by elf_open and then used by every
+ * other function in the elf package
+*/
+typedef struct {
+ void *maddr; /* Start of mapped Elf binary segment in memory */
+ int mlen; /* Length in bytes of the mapped Elf segment */
+ Elf64_Ehdr *ehdr; /* Start of main Elf header (same as maddr) */
+ Elf64_Sym *symtab; /* Start of symbol table */
+ Elf64_Sym *symtab_end; /* End of symbol table (symtab + size) */
+ char *strtab; /* Address of string table */
+ Elf64_Sym *dsymtab; /* Start of dynamic symbol table */
+ Elf64_Sym *dsymtab_end; /* End of dynamic symbol table (dsymtab + size) */
+ char *dstrtab; /* Address of dynamic string table */
+} Elf_obj64;
+
+typedef struct {
+ void *maddr; /* Start of mapped Elf binary segment in memory */
+ int mlen; /* Length in bytes of the mapped Elf segment */
+ Elf32_Ehdr *ehdr; /* Start of main Elf header (same as maddr) */
+ Elf32_Sym *symtab; /* Start of symbol table */
+ Elf32_Sym *symtab_end; /* End of symbol table (symtab + size) */
+ char *strtab; /* Address of string table */
+ Elf32_Sym *dsymtab; /* Start of dynamic symbol table */
+ Elf32_Sym *dsymtab_end; /* End of dynamic symbol table (dsymtab + size) */
+ char *dstrtab; /* Address of dynamic string table */
+} Elf_obj32;
+
+/*
+ * Create and destroy Elf object handles
+ */
+Elf_obj64 *elf_open64(const char *filename);
+Elf_obj32 *elf_open32(const char *filename);
+
+void elf_close64(Elf_obj64 *ep);
+void elf_close32(Elf_obj32 *ep);
+
+/*
+ * Functions for manipulating static symbols
+ */
+
+/* Returns pointer to the first symbol */
+Elf64_Sym *elf_firstsym64(Elf_obj64 *ep);
+Elf32_Sym *elf_firstsym32(Elf_obj32 *ep);
+
+/* Returns pointer to the next symbol, or NULL if no more symbols */
+Elf64_Sym *elf_nextsym64(Elf_obj64 *ep, Elf64_Sym *sym);
+Elf32_Sym *elf_nextsym32(Elf_obj32 *ep, Elf32_Sym *sym);
+
+/* Return symbol string name */
+char *elf_symname64(Elf_obj64 *ep, Elf64_Sym *sym);
+char *elf_symname32(Elf_obj32 *ep, Elf32_Sym *sym);
+
+#endif
diff --git a/include/hw/riscv/riscv_htif.h b/include/hw/riscv/riscv_htif.h
new file mode 100644
index 0000000..d37291d
--- /dev/null
+++ b/include/hw/riscv/riscv_htif.h
@@ -0,0 +1,62 @@
+/*
+ * QEMU RISCV Host Target Interface (HTIF) Emulation
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#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;
+ qemu_irq irq; /* host interrupt line */
+ 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;
+
+/* legacy pre qom */
+HTIFState *htif_mm_init(MemoryRegion *address_space,
+ const char *kernel_filename, qemu_irq irq, MemoryRegion *main_mem,
+ CPURISCVState *env, Chardev *chr);
+
+#endif
--
2.7.0
Richard Henderson
2018-01-04 00:00:17 UTC
Permalink
Post by Michael Clark
+ /*
+ * Find the static and dynamic symbol tables and their string
+ * tables in the the mapped binary. The sh_link field in symbol
+ * table section headers gives the section index of the string
+ * table for that symbol table.
+ */
+ shdr = (Elf64_Shdr *)(ep->maddr + ep->ehdr->e_shoff);
This fails to do any byte swapping such that this code works on a big-endian
host. You should use the routines in "hw/elf_ops.h" as adjusted by "hw/loader.h".


r~
Christoph Hellwig
2018-01-08 14:31:05 UTC
Permalink
Post by Michael Clark
HTIF (Host Target Interface) provides console emulation for QEMU. HTIF
allows identical copies of BBL (Berkeley Boot Loader) and linux to run
on both Spike and QEMU. BBL provides HTIF console access via the
SBI (Supervisor Binary Interface) and the linux kernel SBI console.
Do you have a current spec for the SBI? I haven't been able to find
any current reference since it was removed from the privileged spec.
Michael Clark
2018-02-04 20:19:46 UTC
Permalink
Post by Christoph Hellwig
Post by Michael Clark
HTIF (Host Target Interface) provides console emulation for QEMU. HTIF
allows identical copies of BBL (Berkeley Boot Loader) and linux to run
on both Spike and QEMU. BBL provides HTIF console access via the
SBI (Supervisor Binary Interface) and the linux kernel SBI console.
Do you have a current spec for the SBI? I haven't been able to find
any current reference since it was removed from the privileged spec.
AFAIK it still needs to be documented.

BTW I've created branches in my own personal trees for Privileged ISA
v1.9.1. These trees are what I use for v1.9.1 backward compatibility
testing in QEMU:

- https://github.com/michaeljclark/riscv-linux/tree/riscv-linux-4.6.2
- https://github.com/michaeljclark/riscv-pk/tree/bbl-1.9.1

We need to be a little more disciplined with software releases, especially
when there are backward incompatible changes in the specification. The
repos need to be branched and tagged. It should be possible to somehow
derive the conformance level. Perhaps the SBI should have an API for this.
This is one of the driving reasons behind adding version conformance levels
to QEMU. Previously we had repos in a state of flux and if you didn't have
the magic commit ids you were out of luck. For example, when we have
priv isa v1.11 released, we will still need a priv isa v1.10 mode which
masks out all of the new features. Given there are no "feature bits"
besides the extensions "IMAFDSU", all we have to go on presently is the
privileged ISA spec version number.
Christoph Hellwig
2018-02-04 21:29:32 UTC
Permalink
Post by Michael Clark
BTW I've created branches in my own personal trees for Privileged ISA
v1.9.1. These trees are what I use for v1.9.1 backward compatibility
- https://github.com/michaeljclark/riscv-linux/tree/riscv-linux-4.6.2
- https://github.com/michaeljclark/riscv-pk/tree/bbl-1.9.1
What MMU-enabled chips that implement 1.9.1 are out there? If there
is enough we should support this with a compile time option in the
Linux kernel as well.
Post by Michael Clark
We need to be a little more disciplined with software releases, especially
when there are backward incompatible changes in the specification. The
repos need to be branched and tagged. It should be possible to somehow
derive the conformance level. Perhaps the SBI should have an API for this.
This is one of the driving reasons behind adding version conformance levels
to QEMU. Previously we had repos in a state of flux and if you didn't have
the magic commit ids you were out of luck. For example, when we have
priv isa v1.11 released, we will still need a priv isa v1.10 mode which
masks out all of the new features. Given there are no "feature bits"
besides the extensions "IMAFDSU", all we have to go on presently is the
privileged ISA spec version number.
As far as I can tell privileged ISA changes post 1.10 should be backwards
compatible and only implemement detectable optional CSRs and instructions.

That being said I started a thread on that on the privileged spec list
where I need to follow up on Andrews mail once I get back to my work
mail after a little vacation.
Michael Clark
2018-02-04 23:23:53 UTC
Permalink
Post by Christoph Hellwig
Post by Michael Clark
BTW I've created branches in my own personal trees for Privileged ISA
v1.9.1. These trees are what I use for v1.9.1 backward compatibility
- https://github.com/michaeljclark/riscv-linux/tree/riscv-linux-4.6.2
- https://github.com/michaeljclark/riscv-pk/tree/bbl-1.9.1
What MMU-enabled chips that implement 1.9.1 are out there? If there
is enough we should support this with a compile time option in the
Linux kernel as well.
It's very likely that there are only test chips that are not widely
available. It's likely not worth supporting in the latest version of
linux-kernel.

That said, we've kept priv 1.9.1 support in QEMU for other reasons. There
are a lot of other OS ports and not all of them have necessarily updated to
the new spec version at the same time. We have to draw a line in the sand
somewhere, where we've decided that we're not going to break things for
folk who don't have the magic set of commit hashes. i.e. how it was prior,
at least for riscv-qemu. I know of other emulators that are still on priv
v1.9.1.

We are also going to need to be careful about backwards incompatible
device-tree changes. i.e. if we change device tree, we should target these
changes at priv v1.11 and version the device tree nodes and attributes. Non
additive changes that are not optional need to go in the next version, not
the current version.
Post by Christoph Hellwig
Post by Michael Clark
We need to be a little more disciplined with software releases,
especially
Post by Michael Clark
when there are backward incompatible changes in the specification. The
repos need to be branched and tagged. It should be possible to somehow
derive the conformance level. Perhaps the SBI should have an API for
this.
Post by Michael Clark
This is one of the driving reasons behind adding version conformance
levels
Post by Michael Clark
to QEMU. Previously we had repos in a state of flux and if you didn't
have
Post by Michael Clark
the magic commit ids you were out of luck. For example, when we have
priv isa v1.11 released, we will still need a priv isa v1.10 mode which
masks out all of the new features. Given there are no "feature bits"
besides the extensions "IMAFDSU", all we have to go on presently is the
privileged ISA spec version number.
As far as I can tell privileged ISA changes post 1.10 should be backwards
compatible and only implemement detectable optional CSRs and instructions.
At present there is no way that I know of, other that trapping on illegal
instructions, to detect whether a CSR is implemented or not. I've written a
little tool called riscv-probe, that does such. I'll post the sources at
some point. It may very well be possible to support priv v1.11 on priv
v1.10 with trap and emulate, however it may require running the Supervisor
in U mode. I guess we'll find out whether anything is missing if we tried
to implement it. It would definately require shadow paging, given there is
second level address translation.
Post by Christoph Hellwig
That being said I started a thread on that on the privileged spec list
where I need to follow up on Andrews mail once I get back to my work
mail after a little vacation.
Michael Clark
2018-01-03 00:44:16 UTC
Permalink
Holds the state of a heterogenous array of RISC-V hardware threads.

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/riscv_hart.c | 95 +++++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/riscv_hart.h | 45 ++++++++++++++++++++
2 files changed, 140 insertions(+)
create mode 100644 hw/riscv/riscv_hart.c
create mode 100644 include/hw/riscv/riscv_hart.h

diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
new file mode 100644
index 0000000..a7e079e
--- /dev/null
+++ b/hw/riscv/riscv_hart.c
@@ -0,0 +1,95 @@
+/*
+ * QEMU RISCV Hart Array
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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-model", RISCVHartArrayState, cpu_model),
+ 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_model);
+ s->harts[n].env.mhartid = n;
+ object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]),
+ &error_abort);
+ qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
+ object_property_set_bool(OBJECT(&s->harts[n]), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+}
+
+static void riscv_harts_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = riscv_harts_props;
+ dc->realize = riscv_harts_realize;
+}
+
+static void riscv_harts_init(Object *obj)
+{
+ /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */
+}
+
+static const TypeInfo riscv_harts_info = {
+ .name = TYPE_RISCV_HART_ARRAY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVHartArrayState),
+ .instance_init = riscv_harts_init,
+ .class_init = riscv_harts_class_init,
+};
+
+static void riscv_harts_register_types(void)
+{
+ type_register_static(&riscv_harts_info);
+}
+
+type_init(riscv_harts_register_types)
diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
new file mode 100644
index 0000000..c45e987
--- /dev/null
+++ b/include/hw/riscv/riscv_hart.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU RISC-V Hart Array interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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_model;
+ RISCVCPU *harts;
+} RISCVHartArrayState;
+
+#endif
--
2.7.0
Richard Henderson
2018-01-04 00:08:17 UTC
Permalink
Post by Michael Clark
Holds the state of a heterogenous array of RISC-V hardware threads.
At the moment they are homogeneous, since they are all created from the same
cpu_model. Is that the ultimate intent?
Post by Michael Clark
+static Property riscv_harts_props[] = {
+ DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
+ DEFINE_PROP_STRING("cpu-model", RISCVHartArrayState, cpu_model),
+ DEFINE_PROP_END_OF_LIST(),
+};
How does num_harts interact with max_cpus and smp_cpus, and thus the related
command-line options?


r~
Antony Pavlov
2018-01-05 21:41:05 UTC
Permalink
On Wed, 3 Jan 2018 13:44:16 +1300
Post by Michael Clark
Holds the state of a heterogenous array of RISC-V hardware threads.
...
Post by Michael Clark
--- /dev/null
+++ b/include/hw/riscv/riscv_hart.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU RISC-V Hart Array interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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_model;
+ RISCVCPU *harts;
+} RISCVHartArrayState;
+
+#endif
--
2.7.0
Hmm, you use SysBusDevice, uint32_t and RISCVCPU types but there is no header files
inclusion to define these types.

I propose this fixup:

--- a/include/hw/riscv/riscv_hart.h
+++ b/include/hw/riscv/riscv_hart.h
@@ -27,6 +27,10 @@
#ifndef HW_RISCV_HART_H
#define HW_RISCV_HART_H

+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+
#define TYPE_RISCV_HART_ARRAY "riscv.hart_array"

#define RISCV_HART_ARRAY(obj) \


Some files in include/hw/riscv/ are affected by this problem (e.g. sifive_uart.h).
--
Best regards,
  Antony Pavlov
Eric Blake
2018-01-05 21:44:31 UTC
Permalink
Post by Antony Pavlov
On Wed, 3 Jan 2018 13:44:16 +1300
Post by Michael Clark
Holds the state of a heterogenous array of RISC-V hardware threads.
Hmm, you use SysBusDevice, uint32_t and RISCVCPU types but there is no header files
inclusion to define these types.
--- a/include/hw/riscv/riscv_hart.h
+++ b/include/hw/riscv/riscv_hart.h
@@ -27,6 +27,10 @@
#ifndef HW_RISCV_HART_H
#define HW_RISCV_HART_H
+#include "qemu/osdep.h"
NACK to this part. Our policy is that all .c files should include
osdep.h before anything else, and therefore, all .h files can assume
that osdep.h has already been included. Extending that logic, uint32_t
is always available for use in any .h, without having to add any includes.
Post by Antony Pavlov
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
But including these headers for SysBusDevice and RISCVCPU makes sense.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
Michael Clark
2018-01-03 00:44:19 UTC
Permalink
RISC-V machines compatble with Spike aka riscv-isa-sim, the RISC-V
Instruction Set Simulator. The following machines are implemented:

- 'spike_v1.9'; HTIF console, config-string, Privileged ISA Version 1.9.1
- 'spike_v1.10'; HTIF console, device-tree, Privileged ISA Version 1.10

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/spike_v1_09.c | 207 ++++++++++++++++++++++++++++++++++
hw/riscv/spike_v1_10.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/spike.h | 51 +++++++++
3 files changed, 539 insertions(+)
create mode 100644 hw/riscv/spike_v1_09.c
create mode 100644 hw/riscv/spike_v1_10.c
create mode 100644 include/hw/riscv/spike.h

diff --git a/hw/riscv/spike_v1_09.c b/hw/riscv/spike_v1_09.c
new file mode 100644
index 0000000..9b32c6a
--- /dev/null
+++ b/hw/riscv/spike_v1_09.c
@@ -0,0 +1,207 @@
+/*
+ * QEMU RISC-V Spike Board
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ * This provides a RISC-V Board with the following devices:
+ *
+ * 0) HTIF Test Pass/Fail Reporting (no syscall proxy)
+ * 1) HTIF Console
+ *
+ * These are created by htif_mm_init below.
+ *
+ * This board currently uses a hardcoded devicetree that indicates one hart.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.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 "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 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,
+ /* little_endian = */ 0, ELF_MACHINE, 1, 0) < 0) {
+ error_report("qemu: could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ return kernel_entry;
+}
+
+static void riscv_spike_board_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = spike_memmap;
+
+ SpikeState *s = g_new0(SpikeState, 1);
+ /* const char *cpu_model = machine->cpu_model; */
+ /* const char *kernel_cmdline = machine->kernel_cmdline; */
+ /* const char *initrd_filename = machine->initrd_filename; */
+ 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), TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_09,
+ "cpu-model", &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, 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);
+ }
+
+ 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" "40000000" ";\n"
+ "};\n"
+ "ram {\n"
+ " 0 {\n"
+ " addr 0x" "80000000" ";\n"
+ " size 0x" "%016" PRIx64 ";\n"
+ " };\n"
+ "};\n"
+ "core {\n"
+ " 0" " {\n"
+ " " "0 {\n"
+ " isa " "rv64imafd" ";\n"
+ " timecmp 0x" "40000008" ";\n"
+ " ipi 0x" "40001000" ";\n" /* match dummy ipi region above */
+ " };\n"
+ " };\n"
+ "};\n";
+
+ /* build config string with supplied memory size */
+ size_t config_string_size = strlen(config_string_tmpl) + 16;
+ char *config_string = malloc(config_string_size);
+ snprintf(config_string, config_string_size,
+ config_string_tmpl, (uint64_t)ram_size);
+ size_t config_string_len = strlen(config_string);
+
+ /* copy in the reset vector */
+ cpu_physical_memory_write(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);
+
+ /* add memory mapped htif registers at location specified in the symbol
+ table of the elf being loaded (thus kernel_filename is passed to the
+ init rather than an address) */
+ htif_mm_init(system_memory, machine->kernel_filename,
+ s->soc.harts[0].env.irq[4], boot_rom,
+ &s->soc.harts[0].env, serial_hds[0]);
+
+ /* Core Local Interruptor (timer and IPI) */
+ sifive_clint_create(0x40000000, 0x2000, smp_cpus, 0x1000, 0x8, 0x0);
+}
+
+static int riscv_spike_board_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_spike_board_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_spike_board_sysbus_device_init;
+}
+
+static const TypeInfo riscv_spike_board_device = {
+ .name = TYPE_RISCV_SPIKE_V1_09_1_BOARD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SpikeState),
+ .class_init = riscv_spike_board_class_init,
+};
+
+static void riscv_spike_board_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)";
+ mc->init = riscv_spike_board_init;
+ mc->max_cpus = 1;
+ mc->is_default = 1;
+}
+
+DEFINE_MACHINE("spike_v1.9", riscv_spike_board_machine_init)
+
+static void riscv_spike_board_register_types(void)
+{
+ type_register_static(&riscv_spike_board_device);
+}
+
+type_init(riscv_spike_board_register_types);
diff --git a/hw/riscv/spike_v1_10.c b/hw/riscv/spike_v1_10.c
new file mode 100644
index 0000000..4edff49
--- /dev/null
+++ b/hw/riscv/spike_v1_10.c
@@ -0,0 +1,281 @@
+/*
+ * QEMU RISC-V Spike Board
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ * Author: Michael Clark, ***@sifive.com
+ *
+ * This provides a RISC-V Board with the following devices:
+ *
+ * 0) HTIF Test Pass/Fail Reporting (no syscall proxy)
+ * 1) HTIF Console
+ *
+ * These are created by htif_mm_init below.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.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 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,
+ /* little_endian = */ 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(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 riscv_spike_board_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = spike_memmap;
+
+ SpikeState *s = g_new0(SpikeState, 1);
+ /* const char *cpu_model = machine->cpu_model; */
+ /* const char *kernel_cmdline = machine->kernel_cmdline; */
+ /* const char *initrd_filename = machine->initrd_filename; */
+ 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), TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10,
+ "cpu-model", &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 */
+ cpu_physical_memory_write(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);
+
+ /* add memory mapped htif registers at location specified in the symbol
+ table of the elf being loaded (thus kernel_filename is passed to the
+ init rather than an address) */
+ htif_mm_init(system_memory, machine->kernel_filename,
+ s->soc.harts[0].env.irq[4], boot_rom,
+ &s->soc.harts[0].env, serial_hds[0]);
+
+ /* Core Local Interruptor (timer and IPI) */
+ sifive_clint_create(0x2000000, 0x10000, smp_cpus,
+ SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static int riscv_spike_board_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_spike_board_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_spike_board_sysbus_device_init;
+}
+
+static const TypeInfo riscv_spike_board_device = {
+ .name = TYPE_RISCV_SPIKE_V1_10_0_BOARD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SpikeState),
+ .class_init = riscv_spike_board_class_init,
+};
+
+static void riscv_spike_board_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)";
+ mc->init = riscv_spike_board_init;
+ mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("spike_v1.10", riscv_spike_board_machine_init)
+
+static void riscv_spike_board_register_types(void)
+{
+ type_register_static(&riscv_spike_board_device);
+}
+
+type_init(riscv_spike_board_register_types);
diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h
new file mode 100644
index 0000000..cb4d6ac
--- /dev/null
+++ b/include/hw/riscv/spike.h
@@ -0,0 +1,51 @@
+/*
+ * SiFive U500 series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_SPIKE_H
+#define HW_SPIKE_H
+
+#define TYPE_RISCV_SPIKE_V1_09_1_BOARD "riscv.spike_v1_9"
+#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
+};
+
+#endif
--
2.7.0
Richard Henderson
2018-01-04 00:14:55 UTC
Permalink
Post by Michael Clark
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+ &error_abort);
Ah, right. Nevermind my previous question.


r~
Michael Clark
2018-01-03 00:44:13 UTC
Permalink
Implements the physical memory protection extension as specified in
Privileged ISA Version 1.10.

Signed-off-by: Michael Clark <***@sifive.com>
---
target/riscv/pmp.c | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++++
target/riscv/pmp.h | 70 ++++++++++
2 files changed, 451 insertions(+)
create mode 100644 target/riscv/pmp.c
create mode 100644 target/riscv/pmp.h

diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
new file mode 100644
index 0000000..04f1df7
--- /dev/null
+++ b/target/riscv/pmp.c
@@ -0,0 +1,381 @@
+/*
+ * QEMU RISC-V PMP (Physical Memory Protection)
+ *
+ * Author: Daire McNamara, ***@emdalo.com
+ * Ivan Griffin, ***@emdalo.com
+ *
+ * This provides a RISC-V Physical Memory Protection implementation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu-common.h"
+
+/* #define DEBUG_PMP 1 */
+
+#ifndef CONFIG_USER_ONLY
+
+#ifdef DEBUG_PMP
+#define PMP_PRINTF(fmt, ...) \
+do { fprintf(stderr, "pmp: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define PMP_PRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+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_PRINTF("Ignoring pmpcfg write - locked\n");
+ }
+ } else {
+ PMP_PRINTF("Ignoring pmpcfg write - out of bounds\n");
+ }
+}
+
+static target_ulong pmp_get_napot_base_and_range(target_ulong reg,
+ target_ulong *range)
+{
+ /* construct a mask of all bits bar the top bit */
+ target_ulong mask = 0u;
+ target_ulong base = reg;
+ target_ulong numbits = (sizeof(target_ulong) * 8u) + 2u;
+ mask = (mask - 1u) >> 1;
+
+ while (mask) {
+ if ((reg & mask) == mask) {
+ /* this is the mask to use */
+ base = reg & ~mask;
+ break;
+ }
+ mask >>= 1;
+ numbits--;
+ }
+
+ *range = (1lu << numbits) - 1u;
+ return base;
+}
+
+
+/* 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].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:
+ sa = pmp_get_napot_base_and_range(this_addr, &ea);
+ sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
+ ea += sa;
+ 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_PRINTF("pmp violation - access is partially in /"
+ " partially out\n");
+ 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_PRINTF("hart%d pmpcfg_reg%d val: 0x" TARGET_FMT_lx "\n",
+ env->mhartid, reg_index, val);
+
+ if ((reg_index & 1) && (sizeof(target_ulong) == 8)) {
+ PMP_PRINTF("Ignoring pmpcfg write - incorrect address\n");
+ 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_PRINTF("hart%d pmpcfg_reg%d, (rval: 0x" TARGET_FMT_lx ")\n",
+ 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_PRINTF("hart%d addr%d val: 0x" TARGET_FMT_lx "\n",
+ env->mhartid, addr_index, val);
+
+ /* val &= 0x3ffffffffffffful; */
+
+ 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_PRINTF("Ignoring pmpaddr write - locked\n");
+ }
+ } else {
+ PMP_PRINTF("Ignoring pmpaddr write - out of bounds\n");
+ }
+}
+
+
+/*
+ * Handle a read from a pmpaddr CSP
+ */
+target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index)
+{
+ PMP_PRINTF("hart%d addr%d (val: 0x" TARGET_FMT_lx ")\n",
+ env->mhartid, addr_index,
+ env->pmp_state.pmp[addr_index].addr_reg);
+ return env->pmp_state.pmp[addr_index].addr_reg;
+}
+
+#endif
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
new file mode 100644
index 0000000..9f2c32d
--- /dev/null
+++ b/target/riscv/pmp.h
@@ -0,0 +1,70 @@
+/*
+ * QEMU RISC-V PMP (Physical Memory Protection)
+ *
+ * Author: Daire McNamara, ***@emdalo.com
+ * Ivan Griffin, ***@emdalo.com
+ *
+ * This provides a RISC-V Physical Memory Protection interface
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
--
2.7.0
Richard Henderson
2018-01-03 23:03:19 UTC
Permalink
Post by Michael Clark
+#ifdef DEBUG_PMP
+#define PMP_PRINTF(fmt, ...) \
+do { fprintf(stderr, "pmp: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define PMP_PRINTF(fmt, ...) \
+do {} while (0)
+#endif
Debugging goes to qemu_log.

Rearrange this so that formatting is always compile-time checked.
E.g.

#define DEBUG_PMP 0
#define PMP_PRINTF(fmt, ...) \
do { \
if (DEBUG_PMP) { \
qemu_log("pmp: " fmt, ##__VA_ARGS__); \
} \
} while (0)
Post by Michael Clark
+static target_ulong pmp_get_napot_base_and_range(target_ulong reg,
+ target_ulong *range)
+{
+ /* construct a mask of all bits bar the top bit */
+ target_ulong mask = 0u;
+ target_ulong base = reg;
+ target_ulong numbits = (sizeof(target_ulong) * 8u) + 2u;
+ mask = (mask - 1u) >> 1;
+
+ while (mask) {
+ if ((reg & mask) == mask) {
+ /* this is the mask to use */
+ base = reg & ~mask;
+ break;
+ }
+ mask >>= 1;
+ numbits--;
+ }
+
+ *range = (1lu << numbits) - 1u;
+ return base;
+}
You can compute napot with ctz64(~reg).
More useless LU suffixes.
Post by Michael Clark
+ if (pmp_index >= 1u) {
+ prev_addr = env->pmp_state.pmp[pmp_index].addr_reg;
pmp_index - 1
Post by Michael Clark
+ 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++;
+ }
+ }
Doesn't this mean that pmp_index ordering != pmp_state ordering? Which would
mean that you'd be matching rules in the wrong order for the static prioirity.
Post by Michael Clark
+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;
Given how the range is computed in pmp_update_rule, surely <= ea.
Post by Michael Clark
+ s = pmp_is_in_range(env, i, addr);
+ e = pmp_is_in_range(env, i, addr + size);
Surely addr + size - 1.
Post by Michael Clark
+ /* val &= 0x3ffffffffffffful; */
+
Why is this commented out? Surely that's exactly what the spec says.
Although it's easier to compare as

val = extract64(val, 0, 54);


r~
Michael Clark
2018-01-03 00:44:22 UTC
Permalink
Simple model of the PRCI (Power, Reset, Clock, Interrupt) to emulate
register reads made by the SDK BSP.

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/sifive_prci.c | 107 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_prci.h | 43 +++++++++++++++++
2 files changed, 150 insertions(+)
create mode 100644 hw/riscv/sifive_prci.c
create mode 100644 include/hw/riscv/sifive_prci.h

diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
new file mode 100644
index 0000000..5c27696
--- /dev/null
+++ b/hw/riscv/sifive_prci.c
@@ -0,0 +1,107 @@
+/*
+ * 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
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 Property sifive_prci_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+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 void sifive_prci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = sifive_prci_properties;
+}
+
+static const TypeInfo sifive_prci_info = {
+ .name = TYPE_SIFIVE_PRCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePRCIState),
+ .instance_init = sifive_prci_init,
+ .class_init = sifive_prci_class_init,
+};
+
+static void sifive_prci_register_types(void)
+{
+ type_register_static(&sifive_prci_info);
+}
+
+type_init(sifive_prci_register_types)
+
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_prci_create(hwaddr addr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h
new file mode 100644
index 0000000..0e032e5
--- /dev/null
+++ b/include/hw/riscv/sifive_prci.h
@@ -0,0 +1,43 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
--
2.7.0
KONRAD Frederic
2018-01-03 15:02:27 UTC
Permalink
Post by Michael Clark
Simple model of the PRCI (Power, Reset, Clock, Interrupt) to emulate
register reads made by the SDK BSP.
---
hw/riscv/sifive_prci.c | 107 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_prci.h | 43 +++++++++++++++++
2 files changed, 150 insertions(+)
create mode 100644 hw/riscv/sifive_prci.c
create mode 100644 include/hw/riscv/sifive_prci.h
diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
new file mode 100644
index 0000000..5c27696
--- /dev/null
+++ b/hw/riscv/sifive_prci.c
@@ -0,0 +1,107 @@
+/*
+ * 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
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 Property sifive_prci_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
Is that needed?
Post by Michael Clark
+
+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 void sifive_prci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = sifive_prci_properties;
+}
+
+static const TypeInfo sifive_prci_info = {
+ .name = TYPE_SIFIVE_PRCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePRCIState),
+ .instance_init = sifive_prci_init,
+ .class_init = sifive_prci_class_init,
+};
+
+static void sifive_prci_register_types(void)
+{
+ type_register_static(&sifive_prci_info);
+}
+
+type_init(sifive_prci_register_types)
That's what is missing in the previous patch.

Fred
Post by Michael Clark
+
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_prci_create(hwaddr addr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h
new file mode 100644
index 0000000..0e032e5
--- /dev/null
+++ b/include/hw/riscv/sifive_prci.h
@@ -0,0 +1,43 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
Michael Clark
2018-01-03 22:07:40 UTC
Permalink
Post by KONRAD Frederic
Post by Michael Clark
Simple model of the PRCI (Power, Reset, Clock, Interrupt) to emulate
register reads made by the SDK BSP.
---
hw/riscv/sifive_prci.c | 107 ++++++++++++++++++++++++++++++
+++++++++++
include/hw/riscv/sifive_prci.h | 43 +++++++++++++++++
2 files changed, 150 insertions(+)
create mode 100644 hw/riscv/sifive_prci.c
create mode 100644 include/hw/riscv/sifive_prci.h
diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
new file mode 100644
index 0000000..5c27696
--- /dev/null
+++ b/hw/riscv/sifive_prci.c
@@ -0,0 +1,107 @@
+/*
+ * 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
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 Property sifive_prci_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
Is that needed?
At this point it is redundant.

I will remove it from the next spin of the patch set.

+
Post by KONRAD Frederic
Post by Michael Clark
+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 void sifive_prci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = sifive_prci_properties;
+}
+
+static const TypeInfo sifive_prci_info = {
+ .name = TYPE_SIFIVE_PRCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePRCIState),
+ .instance_init = sifive_prci_init,
+ .class_init = sifive_prci_class_init,
+};
+
+static void sifive_prci_register_types(void)
+{
+ type_register_static(&sifive_prci_info);
+}
+
+type_init(sifive_prci_register_types)
That's what is missing in the previous patch.
Fred
+
Post by Michael Clark
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_prci_create(hwaddr addr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/include/hw/riscv/sifive_prci.h
b/include/hw/riscv/sifive_prci.h
new file mode 100644
index 0000000..0e032e5
--- /dev/null
+++ b/include/hw/riscv/sifive_prci.h
@@ -0,0 +1,43 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
Michael Clark
2018-01-03 00:44:20 UTC
Permalink
RISC-V machine with device-tree, 16550a UART and VirtIO MMIO.
The following machine is implemented:

- 'virt'; CLINT, PLIC, 16550A UART, VirtIO MMIO, device-tree

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/virt.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/virt.h | 73 ++++++++++
2 files changed, 437 insertions(+)
create mode 100644 hw/riscv/virt.c
create mode 100644 include/hw/riscv/virt.h

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
new file mode 100644
index 0000000..7c6fead
--- /dev/null
+++ b/hw/riscv/virt.c
@@ -0,0 +1,364 @@
+/*
+ * QEMU RISC-V VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * RISC-V machine with 16550a UART and VirtIO MMIO
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.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/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_CLINT] = { 0x2000000, 0x10000 },
+ [VIRT_PLIC] = { 0xc000000, 0x4000000 },
+ [VIRT_UART0] = { 0x10000000, 0x100 },
+ [VIRT_VIRTIO] = { 0x10001000, 0x1000 },
+ [VIRT_DRAM] = { 0x80000000, 0x0 },
+};
+
+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,
+ /* little_endian = */ 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(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("/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);
+}
+
+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;
+
+ /* 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), TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10,
+ "cpu-model", &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 */
+ 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) {
+ 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[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */
+ 0x00000000,
+ /* dtb: */
+ };
+
+ /* copy in the reset vector */
+ cpu_physical_memory_write(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);
+
+ for (i = 0; i < VIRTIO_COUNT; i++) {
+ sysbus_create_simple("virtio-mmio",
+ memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
+ SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
+ }
+
+ serial_mm_init(system_memory, memmap[VIRT_UART0].base,
+ 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
+ serial_hds[0], DEVICE_LITTLE_ENDIAN);
+}
+
+static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_virt_board_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_virt_board_sysbus_device_init;
+}
+
+static const TypeInfo riscv_virt_board_device = {
+ .name = TYPE_RISCV_VIRT_BOARD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVVirtState),
+ .class_init = riscv_virt_board_class_init,
+};
+
+static void riscv_virt_board_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)";
+ mc->init = riscv_virt_board_init;
+ mc->max_cpus = 8; /* hardcoded limit in BBL */
+}
+
+DEFINE_MACHINE("virt", riscv_virt_board_machine_init)
+
+static void riscv_virt_board_register_types(void)
+{
+ type_register_static(&riscv_virt_board_device);
+}
+
+type_init(riscv_virt_board_register_types);
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
new file mode 100644
index 0000000..9e5467a
--- /dev/null
+++ b/include/hw/riscv/virt.h
@@ -0,0 +1,73 @@
+/*
+ * SiFive VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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_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
+
+#endif
--
2.7.0
Michael Clark
2018-01-03 00:44:21 UTC
Permalink
QEMU model of the UART on the SiFive E300 and U500 series SOCs.
BBL supports the SiFive UART for early console access via the SBI
(Supervisor Binary Interface) and the linux kernel SBI console.

The SiFive UART implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/sifive_uart.c | 182 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_uart.h | 76 +++++++++++++++++
2 files changed, 258 insertions(+)
create mode 100644 hw/riscv/sifive_uart.c
create mode 100644 include/hw/riscv/sifive_uart.h

diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000..0e73df6
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_uart.h"
+
+/*
+ * Not yet implemented:
+ *
+ * Transmit FIFO using "qemu/fifo8.h"
+ * SIFIVE_UART_IE_TXWM interrupts
+ * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
+ * Rx FIFO watermark interrupt trigger threshold
+ * Tx FIFO watermark interrupt trigger threshold.
+ */
+
+static void update_irq(SiFiveUARTState *s)
+{
+ int cond = 0;
+ if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
+ cond = 1;
+ }
+ if (cond) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveUARTState *s = opaque;
+ unsigned char r;
+ switch (addr) {
+ case SIFIVE_UART_RXFIFO:
+ if (s->rx_fifo_len) {
+ r = s->rx_fifo[0];
+ memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
+ s->rx_fifo_len--;
+ qemu_chr_fe_accept_input(&s->chr);
+ update_irq(s);
+ return r;
+ }
+ return 0x80000000;
+
+ case SIFIVE_UART_TXFIFO:
+ return 0; /* Should check tx fifo */
+ case SIFIVE_UART_IE:
+ return s->ie;
+ case SIFIVE_UART_IP:
+ return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ case SIFIVE_UART_TXCTRL:
+ return s->txctrl;
+ case SIFIVE_UART_RXCTRL:
+ return s->rxctrl;
+ case SIFIVE_UART_DIV:
+ return s->div;
+ }
+
+ hw_error("%s: bad read: addr=0x%x\n",
+ __func__, (int)addr);
+ return 0;
+}
+
+static void
+uart_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveUARTState *s = opaque;
+ uint32_t value = val64;
+ unsigned char ch = value;
+
+ switch (addr) {
+ case SIFIVE_UART_TXFIFO:
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ return;
+ case SIFIVE_UART_IE:
+ s->ie = val64;
+ update_irq(s);
+ return;
+ case SIFIVE_UART_TXCTRL:
+ s->txctrl = val64;
+ return;
+ case SIFIVE_UART_RXCTRL:
+ s->rxctrl = val64;
+ return;
+ case SIFIVE_UART_DIV:
+ s->div = val64;
+ return;
+ }
+ hw_error("%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)value);
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ SiFiveUARTState *s = opaque;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+ s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+ update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ SiFiveUARTState *s = opaque;
+
+ return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+ SiFiveUARTState *s = opaque;
+
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+
+ return 0;
+}
+
+/*
+ * Create UART device.
+ */
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+ Chardev *chr, qemu_irq irq)
+{
+ SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
+ s->irq = irq;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ return s;
+}
diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
new file mode 100644
index 0000000..1ab0106
--- /dev/null
+++ b/include/hw/riscv/sifive_uart.h
@@ -0,0 +1,76 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
--
2.7.0
KONRAD Frederic
2018-01-03 14:57:50 UTC
Permalink
Hi all,
Post by Michael Clark
QEMU model of the UART on the SiFive E300 and U500 series SOCs.
BBL supports the SiFive UART for early console access via the SBI
(Supervisor Binary Interface) and the linux kernel SBI console.
The SiFive UART implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.
---
hw/riscv/sifive_uart.c | 182 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_uart.h | 76 +++++++++++++++++
2 files changed, 258 insertions(+)
create mode 100644 hw/riscv/sifive_uart.c
create mode 100644 include/hw/riscv/sifive_uart.h
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000..0e73df6
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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"
+
+/*
+ *
+ * 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) {
+ 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;
+
+ return 0; /* Should check tx fifo */
+ return s->ie;
+ return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ return s->txctrl;
+ return s->rxctrl;
+ 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) {
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ return;
+ s->ie = val64;
+ update_irq(s);
+ return;
+ s->txctrl = val64;
+ return;
+ s->rxctrl = val64;
+ return;
+ 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");
I'd use qemu_log instead.
Post by Michael Clark
+ 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));
You need to create a qemu object for that instead of using
g_malloc0. There are plenty of example in the code.

eg: hw/char/pl011.c

Thanks,
Fred
Post by Michael Clark
+ s->irq = irq;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ return s;
+}
diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
new file mode 100644
index 0000000..1ab0106
--- /dev/null
+++ b/include/hw/riscv/sifive_uart.h
@@ -0,0 +1,76 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
Michael Clark
2018-01-05 06:38:15 UTC
Permalink
Post by KONRAD Frederic
Hi all,
Post by Michael Clark
QEMU model of the UART on the SiFive E300 and U500 series SOCs.
BBL supports the SiFive UART for early console access via the SBI
(Supervisor Binary Interface) and the linux kernel SBI console.
The SiFive UART implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.
---
hw/riscv/sifive_uart.c | 182 ++++++++++++++++++++++++++++++
+++++++++++
include/hw/riscv/sifive_uart.h | 76 +++++++++++++++++
2 files changed, 258 insertions(+)
create mode 100644 hw/riscv/sifive_uart.c
create mode 100644 include/hw/riscv/sifive_uart.h
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000..0e73df6
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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"
+
+/*
+ *
+ * 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) {
+ 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;
+
+ return 0; /* Should check tx fifo */
+ return s->ie;
+ return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ return s->txctrl;
+ return s->rxctrl;
+ 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) {
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ return;
+ s->ie = val64;
+ update_irq(s);
+ return;
+ s->txctrl = val64;
+ return;
+ s->rxctrl = val64;
+ return;
+ 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");
I'd use qemu_log instead.
It's on our todo list to replace all printf's with
appropiate qemu_log_mask, trace events, assertions etc. The code is
currently littered with printfs and exits.
Post by KONRAD Frederic
+ return;
Post by Michael Clark
+ }
+ 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));
You need to create a qemu object for that instead of using
g_malloc0. There are plenty of example in the code.
eg: hw/char/pl011.c
I'm aware of that. We've encapsulated many of the other hardware widgets
using qom however based on the patch header we followed the lead of
hw/char/serial.c using the legacy pre qom model.

This was done for very deliberate reasons, because there have been quite a
few chardev changes (front-end, back-end split, and hot plugging) since our
forward port, so we used hw/char/serial.c as reference. We're also going to
use hw/char/serial.c as reference for fifo8 as the SiFive UART has an 8
character FIFO.

I can add it to the to-do list. In any case we can remove machines that
depend on SiFive UART from our next patch set if pre qom devices are a
blocker. The virt machine uses hw/char/serial.c and thats the machine the
Linux distro builders want to use because it's the only RISC-V QEMU machine
with working network and block devices.

The todo list is growing ;-) We have to classify things as to whether they
are blockers, or whether they can be done in tree.

Obviously one of the advantage of being in-tree is that we might get help
from the wider qemu-devel community.

There are however a lot of baseline clean-ups we need to do first before we
start expanding device support and we have to choose which device gets
attention first. Currently it's looking like emulation of the SiFive
AON/PRCI block for power-off and reset. This is used by the E-series,
U-series and likely the virt machine. The spike machine uses HTIF.

Thanks,
Post by KONRAD Frederic
Fred
+ s->irq = irq;
Post by Michael Clark
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ return s;
+}
diff --git a/include/hw/riscv/sifive_uart.h
b/include/hw/riscv/sifive_uart.h
new file mode 100644
index 0000000..1ab0106
--- /dev/null
+++ b/include/hw/riscv/sifive_uart.h
@@ -0,0 +1,76 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
Antony Pavlov
2018-01-04 21:07:25 UTC
Permalink
On Wed, 3 Jan 2018 13:44:21 +1300
Post by Michael Clark
QEMU model of the UART on the SiFive E300 and U500 series SOCs.
BBL supports the SiFive UART for early console access via the SBI
(Supervisor Binary Interface) and the linux kernel SBI console.
The SiFive UART implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.
---
hw/riscv/sifive_uart.c | 182 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_uart.h | 76 +++++++++++++++++
2 files changed, 258 insertions(+)
create mode 100644 hw/riscv/sifive_uart.c
create mode 100644 include/hw/riscv/sifive_uart.h
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000..0e73df6
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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"
+
+/*
+ *
+ * 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) {
+ 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 already has code for FIFO implementation.

Can we use Fifo8 as hw/char/serial.c does?

Please see my 'hw/sifive_uart: use generic Fifo8' patch:

https://github.com/riscv/riscv-qemu/pull/60/commits/6d68a4bc9a617b72ec563f72e58e2467998d5c4b
Post by Michael Clark
+ qemu_chr_fe_accept_input(&s->chr);
+ update_irq(s);
+ return r;
+ }
+ return 0x80000000;
+
+ return 0; /* Should check tx fifo */
+ return s->ie;
+ return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ return s->txctrl;
+ return s->rxctrl;
+ 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) {
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ return;
+ s->ie = val64;
+ update_irq(s);
+ return;
+ s->txctrl = val64;
+ return;
+ s->rxctrl = val64;
+ return;
+ s->div = val64;
+ return;
+ }
+ hw_error("%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)value);
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ SiFiveUARTState *s = opaque;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+ s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+ update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ SiFiveUARTState *s = opaque;
+
+ return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+ SiFiveUARTState *s = opaque;
+
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+
+ return 0;
+}
+
+/*
+ * Create UART device.
+ */
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+ Chardev *chr, qemu_irq irq)
+{
+ SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
+ s->irq = irq;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ return s;
+}
diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
new file mode 100644
index 0000000..1ab0106
--- /dev/null
+++ b/include/hw/riscv/sifive_uart.h
@@ -0,0 +1,76 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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
--
2.7.0
--
Best regards,
  Antony Pavlov
Michael Clark
2018-01-05 06:03:11 UTC
Permalink
Post by Antony Pavlov
On Wed, 3 Jan 2018 13:44:21 +1300
Post by Michael Clark
QEMU model of the UART on the SiFive E300 and U500 series SOCs.
BBL supports the SiFive UART for early console access via the SBI
(Supervisor Binary Interface) and the linux kernel SBI console.
The SiFive UART implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.
---
hw/riscv/sifive_uart.c | 182
+++++++++++++++++++++++++++++++++++++++++
Post by Michael Clark
include/hw/riscv/sifive_uart.h | 76 +++++++++++++++++
2 files changed, 258 insertions(+)
create mode 100644 hw/riscv/sifive_uart.c
create mode 100644 include/hw/riscv/sifive_uart.h
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000..0e73df6
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * Permission is hereby granted, free of charge, to any person
obtaining a copy
Post by Michael Clark
+ * of this software and associated documentation files (the
"Software"), to deal
Post by Michael Clark
+ * in the Software without restriction, including without limitation
the rights
Post by Michael Clark
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
Post by Michael Clark
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be
included in
Post by Michael Clark
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
Post by Michael Clark
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
Post by Michael Clark
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL
Post by Michael Clark
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER
Post by Michael Clark
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM,
Post by Michael Clark
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN
Post by Michael Clark
+ * THE SOFTWARE.
+ */
+
+#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"
+
+/*
+ *
+ * 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) {
+ 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 already has code for FIFO implementation.
Can we use Fifo8 as hw/char/serial.c does?
https://github.com/riscv/riscv-qemu/pull/60/commits/6d68a4bc9a617b72ec563f72e58e2467998d5c4b
Yeah sure, we can. I commented on the PR and it’s still open but the patch
needs to be rebased. You’ll notice this comment in the file.

+/*
Post by Antony Pavlov
+ *
+ * Transmit FIFO using "qemu/fifo8.h"
...
Post by Antony Pavlov
Post by Michael Clark
+ qemu_chr_fe_accept_input(&s->chr);
+ update_irq(s);
+ return r;
+ }
+ return 0x80000000;
+
+ return 0; /* Should check tx fifo */
+ return s->ie;
+ return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ return s->txctrl;
+ return s->rxctrl;
+ 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) {
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ return;
+ s->ie = val64;
+ update_irq(s);
+ return;
+ s->txctrl = val64;
+ return;
+ s->rxctrl = val64;
+ return;
+ 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,
Post by Michael Clark
+ Chardev *chr, qemu_irq irq)
+{
+ SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
+ s->irq = irq;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ return s;
+}
diff --git a/include/hw/riscv/sifive_uart.h
b/include/hw/riscv/sifive_uart.h
Post by Michael Clark
new file mode 100644
index 0000000..1ab0106
--- /dev/null
+++ b/include/hw/riscv/sifive_uart.h
@@ -0,0 +1,76 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
obtaining a copy
Post by Michael Clark
+ * of this software and associated documentation files (the
"Software"), to deal
Post by Michael Clark
+ * in the Software without restriction, including without limitation
the rights
Post by Michael Clark
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
Post by Michael Clark
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be
included in
Post by Michael Clark
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
Post by Michael Clark
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
Post by Michael Clark
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL
Post by Michael Clark
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER
Post by Michael Clark
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM,
Post by Michael Clark
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN
Post by Michael Clark
+ * THE SOFTWARE.
+ */
+
+#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 */
Post by Michael Clark
+ SIFIVE_UART_IE_RXWM = 2 /* Receive watermark interrupt
enable */
Post by Michael Clark
+};
+
+enum {
+ SIFIVE_UART_IP_TXWM = 1, /* Transmit watermark interrupt
pending */
Post by Michael Clark
+ SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt
pending */
Post by Michael Clark
+};
+
+#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,
Post by Michael Clark
+ Chardev *chr, qemu_irq irq);
+
+#endif
--
2.7.0
--
Best regards,
Antony Pavlov
Michael Clark
2018-01-03 00:44:12 UTC
Permalink
TCG code generation for the RV32IMAFDC and RV64IMAFDC. The QEMU
RISC-V code generator has complete coverage for the Base ISA v2.2,
Privileged ISA v1.9.1 and Privileged ISA v1.10:

- RISC-V Instruction Set Manual Volume I: User-Level ISA Version 2.2
- RISC-V Instruction Set Manual Volume II: Privileged ISA Version 1.9.1
- RISC-V Instruction Set Manual Volume II: Privileged ISA Version 1.10

Signed-off-by: Michael Clark <***@sifive.com>
---
target/riscv/instmap.h | 377 +++++++++
target/riscv/translate.c | 2032 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 2409 insertions(+)
create mode 100644 target/riscv/instmap.h
create mode 100644 target/riscv/translate.c

diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h
new file mode 100644
index 0000000..5121f63
--- /dev/null
+++ b/target/riscv/instmap.h
@@ -0,0 +1,377 @@
+/*
+ * RISC-V emulation for qemu: Instruction decode helpers
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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(op) (MASK_OP_MAJOR(op) | (op & ((0x7 << 12)\
+ | (0x1F << 27))))
+enum {
+ OPC_RISC_LR_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x02 << 27),
+ OPC_RISC_SC_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x03 << 27),
+ OPC_RISC_AMOSWAP_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x01 << 27),
+ OPC_RISC_AMOADD_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x00 << 27),
+ OPC_RISC_AMOXOR_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x04 << 27),
+ OPC_RISC_AMOAND_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x0C << 27),
+ OPC_RISC_AMOOR_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x08 << 27),
+ OPC_RISC_AMOMIN_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x10 << 27),
+ OPC_RISC_AMOMAX_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x14 << 27),
+ OPC_RISC_AMOMINU_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x18 << 27),
+ OPC_RISC_AMOMAXU_W = OPC_RISC_ATOMIC | (0x2 << 12) | (0x1C << 27),
+
+ OPC_RISC_LR_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x02 << 27),
+ OPC_RISC_SC_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x03 << 27),
+ OPC_RISC_AMOSWAP_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x01 << 27),
+ OPC_RISC_AMOADD_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x00 << 27),
+ OPC_RISC_AMOXOR_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x04 << 27),
+ OPC_RISC_AMOAND_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x0C << 27),
+ OPC_RISC_AMOOR_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x08 << 27),
+ OPC_RISC_AMOMIN_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x10 << 27),
+ OPC_RISC_AMOMAX_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x14 << 27),
+ OPC_RISC_AMOMINU_D = OPC_RISC_ATOMIC | (0x3 << 12) | (0x18 << 27),
+ OPC_RISC_AMOMAXU_D = OPC_RISC_ATOMIC | (0x3 << 12) | (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/translate.c b/target/riscv/translate.c
new file mode 100644
index 0000000..c368bd5
--- /dev/null
+++ b/target/riscv/translate.c
@@ -0,0 +1,2032 @@
+/*
+ * RISC-V emulation for qemu: main translation routines.
+ *
+ * Author: Sagar Karandikar, ***@eecs.berkeley.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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;
+#ifdef CONFIG_USER_ONLY
+static TCGv_i32 cpu_amoinsn;
+#endif
+
+#include "exec/gen-icount.h"
+
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong pc;
+ target_ulong next_pc;
+ uint32_t opcode;
+ int singlestep_enabled;
+ int mem_idx;
+ int bstate;
+} DisasContext;
+
+static inline void kill_unknown(DisasContext *ctx, int excp);
+
+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. */
+};
+
+
+static const char * const 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 "
+};
+
+static const char * const 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"
+};
+
+/* 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 inline 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);
+}
+
+static inline void generate_exception_mbadaddr(DisasContext *ctx, int excp)
+{
+ tcg_gen_movi_tl(cpu_pc, ctx->pc);
+ TCGv_i32 helper_tmp = tcg_const_i32(excp);
+ gen_helper_raise_exception_mbadaddr(cpu_env, helper_tmp, cpu_pc);
+ tcg_temp_free_i32(helper_tmp);
+}
+
+/* unknown instruction */
+static inline void kill_unknown(DisasContext *ctx, int excp)
+{
+ generate_exception(ctx, excp);
+ ctx->bstate = BS_STOP;
+}
+
+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 inline 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_helper_raise_exception_debug(cpu_env);
+ }
+ 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)
+{
+ TCGv t0 = tcg_temp_new();
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(t0, t0, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ TCGv_i64 src1 = tcg_temp_new_i64();
+ TCGv_i64 src2 = tcg_temp_new_i64();
+
+ tcg_gen_mov_i64(src1, cpu_fpr[rs1]);
+ tcg_gen_mov_i64(src2, cpu_fpr[rs2]);
+
+ switch (rm) {
+ case 0: /* fsgnj */
+
+ if (rs1 == rs2) { /* FMOV */
+ tcg_gen_mov_i64(cpu_fpr[rd], src1);
+ }
+
+ tcg_gen_andi_i64(src1, src1, ~min);
+ tcg_gen_andi_i64(src2, src2, min);
+ tcg_gen_or_i64(cpu_fpr[rd], src1, src2);
+ break;
+ case 1: /* fsgnjn */
+ tcg_gen_andi_i64(src1, src1, ~min);
+ tcg_gen_not_i64(src2, src2);
+ tcg_gen_andi_i64(src2, src2, min);
+ tcg_gen_or_i64(cpu_fpr[rd], src1, src2);
+ break;
+ case 2: /* fsgnjx */
+ tcg_gen_andi_i64(src2, src2, min);
+ tcg_gen_xor_i64(cpu_fpr[rd], src1, src2);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ tcg_temp_free_i64(src1);
+ tcg_temp_free_i64(src2);
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ tcg_temp_free(t0);
+}
+
+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:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+
+ 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;
+ source1 = tcg_temp_new();
+ gen_get_gpr(source1, rs1);
+ target_long extra_shamt = 0;
+
+ 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:
+ if ((imm >= 32)) {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ /* fall through to SLLI */
+#endif
+ case OPC_RISC_SLLI:
+ if (imm < TARGET_LONG_BITS) {
+ tcg_gen_shli_tl(source1, source1, imm);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+#if defined(TARGET_RISCV64)
+ case OPC_RISC_SHIFT_RIGHT_IW:
+ if ((imm & 0x3ff) >= 32) {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ tcg_gen_shli_tl(source1, source1, 32);
+ extra_shamt = 32;
+ /* fall through to SHIFT_RIGHT_I */
+#endif
+ case OPC_RISC_SHIFT_RIGHT_I:
+ /* differentiate on IMM */
+ if ((imm & 0x3ff) < TARGET_LONG_BITS) {
+ if (imm & 0x400) {
+ /* SRAI[W] */
+ tcg_gen_sari_tl(source1, source1, (imm ^ 0x400) + extra_shamt);
+ } else {
+ /* SRLI[W] */
+ tcg_gen_shri_tl(source1, source1, imm + extra_shamt);
+ }
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+
+ 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_feature(env, RISCV_FEATURE_RVC)) {
+ if ((next_pc & 0x3) != 0) {
+ generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+ }
+ }
+ 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 = gen_new_label();
+ TCGv t0;
+ 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_feature(env, RISCV_FEATURE_RVC)) {
+ 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);
+
+ gen_set_label(misaligned);
+ generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+ tcg_gen_exit_tb(0);
+ ctx->bstate = BS_BRANCH;
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ 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:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+
+ gen_goto_tb(ctx, 1, ctx->next_pc);
+ gen_set_label(l); /* branch taken */
+ if (!riscv_feature(env, RISCV_FEATURE_RVC) && ((ctx->pc + bimm) & 0x3)) {
+ /* misaligned */
+ generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+ tcg_gen_exit_tb(0);
+ } else {
+ gen_goto_tb(ctx, 0, ctx->pc + bimm);
+ }
+ tcg_temp_free(source1);
+ tcg_temp_free(source2);
+ 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) {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ } else {
+ 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) {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ } else {
+ 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 = tcg_temp_new();
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(t0, t0, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ 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);
+ break;
+ case OPC_RISC_FLD:
+ tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ tcg_temp_free(t0);
+}
+
+static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
+ int rs2, target_long imm)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(t0, t0, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ 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:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_atomic(DisasContext *ctx, uint32_t opc,
+ int rd, int rs1, int rs2)
+{
+#if !defined(CONFIG_USER_ONLY)
+ /* TODO: handle aq, rl bits? - for now just get rid of them: */
+ opc = MASK_OP_ATOMIC_NO_AQ_RL(opc);
+ TCGv source1, source2, dat;
+ TCGLabel *j = gen_new_label();
+ TCGLabel *done = gen_new_label();
+ source1 = tcg_temp_local_new();
+ source2 = tcg_temp_local_new();
+ dat = tcg_temp_local_new();
+ gen_get_gpr(source1, rs1);
+ gen_get_gpr(source2, rs2);
+
+ switch (opc) {
+ /* all currently implemented as non-atomics */
+ case OPC_RISC_LR_W:
+ /* put addr in load_res */
+ tcg_gen_mov_tl(load_res, source1);
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ break;
+ case OPC_RISC_SC_W:
+ tcg_gen_brcond_tl(TCG_COND_NE, load_res, source1, j);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ tcg_gen_movi_tl(dat, 0); /*success */
+ tcg_gen_br(done);
+ gen_set_label(j);
+ tcg_gen_movi_tl(dat, 1); /*fail */
+ gen_set_label(done);
+ break;
+ case OPC_RISC_AMOSWAP_W:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOADD_W:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_add_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOXOR_W:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_xor_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOAND_W:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_and_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOOR_W:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_or_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMIN_W:
+ tcg_gen_ext32s_tl(source2, source2); /* since comparing */
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_LT, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMAX_W:
+ tcg_gen_ext32s_tl(source2, source2); /* since comparing */
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_GT, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMINU_W:
+ tcg_gen_ext32u_tl(source2, source2); /* since comparing */
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_LTU, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ tcg_gen_ext32s_tl(dat, dat); /* since load was TEUL */
+ break;
+ case OPC_RISC_AMOMAXU_W:
+ tcg_gen_ext32u_tl(source2, source2); /* since comparing */
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_GTU, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ tcg_gen_ext32s_tl(dat, dat); /* since load was TEUL */
+ break;
+#if defined(TARGET_RISCV64)
+ case OPC_RISC_LR_D:
+ /* put addr in load_res */
+ tcg_gen_mov_tl(load_res, source1);
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_SC_D:
+ tcg_gen_brcond_tl(TCG_COND_NE, load_res, source1, j);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_movi_tl(dat, 0); /* success */
+ tcg_gen_br(done);
+ gen_set_label(j);
+ tcg_gen_movi_tl(dat, 1); /* fail */
+ gen_set_label(done);
+ break;
+ case OPC_RISC_AMOSWAP_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOADD_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_add_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOXOR_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_xor_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOAND_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_and_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOOR_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_or_tl(source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMIN_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_LT, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMAX_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_GT, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMINU_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_LTU, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+ case OPC_RISC_AMOMAXU_D:
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ tcg_gen_movcond_tl(TCG_COND_GTU, source2, dat, source2, dat, source2);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEQ | MO_ALIGN);
+ break;
+#endif
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+
+ gen_set_gpr(rd, dat);
+ tcg_temp_free(source1);
+ tcg_temp_free(source2);
+ tcg_temp_free(dat);
+#else
+ tcg_gen_movi_i32(cpu_amoinsn, ctx->opcode);
+ generate_exception(ctx, QEMU_USER_EXCP_ATOMIC);
+#endif
+}
+
+static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd,
+ int rs1, int rs2, int rs3, int rm)
+{
+ TCGv_i64 rm_reg = tcg_temp_new_i64();
+ tcg_gen_movi_i64(rm_reg, rm);
+
+ switch (opc) {
+ case OPC_RISC_FMADD_S:
+ gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ case OPC_RISC_FMADD_D:
+ gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ tcg_temp_free_i64(rm_reg);
+
+}
+
+static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd,
+ int rs1, int rs2, int rs3, int rm)
+{
+ TCGv_i64 rm_reg = tcg_temp_new_i64();
+ tcg_gen_movi_i64(rm_reg, rm);
+
+ switch (opc) {
+ case OPC_RISC_FMSUB_S:
+ gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ case OPC_RISC_FMSUB_D:
+ gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ tcg_temp_free_i64(rm_reg);
+}
+
+static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd,
+ int rs1, int rs2, int rs3, int rm)
+{
+ TCGv_i64 rm_reg = tcg_temp_new_i64();
+ tcg_gen_movi_i64(rm_reg, rm);
+
+ switch (opc) {
+ case OPC_RISC_FNMSUB_S:
+ gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ case OPC_RISC_FNMSUB_D:
+ gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ tcg_temp_free_i64(rm_reg);
+}
+
+static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd,
+ int rs1, int rs2, int rs3, int rm)
+{
+ TCGv_i64 rm_reg = tcg_temp_new_i64();
+ tcg_gen_movi_i64(rm_reg, rm);
+
+ switch (opc) {
+ case OPC_RISC_FNMADD_S:
+ gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ case OPC_RISC_FNMADD_D:
+ gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ cpu_fpr[rs3], rm_reg);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ tcg_temp_free_i64(rm_reg);
+}
+
+static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
+ int rs1, int rs2, int rm)
+{
+ TCGv_i64 rm_reg = tcg_temp_new_i64();
+ TCGv write_int_rd = tcg_temp_new();
+ tcg_gen_movi_i64(rm_reg, rm);
+
+ switch (opc) {
+ case OPC_RISC_FADD_S:
+ gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ break;
+ case OPC_RISC_FSUB_S:
+ gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ break;
+ case OPC_RISC_FMUL_S:
+ gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ break;
+ case OPC_RISC_FDIV_S:
+ gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ 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 */
+ if (rm == 0x0) {
+ gen_helper_fmin_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else if (rm == 0x1) {
+ gen_helper_fmax_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+ case OPC_RISC_FSQRT_S:
+ gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg);
+ break;
+ case OPC_RISC_FEQ_S:
+ /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */
+ if (rm == 0x0) {
+ gen_helper_fle_s(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else if (rm == 0x1) {
+ gen_helper_flt_s(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else if (rm == 0x2) {
+ gen_helper_feq_s(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ gen_set_gpr(rd, write_int_rd);
+ break;
+ case OPC_RISC_FCVT_W_S:
+ /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */
+ if (rs2 == 0x0) { /* FCVT_W_S */
+ gen_helper_fcvt_w_s(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+ } else if (rs2 == 0x1) { /* FCVT_WU_S */
+ gen_helper_fcvt_wu_s(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+ } else if (rs2 == 0x2) { /* FCVT_L_S */
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_l_s(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else if (rs2 == 0x3) { /* FCVT_LU_S */
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_lu_s(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ gen_set_gpr(rd, write_int_rd);
+ break;
+ case OPC_RISC_FCVT_S_W:
+ /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */
+ gen_get_gpr(write_int_rd, rs1);
+ if (rs2 == 0) { /* FCVT_S_W */
+ gen_helper_fcvt_s_w(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+ } else if (rs2 == 0x1) { /* FCVT_S_WU */
+ gen_helper_fcvt_s_wu(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+ } else if (rs2 == 0x2) { /* FCVT_S_L */
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_s_l(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else if (rs2 == 0x3) { /* FCVT_S_LU */
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_s_lu(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+ case OPC_RISC_FMV_X_S: {
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(write_int_rd, cpu_env,
+ offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(write_int_rd, write_int_rd, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, write_int_rd, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ /* also OPC_RISC_FCLASS_S */
+ if (rm == 0x0) { /* FMV */
+#if defined(TARGET_RISCV64)
+ tcg_gen_ext32s_tl(write_int_rd, cpu_fpr[rs1]);
+#else
+ tcg_gen_extrl_i64_i32(write_int_rd, cpu_fpr[rs1]);
+#endif
+ } else if (rm == 0x1) {
+ gen_helper_fclass_s(write_int_rd, cpu_env, cpu_fpr[rs1]);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ gen_set_gpr(rd, write_int_rd);
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ break;
+ }
+ case OPC_RISC_FMV_S_X:
+ {
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(write_int_rd, cpu_env,
+ offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(write_int_rd, write_int_rd, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, write_int_rd, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ gen_get_gpr(write_int_rd, rs1);
+#if defined(TARGET_RISCV64)
+ tcg_gen_mov_tl(cpu_fpr[rd], write_int_rd);
+#else
+ tcg_gen_extu_i32_i64(cpu_fpr[rd], write_int_rd);
+#endif
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ break;
+ }
+ /* double */
+ case OPC_RISC_FADD_D:
+ gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ break;
+ case OPC_RISC_FSUB_D:
+ gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ break;
+ case OPC_RISC_FMUL_D:
+ gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ break;
+ case OPC_RISC_FDIV_D:
+ gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2],
+ rm_reg);
+ 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 */
+ if (rm == 0x0) {
+ gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else if (rm == 0x1) {
+ gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+ case OPC_RISC_FCVT_S_D:
+ if (rs2 == 0x1) {
+ gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+ case OPC_RISC_FCVT_D_S:
+ if (rs2 == 0x0) {
+ gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+ case OPC_RISC_FSQRT_D:
+ gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg);
+ break;
+ case OPC_RISC_FEQ_D:
+ /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */
+ if (rm == 0x0) {
+ gen_helper_fle_d(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else if (rm == 0x1) {
+ gen_helper_flt_d(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else if (rm == 0x2) {
+ gen_helper_feq_d(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ gen_set_gpr(rd, write_int_rd);
+ break;
+ case OPC_RISC_FCVT_W_D:
+ /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */
+ if (rs2 == 0x0) {
+ gen_helper_fcvt_w_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+ } else if (rs2 == 0x1) {
+ gen_helper_fcvt_wu_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+ } else if (rs2 == 0x2) {
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_l_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else if (rs2 == 0x3) {
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_lu_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ gen_set_gpr(rd, write_int_rd);
+ break;
+ case OPC_RISC_FCVT_D_W:
+ /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */
+ gen_get_gpr(write_int_rd, rs1);
+ if (rs2 == 0x0) {
+ gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+ } else if (rs2 == 0x1) {
+ gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+ } else if (rs2 == 0x2) {
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else if (rs2 == 0x3) {
+#if defined(TARGET_RISCV64)
+ gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg);
+#else
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+#endif
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ break;
+#if defined(TARGET_RISCV64)
+ case OPC_RISC_FMV_X_D:
+ {
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(write_int_rd, cpu_env,
+ offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(write_int_rd, write_int_rd, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, write_int_rd, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ /* also OPC_RISC_FCLASS_D */
+ if (rm == 0x0) { /* FMV */
+ tcg_gen_mov_tl(write_int_rd, cpu_fpr[rs1]);
+ } else if (rm == 0x1) {
+ gen_helper_fclass_d(write_int_rd, cpu_env, cpu_fpr[rs1]);
+ } else {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ }
+ gen_set_gpr(rd, write_int_rd);
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ break;
+ }
+ case OPC_RISC_FMV_D_X:
+ {
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(write_int_rd, cpu_env,
+ offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(write_int_rd, write_int_rd, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, write_int_rd, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
+
+ /* proceed with operation */
+ gen_set_label(fp_ok);
+#endif
+ gen_get_gpr(write_int_rd, rs1);
+ tcg_gen_mov_tl(cpu_fpr[rd], write_int_rd);
+#if !defined(CONFIG_USER_ONLY)
+ gen_set_label(done);
+#endif
+ break;
+ }
+#endif
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ tcg_temp_free_i64(rm_reg);
+ tcg_temp_free(write_int_rd);
+}
+
+static void gen_system(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 */
+
+ 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 */
+ printf("URET unimplemented\n");
+ exit(1);
+ break;
+ case 0x102: /* SRET */
+ gen_helper_sret(cpu_pc, cpu_env, cpu_pc);
+ tcg_gen_exit_tb(0); /* no chaining */
+ ctx->bstate = BS_BRANCH;
+ break;
+ case 0x202: /* HRET */
+ printf("HRET unimplemented\n");
+ exit(1);
+ 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 */
+ printf("DRET unimplemented\n");
+ exit(1);
+ 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;
+ case 0x120: /* SFENCE.VMA */
+ /* TODO: handle ASID specific fences */
+ gen_helper_tlb_flush(cpu_env);
+ break;
+#endif
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ 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:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+ 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) {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ } 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 */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ 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(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
+ /* standard fence is nop, fence_i flushes TB (like an icache): */
+ if (ctx->opcode & 0x1000) { /* FENCE_I */
+ gen_helper_fence_i(cpu_env);
+ tcg_gen_movi_tl(cpu_pc, ctx->next_pc);
+ tcg_gen_exit_tb(0); /* no chaining */
+ ctx->bstate = BS_BRANCH;
+ }
+#endif
+ break;
+ case OPC_RISC_SYSTEM:
+ gen_system(ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
+ (ctx->opcode & 0xFFF00000) >> 20);
+ break;
+ default:
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ break;
+ }
+}
+
+static void decode_opc(CPURISCVState *env, DisasContext *ctx)
+{
+ /* check for compressed insn */
+ if (extract32(ctx->opcode, 0, 2) != 3) {
+ if (!riscv_feature(env, RISCV_FEATURE_RVC)) {
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ } 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.mem_idx = cpu_mmu_index(env, false);
+ 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_helper_raise_exception_debug(cpu_env);
+ /* 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();
+ }
+ if (cs->singlestep_enabled && ctx.bstate != BS_BRANCH) {
+ if (ctx.bstate == BS_NONE) {
+ tcg_gen_movi_tl(cpu_pc, ctx.pc);
+ }
+ gen_helper_raise_exception_debug(cpu_env);
+ } else {
+ 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);
+ 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_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, "pc=0x" TARGET_FMT_lx "\n", env->pc);
+ for (i = 0; i < 32; i++) {
+ cpu_fprintf(f, " %s " TARGET_FMT_lx, regnames[i], env->gpr[i]);
+ if ((i & 3) == 3) {
+ cpu_fprintf(f, "\n");
+ }
+ }
+
+#ifndef CONFIG_USER_ONLY
+ cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "MSTATUS ",
+ env->mstatus);
+ cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "MIP ", env->mip);
+ cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "MIE ", env->mie);
+#endif
+
+ for (i = 0; i < 32; i++) {
+ if ((i & 3) == 0) {
+ cpu_fprintf(f, "FPR%02d:", i);
+ }
+ cpu_fprintf(f, " %s %016" PRIx64, fpr_regnames[i], env->fpr[i]);
+ if ((i & 3) == 3) {
+ cpu_fprintf(f, "\n");
+ }
+ }
+}
+
+void riscv_translate_init(void)
+{
+ int i;
+
+ /* WARNING: cpu_gpr[0] is not allocated ON PURPOSE. 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 */
+ TCGV_UNUSED(cpu_gpr[0]);
+ for (i = 1; i < 32; i++) {
+ cpu_gpr[i] = tcg_global_mem_new(cpu_env,
+ offsetof(CPURISCVState, gpr[i]), regnames[i]);
+ }
+
+ for (i = 0; i < 32; i++) {
+ cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env,
+ offsetof(CPURISCVState, fpr[i]), 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");
+
+#ifdef CONFIG_USER_ONLY
+ cpu_amoinsn = tcg_global_mem_new_i32(cpu_env,
+ offsetof(CPURISCVState, amoinsn),
+ "amoinsn");
+#endif
+}
--
2.7.0
Richard Henderson
2018-01-03 21:35:49 UTC
Permalink
Post by Michael Clark
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong pc;
+ target_ulong next_pc;
+ uint32_t opcode;
+ int singlestep_enabled;
+ int mem_idx;
+ int bstate;
+} DisasContext;
+
+static inline void kill_unknown(DisasContext *ctx, int excp);
+
+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. */
+};
I would prefer this to be updated to use exec/translator.h, TranslatorOps.
However, please use DisasJumpType and DisasContextBase right away.
Post by Michael Clark
+static inline 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);
+}
Drop inline unless required. Let the compiler choose.
Post by Michael Clark
+static inline 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_helper_raise_exception_debug(cpu_env);
+ }
else
Post by Michael Clark
+ tcg_gen_exit_tb(0);
tcg_gen_lookup_and_goto_ptr

and set ctx->base.is_jmp = DISAS_NORETURN.
Post by Michael Clark
+static void gen_fsgnj(DisasContext *ctx, uint32_t rd, uint32_t rs1,
+ uint32_t rs2, int rm, uint64_t min)
+{
+ TCGv t0 = tcg_temp_new();
+#if !defined(CONFIG_USER_ONLY)
+ TCGLabel *fp_ok = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ /* check MSTATUS.FS */
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_gen_andi_tl(t0, t0, MSTATUS_FS);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, fp_ok);
+ /* MSTATUS_FS field was zero */
+ kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST);
+ tcg_gen_br(done);
As I suggested for the FP helper patch, this could become

if (!(ctx->base.tb->flags & TB_FLAG_MSTATUS_FS)) {
kill_unknown(...);
return;
}
Post by Michael Clark
+#if defined(TARGET_RISCV64)
+ /* 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 */
fallthru after break?
Post by Michael Clark
+ * 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);
Pointless cast.
Post by Michael Clark
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L));
Pointless cast and pointless L.
Post by Michael Clark
+ tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+ tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+ resultopt1);
You can use cond1 instead of resultopt1 here, since cond1 is either 0 or 1.
Post by Michael Clark
+ 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);
The setcond is useless. Let the movconds use source2 directly.

Similarly in REM and REMU.
Post by Michael Clark
+#if defined(TARGET_RISCV64)
+#endif
CASE_OP_32_64?
Post by Michael Clark
+ gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */
What do you mean by this?
Post by Michael Clark
+ /* no chaining with JALR */
We can now chain indirect branches.
Post by Michael Clark
+ TCGLabel *misaligned = gen_new_label();
+ TCGv t0;
+ t0 = tcg_temp_new();
These may not be needed...
Post by Michael Clark
+
+ switch (opc) {
+ 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_feature(env, RISCV_FEATURE_RVC)) {
... unless this condition is true. In particular you can avoid emitting the
label unless it is used.
Post by Michael Clark
+ 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);
tcg_gen_lookup_and_goto_ptr
Post by Michael Clark
+
+ gen_set_label(misaligned);
+ generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+ tcg_gen_exit_tb(0);
exit_tb is unreached, since the exception exits the loop.
Post by Michael Clark
+ switch (opc) {
+ tcg_gen_brcond_tl(TCG_COND_EQ, source1, source2, l);
+ break;
+ tcg_gen_brcond_tl(TCG_COND_NE, source1, source2, l);
+ break;
+ tcg_gen_brcond_tl(TCG_COND_LT, source1, source2, l);
+ break;
+ tcg_gen_brcond_tl(TCG_COND_GE, source1, source2, l);
+ break;
+ tcg_gen_brcond_tl(TCG_COND_LTU, source1, source2, l);
+ break;
+ tcg_gen_brcond_tl(TCG_COND_GEU, source1, source2, l);
+ break;
You could just set the condition in the switch, or load it from a table.
Post by Michael Clark
+ gen_goto_tb(ctx, 1, ctx->next_pc);
+ gen_set_label(l); /* branch taken */
+ if (!riscv_feature(env, RISCV_FEATURE_RVC) && ((ctx->pc + bimm) & 0x3)) {
+ /* misaligned */
+ generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+ tcg_gen_exit_tb(0);
Unreached.
Post by Michael Clark
+static void gen_atomic(DisasContext *ctx, uint32_t opc,
+ int rd, int rs1, int rs2)
+{
+#if !defined(CONFIG_USER_ONLY)
+ /* TODO: handle aq, rl bits? - for now just get rid of them: */
+ opc = MASK_OP_ATOMIC_NO_AQ_RL(opc);
+ TCGv source1, source2, dat;
+ TCGLabel *j = gen_new_label();
+ TCGLabel *done = gen_new_label();
+ source1 = tcg_temp_local_new();
+ source2 = tcg_temp_local_new();
+ dat = tcg_temp_local_new();
+ gen_get_gpr(source1, rs1);
+ gen_get_gpr(source2, rs2);
+
+ switch (opc) {
+ /* all currently implemented as non-atomics */
+ /* put addr in load_res */
+ tcg_gen_mov_tl(load_res, source1);
+ tcg_gen_qemu_ld_tl(dat, source1, ctx->mem_idx, MO_TESL | MO_ALIGN);
+ break;
+ tcg_gen_brcond_tl(TCG_COND_NE, load_res, source1, j);
+ tcg_gen_qemu_st_tl(source2, source1, ctx->mem_idx, MO_TEUL | MO_ALIGN);
+ tcg_gen_movi_tl(dat, 0); /*success */
+ tcg_gen_br(done);
+ gen_set_label(j);
+ tcg_gen_movi_tl(dat, 1); /*fail */
+ gen_set_label(done);
+ break;
We have atomic primitives now. You'll want to use them.
Post by Michael Clark
+#else
+ tcg_gen_movi_i32(cpu_amoinsn, ctx->opcode);
+ generate_exception(ctx, QEMU_USER_EXCP_ATOMIC);
+#endif
Including for CONFIG_USER_ONLY. No need for cpu_amoinsn or the exception.
Post by Michael Clark
+#ifndef CONFIG_USER_ONLY
+ case 0x002: /* URET */
+ printf("URET unimplemented\n");
+ exit(1);
No exit, ever. qemu_log_mask w/ LOG_UNIMP and kill_unknown.
Post by Michael Clark
+#ifndef CONFIG_USER_ONLY
+ /* standard fence is nop, fence_i flushes TB (like an icache): */
Standard fence should be tcg_gen_mb with some combination of TCGBar bits.

QEMU will automatically detect self-modifying code. FENCE_I should either be a
nop or another tcg_gen_mb.
Post by Michael Clark
+ ctx.mem_idx = cpu_mmu_index(env, false);
tb->flags must contain enough to cover the bits you're testing here. Otherwise
hashing may select a TB with different permissions.

Depending on how complicated the test is, another way to accomplish this is to
actually store cpu_mmu_index into tb->flags. At which point you would retrieve
ctx.mem_idx directly from tb->flags.
Post by Michael Clark
+void riscv_translate_init(void)
+{
+ int i;
+
+ /* WARNING: cpu_gpr[0] is not allocated ON PURPOSE. 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 */
+ TCGV_UNUSED(cpu_gpr[0]);
A patch to remove TCGV_UNUSED is in queue.
This would simply be cpu_gpr[0] = NULL now.


r~
Michael Clark
2018-01-03 00:44:23 UTC
Permalink
This provides a RISC-V Board compatible with the the SiFive E300 SDK.
The following machine is implemented:

- 'sifive_e300'; CLINT, PLIC, UART, AON, GPIO, QSPI, PWM

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/sifive_e300.c | 232 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_e300.h | 79 ++++++++++++++
2 files changed, 311 insertions(+)
create mode 100644 hw/riscv/sifive_e300.c
create mode 100644 include/hw/riscv/sifive_e300.h

diff --git a/hw/riscv/sifive_e300.c b/hw/riscv/sifive_e300.c
new file mode 100644
index 0000000..bbea55a
--- /dev/null
+++ b/hw/riscv/sifive_e300.c
@@ -0,0 +1,232 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive E300 SDK
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the bsp in the SiFive E300 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.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.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_e300.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_e300_memmap[] = {
+ [SIFIVE_E300_DEBUG] = { 0x0, 0x100 },
+ [SIFIVE_E300_MROM] = { 0x1000, 0x2000 },
+ [SIFIVE_E300_OTP] = { 0x20000, 0x2000 },
+ [SIFIVE_E300_CLINT] = { 0x2000000, 0x10000 },
+ [SIFIVE_E300_PLIC] = { 0xc000000, 0x4000000 },
+ [SIFIVE_E300_AON] = { 0x10000000, 0x8000 },
+ [SIFIVE_E300_PRCI] = { 0x10008000, 0x8000 },
+ [SIFIVE_E300_OTP_CTRL] = { 0x10010000, 0x1000 },
+ [SIFIVE_E300_GPIO0] = { 0x10012000, 0x1000 },
+ [SIFIVE_E300_UART0] = { 0x10013000, 0x1000 },
+ [SIFIVE_E300_QSPI0] = { 0x10014000, 0x1000 },
+ [SIFIVE_E300_PWM0] = { 0x10015000, 0x1000 },
+ [SIFIVE_E300_UART1] = { 0x10023000, 0x1000 },
+ [SIFIVE_E300_QSPI1] = { 0x10024000, 0x1000 },
+ [SIFIVE_E300_PWM1] = { 0x10025000, 0x1000 },
+ [SIFIVE_E300_QSPI2] = { 0x10034000, 0x1000 },
+ [SIFIVE_E300_PWM2] = { 0x10035000, 0x1000 },
+ [SIFIVE_E300_XIP] = { 0x20000000, 0x20000000 },
+ [SIFIVE_E300_DTIM] = { 0x80000000, 0x4000 }
+};
+
+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,
+ /* little_endian = */ 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_e300_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = sifive_e300_memmap;
+
+ SiFiveE300State *s = g_new0(SiFiveE300State, 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), TYPE_RISCV_CPU_IMAC_PRIV_1_10,
+ "cpu-model", &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.e300.ram",
+ memmap[SIFIVE_E300_DTIM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem,
+ memmap[SIFIVE_E300_DTIM].base, main_mem);
+
+ /* Mask ROM */
+ memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e300.mrom",
+ memmap[SIFIVE_E300_MROM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem,
+ memmap[SIFIVE_E300_MROM].base, mask_rom);
+
+ /* MMIO */
+ s->plic = sifive_plic_create(memmap[SIFIVE_E300_PLIC].base,
+ (char *)SIFIVE_E300_PLIC_HART_CONFIG,
+ SIFIVE_E300_PLIC_NUM_SOURCES,
+ SIFIVE_E300_PLIC_NUM_PRIORITIES,
+ SIFIVE_E300_PLIC_PRIORITY_BASE,
+ SIFIVE_E300_PLIC_PENDING_BASE,
+ SIFIVE_E300_PLIC_ENABLE_BASE,
+ SIFIVE_E300_PLIC_ENABLE_STRIDE,
+ SIFIVE_E300_PLIC_CONTEXT_BASE,
+ SIFIVE_E300_PLIC_CONTEXT_STRIDE,
+ memmap[SIFIVE_E300_PLIC].size);
+ sifive_clint_create(memmap[SIFIVE_E300_CLINT].base,
+ memmap[SIFIVE_E300_CLINT].size, smp_cpus,
+ SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.aon",
+ memmap[SIFIVE_E300_AON].base, memmap[SIFIVE_E300_AON].size);
+ sifive_prci_create(memmap[SIFIVE_E300_PRCI].base);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.gpio0",
+ memmap[SIFIVE_E300_GPIO0].base, memmap[SIFIVE_E300_GPIO0].size);
+ sifive_uart_create(sys_mem, memmap[SIFIVE_E300_UART0].base,
+ serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E300_UART0_IRQ]);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.qspi0",
+ memmap[SIFIVE_E300_QSPI0].base, memmap[SIFIVE_E300_QSPI0].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.pwm0",
+ memmap[SIFIVE_E300_PWM0].base, memmap[SIFIVE_E300_PWM0].size);
+ /* sifive_uart_create(sys_mem, memmap[SIFIVE_E300_UART1].base,
+ serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E300_UART1_IRQ]); */
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.qspi1",
+ memmap[SIFIVE_E300_QSPI1].base, memmap[SIFIVE_E300_QSPI1].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.pwm1",
+ memmap[SIFIVE_E300_PWM1].base, memmap[SIFIVE_E300_PWM1].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.qspi2",
+ memmap[SIFIVE_E300_QSPI2].base, memmap[SIFIVE_E300_QSPI2].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e300.pwm2",
+ memmap[SIFIVE_E300_PWM2].base, memmap[SIFIVE_E300_PWM2].size);
+
+ /* Flash memory */
+ memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e300.xip",
+ memmap[SIFIVE_E300_XIP].size, &error_fatal);
+ memory_region_set_readonly(xip_mem, true);
+ memory_region_add_subregion(sys_mem,
+ memmap[SIFIVE_E300_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 */
+ cpu_physical_memory_write(0x1000, 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_e300_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_sifive_e300_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_sifive_e300_sysbus_device_init;
+}
+
+static const TypeInfo riscv_sifive_e300_device = {
+ .name = TYPE_SIFIVE_E300,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveE300State),
+ .class_init = riscv_sifive_e300_class_init,
+};
+
+static void riscv_sifive_e300_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Board compatible with SiFive E300 SDK";
+ mc->init = riscv_sifive_e300_init;
+ mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_e300", riscv_sifive_e300_machine_init)
+
+static void riscv_sifive_e300_register_types(void)
+{
+ type_register_static(&riscv_sifive_e300_device);
+}
+
+type_init(riscv_sifive_e300_register_types);
diff --git a/include/hw/riscv/sifive_e300.h b/include/hw/riscv/sifive_e300.h
new file mode 100644
index 0000000..453c43b
--- /dev/null
+++ b/include/hw/riscv/sifive_e300.h
@@ -0,0 +1,79 @@
+/*
+ * SiFive E300 series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_SIFIVE_E300_H
+#define HW_SIFIVE_E300_H
+
+#define TYPE_SIFIVE_E300 "riscv.sifive.e300"
+
+#define SIFIVE_E300(obj) \
+ OBJECT_CHECK(SiFiveE300State, (obj), TYPE_SIFIVE_E300)
+
+typedef struct SiFiveE300State {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ RISCVHartArrayState soc;
+ DeviceState *plic;
+} SiFiveE300State;
+
+enum {
+ SIFIVE_E300_DEBUG,
+ SIFIVE_E300_MROM,
+ SIFIVE_E300_OTP,
+ SIFIVE_E300_CLINT,
+ SIFIVE_E300_PLIC,
+ SIFIVE_E300_AON,
+ SIFIVE_E300_PRCI,
+ SIFIVE_E300_OTP_CTRL,
+ SIFIVE_E300_GPIO0,
+ SIFIVE_E300_UART0,
+ SIFIVE_E300_QSPI0,
+ SIFIVE_E300_PWM0,
+ SIFIVE_E300_UART1,
+ SIFIVE_E300_QSPI1,
+ SIFIVE_E300_PWM1,
+ SIFIVE_E300_QSPI2,
+ SIFIVE_E300_PWM2,
+ SIFIVE_E300_XIP,
+ SIFIVE_E300_DTIM
+};
+
+enum {
+ SIFIVE_E300_UART0_IRQ = 3,
+ SIFIVE_E300_UART1_IRQ = 4
+};
+
+#define SIFIVE_E300_PLIC_HART_CONFIG "M"
+#define SIFIVE_E300_PLIC_NUM_SOURCES 127
+#define SIFIVE_E300_PLIC_NUM_PRIORITIES 7
+#define SIFIVE_E300_PLIC_PRIORITY_BASE 0x0
+#define SIFIVE_E300_PLIC_PENDING_BASE 0x1000
+#define SIFIVE_E300_PLIC_ENABLE_BASE 0x2000
+#define SIFIVE_E300_PLIC_ENABLE_STRIDE 0x80
+#define SIFIVE_E300_PLIC_CONTEXT_BASE 0x200000
+#define SIFIVE_E300_PLIC_CONTEXT_STRIDE 0x1000
+
+#endif
--
2.7.0
Antony Pavlov
2018-01-05 21:54:02 UTC
Permalink
On Wed, 3 Jan 2018 13:44:23 +1300
Post by Michael Clark
This provides a RISC-V Board compatible with the the SiFive E300 SDK.
- 'sifive_e300'; CLINT, PLIC, UART, AON, GPIO, QSPI, PWM
...
Post by Michael Clark
diff --git a/include/hw/riscv/sifive_e300.h b/include/hw/riscv/sifive_e300.h
new file mode 100644
index 0000000..453c43b
--- /dev/null
+++ b/include/hw/riscv/sifive_e300.h
@@ -0,0 +1,79 @@
+/*
+ * SiFive E300 series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_SIFIVE_E300_H
+#define HW_SIFIVE_E300_H
+
+#define TYPE_SIFIVE_E300 "riscv.sifive.e300"
+
+#define SIFIVE_E300(obj) \
+ OBJECT_CHECK(SiFiveE300State, (obj), TYPE_SIFIVE_E300)
+
+typedef struct SiFiveE300State {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ RISCVHartArrayState soc;
I suppose that name 'soc' is misleading because it contain only CPU core-related information
but it does not contain any SoC-related information.
Post by Michael Clark
+ DeviceState *plic;
+} SiFiveE300State;
+
--
Best regards,
  Antony Pavlov
Michael Clark
2018-01-03 00:44:24 UTC
Permalink
This provides a RISC-V Board compatible with the the SiFive U500 SDK.
The following machine is implemented:

- 'sifive_u500'; CLINT, PLIC, UART, device-tree

Signed-off-by: Michael Clark <***@sifive.com>
---
hw/riscv/sifive_u500.c | 338 +++++++++++++++++++++++++++++++++++++++++
include/hw/riscv/sifive_u500.h | 69 +++++++++
2 files changed, 407 insertions(+)
create mode 100644 hw/riscv/sifive_u500.c
create mode 100644 include/hw/riscv/sifive_u500.h

diff --git a/hw/riscv/sifive_u500.c b/hw/riscv/sifive_u500.c
new file mode 100644
index 0000000..9cc411e
--- /dev/null
+++ b/hw/riscv/sifive_u500.c
@@ -0,0 +1,338 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive U500 SDK
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, ***@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a RISC-V Board compatible with the the SiFive U500 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.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.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_u500.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_u500_memmap[] = {
+ [SIFIVE_U500_DEBUG] = { 0x0, 0x100 },
+ [SIFIVE_U500_MROM] = { 0x1000, 0x2000 },
+ [SIFIVE_U500_CLINT] = { 0x2000000, 0x10000 },
+ [SIFIVE_U500_PLIC] = { 0xc000000, 0x4000000 },
+ [SIFIVE_U500_UART0] = { 0x10013000, 0x1000 },
+ [SIFIVE_U500_UART1] = { 0x10023000, 0x1000 },
+ [SIFIVE_U500_DRAM] = { 0x80000000, 0x0 },
+};
+
+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,
+ /* little_endian = */ 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(SiFiveU500State *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_U500_DRAM].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ memmap[SIFIVE_U500_DRAM].base >> 32, memmap[SIFIVE_U500_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_U500_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_U500_CLINT].base,
+ 0x0, memmap[SIFIVE_U500_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_U500_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_U500_PLIC].base,
+ 0x0, memmap[SIFIVE_U500_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_U500_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_U500_UART0].base,
+ 0x0, memmap[SIFIVE_U500_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_u500_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = sifive_u500_memmap;
+
+ SiFiveU500State *s = g_new0(SiFiveU500State, 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), TYPE_RISCV_CPU_IMAFDCSU_PRIV_1_10,
+ "cpu-model", &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.u500.ram",
+ machine->ram_size, &error_fatal);
+ /* for phys mem size check in page table walk */
+ memory_region_add_subregion(sys_memory, memmap[SIFIVE_U500_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.u500.bootrom",
+ 0x10000, &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,