197e4d2988
Zcmop defines eight 16-bit MOP instructions named C.MOP.n, where n is an odd integer between 1 and 15, inclusive. C.MOP.n is encoded in the reserved encoding space corresponding to C.LUI xn, 0. Unlike the MOPs defined in the Zimop extension, the C.MOP.n instructions are defined to not write any register. In current implementation, C.MOP.n only has an check function, without any other more behavior. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Deepak Gupta <debug@rivosinc.com> Message-ID: <20240709113652.1239-4-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
1434 lines
41 KiB
C
1434 lines
41 KiB
C
/*
|
|
* riscv TCG cpu class initialization
|
|
*
|
|
* 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 "exec/exec-all.h"
|
|
#include "tcg-cpu.h"
|
|
#include "cpu.h"
|
|
#include "internals.h"
|
|
#include "pmu.h"
|
|
#include "time_helper.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/visitor.h"
|
|
#include "qemu/accel.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/log.h"
|
|
#include "hw/core/accel-cpu.h"
|
|
#include "hw/core/tcg-cpu-ops.h"
|
|
#include "tcg/tcg.h"
|
|
#ifndef CONFIG_USER_ONLY
|
|
#include "hw/boards.h"
|
|
#endif
|
|
|
|
/* Hash that stores user set extensions */
|
|
static GHashTable *multi_ext_user_opts;
|
|
static GHashTable *misa_ext_user_opts;
|
|
|
|
static GHashTable *multi_ext_implied_rules;
|
|
static GHashTable *misa_ext_implied_rules;
|
|
|
|
static bool cpu_cfg_ext_is_user_set(uint32_t ext_offset)
|
|
{
|
|
return g_hash_table_contains(multi_ext_user_opts,
|
|
GUINT_TO_POINTER(ext_offset));
|
|
}
|
|
|
|
static bool cpu_misa_ext_is_user_set(uint32_t misa_bit)
|
|
{
|
|
return g_hash_table_contains(misa_ext_user_opts,
|
|
GUINT_TO_POINTER(misa_bit));
|
|
}
|
|
|
|
static void cpu_cfg_ext_add_user_opt(uint32_t ext_offset, bool value)
|
|
{
|
|
g_hash_table_insert(multi_ext_user_opts, GUINT_TO_POINTER(ext_offset),
|
|
(gpointer)value);
|
|
}
|
|
|
|
static void cpu_misa_ext_add_user_opt(uint32_t bit, bool value)
|
|
{
|
|
g_hash_table_insert(misa_ext_user_opts, GUINT_TO_POINTER(bit),
|
|
(gpointer)value);
|
|
}
|
|
|
|
static void riscv_cpu_write_misa_bit(RISCVCPU *cpu, uint32_t bit,
|
|
bool enabled)
|
|
{
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
if (enabled) {
|
|
env->misa_ext |= bit;
|
|
env->misa_ext_mask |= bit;
|
|
} else {
|
|
env->misa_ext &= ~bit;
|
|
env->misa_ext_mask &= ~bit;
|
|
}
|
|
}
|
|
|
|
static const char *cpu_priv_ver_to_str(int priv_ver)
|
|
{
|
|
const char *priv_spec_str = priv_spec_to_str(priv_ver);
|
|
|
|
g_assert(priv_spec_str);
|
|
|
|
return priv_spec_str;
|
|
}
|
|
|
|
static void riscv_cpu_synchronize_from_tb(CPUState *cs,
|
|
const TranslationBlock *tb)
|
|
{
|
|
if (!(tb_cflags(tb) & CF_PCREL)) {
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
|
|
|
|
tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
|
|
|
|
if (xl == MXL_RV32) {
|
|
env->pc = (int32_t) tb->pc;
|
|
} else {
|
|
env->pc = tb->pc;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void riscv_restore_state_to_opc(CPUState *cs,
|
|
const TranslationBlock *tb,
|
|
const uint64_t *data)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
|
|
target_ulong pc;
|
|
|
|
if (tb_cflags(tb) & CF_PCREL) {
|
|
pc = (env->pc & TARGET_PAGE_MASK) | data[0];
|
|
} else {
|
|
pc = data[0];
|
|
}
|
|
|
|
if (xl == MXL_RV32) {
|
|
env->pc = (int32_t)pc;
|
|
} else {
|
|
env->pc = pc;
|
|
}
|
|
env->bins = data[1];
|
|
}
|
|
|
|
static const TCGCPUOps riscv_tcg_ops = {
|
|
.initialize = riscv_translate_init,
|
|
.synchronize_from_tb = riscv_cpu_synchronize_from_tb,
|
|
.restore_state_to_opc = riscv_restore_state_to_opc,
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
.tlb_fill = riscv_cpu_tlb_fill,
|
|
.cpu_exec_interrupt = riscv_cpu_exec_interrupt,
|
|
.cpu_exec_halt = riscv_cpu_has_work,
|
|
.do_interrupt = riscv_cpu_do_interrupt,
|
|
.do_transaction_failed = riscv_cpu_do_transaction_failed,
|
|
.do_unaligned_access = riscv_cpu_do_unaligned_access,
|
|
.debug_excp_handler = riscv_cpu_debug_excp_handler,
|
|
.debug_check_breakpoint = riscv_cpu_debug_check_breakpoint,
|
|
.debug_check_watchpoint = riscv_cpu_debug_check_watchpoint,
|
|
#endif /* !CONFIG_USER_ONLY */
|
|
};
|
|
|
|
static int cpu_cfg_ext_get_min_version(uint32_t ext_offset)
|
|
{
|
|
const RISCVIsaExtData *edata;
|
|
|
|
for (edata = isa_edata_arr; edata && edata->name; edata++) {
|
|
if (edata->ext_enable_offset != ext_offset) {
|
|
continue;
|
|
}
|
|
|
|
return edata->min_version;
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
static const char *cpu_cfg_ext_get_name(uint32_t ext_offset)
|
|
{
|
|
const RISCVCPUMultiExtConfig *feat;
|
|
const RISCVIsaExtData *edata;
|
|
|
|
for (edata = isa_edata_arr; edata->name != NULL; edata++) {
|
|
if (edata->ext_enable_offset == ext_offset) {
|
|
return edata->name;
|
|
}
|
|
}
|
|
|
|
for (feat = riscv_cpu_named_features; feat->name != NULL; feat++) {
|
|
if (feat->offset == ext_offset) {
|
|
return feat->name;
|
|
}
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
static bool cpu_cfg_offset_is_named_feat(uint32_t ext_offset)
|
|
{
|
|
const RISCVCPUMultiExtConfig *feat;
|
|
|
|
for (feat = riscv_cpu_named_features; feat->name != NULL; feat++) {
|
|
if (feat->offset == ext_offset) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void riscv_cpu_enable_named_feat(RISCVCPU *cpu, uint32_t feat_offset)
|
|
{
|
|
/*
|
|
* All other named features are already enabled
|
|
* in riscv_tcg_cpu_instance_init().
|
|
*/
|
|
if (feat_offset == CPU_CFG_OFFSET(ext_zic64b)) {
|
|
cpu->cfg.cbom_blocksize = 64;
|
|
cpu->cfg.cbop_blocksize = 64;
|
|
cpu->cfg.cboz_blocksize = 64;
|
|
}
|
|
}
|
|
|
|
static void cpu_bump_multi_ext_priv_ver(CPURISCVState *env,
|
|
uint32_t ext_offset)
|
|
{
|
|
int ext_priv_ver;
|
|
|
|
if (env->priv_ver == PRIV_VERSION_LATEST) {
|
|
return;
|
|
}
|
|
|
|
ext_priv_ver = cpu_cfg_ext_get_min_version(ext_offset);
|
|
|
|
if (env->priv_ver < ext_priv_ver) {
|
|
/*
|
|
* Note: the 'priv_spec' command line option, if present,
|
|
* will take precedence over this priv_ver bump.
|
|
*/
|
|
env->priv_ver = ext_priv_ver;
|
|
}
|
|
}
|
|
|
|
static void cpu_cfg_ext_auto_update(RISCVCPU *cpu, uint32_t ext_offset,
|
|
bool value)
|
|
{
|
|
CPURISCVState *env = &cpu->env;
|
|
bool prev_val = isa_ext_is_enabled(cpu, ext_offset);
|
|
int min_version;
|
|
|
|
if (prev_val == value) {
|
|
return;
|
|
}
|
|
|
|
if (cpu_cfg_ext_is_user_set(ext_offset)) {
|
|
return;
|
|
}
|
|
|
|
if (value && env->priv_ver != PRIV_VERSION_LATEST) {
|
|
/* Do not enable it if priv_ver is older than min_version */
|
|
min_version = cpu_cfg_ext_get_min_version(ext_offset);
|
|
if (env->priv_ver < min_version) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
isa_ext_update_enabled(cpu, ext_offset, value);
|
|
}
|
|
|
|
static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp)
|
|
{
|
|
if (riscv_has_ext(env, RVH) && env->priv_ver < PRIV_VERSION_1_12_0) {
|
|
error_setg(errp, "H extension requires priv spec 1.12.0");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg,
|
|
Error **errp)
|
|
{
|
|
uint32_t vlen = cfg->vlenb << 3;
|
|
|
|
if (vlen > RV_VLEN_MAX || vlen < 128) {
|
|
error_setg(errp,
|
|
"Vector extension implementation only supports VLEN "
|
|
"in the range [128, %d]", RV_VLEN_MAX);
|
|
return;
|
|
}
|
|
|
|
if (cfg->elen > 64 || cfg->elen < 8) {
|
|
error_setg(errp,
|
|
"Vector extension implementation only supports ELEN "
|
|
"in the range [8, 64]");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu)
|
|
{
|
|
CPURISCVState *env = &cpu->env;
|
|
const RISCVIsaExtData *edata;
|
|
|
|
/* Force disable extensions if priv spec version does not match */
|
|
for (edata = isa_edata_arr; edata && edata->name; edata++) {
|
|
if (isa_ext_is_enabled(cpu, edata->ext_enable_offset) &&
|
|
(env->priv_ver < edata->min_version)) {
|
|
/*
|
|
* These two extensions are always enabled as they were supported
|
|
* by QEMU before they were added as extensions in the ISA.
|
|
*/
|
|
if (!strcmp(edata->name, "zicntr") ||
|
|
!strcmp(edata->name, "zihpm")) {
|
|
continue;
|
|
}
|
|
|
|
isa_ext_update_enabled(cpu, edata->ext_enable_offset, false);
|
|
#ifndef CONFIG_USER_ONLY
|
|
warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx
|
|
" because privilege spec version does not match",
|
|
edata->name, env->mhartid);
|
|
#else
|
|
warn_report("disabling %s extension because "
|
|
"privilege spec version does not match",
|
|
edata->name);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_update_named_features(RISCVCPU *cpu)
|
|
{
|
|
if (cpu->env.priv_ver >= PRIV_VERSION_1_11_0) {
|
|
cpu->cfg.has_priv_1_11 = true;
|
|
}
|
|
|
|
if (cpu->env.priv_ver >= PRIV_VERSION_1_12_0) {
|
|
cpu->cfg.has_priv_1_12 = true;
|
|
}
|
|
|
|
if (cpu->env.priv_ver >= PRIV_VERSION_1_13_0) {
|
|
cpu->cfg.has_priv_1_13 = true;
|
|
}
|
|
|
|
/* zic64b is 1.12 or later */
|
|
cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 &&
|
|
cpu->cfg.cbop_blocksize == 64 &&
|
|
cpu->cfg.cboz_blocksize == 64 &&
|
|
cpu->cfg.has_priv_1_12;
|
|
}
|
|
|
|
static void riscv_cpu_validate_g(RISCVCPU *cpu)
|
|
{
|
|
const char *warn_msg = "RVG mandates disabled extension %s";
|
|
uint32_t g_misa_bits[] = {RVI, RVM, RVA, RVF, RVD};
|
|
bool send_warn = cpu_misa_ext_is_user_set(RVG);
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(g_misa_bits); i++) {
|
|
uint32_t bit = g_misa_bits[i];
|
|
|
|
if (riscv_has_ext(&cpu->env, bit)) {
|
|
continue;
|
|
}
|
|
|
|
if (!cpu_misa_ext_is_user_set(bit)) {
|
|
riscv_cpu_write_misa_bit(cpu, bit, true);
|
|
continue;
|
|
}
|
|
|
|
if (send_warn) {
|
|
warn_report(warn_msg, riscv_get_misa_ext_name(bit));
|
|
}
|
|
}
|
|
|
|
if (!cpu->cfg.ext_zicsr) {
|
|
if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicsr))) {
|
|
cpu->cfg.ext_zicsr = true;
|
|
} else if (send_warn) {
|
|
warn_report(warn_msg, "zicsr");
|
|
}
|
|
}
|
|
|
|
if (!cpu->cfg.ext_zifencei) {
|
|
if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zifencei))) {
|
|
cpu->cfg.ext_zifencei = true;
|
|
} else if (send_warn) {
|
|
warn_report(warn_msg, "zifencei");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_validate_b(RISCVCPU *cpu)
|
|
{
|
|
const char *warn_msg = "RVB mandates disabled extension %s";
|
|
|
|
if (!cpu->cfg.ext_zba) {
|
|
if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zba))) {
|
|
cpu->cfg.ext_zba = true;
|
|
} else {
|
|
warn_report(warn_msg, "zba");
|
|
}
|
|
}
|
|
|
|
if (!cpu->cfg.ext_zbb) {
|
|
if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zbb))) {
|
|
cpu->cfg.ext_zbb = true;
|
|
} else {
|
|
warn_report(warn_msg, "zbb");
|
|
}
|
|
}
|
|
|
|
if (!cpu->cfg.ext_zbs) {
|
|
if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zbs))) {
|
|
cpu->cfg.ext_zbs = true;
|
|
} else {
|
|
warn_report(warn_msg, "zbs");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check consistency between chosen extensions while setting
|
|
* cpu->cfg accordingly.
|
|
*/
|
|
void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
|
|
{
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
|
CPURISCVState *env = &cpu->env;
|
|
Error *local_err = NULL;
|
|
|
|
if (riscv_has_ext(env, RVG)) {
|
|
riscv_cpu_validate_g(cpu);
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVB)) {
|
|
riscv_cpu_validate_b(cpu);
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) {
|
|
error_setg(errp,
|
|
"I and E extensions are incompatible");
|
|
return;
|
|
}
|
|
|
|
if (!riscv_has_ext(env, RVI) && !riscv_has_ext(env, RVE)) {
|
|
error_setg(errp,
|
|
"Either I or E extension must be set");
|
|
return;
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVS) && !riscv_has_ext(env, RVU)) {
|
|
error_setg(errp,
|
|
"Setting S extension without U extension is illegal");
|
|
return;
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVI)) {
|
|
error_setg(errp,
|
|
"H depends on an I base integer ISA with 32 x registers");
|
|
return;
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVS)) {
|
|
error_setg(errp, "H extension implicitly requires S-mode");
|
|
return;
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_zicsr) {
|
|
error_setg(errp, "F extension requires Zicsr");
|
|
return;
|
|
}
|
|
|
|
if ((cpu->cfg.ext_zacas) && !riscv_has_ext(env, RVA)) {
|
|
error_setg(errp, "Zacas extension requires A extension");
|
|
return;
|
|
}
|
|
|
|
if ((cpu->cfg.ext_zawrs) && !riscv_has_ext(env, RVA)) {
|
|
error_setg(errp, "Zawrs extension requires A extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) {
|
|
error_setg(errp, "Zfa extension requires F extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) {
|
|
error_setg(errp, "Zfh/Zfhmin extensions require F extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) {
|
|
error_setg(errp, "Zfbfmin extension depends on F extension");
|
|
return;
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) {
|
|
error_setg(errp, "D extension requires F extension");
|
|
return;
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVV)) {
|
|
riscv_cpu_validate_v(env, &cpu->cfg, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* The Zve64d extension depends on the Zve64f extension */
|
|
if (cpu->cfg.ext_zve64d) {
|
|
if (!riscv_has_ext(env, RVD)) {
|
|
error_setg(errp, "Zve64d/V extensions require D extension");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* The Zve32f extension depends on the Zve32x extension */
|
|
if (cpu->cfg.ext_zve32f) {
|
|
if (!riscv_has_ext(env, RVF)) {
|
|
error_setg(errp, "Zve32f/Zve64f extensions require F extension");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) {
|
|
error_setg(errp, "Zvfh/Zvfhmin extensions require Zve32f extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zvfh && !cpu->cfg.ext_zfhmin) {
|
|
error_setg(errp, "Zvfh extensions requires Zfhmin extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) {
|
|
error_setg(errp, "Zvfbfmin extension depends on Zve32f extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) {
|
|
error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension");
|
|
return;
|
|
}
|
|
|
|
if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) {
|
|
error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zfinx) {
|
|
if (!cpu->cfg.ext_zicsr) {
|
|
error_setg(errp, "Zfinx extension requires Zicsr");
|
|
return;
|
|
}
|
|
if (riscv_has_ext(env, RVF)) {
|
|
error_setg(errp,
|
|
"Zfinx cannot be supported together with F extension");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cpu->cfg.ext_zcmop && !cpu->cfg.ext_zca) {
|
|
error_setg(errp, "Zcmop extensions require Zca");
|
|
return;
|
|
}
|
|
|
|
if (mcc->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) {
|
|
error_setg(errp, "Zcf extension is only relevant to RV32");
|
|
return;
|
|
}
|
|
|
|
if (!riscv_has_ext(env, RVF) && cpu->cfg.ext_zcf) {
|
|
error_setg(errp, "Zcf extension requires F extension");
|
|
return;
|
|
}
|
|
|
|
if (!riscv_has_ext(env, RVD) && cpu->cfg.ext_zcd) {
|
|
error_setg(errp, "Zcd extension requires D extension");
|
|
return;
|
|
}
|
|
|
|
if ((cpu->cfg.ext_zcf || cpu->cfg.ext_zcd || cpu->cfg.ext_zcb ||
|
|
cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt) && !cpu->cfg.ext_zca) {
|
|
error_setg(errp, "Zcf/Zcd/Zcb/Zcmp/Zcmt extensions require Zca "
|
|
"extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zcd && (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt)) {
|
|
error_setg(errp, "Zcmp/Zcmt extensions are incompatible with "
|
|
"Zcd extension");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_zicsr) {
|
|
error_setg(errp, "Zcmt extension requires Zicsr extension");
|
|
return;
|
|
}
|
|
|
|
if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkb || cpu->cfg.ext_zvkg ||
|
|
cpu->cfg.ext_zvkned || cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed ||
|
|
cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32x) {
|
|
error_setg(errp,
|
|
"Vector crypto extensions require V or Zve* extensions");
|
|
return;
|
|
}
|
|
|
|
if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64x) {
|
|
error_setg(
|
|
errp,
|
|
"Zvbc and Zvknhb extensions require V or Zve64x extensions");
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zicntr && !cpu->cfg.ext_zicsr) {
|
|
if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicntr))) {
|
|
error_setg(errp, "zicntr requires zicsr");
|
|
return;
|
|
}
|
|
cpu->cfg.ext_zicntr = false;
|
|
}
|
|
|
|
if (cpu->cfg.ext_zihpm && !cpu->cfg.ext_zicsr) {
|
|
if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zihpm))) {
|
|
error_setg(errp, "zihpm requires zicsr");
|
|
return;
|
|
}
|
|
cpu->cfg.ext_zihpm = false;
|
|
}
|
|
|
|
if (!cpu->cfg.ext_zihpm) {
|
|
cpu->cfg.pmu_mask = 0;
|
|
cpu->pmu_avail_ctrs = 0;
|
|
}
|
|
|
|
/*
|
|
* Disable isa extensions based on priv spec after we
|
|
* validated and set everything we need.
|
|
*/
|
|
riscv_cpu_disable_priv_spec_isa_exts(cpu);
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
static bool riscv_cpu_validate_profile_satp(RISCVCPU *cpu,
|
|
RISCVCPUProfile *profile,
|
|
bool send_warn)
|
|
{
|
|
int satp_max = satp_mode_max_from_map(cpu->cfg.satp_mode.supported);
|
|
|
|
if (profile->satp_mode > satp_max) {
|
|
if (send_warn) {
|
|
bool is_32bit = riscv_cpu_is_32bit(cpu);
|
|
const char *req_satp = satp_mode_str(profile->satp_mode, is_32bit);
|
|
const char *cur_satp = satp_mode_str(satp_max, is_32bit);
|
|
|
|
warn_report("Profile %s requires satp mode %s, "
|
|
"but satp mode %s was set", profile->name,
|
|
req_satp, cur_satp);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static void riscv_cpu_validate_profile(RISCVCPU *cpu,
|
|
RISCVCPUProfile *profile)
|
|
{
|
|
CPURISCVState *env = &cpu->env;
|
|
const char *warn_msg = "Profile %s mandates disabled extension %s";
|
|
bool send_warn = profile->user_set && profile->enabled;
|
|
bool parent_enabled, profile_impl = true;
|
|
int i;
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) {
|
|
profile_impl = riscv_cpu_validate_profile_satp(cpu, profile,
|
|
send_warn);
|
|
}
|
|
#endif
|
|
|
|
if (profile->priv_spec != RISCV_PROFILE_ATTR_UNUSED &&
|
|
profile->priv_spec != env->priv_ver) {
|
|
profile_impl = false;
|
|
|
|
if (send_warn) {
|
|
warn_report("Profile %s requires priv spec %s, "
|
|
"but priv ver %s was set", profile->name,
|
|
cpu_priv_ver_to_str(profile->priv_spec),
|
|
cpu_priv_ver_to_str(env->priv_ver));
|
|
}
|
|
}
|
|
|
|
for (i = 0; misa_bits[i] != 0; i++) {
|
|
uint32_t bit = misa_bits[i];
|
|
|
|
if (!(profile->misa_ext & bit)) {
|
|
continue;
|
|
}
|
|
|
|
if (!riscv_has_ext(&cpu->env, bit)) {
|
|
profile_impl = false;
|
|
|
|
if (send_warn) {
|
|
warn_report(warn_msg, profile->name,
|
|
riscv_get_misa_ext_name(bit));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) {
|
|
int ext_offset = profile->ext_offsets[i];
|
|
|
|
if (!isa_ext_is_enabled(cpu, ext_offset)) {
|
|
profile_impl = false;
|
|
|
|
if (send_warn) {
|
|
warn_report(warn_msg, profile->name,
|
|
cpu_cfg_ext_get_name(ext_offset));
|
|
}
|
|
}
|
|
}
|
|
|
|
profile->enabled = profile_impl;
|
|
|
|
if (profile->parent != NULL) {
|
|
parent_enabled = object_property_get_bool(OBJECT(cpu),
|
|
profile->parent->name,
|
|
NULL);
|
|
profile->enabled = profile->enabled && parent_enabled;
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_validate_profiles(RISCVCPU *cpu)
|
|
{
|
|
for (int i = 0; riscv_profiles[i] != NULL; i++) {
|
|
riscv_cpu_validate_profile(cpu, riscv_profiles[i]);
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_init_implied_exts_rules(void)
|
|
{
|
|
RISCVCPUImpliedExtsRule *rule;
|
|
#ifndef CONFIG_USER_ONLY
|
|
MachineState *ms = MACHINE(qdev_get_machine());
|
|
#endif
|
|
static bool initialized;
|
|
int i;
|
|
|
|
/* Implied rules only need to be initialized once. */
|
|
if (initialized) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) {
|
|
#ifndef CONFIG_USER_ONLY
|
|
rule->enabled = bitmap_new(ms->smp.cpus);
|
|
#endif
|
|
g_hash_table_insert(misa_ext_implied_rules,
|
|
GUINT_TO_POINTER(rule->ext), (gpointer)rule);
|
|
}
|
|
|
|
for (i = 0; (rule = riscv_multi_ext_implied_rules[i]); i++) {
|
|
#ifndef CONFIG_USER_ONLY
|
|
rule->enabled = bitmap_new(ms->smp.cpus);
|
|
#endif
|
|
g_hash_table_insert(multi_ext_implied_rules,
|
|
GUINT_TO_POINTER(rule->ext), (gpointer)rule);
|
|
}
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
static void cpu_enable_implied_rule(RISCVCPU *cpu,
|
|
RISCVCPUImpliedExtsRule *rule)
|
|
{
|
|
CPURISCVState *env = &cpu->env;
|
|
RISCVCPUImpliedExtsRule *ir;
|
|
bool enabled = false;
|
|
int i;
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
enabled = test_bit(cpu->env.mhartid, rule->enabled);
|
|
#endif
|
|
|
|
if (!enabled) {
|
|
/* Enable the implied MISAs. */
|
|
if (rule->implied_misa_exts) {
|
|
riscv_cpu_set_misa_ext(env,
|
|
env->misa_ext | rule->implied_misa_exts);
|
|
|
|
for (i = 0; misa_bits[i] != 0; i++) {
|
|
if (rule->implied_misa_exts & misa_bits[i]) {
|
|
ir = g_hash_table_lookup(misa_ext_implied_rules,
|
|
GUINT_TO_POINTER(misa_bits[i]));
|
|
|
|
if (ir) {
|
|
cpu_enable_implied_rule(cpu, ir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Enable the implied extensions. */
|
|
for (i = 0;
|
|
rule->implied_multi_exts[i] != RISCV_IMPLIED_EXTS_RULE_END; i++) {
|
|
cpu_cfg_ext_auto_update(cpu, rule->implied_multi_exts[i], true);
|
|
|
|
ir = g_hash_table_lookup(multi_ext_implied_rules,
|
|
GUINT_TO_POINTER(
|
|
rule->implied_multi_exts[i]));
|
|
|
|
if (ir) {
|
|
cpu_enable_implied_rule(cpu, ir);
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
bitmap_set(rule->enabled, cpu->env.mhartid, 1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Zc extension has special implied rules that need to be handled separately. */
|
|
static void cpu_enable_zc_implied_rules(RISCVCPU *cpu)
|
|
{
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
if (cpu->cfg.ext_zce) {
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcb), true);
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true);
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true);
|
|
|
|
if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) {
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true);
|
|
}
|
|
}
|
|
|
|
/* Zca, Zcd and Zcf has a PRIV 1.12.0 restriction */
|
|
if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) {
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
|
|
|
|
if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) {
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true);
|
|
}
|
|
|
|
if (riscv_has_ext(env, RVD)) {
|
|
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcd), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_enable_implied_rules(RISCVCPU *cpu)
|
|
{
|
|
RISCVCPUImpliedExtsRule *rule;
|
|
int i;
|
|
|
|
/* Enable the implied extensions for Zc. */
|
|
cpu_enable_zc_implied_rules(cpu);
|
|
|
|
/* Enable the implied MISAs. */
|
|
for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) {
|
|
if (riscv_has_ext(&cpu->env, rule->ext)) {
|
|
cpu_enable_implied_rule(cpu, rule);
|
|
}
|
|
}
|
|
|
|
/* Enable the implied extensions. */
|
|
for (i = 0; (rule = riscv_multi_ext_implied_rules[i]); i++) {
|
|
if (isa_ext_is_enabled(cpu, rule->ext)) {
|
|
cpu_enable_implied_rule(cpu, rule);
|
|
}
|
|
}
|
|
}
|
|
|
|
void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
|
|
{
|
|
CPURISCVState *env = &cpu->env;
|
|
Error *local_err = NULL;
|
|
|
|
riscv_cpu_init_implied_exts_rules();
|
|
riscv_cpu_enable_implied_rules(cpu);
|
|
|
|
riscv_cpu_validate_misa_priv(env, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
|
|
riscv_cpu_update_named_features(cpu);
|
|
riscv_cpu_validate_profiles(cpu);
|
|
|
|
if (cpu->cfg.ext_smepmp && !cpu->cfg.pmp) {
|
|
/*
|
|
* Enhanced PMP should only be available
|
|
* on harts with PMP support
|
|
*/
|
|
error_setg(errp, "Invalid configuration: Smepmp requires PMP support");
|
|
return;
|
|
}
|
|
|
|
riscv_cpu_validate_set_extensions(cpu, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu)
|
|
{
|
|
GPtrArray *dynamic_decoders;
|
|
dynamic_decoders = g_ptr_array_sized_new(decoder_table_size);
|
|
for (size_t i = 0; i < decoder_table_size; ++i) {
|
|
if (decoder_table[i].guard_func &&
|
|
decoder_table[i].guard_func(&cpu->cfg)) {
|
|
g_ptr_array_add(dynamic_decoders,
|
|
(gpointer)decoder_table[i].riscv_cpu_decode_fn);
|
|
}
|
|
}
|
|
|
|
cpu->decoders = dynamic_decoders;
|
|
}
|
|
|
|
bool riscv_cpu_tcg_compatible(RISCVCPU *cpu)
|
|
{
|
|
return object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST) == NULL;
|
|
}
|
|
|
|
static bool riscv_cpu_is_generic(Object *cpu_obj)
|
|
{
|
|
return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
|
|
}
|
|
|
|
/*
|
|
* We'll get here via the following path:
|
|
*
|
|
* riscv_cpu_realize()
|
|
* -> cpu_exec_realizefn()
|
|
* -> tcg_cpu_realize() (via accel_cpu_common_realize())
|
|
*/
|
|
static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
|
|
if (!riscv_cpu_tcg_compatible(cpu)) {
|
|
g_autofree char *name = riscv_cpu_get_name(cpu);
|
|
error_setg(errp, "'%s' CPU is not compatible with TCG acceleration",
|
|
name);
|
|
return false;
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
CPURISCVState *env = &cpu->env;
|
|
Error *local_err = NULL;
|
|
|
|
tcg_cflags_set(CPU(cs), CF_PCREL);
|
|
|
|
if (cpu->cfg.ext_sstc) {
|
|
riscv_timer_init(cpu);
|
|
}
|
|
|
|
if (cpu->cfg.pmu_mask) {
|
|
riscv_pmu_init(cpu, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return false;
|
|
}
|
|
|
|
if (cpu->cfg.ext_sscofpmf) {
|
|
cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
|
riscv_pmu_timer_cb, cpu);
|
|
}
|
|
}
|
|
|
|
/* With H-Ext, VSSIP, VSTIP, VSEIP and SGEIP are hardwired to one. */
|
|
if (riscv_has_ext(env, RVH)) {
|
|
env->mideleg = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | MIP_SGEIP;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef struct RISCVCPUMisaExtConfig {
|
|
target_ulong misa_bit;
|
|
bool enabled;
|
|
} RISCVCPUMisaExtConfig;
|
|
|
|
static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
|
|
target_ulong misa_bit = misa_ext_cfg->misa_bit;
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
CPURISCVState *env = &cpu->env;
|
|
bool vendor_cpu = riscv_cpu_is_vendor(obj);
|
|
bool prev_val, value;
|
|
|
|
if (!visit_type_bool(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
|
|
cpu_misa_ext_add_user_opt(misa_bit, value);
|
|
|
|
prev_val = env->misa_ext & misa_bit;
|
|
|
|
if (value == prev_val) {
|
|
return;
|
|
}
|
|
|
|
if (value) {
|
|
if (vendor_cpu) {
|
|
g_autofree char *cpuname = riscv_cpu_get_name(cpu);
|
|
error_setg(errp, "'%s' CPU does not allow enabling extensions",
|
|
cpuname);
|
|
return;
|
|
}
|
|
|
|
if (misa_bit == RVH && env->priv_ver < PRIV_VERSION_1_12_0) {
|
|
/*
|
|
* Note: the 'priv_spec' command line option, if present,
|
|
* will take precedence over this priv_ver bump.
|
|
*/
|
|
env->priv_ver = PRIV_VERSION_1_12_0;
|
|
}
|
|
}
|
|
|
|
riscv_cpu_write_misa_bit(cpu, misa_bit, value);
|
|
}
|
|
|
|
static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
|
|
target_ulong misa_bit = misa_ext_cfg->misa_bit;
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
CPURISCVState *env = &cpu->env;
|
|
bool value;
|
|
|
|
value = env->misa_ext & misa_bit;
|
|
|
|
visit_type_bool(v, name, &value, errp);
|
|
}
|
|
|
|
#define MISA_CFG(_bit, _enabled) \
|
|
{.misa_bit = _bit, .enabled = _enabled}
|
|
|
|
static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
|
|
MISA_CFG(RVA, true),
|
|
MISA_CFG(RVC, true),
|
|
MISA_CFG(RVD, true),
|
|
MISA_CFG(RVF, true),
|
|
MISA_CFG(RVI, true),
|
|
MISA_CFG(RVE, false),
|
|
MISA_CFG(RVM, true),
|
|
MISA_CFG(RVS, true),
|
|
MISA_CFG(RVU, true),
|
|
MISA_CFG(RVH, true),
|
|
MISA_CFG(RVJ, false),
|
|
MISA_CFG(RVV, false),
|
|
MISA_CFG(RVG, false),
|
|
MISA_CFG(RVB, false),
|
|
};
|
|
|
|
/*
|
|
* We do not support user choice tracking for MISA
|
|
* extensions yet because, so far, we do not silently
|
|
* change MISA bits during realize() (RVG enables MISA
|
|
* bits but the user is warned about it).
|
|
*/
|
|
static void riscv_cpu_add_misa_properties(Object *cpu_obj)
|
|
{
|
|
bool use_def_vals = riscv_cpu_is_generic(cpu_obj);
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) {
|
|
const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
|
|
int bit = misa_cfg->misa_bit;
|
|
const char *name = riscv_get_misa_ext_name(bit);
|
|
const char *desc = riscv_get_misa_ext_description(bit);
|
|
|
|
/* Check if KVM already created the property */
|
|
if (object_property_find(cpu_obj, name)) {
|
|
continue;
|
|
}
|
|
|
|
object_property_add(cpu_obj, name, "bool",
|
|
cpu_get_misa_ext_cfg,
|
|
cpu_set_misa_ext_cfg,
|
|
NULL, (void *)misa_cfg);
|
|
object_property_set_description(cpu_obj, name, desc);
|
|
if (use_def_vals) {
|
|
riscv_cpu_write_misa_bit(RISCV_CPU(cpu_obj), bit,
|
|
misa_cfg->enabled);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cpu_set_profile(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
RISCVCPUProfile *profile = opaque;
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
bool value;
|
|
int i, ext_offset;
|
|
|
|
if (riscv_cpu_is_vendor(obj)) {
|
|
error_setg(errp, "Profile %s is not available for vendor CPUs",
|
|
profile->name);
|
|
return;
|
|
}
|
|
|
|
if (cpu->env.misa_mxl != MXL_RV64) {
|
|
error_setg(errp, "Profile %s only available for 64 bit CPUs",
|
|
profile->name);
|
|
return;
|
|
}
|
|
|
|
if (!visit_type_bool(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
|
|
profile->user_set = true;
|
|
profile->enabled = value;
|
|
|
|
if (profile->parent != NULL) {
|
|
object_property_set_bool(obj, profile->parent->name,
|
|
profile->enabled, NULL);
|
|
}
|
|
|
|
if (profile->enabled) {
|
|
cpu->env.priv_ver = profile->priv_spec;
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) {
|
|
object_property_set_bool(obj, "mmu", true, NULL);
|
|
const char *satp_prop = satp_mode_str(profile->satp_mode,
|
|
riscv_cpu_is_32bit(cpu));
|
|
object_property_set_bool(obj, satp_prop, profile->enabled, NULL);
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; misa_bits[i] != 0; i++) {
|
|
uint32_t bit = misa_bits[i];
|
|
|
|
if (!(profile->misa_ext & bit)) {
|
|
continue;
|
|
}
|
|
|
|
if (bit == RVI && !profile->enabled) {
|
|
/*
|
|
* Disabling profiles will not disable the base
|
|
* ISA RV64I.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
cpu_misa_ext_add_user_opt(bit, profile->enabled);
|
|
riscv_cpu_write_misa_bit(cpu, bit, profile->enabled);
|
|
}
|
|
|
|
for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) {
|
|
ext_offset = profile->ext_offsets[i];
|
|
|
|
if (profile->enabled) {
|
|
if (cpu_cfg_offset_is_named_feat(ext_offset)) {
|
|
riscv_cpu_enable_named_feat(cpu, ext_offset);
|
|
}
|
|
|
|
cpu_bump_multi_ext_priv_ver(&cpu->env, ext_offset);
|
|
}
|
|
|
|
cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled);
|
|
isa_ext_update_enabled(cpu, ext_offset, profile->enabled);
|
|
}
|
|
}
|
|
|
|
static void cpu_get_profile(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
RISCVCPUProfile *profile = opaque;
|
|
bool value = profile->enabled;
|
|
|
|
visit_type_bool(v, name, &value, errp);
|
|
}
|
|
|
|
static void riscv_cpu_add_profiles(Object *cpu_obj)
|
|
{
|
|
for (int i = 0; riscv_profiles[i] != NULL; i++) {
|
|
const RISCVCPUProfile *profile = riscv_profiles[i];
|
|
|
|
object_property_add(cpu_obj, profile->name, "bool",
|
|
cpu_get_profile, cpu_set_profile,
|
|
NULL, (void *)profile);
|
|
|
|
/*
|
|
* CPUs might enable a profile right from the start.
|
|
* Enable its mandatory extensions right away in this
|
|
* case.
|
|
*/
|
|
if (profile->enabled) {
|
|
object_property_set_bool(cpu_obj, profile->name, true, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool cpu_ext_is_deprecated(const char *ext_name)
|
|
{
|
|
return isupper(ext_name[0]);
|
|
}
|
|
|
|
/*
|
|
* String will be allocated in the heap. Caller is responsible
|
|
* for freeing it.
|
|
*/
|
|
static char *cpu_ext_to_lower(const char *ext_name)
|
|
{
|
|
char *ret = g_malloc0(strlen(ext_name) + 1);
|
|
|
|
strcpy(ret, ext_name);
|
|
ret[0] = tolower(ret[0]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque;
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
bool vendor_cpu = riscv_cpu_is_vendor(obj);
|
|
bool prev_val, value;
|
|
|
|
if (!visit_type_bool(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (cpu_ext_is_deprecated(multi_ext_cfg->name)) {
|
|
g_autofree char *lower = cpu_ext_to_lower(multi_ext_cfg->name);
|
|
|
|
warn_report("CPU property '%s' is deprecated. Please use '%s' instead",
|
|
multi_ext_cfg->name, lower);
|
|
}
|
|
|
|
cpu_cfg_ext_add_user_opt(multi_ext_cfg->offset, value);
|
|
|
|
prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset);
|
|
|
|
if (value == prev_val) {
|
|
return;
|
|
}
|
|
|
|
if (value && vendor_cpu) {
|
|
g_autofree char *cpuname = riscv_cpu_get_name(cpu);
|
|
error_setg(errp, "'%s' CPU does not allow enabling extensions",
|
|
cpuname);
|
|
return;
|
|
}
|
|
|
|
if (value) {
|
|
cpu_bump_multi_ext_priv_ver(&cpu->env, multi_ext_cfg->offset);
|
|
}
|
|
|
|
isa_ext_update_enabled(cpu, multi_ext_cfg->offset, value);
|
|
}
|
|
|
|
static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque;
|
|
bool value = isa_ext_is_enabled(RISCV_CPU(obj), multi_ext_cfg->offset);
|
|
|
|
visit_type_bool(v, name, &value, errp);
|
|
}
|
|
|
|
static void cpu_add_multi_ext_prop(Object *cpu_obj,
|
|
const RISCVCPUMultiExtConfig *multi_cfg)
|
|
{
|
|
bool generic_cpu = riscv_cpu_is_generic(cpu_obj);
|
|
bool deprecated_ext = cpu_ext_is_deprecated(multi_cfg->name);
|
|
|
|
object_property_add(cpu_obj, multi_cfg->name, "bool",
|
|
cpu_get_multi_ext_cfg,
|
|
cpu_set_multi_ext_cfg,
|
|
NULL, (void *)multi_cfg);
|
|
|
|
if (!generic_cpu || deprecated_ext) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Set def val directly instead of using
|
|
* object_property_set_bool() to save the set()
|
|
* callback hash for user inputs.
|
|
*/
|
|
isa_ext_update_enabled(RISCV_CPU(cpu_obj), multi_cfg->offset,
|
|
multi_cfg->enabled);
|
|
}
|
|
|
|
static void riscv_cpu_add_multiext_prop_array(Object *obj,
|
|
const RISCVCPUMultiExtConfig *array)
|
|
{
|
|
const RISCVCPUMultiExtConfig *prop;
|
|
|
|
g_assert(array);
|
|
|
|
for (prop = array; prop && prop->name; prop++) {
|
|
cpu_add_multi_ext_prop(obj, prop);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add CPU properties with user-facing flags.
|
|
*
|
|
* This will overwrite existing env->misa_ext values with the
|
|
* defaults set via riscv_cpu_add_misa_properties().
|
|
*/
|
|
static void riscv_cpu_add_user_properties(Object *obj)
|
|
{
|
|
#ifndef CONFIG_USER_ONLY
|
|
riscv_add_satp_mode_properties(obj);
|
|
#endif
|
|
|
|
riscv_cpu_add_misa_properties(obj);
|
|
|
|
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_extensions);
|
|
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts);
|
|
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts);
|
|
|
|
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_deprecated_exts);
|
|
|
|
riscv_cpu_add_profiles(obj);
|
|
}
|
|
|
|
/*
|
|
* The 'max' type CPU will have all possible ratified
|
|
* non-vendor extensions enabled.
|
|
*/
|
|
static void riscv_init_max_cpu_extensions(Object *obj)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
CPURISCVState *env = &cpu->env;
|
|
const RISCVCPUMultiExtConfig *prop;
|
|
|
|
/* Enable RVG, RVJ and RVV that are disabled by default */
|
|
riscv_cpu_set_misa_ext(env, env->misa_ext | RVB | RVG | RVJ | RVV);
|
|
|
|
for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
|
|
isa_ext_update_enabled(cpu, prop->offset, true);
|
|
}
|
|
|
|
/*
|
|
* Some extensions can't be added without backward compatibilty concerns.
|
|
* Disable those, the user can still opt in to them on the command line.
|
|
*/
|
|
cpu->cfg.ext_svade = false;
|
|
|
|
/* set vector version */
|
|
env->vext_ver = VEXT_VERSION_1_00_0;
|
|
|
|
/* Zfinx is not compatible with F. Disable it */
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zfinx), false);
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zdinx), false);
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinx), false);
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinxmin), false);
|
|
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zce), false);
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmp), false);
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmt), false);
|
|
|
|
if (env->misa_mxl != MXL_RV32) {
|
|
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false);
|
|
}
|
|
}
|
|
|
|
static bool riscv_cpu_has_max_extensions(Object *cpu_obj)
|
|
{
|
|
return object_dynamic_cast(cpu_obj, TYPE_RISCV_CPU_MAX) != NULL;
|
|
}
|
|
|
|
static void riscv_tcg_cpu_instance_init(CPUState *cs)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
Object *obj = OBJECT(cpu);
|
|
|
|
misa_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
|
|
multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
|
|
|
|
if (!misa_ext_implied_rules) {
|
|
misa_ext_implied_rules = g_hash_table_new(NULL, g_direct_equal);
|
|
}
|
|
|
|
if (!multi_ext_implied_rules) {
|
|
multi_ext_implied_rules = g_hash_table_new(NULL, g_direct_equal);
|
|
}
|
|
|
|
riscv_cpu_add_user_properties(obj);
|
|
|
|
if (riscv_cpu_has_max_extensions(obj)) {
|
|
riscv_init_max_cpu_extensions(obj);
|
|
}
|
|
}
|
|
|
|
static void riscv_tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc)
|
|
{
|
|
/*
|
|
* All cpus use the same set of operations.
|
|
*/
|
|
cc->tcg_ops = &riscv_tcg_ops;
|
|
}
|
|
|
|
static void riscv_tcg_cpu_class_init(CPUClass *cc)
|
|
{
|
|
cc->init_accel_cpu = riscv_tcg_cpu_init_ops;
|
|
}
|
|
|
|
static void riscv_tcg_cpu_accel_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
|
|
|
|
acc->cpu_class_init = riscv_tcg_cpu_class_init;
|
|
acc->cpu_instance_init = riscv_tcg_cpu_instance_init;
|
|
acc->cpu_target_realize = riscv_tcg_cpu_realize;
|
|
}
|
|
|
|
static const TypeInfo riscv_tcg_cpu_accel_type_info = {
|
|
.name = ACCEL_CPU_NAME("tcg"),
|
|
|
|
.parent = TYPE_ACCEL_CPU,
|
|
.class_init = riscv_tcg_cpu_accel_class_init,
|
|
.abstract = true,
|
|
};
|
|
|
|
static void riscv_tcg_cpu_accel_register_types(void)
|
|
{
|
|
type_register_static(&riscv_tcg_cpu_accel_type_info);
|
|
}
|
|
type_init(riscv_tcg_cpu_accel_register_types);
|