/* * QEMU RISC-V CPU * * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu * Copyright (c) 2017-2018 SiFive, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" #include "qapi/error.h" #include "uc_priv.h" /* RISC-V CPU definitions */ const char * const riscv_int_regnames[] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" }; const char * const riscv_fpr_regnames[] = { "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11" }; const char * const riscv_excp_names[] = { "misaligned_fetch", "fault_fetch", "illegal_instruction", "breakpoint", "misaligned_load", "fault_load", "misaligned_store", "fault_store", "user_ecall", "supervisor_ecall", "hypervisor_ecall", "machine_ecall", "exec_page_fault", "load_page_fault", "reserved", "store_page_fault" }; const char * const riscv_intr_names[] = { "u_software", "s_software", "h_software", "m_software", "u_timer", "s_timer", "h_timer", "m_timer", "u_external", "s_external", "h_external", "m_external", "reserved", "reserved", "reserved", "reserved" }; static void set_misa(CPURISCVState *env, target_ulong misa) { env->misa_mask = env->misa = misa; } static void set_versions(CPURISCVState *env, int user_ver, int priv_ver) { env->user_ver = user_ver; env->priv_ver = priv_ver; } static void set_feature(CPURISCVState *env, int feature) { env->features |= (1ULL << feature); } static void set_resetvec(CPURISCVState *env, int resetvec) { #ifndef CONFIG_USER_ONLY env->resetvec = resetvec; #endif } static void riscv_any_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); } #if defined(TARGET_RISCV32) static void riscv_base32_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); } static void rv32gcsu_priv1_09_1_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); } static void rv32gcsu_priv1_10_0_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); } static void rv32imacu_nommu_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_PMP); } #elif defined(TARGET_RISCV64) static void riscv_base64_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); } static void rv64gcsu_priv1_09_1_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); } static void rv64gcsu_priv1_10_0_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); } static void rv64imacu_nommu_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPURISCVState *env = &RISCV_CPU(uc, obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_PMP); } #endif static ObjectClass *riscv_cpu_class_by_name(struct uc_struct *uc, 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(uc, typename); g_strfreev(cpuname); g_free(typename); if (!oc || !object_class_dynamic_cast(uc, oc, TYPE_RISCV_CPU) || object_class_is_abstract(oc)) { return NULL; } return oc; } // Unicorn: if'd out #if 0 static void riscv_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; int i; cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); #ifndef CONFIG_USER_ONLY cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", (target_ulong)atomic_read(&env->mip)); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc); cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause); #endif for (i = 0; i < 32; i++) { cpu_fprintf(f, " %s " TARGET_FMT_lx, riscv_int_regnames[i], env->gpr[i]); if ((i & 3) == 3) { cpu_fprintf(f, "\n"); } } if (flags & CPU_DUMP_FPU) { for (i = 0; i < 32; i++) { cpu_fprintf(f, " %s %016" PRIx64, riscv_fpr_regnames[i], env->fpr[i]); if ((i & 3) == 3) { cpu_fprintf(f, "\n"); } } } } #endif static void riscv_cpu_set_pc(CPUState *cs, vaddr value) { RISCVCPU *cpu = RISCV_CPU(cs->uc, cs); CPURISCVState *env = &cpu->env; env->pc = value; } static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) { RISCVCPU *cpu = RISCV_CPU(cs->uc, cs); CPURISCVState *env = &cpu->env; env->pc = tb->pc; } static bool riscv_cpu_has_work(CPUState *cs) { #ifndef CONFIG_USER_ONLY RISCVCPU *cpu = RISCV_CPU(cs->uc, cs); CPURISCVState *env = &cpu->env; /* * Definition of the WFI instruction requires it to ignore the privilege * mode and delegation registers, but respect individual enables */ return (atomic_read(&env->mip) & env->mie) != 0; #else return true; #endif } void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, target_ulong *data) { env->pc = data[0]; } static void riscv_cpu_reset(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs->uc, cs); RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs->uc, cpu); CPURISCVState *env = &cpu->env; mcc->parent_reset(cs); #ifndef CONFIG_USER_ONLY env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); env->mcause = 0; env->pc = env->resetvec; #endif cs->exception_index = EXCP_NONE; set_default_nan_mode(1, &env->fp_status); } // Unicorn: if'd out #if 0 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 } #endif static int riscv_cpu_realize(struct uc_struct *uc, DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(uc, dev); cpu_reset(cs); qemu_init_vcpu(cs); mcc->parent_realize(uc, dev, errp); return 0; } static void riscv_cpu_init(struct uc_struct *uc, Object *obj, void *opaque) { CPUState *cs = CPU(obj); RISCVCPU *cpu = RISCV_CPU(uc, obj); CPURISCVState *env = &cpu->env; cs->env_ptr = env; cpu_exec_init(cs, &error_abort, opaque); } static void riscv_cpu_class_init(struct uc_struct *uc, ObjectClass *oc, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(uc, oc); CPUClass *cc = CPU_CLASS(uc, oc); DeviceClass *dc = DEVICE_CLASS(uc, oc); 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; #ifndef CONFIG_USER_ONLY 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; cc->tlb_fill = riscv_cpu_tlb_fill; #endif /* For now, mark unmigratable: */ //cc->vmsd = &vmstate_riscv_cpu; } #define DEFINE_CPU(type_name, initfn) \ { \ .name = type_name, \ .parent = TYPE_RISCV_CPU, \ .instance_init = initfn \ } static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init), #if defined(TARGET_RISCV32) DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init), #elif defined(TARGET_RISCV64) DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base64_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init), #endif { .name = NULL } }; void riscv_cpu_register_types(void *opaque) { const TypeInfo* cpu_infos = riscv_cpu_type_infos; TypeInfo parent_type_info = { .name = TYPE_RISCV_CPU, .parent = TYPE_CPU, .instance_userdata = opaque, .instance_size = sizeof(RISCVCPU), .instance_init = riscv_cpu_init, .abstract = true, .class_size = sizeof(RISCVCPUClass), .class_init = riscv_cpu_class_init, }; type_register(opaque, &parent_type_info); while (cpu_infos->name) { type_register(opaque, cpu_infos); ++cpu_infos; } }