mirror of
https://github.com/qemu/qemu.git
synced 2024-11-28 22:33:36 +08:00
f18637cd61
This patch adds support for writing misa. misa is validated based on rules in the ISA specification. 'E' is mutually exclusive with all other extensions. 'D' depends on 'F' so 'D' bit is dropped if 'F' is not present. A conservative approach to consistency is taken by flushing the translation cache on misa writes. misa_mask is added to the CPU struct to store the original set of extensions. Signed-off-by: Michael Clark <mjc@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Palmer Dabbelt <palmer@sifive.com> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
458 lines
13 KiB
C
458 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 "migration/vmstate.h"
|
|
|
|
/* RISC-V CPU definitions */
|
|
|
|
static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG";
|
|
|
|
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"
|
|
};
|
|
|
|
typedef struct RISCVCPUInfo {
|
|
const int bit_widths;
|
|
const char *name;
|
|
void (*initfn)(Object *obj);
|
|
} RISCVCPUInfo;
|
|
|
|
static void set_misa(CPURISCVState *env, target_ulong misa)
|
|
{
|
|
env->misa_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(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
}
|
|
|
|
#if defined(TARGET_RISCV32)
|
|
|
|
static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv32imacu_nommu_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
#elif defined(TARGET_RISCV64)
|
|
|
|
static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv64imacu_nommu_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
#endif
|
|
|
|
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
|
|
{
|
|
ObjectClass *oc;
|
|
char *typename;
|
|
char **cpuname;
|
|
|
|
cpuname = g_strsplit(cpu_model, ",", 1);
|
|
typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]);
|
|
oc = object_class_by_name(typename);
|
|
g_strfreev(cpuname);
|
|
g_free(typename);
|
|
if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) ||
|
|
object_class_is_abstract(oc)) {
|
|
return NULL;
|
|
}
|
|
return oc;
|
|
}
|
|
|
|
static void riscv_cpu_dump_state(CPUState *cs, FILE *f,
|
|
fprintf_function cpu_fprintf, int flags)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
int i;
|
|
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc);
|
|
#ifndef CONFIG_USER_ONLY
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ",
|
|
(target_ulong)atomic_read(&env->mip));
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc);
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause);
|
|
#endif
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
cpu_fprintf(f, " %s " TARGET_FMT_lx,
|
|
riscv_int_regnames[i], env->gpr[i]);
|
|
if ((i & 3) == 3) {
|
|
cpu_fprintf(f, "\n");
|
|
}
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
env->pc = value;
|
|
}
|
|
|
|
static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
env->pc = tb->pc;
|
|
}
|
|
|
|
static bool riscv_cpu_has_work(CPUState *cs)
|
|
{
|
|
#ifndef CONFIG_USER_ONLY
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
/*
|
|
* Definition of the WFI instruction requires it to ignore the privilege
|
|
* mode and delegation registers, but respect individual enables
|
|
*/
|
|
return (atomic_read(&env->mip) & env->mie) != 0;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
|
|
target_ulong *data)
|
|
{
|
|
env->pc = data[0];
|
|
}
|
|
|
|
static void riscv_cpu_reset(CPUState *cs)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
mcc->parent_reset(cs);
|
|
#ifndef CONFIG_USER_ONLY
|
|
env->priv = PRV_M;
|
|
env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
|
|
env->mcause = 0;
|
|
env->pc = env->resetvec;
|
|
#endif
|
|
cs->exception_index = EXCP_NONE;
|
|
set_default_nan_mode(1, &env->fp_status);
|
|
}
|
|
|
|
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
|
|
{
|
|
#if defined(TARGET_RISCV32)
|
|
info->print_insn = print_insn_riscv32;
|
|
#elif defined(TARGET_RISCV64)
|
|
info->print_insn = print_insn_riscv64;
|
|
#endif
|
|
}
|
|
|
|
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
CPUState *cs = CPU(dev);
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
|
|
Error *local_err = NULL;
|
|
|
|
cpu_exec_realizefn(cs, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
|
|
qemu_init_vcpu(cs);
|
|
cpu_reset(cs);
|
|
|
|
mcc->parent_realize(dev, errp);
|
|
}
|
|
|
|
static void riscv_cpu_init(Object *obj)
|
|
{
|
|
CPUState *cs = CPU(obj);
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
|
|
cs->env_ptr = &cpu->env;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_riscv_cpu = {
|
|
.name = "cpu",
|
|
.unmigratable = 1,
|
|
};
|
|
|
|
static void riscv_cpu_class_init(ObjectClass *c, void *data)
|
|
{
|
|
RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
|
|
CPUClass *cc = CPU_CLASS(c);
|
|
DeviceClass *dc = DEVICE_CLASS(c);
|
|
|
|
device_class_set_parent_realize(dc, riscv_cpu_realize,
|
|
&mcc->parent_realize);
|
|
|
|
mcc->parent_reset = cc->reset;
|
|
cc->reset = riscv_cpu_reset;
|
|
|
|
cc->class_by_name = riscv_cpu_class_by_name;
|
|
cc->has_work = riscv_cpu_has_work;
|
|
cc->do_interrupt = riscv_cpu_do_interrupt;
|
|
cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
|
|
cc->dump_state = riscv_cpu_dump_state;
|
|
cc->set_pc = riscv_cpu_set_pc;
|
|
cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb;
|
|
cc->gdb_read_register = riscv_cpu_gdb_read_register;
|
|
cc->gdb_write_register = riscv_cpu_gdb_write_register;
|
|
cc->gdb_num_core_regs = 65;
|
|
cc->gdb_stop_before_watchpoint = true;
|
|
cc->disas_set_info = riscv_cpu_disas_set_info;
|
|
#ifdef CONFIG_USER_ONLY
|
|
cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault;
|
|
#else
|
|
cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
|
|
cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
|
|
#endif
|
|
#ifdef CONFIG_TCG
|
|
cc->tcg_initialize = riscv_translate_init;
|
|
#endif
|
|
/* For now, mark unmigratable: */
|
|
cc->vmsd = &vmstate_riscv_cpu;
|
|
}
|
|
|
|
char *riscv_isa_string(RISCVCPU *cpu)
|
|
{
|
|
int i;
|
|
const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1;
|
|
char *isa_str = g_new(char, maxlen);
|
|
char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS);
|
|
for (i = 0; i < sizeof(riscv_exts); i++) {
|
|
if (cpu->env.misa & RV(riscv_exts[i])) {
|
|
*p++ = qemu_tolower(riscv_exts[i]);
|
|
}
|
|
}
|
|
*p = '\0';
|
|
return isa_str;
|
|
}
|
|
|
|
typedef struct RISCVCPUListState {
|
|
fprintf_function cpu_fprintf;
|
|
FILE *file;
|
|
} RISCVCPUListState;
|
|
|
|
static gint riscv_cpu_list_compare(gconstpointer a, gconstpointer b)
|
|
{
|
|
ObjectClass *class_a = (ObjectClass *)a;
|
|
ObjectClass *class_b = (ObjectClass *)b;
|
|
const char *name_a, *name_b;
|
|
|
|
name_a = object_class_get_name(class_a);
|
|
name_b = object_class_get_name(class_b);
|
|
return strcmp(name_a, name_b);
|
|
}
|
|
|
|
static void riscv_cpu_list_entry(gpointer data, gpointer user_data)
|
|
{
|
|
RISCVCPUListState *s = user_data;
|
|
const char *typename = object_class_get_name(OBJECT_CLASS(data));
|
|
int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX);
|
|
|
|
(*s->cpu_fprintf)(s->file, "%.*s\n", len, typename);
|
|
}
|
|
|
|
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
|
|
{
|
|
RISCVCPUListState s = {
|
|
.cpu_fprintf = cpu_fprintf,
|
|
.file = f,
|
|
};
|
|
GSList *list;
|
|
|
|
list = object_class_get_list(TYPE_RISCV_CPU, false);
|
|
list = g_slist_sort(list, riscv_cpu_list_compare);
|
|
g_slist_foreach(list, riscv_cpu_list_entry, &s);
|
|
g_slist_free(list);
|
|
}
|
|
|
|
#define DEFINE_CPU(type_name, initfn) \
|
|
{ \
|
|
.name = type_name, \
|
|
.parent = TYPE_RISCV_CPU, \
|
|
.instance_init = initfn \
|
|
}
|
|
|
|
static const TypeInfo riscv_cpu_type_infos[] = {
|
|
{
|
|
.name = TYPE_RISCV_CPU,
|
|
.parent = TYPE_CPU,
|
|
.instance_size = sizeof(RISCVCPU),
|
|
.instance_init = riscv_cpu_init,
|
|
.abstract = true,
|
|
.class_size = sizeof(RISCVCPUClass),
|
|
.class_init = riscv_cpu_class_init,
|
|
},
|
|
DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
|
|
#if defined(TARGET_RISCV32)
|
|
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_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
|
|
};
|
|
|
|
DEFINE_TYPES(riscv_cpu_type_infos)
|