qemu/target/riscv/cpu.c
Markus Armbruster 90c84c5600 qom/cpu: Simplify how CPUClass:cpu_dump_state() prints
CPUClass method dump_statistics() takes an fprintf()-like callback and
a FILE * to pass to it.  Most callers pass fprintf() and stderr.
log_cpu_state() passes fprintf() and qemu_log_file.
hmp_info_registers() passes monitor_fprintf() and the current monitor
cast to FILE *.  monitor_fprintf() casts it right back, and is
otherwise identical to monitor_printf().

The callback gets passed around a lot, which is tiresome.  The
type-punning around monitor_fprintf() is ugly.

Drop the callback, and call qemu_fprintf() instead.  Also gets rid of
the type-punning, since qemu_fprintf() takes NULL instead of the
current monitor cast to FILE *.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-Id: <20190417191805.28198-15-armbru@redhat.com>
2019-04-18 22:18:59 +02:00

449 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/qemu-print.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"
};
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, int flags)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
int i;
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc);
#ifndef CONFIG_USER_ONLY
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ",
(target_ulong)atomic_read(&env->mip));
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc);
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause);
#endif
for (i = 0; i < 32; i++) {
qemu_fprintf(f, " %s " TARGET_FMT_lx,
riscv_int_regnames[i], env->gpr[i]);
if ((i & 3) == 3) {
qemu_fprintf(f, "\n");
}
}
if (flags & CPU_DUMP_FPU) {
for (i = 0; i < 32; i++) {
qemu_fprintf(f, " %s %016" PRIx64,
riscv_fpr_regnames[i], env->fpr[i]);
if ((i & 3) == 3) {
qemu_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;
}
riscv_cpu_register_gdb_regs_for_features(cs);
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 = 33;
#if defined(TARGET_RISCV32)
cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
#elif defined(TARGET_RISCV64)
cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
#endif
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;
}
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)
{
const char *typename = object_class_get_name(OBJECT_CLASS(data));
int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX);
qemu_printf("%.*s\n", len, typename);
}
void riscv_cpu_list(void)
{
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, NULL);
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)