mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-24 17:35:39 +00:00
ea2fee2d4d
At the same time deprecate the ISA string CPUs. It is dobtful anyone specifies the CPUs, but we are keeping them for the Spike machine (which is about to be depreated) so we may as well just mark them as deprecated. Backports commit 8903bf6e6d73d03b988b4a8197132de2ad681ff5 from qemu
418 lines
13 KiB
C
418 lines
13 KiB
C
/*
|
|
* QEMU RISC-V CPU
|
|
*
|
|
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
|
* Copyright (c) 2017-2018 SiFive, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2 or later, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/log.h"
|
|
#include "cpu.h"
|
|
#include "exec/exec-all.h"
|
|
#include "qapi/error.h"
|
|
|
|
#include "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;
|
|
}
|
|
}
|