2018-03-02 15:31:10 +03:00
|
|
|
/*
|
|
|
|
* 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"
|
2019-04-17 22:17:57 +03:00
|
|
|
#include "qemu/qemu-print.h"
|
2019-05-23 17:35:06 +03:00
|
|
|
#include "qemu/ctype.h"
|
2018-03-02 15:31:10 +03:00
|
|
|
#include "qemu/log.h"
|
|
|
|
#include "cpu.h"
|
2020-10-26 14:55:26 +03:00
|
|
|
#include "internals.h"
|
2018-03-02 15:31:10 +03:00
|
|
|
#include "exec/exec-all.h"
|
|
|
|
#include "qapi/error.h"
|
2019-05-07 01:49:53 +03:00
|
|
|
#include "qemu/error-report.h"
|
2019-04-20 05:24:01 +03:00
|
|
|
#include "hw/qdev-properties.h"
|
2018-03-02 15:31:10 +03:00
|
|
|
#include "migration/vmstate.h"
|
2019-08-08 19:29:41 +03:00
|
|
|
#include "fpu/softfloat-helpers.h"
|
2018-03-02 15:31:10 +03:00
|
|
|
|
|
|
|
/* RISC-V CPU definitions */
|
|
|
|
|
2018-03-05 03:28:00 +03:00
|
|
|
static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG";
|
2018-03-02 15:31:10 +03:00
|
|
|
|
|
|
|
const char * const riscv_int_regnames[] = {
|
2019-08-23 18:21:19 +03:00
|
|
|
"x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1",
|
|
|
|
"x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3",
|
|
|
|
"x14/a4", "x15/a5", "x16/a6", "x17/a7", "x18/s2", "x19/s3", "x20/s4",
|
|
|
|
"x21/s5", "x22/s6", "x23/s7", "x24/s8", "x25/s9", "x26/s10", "x27/s11",
|
|
|
|
"x28/t3", "x29/t4", "x30/t5", "x31/t6"
|
2018-03-02 15:31:10 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const char * const riscv_fpr_regnames[] = {
|
2019-08-23 18:21:19 +03:00
|
|
|
"f0/ft0", "f1/ft1", "f2/ft2", "f3/ft3", "f4/ft4", "f5/ft5",
|
|
|
|
"f6/ft6", "f7/ft7", "f8/fs0", "f9/fs1", "f10/fa0", "f11/fa1",
|
|
|
|
"f12/fa2", "f13/fa3", "f14/fa4", "f15/fa5", "f16/fa6", "f17/fa7",
|
|
|
|
"f18/fs2", "f19/fs3", "f20/fs4", "f21/fs5", "f22/fs6", "f23/fs7",
|
|
|
|
"f24/fs8", "f25/fs9", "f26/fs10", "f27/fs11", "f28/ft8", "f29/ft9",
|
|
|
|
"f30/ft10", "f31/ft11"
|
2018-03-02 15:31:10 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
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",
|
2020-03-05 19:46:20 +03:00
|
|
|
"store_page_fault",
|
2020-02-01 04:01:46 +03:00
|
|
|
"reserved",
|
|
|
|
"reserved",
|
|
|
|
"reserved",
|
|
|
|
"reserved",
|
|
|
|
"guest_exec_page_fault",
|
|
|
|
"guest_load_page_fault",
|
|
|
|
"reserved",
|
2020-03-05 19:46:20 +03:00
|
|
|
"guest_store_page_fault",
|
2018-03-02 15:31:10 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const char * const riscv_intr_names[] = {
|
|
|
|
"u_software",
|
|
|
|
"s_software",
|
2020-02-01 04:01:49 +03:00
|
|
|
"vs_software",
|
2018-03-02 15:31:10 +03:00
|
|
|
"m_software",
|
|
|
|
"u_timer",
|
|
|
|
"s_timer",
|
2020-02-01 04:01:49 +03:00
|
|
|
"vs_timer",
|
2018-03-02 15:31:10 +03:00
|
|
|
"m_timer",
|
|
|
|
"u_external",
|
2021-04-21 16:32:36 +03:00
|
|
|
"s_external",
|
2020-02-01 04:01:49 +03:00
|
|
|
"vs_external",
|
2018-03-02 15:31:10 +03:00
|
|
|
"m_external",
|
2018-03-06 00:51:53 +03:00
|
|
|
"reserved",
|
|
|
|
"reserved",
|
|
|
|
"reserved",
|
|
|
|
"reserved"
|
2018-03-02 15:31:10 +03:00
|
|
|
};
|
|
|
|
|
2020-08-14 06:58:19 +03:00
|
|
|
const char *riscv_cpu_get_trap_name(target_ulong cause, bool async)
|
|
|
|
{
|
|
|
|
if (async) {
|
|
|
|
return (cause < ARRAY_SIZE(riscv_intr_names)) ?
|
|
|
|
riscv_intr_names[cause] : "(unknown)";
|
|
|
|
} else {
|
|
|
|
return (cause < ARRAY_SIZE(riscv_excp_names)) ?
|
|
|
|
riscv_excp_names[cause] : "(unknown)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:22:51 +03:00
|
|
|
bool riscv_cpu_is_32bit(CPURISCVState *env)
|
|
|
|
{
|
|
|
|
if (env->misa & RV64) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-02 15:31:10 +03:00
|
|
|
static void set_misa(CPURISCVState *env, target_ulong misa)
|
|
|
|
{
|
2019-01-15 02:59:00 +03:00
|
|
|
env->misa_mask = env->misa = misa;
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2019-06-18 04:31:19 +03:00
|
|
|
static void set_priv_version(CPURISCVState *env, int priv_ver)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
|
|
|
env->priv_ver = priv_ver;
|
|
|
|
}
|
|
|
|
|
2020-07-01 18:24:50 +03:00
|
|
|
static void set_vext_version(CPURISCVState *env, int vext_ver)
|
|
|
|
{
|
|
|
|
env->vext_ver = vext_ver;
|
|
|
|
}
|
|
|
|
|
2018-03-02 15:31:10 +03:00
|
|
|
static void set_feature(CPURISCVState *env, int feature)
|
|
|
|
{
|
|
|
|
env->features |= (1ULL << feature);
|
|
|
|
}
|
|
|
|
|
2021-03-29 06:48:01 +03:00
|
|
|
static void set_resetvec(CPURISCVState *env, target_ulong resetvec)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
|
|
env->resetvec = resetvec;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void riscv_any_cpu_init(Object *obj)
|
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
2021-04-24 06:28:33 +03:00
|
|
|
#if defined(TARGET_RISCV32)
|
|
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
|
|
|
|
#elif defined(TARGET_RISCV64)
|
|
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
|
|
|
|
#endif
|
2019-06-18 04:31:19 +03:00
|
|
|
set_priv_version(env, PRIV_VERSION_1_11_0);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:23:05 +03:00
|
|
|
#if defined(TARGET_RISCV64)
|
|
|
|
static void rv64_base_cpu_init(Object *obj)
|
2019-04-20 05:24:09 +03:00
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
2019-05-07 01:49:53 +03:00
|
|
|
/* We set this in the realise function */
|
2020-12-16 21:23:05 +03:00
|
|
|
set_misa(env, RV64);
|
2019-04-20 05:24:09 +03:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:22:54 +03:00
|
|
|
static void rv64_sifive_u_cpu_init(Object *obj)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
2020-12-16 21:22:54 +03:00
|
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
2019-06-18 04:31:19 +03:00
|
|
|
set_priv_version(env, PRIV_VERSION_1_10_0);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:22:54 +03:00
|
|
|
static void rv64_sifive_e_cpu_init(Object *obj)
|
2020-04-23 20:50:09 +03:00
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
2020-12-16 21:22:54 +03:00
|
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
|
2020-04-23 20:50:09 +03:00
|
|
|
set_priv_version(env, PRIV_VERSION_1_10_0);
|
|
|
|
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
|
|
|
}
|
2020-12-16 21:22:54 +03:00
|
|
|
#else
|
2020-12-16 21:23:05 +03:00
|
|
|
static void rv32_base_cpu_init(Object *obj)
|
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
|
|
/* We set this in the realise function */
|
|
|
|
set_misa(env, RV32);
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:22:54 +03:00
|
|
|
static void rv32_sifive_u_cpu_init(Object *obj)
|
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
|
|
set_priv_version(env, PRIV_VERSION_1_10_0);
|
|
|
|
}
|
2020-04-23 20:50:09 +03:00
|
|
|
|
2020-12-16 21:22:54 +03:00
|
|
|
static void rv32_sifive_e_cpu_init(Object *obj)
|
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
|
|
|
|
set_priv_version(env, PRIV_VERSION_1_10_0);
|
|
|
|
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
|
|
|
}
|
2020-06-11 04:08:48 +03:00
|
|
|
|
2020-06-16 03:50:37 +03:00
|
|
|
static void rv32_ibex_cpu_init(Object *obj)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
2020-06-11 04:08:48 +03:00
|
|
|
set_misa(env, RV32 | RVI | RVM | RVC | RVU);
|
2019-06-18 04:31:19 +03:00
|
|
|
set_priv_version(env, PRIV_VERSION_1_10_0);
|
2020-05-27 02:06:02 +03:00
|
|
|
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
2021-04-19 09:18:06 +03:00
|
|
|
qdev_prop_set_bit(DEVICE(obj), "x-epmp", true);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2020-06-11 04:08:49 +03:00
|
|
|
static void rv32_imafcu_nommu_cpu_init(Object *obj)
|
2020-03-13 22:34:29 +03:00
|
|
|
{
|
|
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVC | RVU);
|
|
|
|
set_priv_version(env, PRIV_VERSION_1_10_0);
|
|
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
2020-05-27 02:06:02 +03:00
|
|
|
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
2020-03-13 22:34:29 +03:00
|
|
|
}
|
2018-03-09 01:12:31 +03:00
|
|
|
#endif
|
2018-03-02 15:31:10 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:18:02 +03:00
|
|
|
static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
|
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
int i;
|
|
|
|
|
2020-02-01 04:02:02 +03:00
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s %d\n", "V = ", riscv_cpu_virt_enabled(env));
|
|
|
|
}
|
|
|
|
#endif
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc);
|
2018-03-02 15:31:10 +03:00
|
|
|
#ifndef CONFIG_USER_ONLY
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
|
2020-10-26 14:55:25 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", (target_ulong)env->mstatus);
|
2020-12-16 21:22:56 +03:00
|
|
|
if (riscv_cpu_is_32bit(env)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ",
|
|
|
|
(target_ulong)(env->mstatus >> 32));
|
|
|
|
}
|
2020-02-01 04:02:02 +03:00
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hstatus ", env->hstatus);
|
2020-10-26 14:55:25 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus ",
|
|
|
|
(target_ulong)env->vsstatus);
|
2020-02-01 04:02:02 +03:00
|
|
|
}
|
2020-02-01 04:01:38 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", env->mip);
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie);
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
|
2020-02-01 04:02:02 +03:00
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hideleg ", env->hideleg);
|
|
|
|
}
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
|
2020-02-01 04:02:02 +03:00
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hedeleg ", env->hedeleg);
|
|
|
|
}
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec);
|
2020-02-01 04:02:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stvec ", env->stvec);
|
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vstvec ", env->vstvec);
|
|
|
|
}
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc);
|
2020-02-01 04:02:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "sepc ", env->sepc);
|
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsepc ", env->vsepc);
|
|
|
|
}
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause);
|
2020-02-01 04:02:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "scause ", env->scause);
|
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vscause ", env->vscause);
|
|
|
|
}
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval ", env->mtval);
|
2021-03-19 22:45:29 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stval ", env->stval);
|
2020-02-01 04:02:02 +03:00
|
|
|
if (riscv_has_ext(env, RVH)) {
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "htval ", env->htval);
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval2 ", env->mtval2);
|
|
|
|
}
|
2018-03-02 15:31:10 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx,
|
|
|
|
riscv_int_regnames[i], env->gpr[i]);
|
2018-03-02 15:31:10 +03:00
|
|
|
if ((i & 3) == 3) {
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, "\n");
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
}
|
2018-05-11 06:31:33 +03:00
|
|
|
if (flags & CPU_DUMP_FPU) {
|
|
|
|
for (i = 0; i < 32; i++) {
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, " %s %016" PRIx64,
|
|
|
|
riscv_fpr_regnames[i], env->fpr[i]);
|
2018-05-11 06:31:33 +03:00
|
|
|
if ((i & 3) == 3) {
|
2019-04-17 22:18:02 +03:00
|
|
|
qemu_fprintf(f, "\n");
|
2018-05-11 06:31:33 +03:00
|
|
|
}
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
|
|
|
|
{
|
|
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
env->pc = value;
|
|
|
|
}
|
|
|
|
|
2020-10-29 22:30:01 +03:00
|
|
|
static void riscv_cpu_synchronize_from_tb(CPUState *cs,
|
|
|
|
const TranslationBlock *tb)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
2019-10-09 01:04:18 +03:00
|
|
|
return (env->mip & env->mie) != 0;
|
2018-03-02 15:31:10 +03:00
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
|
|
|
|
target_ulong *data)
|
|
|
|
{
|
|
|
|
env->pc = data[0];
|
|
|
|
}
|
|
|
|
|
cpu: Use DeviceClass reset instead of a special CPUClass reset
The CPUClass has a 'reset' method. This is a legacy from when
TYPE_CPU used not to inherit from TYPE_DEVICE. We don't need it any
more, as we can simply use the TYPE_DEVICE reset. The 'cpu_reset()'
function is kept as the API which most places use to reset a CPU; it
is now a wrapper which calls device_cold_reset() and then the
tracepoint function.
This change should not cause CPU objects to be reset more often
than they are at the moment, because:
* nobody is directly calling device_cold_reset() or
qdev_reset_all() on CPU objects
* no CPU object is on a qbus, so they will not be reset either
by somebody calling qbus_reset_all()/bus_cold_reset(), or
by the main "reset sysbus and everything in the qbus tree"
reset that most devices are reset by
Note that this does not change the need for each machine or whatever
to use qemu_register_reset() to arrange to call cpu_reset() -- that
is necessary because CPU objects are not on any qbus, so they don't
get reset when the qbus tree rooted at the sysbus bus is reset, and
this isn't being changed here.
All the changes to the files under target/ were made using the
included Coccinelle script, except:
(1) the deletion of the now-inaccurate and not terribly useful
"CPUClass::reset" comments was done with a perl one-liner afterwards:
perl -n -i -e '/ CPUClass::reset/ or print' target/*/*.c
(2) this bit of the s390 change was done by hand, because the
Coccinelle script is not sophisticated enough to handle the
parent_reset call being inside another function:
| @@ -96,8 +96,9 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type)
| S390CPU *cpu = S390_CPU(s);
| S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
| CPUS390XState *env = &cpu->env;
|+ DeviceState *dev = DEVICE(s);
|
|- scc->parent_reset(s);
|+ scc->parent_reset(dev);
| cpu->env.sigp_order = 0;
| s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20200303100511.5498-1-peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2020-03-03 13:05:11 +03:00
|
|
|
static void riscv_cpu_reset(DeviceState *dev)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
cpu: Use DeviceClass reset instead of a special CPUClass reset
The CPUClass has a 'reset' method. This is a legacy from when
TYPE_CPU used not to inherit from TYPE_DEVICE. We don't need it any
more, as we can simply use the TYPE_DEVICE reset. The 'cpu_reset()'
function is kept as the API which most places use to reset a CPU; it
is now a wrapper which calls device_cold_reset() and then the
tracepoint function.
This change should not cause CPU objects to be reset more often
than they are at the moment, because:
* nobody is directly calling device_cold_reset() or
qdev_reset_all() on CPU objects
* no CPU object is on a qbus, so they will not be reset either
by somebody calling qbus_reset_all()/bus_cold_reset(), or
by the main "reset sysbus and everything in the qbus tree"
reset that most devices are reset by
Note that this does not change the need for each machine or whatever
to use qemu_register_reset() to arrange to call cpu_reset() -- that
is necessary because CPU objects are not on any qbus, so they don't
get reset when the qbus tree rooted at the sysbus bus is reset, and
this isn't being changed here.
All the changes to the files under target/ were made using the
included Coccinelle script, except:
(1) the deletion of the now-inaccurate and not terribly useful
"CPUClass::reset" comments was done with a perl one-liner afterwards:
perl -n -i -e '/ CPUClass::reset/ or print' target/*/*.c
(2) this bit of the s390 change was done by hand, because the
Coccinelle script is not sophisticated enough to handle the
parent_reset call being inside another function:
| @@ -96,8 +96,9 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type)
| S390CPU *cpu = S390_CPU(s);
| S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
| CPUS390XState *env = &cpu->env;
|+ DeviceState *dev = DEVICE(s);
|
|- scc->parent_reset(s);
|+ scc->parent_reset(dev);
| cpu->env.sigp_order = 0;
| s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20200303100511.5498-1-peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2020-03-03 13:05:11 +03:00
|
|
|
CPUState *cs = CPU(dev);
|
2018-03-02 15:31:10 +03:00
|
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
|
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
|
cpu: Use DeviceClass reset instead of a special CPUClass reset
The CPUClass has a 'reset' method. This is a legacy from when
TYPE_CPU used not to inherit from TYPE_DEVICE. We don't need it any
more, as we can simply use the TYPE_DEVICE reset. The 'cpu_reset()'
function is kept as the API which most places use to reset a CPU; it
is now a wrapper which calls device_cold_reset() and then the
tracepoint function.
This change should not cause CPU objects to be reset more often
than they are at the moment, because:
* nobody is directly calling device_cold_reset() or
qdev_reset_all() on CPU objects
* no CPU object is on a qbus, so they will not be reset either
by somebody calling qbus_reset_all()/bus_cold_reset(), or
by the main "reset sysbus and everything in the qbus tree"
reset that most devices are reset by
Note that this does not change the need for each machine or whatever
to use qemu_register_reset() to arrange to call cpu_reset() -- that
is necessary because CPU objects are not on any qbus, so they don't
get reset when the qbus tree rooted at the sysbus bus is reset, and
this isn't being changed here.
All the changes to the files under target/ were made using the
included Coccinelle script, except:
(1) the deletion of the now-inaccurate and not terribly useful
"CPUClass::reset" comments was done with a perl one-liner afterwards:
perl -n -i -e '/ CPUClass::reset/ or print' target/*/*.c
(2) this bit of the s390 change was done by hand, because the
Coccinelle script is not sophisticated enough to handle the
parent_reset call being inside another function:
| @@ -96,8 +96,9 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type)
| S390CPU *cpu = S390_CPU(s);
| S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
| CPUS390XState *env = &cpu->env;
|+ DeviceState *dev = DEVICE(s);
|
|- scc->parent_reset(s);
|+ scc->parent_reset(dev);
| cpu->env.sigp_order = 0;
| s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20200303100511.5498-1-peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2020-03-03 13:05:11 +03:00
|
|
|
mcc->parent_reset(dev);
|
2018-03-02 15:31:10 +03:00
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
|
|
env->priv = PRV_M;
|
|
|
|
env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
|
|
|
|
env->mcause = 0;
|
|
|
|
env->pc = env->resetvec;
|
2021-03-19 17:14:59 +03:00
|
|
|
env->two_stage_lookup = false;
|
2018-03-02 15:31:10 +03:00
|
|
|
#endif
|
2021-04-01 18:17:29 +03:00
|
|
|
cs->exception_index = RISCV_EXCP_NONE;
|
2019-06-24 21:08:38 +03:00
|
|
|
env->load_res = -1;
|
2018-03-02 15:31:10 +03:00
|
|
|
set_default_nan_mode(1, &env->fp_status);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
|
|
|
|
{
|
2020-12-16 21:22:56 +03:00
|
|
|
RISCVCPU *cpu = RISCV_CPU(s);
|
|
|
|
if (riscv_cpu_is_32bit(&cpu->env)) {
|
|
|
|
info->print_insn = print_insn_riscv32;
|
|
|
|
} else {
|
|
|
|
info->print_insn = print_insn_riscv64;
|
|
|
|
}
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
CPUState *cs = CPU(dev);
|
2019-04-20 05:24:01 +03:00
|
|
|
RISCVCPU *cpu = RISCV_CPU(dev);
|
|
|
|
CPURISCVState *env = &cpu->env;
|
2018-03-02 15:31:10 +03:00
|
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
|
2019-06-18 04:31:11 +03:00
|
|
|
int priv_version = PRIV_VERSION_1_11_0;
|
2020-07-01 18:24:50 +03:00
|
|
|
int vext_version = VEXT_VERSION_0_07_1;
|
2020-12-16 21:23:05 +03:00
|
|
|
target_ulong target_misa = env->misa;
|
2018-03-02 15:31:10 +03:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
cpu_exec_realizefn(cs, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-20 05:24:01 +03:00
|
|
|
if (cpu->cfg.priv_spec) {
|
2019-06-18 04:31:11 +03:00
|
|
|
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
|
|
|
|
priv_version = PRIV_VERSION_1_11_0;
|
|
|
|
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
|
2019-04-20 05:24:01 +03:00
|
|
|
priv_version = PRIV_VERSION_1_10_0;
|
|
|
|
} else {
|
|
|
|
error_setg(errp,
|
|
|
|
"Unsupported privilege spec version '%s'",
|
|
|
|
cpu->cfg.priv_spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 04:31:19 +03:00
|
|
|
set_priv_version(env, priv_version);
|
2020-07-01 18:24:50 +03:00
|
|
|
set_vext_version(env, vext_version);
|
2019-04-20 05:24:01 +03:00
|
|
|
|
|
|
|
if (cpu->cfg.mmu) {
|
|
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpu->cfg.pmp) {
|
|
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
2021-04-19 09:17:25 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Enhanced PMP should only be available
|
|
|
|
* on harts with PMP support
|
|
|
|
*/
|
|
|
|
if (cpu->cfg.epmp) {
|
|
|
|
set_feature(env, RISCV_FEATURE_EPMP);
|
|
|
|
}
|
2019-04-20 05:24:01 +03:00
|
|
|
}
|
|
|
|
|
2020-09-01 04:38:58 +03:00
|
|
|
set_resetvec(env, cpu->cfg.resetvec);
|
|
|
|
|
2020-12-16 21:23:05 +03:00
|
|
|
/* If only XLEN is set for misa, then set misa from properties */
|
|
|
|
if (env->misa == RV32 || env->misa == RV64) {
|
2019-05-07 01:49:53 +03:00
|
|
|
/* Do some ISA extension error checking */
|
|
|
|
if (cpu->cfg.ext_i && cpu->cfg.ext_e) {
|
|
|
|
error_setg(errp,
|
|
|
|
"I and E extensions are incompatible");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-18 04:31:16 +03:00
|
|
|
if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Either I or E extension must be set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-07 01:49:53 +03:00
|
|
|
if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m &
|
|
|
|
cpu->cfg.ext_a & cpu->cfg.ext_f &
|
|
|
|
cpu->cfg.ext_d)) {
|
|
|
|
warn_report("Setting G will also set IMAFD");
|
|
|
|
cpu->cfg.ext_i = true;
|
|
|
|
cpu->cfg.ext_m = true;
|
|
|
|
cpu->cfg.ext_a = true;
|
|
|
|
cpu->cfg.ext_f = true;
|
|
|
|
cpu->cfg.ext_d = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the ISA extensions, checks should have happened above */
|
|
|
|
if (cpu->cfg.ext_i) {
|
|
|
|
target_misa |= RVI;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_e) {
|
|
|
|
target_misa |= RVE;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_m) {
|
|
|
|
target_misa |= RVM;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_a) {
|
|
|
|
target_misa |= RVA;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_f) {
|
|
|
|
target_misa |= RVF;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_d) {
|
|
|
|
target_misa |= RVD;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_c) {
|
|
|
|
target_misa |= RVC;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_s) {
|
|
|
|
target_misa |= RVS;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.ext_u) {
|
|
|
|
target_misa |= RVU;
|
|
|
|
}
|
2020-02-01 04:03:11 +03:00
|
|
|
if (cpu->cfg.ext_h) {
|
|
|
|
target_misa |= RVH;
|
|
|
|
}
|
2020-07-01 18:25:49 +03:00
|
|
|
if (cpu->cfg.ext_v) {
|
|
|
|
target_misa |= RVV;
|
|
|
|
if (!is_power_of_2(cpu->cfg.vlen)) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Vector extension VLEN must be power of 2");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.vlen > RV_VLEN_MAX || cpu->cfg.vlen < 128) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Vector extension implementation only supports VLEN "
|
|
|
|
"in the range [128, %d]", RV_VLEN_MAX);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!is_power_of_2(cpu->cfg.elen)) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Vector extension ELEN must be power of 2");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.elen > 64 || cpu->cfg.vlen < 8) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Vector extension implementation only supports ELEN "
|
|
|
|
"in the range [8, 64]");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cpu->cfg.vext_spec) {
|
|
|
|
if (!g_strcmp0(cpu->cfg.vext_spec, "v0.7.1")) {
|
|
|
|
vext_version = VEXT_VERSION_0_07_1;
|
|
|
|
} else {
|
|
|
|
error_setg(errp,
|
|
|
|
"Unsupported vector spec version '%s'",
|
|
|
|
cpu->cfg.vext_spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
2021-03-09 14:15:10 +03:00
|
|
|
qemu_log("vector version is not specified, "
|
2020-07-01 18:25:49 +03:00
|
|
|
"use the default value v0.7.1\n");
|
|
|
|
}
|
|
|
|
set_vext_version(env, vext_version);
|
|
|
|
}
|
2019-05-07 01:49:53 +03:00
|
|
|
|
2020-12-16 21:23:05 +03:00
|
|
|
set_misa(env, target_misa);
|
2019-05-07 01:49:53 +03:00
|
|
|
}
|
|
|
|
|
2019-03-15 13:26:59 +03:00
|
|
|
riscv_cpu_register_gdb_regs_for_features(cs);
|
|
|
|
|
2018-03-02 15:31:10 +03:00
|
|
|
qemu_init_vcpu(cs);
|
|
|
|
cpu_reset(cs);
|
|
|
|
|
|
|
|
mcc->parent_realize(dev, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void riscv_cpu_init(Object *obj)
|
|
|
|
{
|
|
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
|
|
|
2019-03-29 00:26:22 +03:00
|
|
|
cpu_set_cpustate_pointers(cpu);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2019-04-20 05:24:01 +03:00
|
|
|
static Property riscv_cpu_properties[] = {
|
2019-05-07 01:49:53 +03:00
|
|
|
DEFINE_PROP_BOOL("i", RISCVCPU, cfg.ext_i, true),
|
|
|
|
DEFINE_PROP_BOOL("e", RISCVCPU, cfg.ext_e, false),
|
|
|
|
DEFINE_PROP_BOOL("g", RISCVCPU, cfg.ext_g, true),
|
|
|
|
DEFINE_PROP_BOOL("m", RISCVCPU, cfg.ext_m, true),
|
|
|
|
DEFINE_PROP_BOOL("a", RISCVCPU, cfg.ext_a, true),
|
|
|
|
DEFINE_PROP_BOOL("f", RISCVCPU, cfg.ext_f, true),
|
|
|
|
DEFINE_PROP_BOOL("d", RISCVCPU, cfg.ext_d, true),
|
|
|
|
DEFINE_PROP_BOOL("c", RISCVCPU, cfg.ext_c, true),
|
|
|
|
DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true),
|
|
|
|
DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true),
|
2020-02-01 04:03:11 +03:00
|
|
|
/* This is experimental so mark with 'x-' */
|
|
|
|
DEFINE_PROP_BOOL("x-h", RISCVCPU, cfg.ext_h, false),
|
2020-07-01 18:25:49 +03:00
|
|
|
DEFINE_PROP_BOOL("x-v", RISCVCPU, cfg.ext_v, false),
|
2019-06-18 04:31:22 +03:00
|
|
|
DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true),
|
2019-06-24 11:59:05 +03:00
|
|
|
DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true),
|
2019-06-24 11:59:51 +03:00
|
|
|
DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true),
|
2019-04-20 05:24:01 +03:00
|
|
|
DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
|
2020-07-01 18:25:49 +03:00
|
|
|
DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec),
|
|
|
|
DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128),
|
|
|
|
DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64),
|
2019-04-20 05:24:01 +03:00
|
|
|
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
|
|
|
|
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
|
2021-04-19 09:17:25 +03:00
|
|
|
DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false),
|
|
|
|
|
2020-09-01 04:38:56 +03:00
|
|
|
DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
|
2019-04-20 05:24:01 +03:00
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
2021-01-06 23:41:41 +03:00
|
|
|
static gchar *riscv_gdb_arch_name(CPUState *cs)
|
|
|
|
{
|
|
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
|
|
|
|
if (riscv_cpu_is_32bit(env)) {
|
|
|
|
return g_strdup("riscv:rv32");
|
|
|
|
} else {
|
|
|
|
return g_strdup("riscv:rv64");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-16 08:41:22 +03:00
|
|
|
static const char *riscv_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname)
|
|
|
|
{
|
|
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
|
|
|
|
|
|
if (strcmp(xmlname, "riscv-csr.xml") == 0) {
|
|
|
|
return cpu->dyn_csr_xml;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-02-04 19:39:23 +03:00
|
|
|
#include "hw/core/tcg-cpu-ops.h"
|
|
|
|
|
|
|
|
static struct TCGCPUOps riscv_tcg_ops = {
|
|
|
|
.initialize = riscv_translate_init,
|
|
|
|
.synchronize_from_tb = riscv_cpu_synchronize_from_tb,
|
|
|
|
.cpu_exec_interrupt = riscv_cpu_exec_interrupt,
|
|
|
|
.tlb_fill = riscv_cpu_tlb_fill,
|
|
|
|
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
|
|
.do_interrupt = riscv_cpu_do_interrupt,
|
|
|
|
.do_transaction_failed = riscv_cpu_do_transaction_failed,
|
|
|
|
.do_unaligned_access = riscv_cpu_do_unaligned_access,
|
|
|
|
#endif /* !CONFIG_USER_ONLY */
|
|
|
|
};
|
|
|
|
|
2018-03-02 15:31:10 +03:00
|
|
|
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);
|
|
|
|
|
2018-11-26 06:20:38 +03:00
|
|
|
device_class_set_parent_realize(dc, riscv_cpu_realize,
|
|
|
|
&mcc->parent_realize);
|
2018-03-02 15:31:10 +03:00
|
|
|
|
cpu: Use DeviceClass reset instead of a special CPUClass reset
The CPUClass has a 'reset' method. This is a legacy from when
TYPE_CPU used not to inherit from TYPE_DEVICE. We don't need it any
more, as we can simply use the TYPE_DEVICE reset. The 'cpu_reset()'
function is kept as the API which most places use to reset a CPU; it
is now a wrapper which calls device_cold_reset() and then the
tracepoint function.
This change should not cause CPU objects to be reset more often
than they are at the moment, because:
* nobody is directly calling device_cold_reset() or
qdev_reset_all() on CPU objects
* no CPU object is on a qbus, so they will not be reset either
by somebody calling qbus_reset_all()/bus_cold_reset(), or
by the main "reset sysbus and everything in the qbus tree"
reset that most devices are reset by
Note that this does not change the need for each machine or whatever
to use qemu_register_reset() to arrange to call cpu_reset() -- that
is necessary because CPU objects are not on any qbus, so they don't
get reset when the qbus tree rooted at the sysbus bus is reset, and
this isn't being changed here.
All the changes to the files under target/ were made using the
included Coccinelle script, except:
(1) the deletion of the now-inaccurate and not terribly useful
"CPUClass::reset" comments was done with a perl one-liner afterwards:
perl -n -i -e '/ CPUClass::reset/ or print' target/*/*.c
(2) this bit of the s390 change was done by hand, because the
Coccinelle script is not sophisticated enough to handle the
parent_reset call being inside another function:
| @@ -96,8 +96,9 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type)
| S390CPU *cpu = S390_CPU(s);
| S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
| CPUS390XState *env = &cpu->env;
|+ DeviceState *dev = DEVICE(s);
|
|- scc->parent_reset(s);
|+ scc->parent_reset(dev);
| cpu->env.sigp_order = 0;
| s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20200303100511.5498-1-peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2020-03-03 13:05:11 +03:00
|
|
|
device_class_set_parent_reset(dc, riscv_cpu_reset, &mcc->parent_reset);
|
2018-03-02 15:31:10 +03:00
|
|
|
|
|
|
|
cc->class_by_name = riscv_cpu_class_by_name;
|
|
|
|
cc->has_work = riscv_cpu_has_work;
|
|
|
|
cc->dump_state = riscv_cpu_dump_state;
|
|
|
|
cc->set_pc = riscv_cpu_set_pc;
|
|
|
|
cc->gdb_read_register = riscv_cpu_gdb_read_register;
|
|
|
|
cc->gdb_write_register = riscv_cpu_gdb_write_register;
|
2019-03-15 13:26:59 +03:00
|
|
|
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
|
2018-03-02 15:31:10 +03:00
|
|
|
cc->gdb_stop_before_watchpoint = true;
|
|
|
|
cc->disas_set_info = riscv_cpu_disas_set_info;
|
2019-04-02 13:12:38 +03:00
|
|
|
#ifndef CONFIG_USER_ONLY
|
2018-03-02 15:31:10 +03:00
|
|
|
cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
|
cpu: Rename CPUClass vmsd -> legacy_vmsd
Quoting Peter Maydell [*]:
There are two ways to handle migration for
a CPU object:
(1) like any other device, so it has a dc->vmsd that covers
migration for the whole object. As usual for objects that are a
subclass of a parent that has state, the first entry in the
VMStateDescription field list is VMSTATE_CPU(), which migrates
the cpu_common fields, followed by whatever the CPU's own migration
fields are.
(2) a backwards-compatible mechanism for CPUs that were
originally migrated using manual "write fields to the migration
stream structures". The on-the-wire migration format
for those is based on the 'env' pointer (which isn't a QOM object),
and the cpu_common part of the migration data is elsewhere.
cpu_exec_realizefn() handles both possibilities:
* for type 1, dc->vmsd is set and cc->vmsd is not,
so cpu_exec_realizefn() does nothing, and the standard
"register dc->vmsd for a device" code does everything needed
* for type 2, dc->vmsd is NULL and so we register the
vmstate_cpu_common directly to handle the cpu-common fields,
and the cc->vmsd to handle the per-CPU stuff
You can't change a CPU from one type to the other without breaking
migration compatibility, which is why some guest architectures
are stuck on the cc->vmsd form. New targets should use dc->vmsd.
To avoid new targets to start using type (2), rename cc->vmsd as
cc->legacy_vmsd. The correct field to implement is dc->vmsd (the
DeviceClass one).
See also commit b170fce3dd0 ("cpu: Register VMStateDescription
through CPUState") for historic background.
[*] https://www.mail-archive.com/qemu-devel@nongnu.org/msg800849.html
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Cc: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20210517105140.1062037-13-f4bug@amsat.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2021-05-17 13:51:29 +03:00
|
|
|
cc->legacy_vmsd = &vmstate_riscv_cpu;
|
2021-02-01 15:44:58 +03:00
|
|
|
cc->write_elf64_note = riscv_cpu_write_elf64_note;
|
|
|
|
cc->write_elf32_note = riscv_cpu_write_elf32_note;
|
2018-03-02 15:31:10 +03:00
|
|
|
#endif
|
2021-01-06 23:41:41 +03:00
|
|
|
cc->gdb_arch_name = riscv_gdb_arch_name;
|
2021-01-16 08:41:22 +03:00
|
|
|
cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml;
|
2021-02-04 19:39:23 +03:00
|
|
|
cc->tcg_ops = &riscv_tcg_ops;
|
2021-02-04 19:39:10 +03:00
|
|
|
|
2020-01-10 18:30:32 +03:00
|
|
|
device_class_set_props(dc, riscv_cpu_properties);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
char *riscv_isa_string(RISCVCPU *cpu)
|
|
|
|
{
|
|
|
|
int i;
|
2018-03-20 00:18:49 +03:00
|
|
|
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);
|
2018-03-02 15:31:10 +03:00
|
|
|
for (i = 0; i < sizeof(riscv_exts); i++) {
|
|
|
|
if (cpu->env.misa & RV(riscv_exts[i])) {
|
2018-03-20 00:18:49 +03:00
|
|
|
*p++ = qemu_tolower(riscv_exts[i]);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
}
|
2018-03-20 00:18:49 +03:00
|
|
|
*p = '\0';
|
|
|
|
return isa_str;
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2018-03-09 01:12:31 +03:00
|
|
|
static gint riscv_cpu_list_compare(gconstpointer a, gconstpointer b)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
2018-03-09 01:12:31 +03:00
|
|
|
ObjectClass *class_a = (ObjectClass *)a;
|
|
|
|
ObjectClass *class_b = (ObjectClass *)b;
|
|
|
|
const char *name_a, *name_b;
|
2018-03-02 15:31:10 +03:00
|
|
|
|
2018-03-09 01:12:31 +03:00
|
|
|
name_a = object_class_get_name(class_a);
|
|
|
|
name_b = object_class_get_name(class_b);
|
|
|
|
return strcmp(name_a, name_b);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2018-03-09 01:12:31 +03:00
|
|
|
static void riscv_cpu_list_entry(gpointer data, gpointer user_data)
|
2018-03-02 15:31:10 +03:00
|
|
|
{
|
2018-03-09 01:12:31 +03:00
|
|
|
const char *typename = object_class_get_name(OBJECT_CLASS(data));
|
|
|
|
int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX);
|
2018-03-02 15:31:10 +03:00
|
|
|
|
2019-04-17 22:17:57 +03:00
|
|
|
qemu_printf("%.*s\n", len, typename);
|
2018-03-09 01:12:31 +03:00
|
|
|
}
|
2018-03-02 15:31:10 +03:00
|
|
|
|
2019-04-17 22:17:57 +03:00
|
|
|
void riscv_cpu_list(void)
|
2018-03-09 01:12:31 +03:00
|
|
|
{
|
|
|
|
GSList *list;
|
|
|
|
|
|
|
|
list = object_class_get_list(TYPE_RISCV_CPU, false);
|
|
|
|
list = g_slist_sort(list, riscv_cpu_list_compare);
|
2019-04-17 22:17:57 +03:00
|
|
|
g_slist_foreach(list, riscv_cpu_list_entry, NULL);
|
2018-03-09 01:12:31 +03:00
|
|
|
g_slist_free(list);
|
2018-03-02 15:31:10 +03:00
|
|
|
}
|
|
|
|
|
2018-03-09 01:12:31 +03:00
|
|
|
#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),
|
2020-09-16 03:46:37 +03:00
|
|
|
.instance_align = __alignof__(RISCVCPU),
|
2018-03-09 01:12:31 +03:00
|
|
|
.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)
|
2020-12-16 21:23:05 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, rv32_base_cpu_init),
|
2020-06-16 03:50:37 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32_ibex_cpu_init),
|
2020-12-16 21:22:54 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32_sifive_e_cpu_init),
|
2020-06-11 04:08:49 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32_imafcu_nommu_cpu_init),
|
2020-12-16 21:22:54 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32_sifive_u_cpu_init),
|
2018-03-09 01:12:31 +03:00
|
|
|
#elif defined(TARGET_RISCV64)
|
2020-12-16 21:23:05 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_BASE64, rv64_base_cpu_init),
|
2020-12-16 21:22:54 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64_sifive_e_cpu_init),
|
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64_sifive_u_cpu_init),
|
2021-04-01 21:14:54 +03:00
|
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SHAKTI_C, rv64_sifive_u_cpu_init),
|
2018-03-09 01:12:31 +03:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_TYPES(riscv_cpu_type_infos)
|