diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/riscv/cpu.c | 384 | ||||
-rw-r--r-- | target/riscv/cpu.h | 117 | ||||
-rw-r--r-- | target/riscv/cpu_cfg.h | 136 | ||||
-rw-r--r-- | target/riscv/cpu_helper.c | 37 | ||||
-rw-r--r-- | target/riscv/csr.c | 75 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_privileged.c.inc | 2 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_rvd.c.inc | 12 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_rvf.c.inc | 21 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_rvi.c.inc | 46 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_rvv.c.inc | 4 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_rvzawrs.c.inc | 2 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_rvzce.c.inc | 10 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_xthead.c.inc | 2 | ||||
-rw-r--r-- | target/riscv/pmp.c | 205 | ||||
-rw-r--r-- | target/riscv/pmp.h | 11 | ||||
-rw-r--r-- | target/riscv/translate.c | 99 | ||||
-rw-r--r-- | target/riscv/vector_helper.c | 33 |
17 files changed, 698 insertions, 498 deletions
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index db0875fb43..881bddf393 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -119,6 +119,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), + ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), @@ -247,16 +248,6 @@ static void set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext) env->misa_ext_mask = env->misa_ext = ext; } -static void set_priv_version(CPURISCVState *env, int priv_ver) -{ - env->priv_ver = priv_ver; -} - -static void set_vext_version(CPURISCVState *env, int vext_ver) -{ - env->vext_ver = vext_ver; -} - #ifndef CONFIG_USER_ONLY static uint8_t satp_mode_from_str(const char *satp_mode_str) { @@ -342,7 +333,8 @@ static void set_satp_mode_default_map(RISCVCPU *cpu) static void riscv_any_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; #if defined(TARGET_RISCV32) set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVU); #elif defined(TARGET_RISCV64) @@ -355,7 +347,13 @@ static void riscv_any_cpu_init(Object *obj) VM_1_10_SV32 : VM_1_10_SV57); #endif - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_LATEST; + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } #if defined(TARGET_RISCV64) @@ -366,7 +364,7 @@ static void rv64_base_cpu_init(Object *obj) set_misa(env, MXL_RV64, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_LATEST; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif @@ -374,12 +372,19 @@ static void rv64_base_cpu_init(Object *obj) static void rv64_sifive_u_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } static void rv64_sifive_e_cpu_init(Object *obj) @@ -388,11 +393,15 @@ static void rv64_sifive_e_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - cpu->cfg.mmu = false; + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } static void rv64_thead_c906_cpu_init(Object *obj) @@ -401,7 +410,7 @@ static void rv64_thead_c906_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_11_0); + env->priv_ver = PRIV_VERSION_1_11_0; cpu->cfg.ext_zfh = true; cpu->cfg.mmu = true; @@ -420,6 +429,9 @@ static void rv64_thead_c906_cpu_init(Object *obj) #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_SV39); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.pmp = true; } static void rv64_veyron_v1_cpu_init(Object *obj) @@ -472,7 +484,7 @@ static void rv128_base_cpu_init(Object *obj) set_misa(env, MXL_RV128, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_LATEST; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif @@ -485,7 +497,7 @@ static void rv32_base_cpu_init(Object *obj) set_misa(env, MXL_RV32, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_LATEST; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); #endif @@ -493,12 +505,19 @@ static void rv32_base_cpu_init(Object *obj) static void rv32_sifive_u_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } static void rv32_sifive_e_cpu_init(Object *obj) @@ -507,11 +526,15 @@ static void rv32_sifive_e_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - cpu->cfg.mmu = false; + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } static void rv32_ibex_cpu_init(Object *obj) @@ -520,12 +543,16 @@ static void rv32_ibex_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_11_0); - cpu->cfg.mmu = false; + env->priv_ver = PRIV_VERSION_1_11_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif cpu->cfg.epmp = true; + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } static void rv32_imafcu_nommu_cpu_init(Object *obj) @@ -534,11 +561,15 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - cpu->cfg.mmu = false; + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } #endif @@ -690,16 +721,18 @@ static vaddr riscv_cpu_get_pc(CPUState *cs) static void riscv_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); + 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(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - if (xl == MXL_RV32) { - env->pc = (int32_t) tb->pc; - } else { - env->pc = tb->pc; + if (xl == MXL_RV32) { + env->pc = (int32_t) tb->pc; + } else { + env->pc = tb->pc; + } } } @@ -725,11 +758,18 @@ static void riscv_restore_state_to_opc(CPUState *cs, 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)data[0]; + env->pc = (int32_t)pc; } else { - env->pc = data[0]; + env->pc = pc; } env->bins = data[1]; } @@ -818,6 +858,7 @@ static void riscv_cpu_reset_hold(Object *obj) static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) { RISCVCPU *cpu = RISCV_CPU(s); + info->target_info = &cpu->cfg; switch (riscv_cpu_mxl(&cpu->env)) { case MXL_RV32: @@ -834,13 +875,127 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) } } +static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, + Error **errp) +{ + int vext_version = VEXT_VERSION_1_00_0; + + if (!is_power_of_2(cfg->vlen)) { + error_setg(errp, "Vector extension VLEN must be power of 2"); + return; + } + if (cfg->vlen > RV_VLEN_MAX || 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(cfg->elen)) { + error_setg(errp, "Vector extension ELEN must be power of 2"); + return; + } + if (cfg->elen > 64 || cfg->elen < 8) { + error_setg(errp, + "Vector extension implementation only supports ELEN " + "in the range [8, 64]"); + return; + } + if (cfg->vext_spec) { + if (!g_strcmp0(cfg->vext_spec, "v1.0")) { + vext_version = VEXT_VERSION_1_00_0; + } else { + error_setg(errp, "Unsupported vector spec version '%s'", + cfg->vext_spec); + return; + } + } else { + qemu_log("vector version is not specified, " + "use the default value v1.0\n"); + } + env->vext_ver = vext_version; +} + +static void riscv_cpu_validate_priv_spec(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + int priv_version = -1; + + if (cpu->cfg.priv_spec) { + if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { + priv_version = PRIV_VERSION_1_12_0; + } else 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")) { + priv_version = PRIV_VERSION_1_10_0; + } else { + error_setg(errp, + "Unsupported privilege spec version '%s'", + cpu->cfg.priv_spec); + return; + } + + env->priv_ver = priv_version; + } +} + +static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) +{ + CPURISCVState *env = &cpu->env; + int i; + + /* Force disable extensions if priv spec version does not match */ + for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { + if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) && + (env->priv_ver < isa_edata_arr[i].min_version)) { + isa_ext_update_enabled(cpu, &isa_edata_arr[i], false); +#ifndef CONFIG_USER_ONLY + warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx + " because privilege spec version does not match", + isa_edata_arr[i].name, env->mhartid); +#else + warn_report("disabling %s extension because " + "privilege spec version does not match", + isa_edata_arr[i].name); +#endif + } + } +} + +static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + CPUClass *cc = CPU_CLASS(mcc); + CPURISCVState *env = &cpu->env; + + /* Validate that MISA_MXL is set properly. */ + switch (env->misa_mxl_max) { +#ifdef TARGET_RISCV64 + case MXL_RV64: + case MXL_RV128: + cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; + break; +#endif + case MXL_RV32: + cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; + break; + default: + g_assert_not_reached(); + } + + if (env->misa_mxl_max != env->misa_mxl) { + error_setg(errp, "misa_mxl_max must be equal to misa_mxl"); + return; + } +} + /* * Check consistency between chosen extensions while setting * cpu->cfg accordingly. */ -static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) +void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) { CPURISCVState *env = &cpu->env; + Error *local_err = NULL; /* Do some ISA extension error checking */ if (riscv_has_ext(env, RVG) && @@ -853,7 +1008,7 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_ifencei = true; env->misa_ext |= RVI | RVM | RVA | RVF | RVD; - env->misa_ext_mask = env->misa_ext; + env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD; } if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) { @@ -909,8 +1064,14 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - /* The V vector extension depends on the Zve64d extension */ 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 V vector extension depends on the Zve64d extension */ cpu->cfg.ext_zve64d = true; } @@ -1046,45 +1207,11 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_zksh = true; } - if (riscv_has_ext(env, RVV)) { - int vext_version = VEXT_VERSION_1_00_0; - 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.elen < 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, "v1.0")) { - vext_version = VEXT_VERSION_1_00_0; - } else { - error_setg(errp, - "Unsupported vector spec version '%s'", - cpu->cfg.vext_spec); - return; - } - } else { - qemu_log("vector version is not specified, " - "use the default value v1.0\n"); - } - set_vext_version(env, vext_version); - } + /* + * 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 @@ -1183,8 +1310,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) RISCVCPU *cpu = RISCV_CPU(dev); CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); - CPUClass *cc = CPU_CLASS(mcc); - int i, priv_version = -1; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -1193,23 +1318,16 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } - if (cpu->cfg.priv_spec) { - if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { - priv_version = PRIV_VERSION_1_12_0; - } else 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")) { - priv_version = PRIV_VERSION_1_10_0; - } else { - error_setg(errp, - "Unsupported privilege spec version '%s'", - cpu->cfg.priv_spec); - return; - } + riscv_cpu_validate_misa_mxl(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; } - if (priv_version >= PRIV_VERSION_1_10_0) { - set_priv_version(env, priv_version); + riscv_cpu_validate_priv_spec(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; } riscv_cpu_validate_misa_priv(env, &local_err); @@ -1218,23 +1336,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } - /* Force disable extensions if priv spec version does not match */ - for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) && - (env->priv_ver < isa_edata_arr[i].min_version)) { - isa_ext_update_enabled(cpu, &isa_edata_arr[i], false); -#ifndef CONFIG_USER_ONLY - warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx - " because privilege spec version does not match", - isa_edata_arr[i].name, env->mhartid); -#else - warn_report("disabling %s extension because " - "privilege spec version does not match", - isa_edata_arr[i].name); -#endif - } - } - if (cpu->cfg.epmp && !cpu->cfg.pmp) { /* * Enhanced PMP should only be available @@ -1244,29 +1345,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } - -#ifndef CONFIG_USER_ONLY - if (cpu->cfg.ext_sstc) { - riscv_timer_init(cpu); - } -#endif /* CONFIG_USER_ONLY */ - - /* Validate that MISA_MXL is set properly. */ - switch (env->misa_mxl_max) { -#ifdef TARGET_RISCV64 - case MXL_RV64: - case MXL_RV128: - cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; - break; -#endif - case MXL_RV32: - cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; - break; - default: - g_assert_not_reached(); - } - assert(env->misa_mxl_max == env->misa_mxl); - riscv_cpu_validate_set_extensions(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1274,6 +1352,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } #ifndef CONFIG_USER_ONLY + cs->tcg_cflags |= CF_PCREL; + + if (cpu->cfg.ext_sstc) { + riscv_timer_init(cpu); + } + if (cpu->cfg.pmu_num) { if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, @@ -1410,11 +1494,6 @@ static void riscv_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); - cpu->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - cpu_set_cpustate_pointers(cpu); #ifndef CONFIG_USER_ONLY @@ -1535,8 +1614,8 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), + DEFINE_PROP_BOOL("smstateen", RISCVCPU, cfg.ext_smstateen, false), DEFINE_PROP_BOOL("svadu", RISCVCPU, cfg.ext_svadu, true), - DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false), DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), DEFINE_PROP_BOOL("svpbmt", RISCVCPU, cfg.ext_svpbmt, false), @@ -1571,6 +1650,14 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("zmmul", RISCVCPU, cfg.ext_zmmul, false), + DEFINE_PROP_BOOL("zca", RISCVCPU, cfg.ext_zca, false), + DEFINE_PROP_BOOL("zcb", RISCVCPU, cfg.ext_zcb, false), + DEFINE_PROP_BOOL("zcd", RISCVCPU, cfg.ext_zcd, false), + DEFINE_PROP_BOOL("zce", RISCVCPU, cfg.ext_zce, false), + DEFINE_PROP_BOOL("zcf", RISCVCPU, cfg.ext_zcf, false), + DEFINE_PROP_BOOL("zcmp", RISCVCPU, cfg.ext_zcmp, false), + DEFINE_PROP_BOOL("zcmt", RISCVCPU, cfg.ext_zcmt, false), + /* Vendor-specific custom extensions */ DEFINE_PROP_BOOL("xtheadba", RISCVCPU, cfg.ext_xtheadba, false), DEFINE_PROP_BOOL("xtheadbb", RISCVCPU, cfg.ext_xtheadbb, false), @@ -1588,14 +1675,6 @@ static Property riscv_cpu_extensions[] = { /* These are experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-zicond", RISCVCPU, cfg.ext_zicond, false), - DEFINE_PROP_BOOL("x-zca", RISCVCPU, cfg.ext_zca, false), - DEFINE_PROP_BOOL("x-zcb", RISCVCPU, cfg.ext_zcb, false), - DEFINE_PROP_BOOL("x-zcd", RISCVCPU, cfg.ext_zcd, false), - DEFINE_PROP_BOOL("x-zce", RISCVCPU, cfg.ext_zce, false), - DEFINE_PROP_BOOL("x-zcf", RISCVCPU, cfg.ext_zcf, false), - DEFINE_PROP_BOOL("x-zcmp", RISCVCPU, cfg.ext_zcmp, false), - DEFINE_PROP_BOOL("x-zcmt", RISCVCPU, cfg.ext_zcmt, false), - /* ePMP 0.9.3 */ DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), DEFINE_PROP_BOOL("x-smaia", RISCVCPU, cfg.ext_smaia, false), @@ -1761,7 +1840,8 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int i; for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { + if (cpu->env.priv_ver >= isa_edata_arr[i].min_version && + isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL); g_free(old); old = new; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index de7e43126a..e3e08d315f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -27,6 +27,7 @@ #include "qom/object.h" #include "qemu/int128.h" #include "cpu_bits.h" +#include "cpu_cfg.h" #include "qapi/qapi-types-common.h" #include "cpu-qom.h" @@ -61,6 +62,8 @@ enum { PRIV_VERSION_1_10_0 = 0, PRIV_VERSION_1_11_0, PRIV_VERSION_1_12_0, + + PRIV_VERSION_LATEST = PRIV_VERSION_1_12_0, }; #define VEXT_VERSION_1_00_0 0x00010000 @@ -369,119 +372,6 @@ struct CPUArchState { }; /* - * map is a 16-bit bitmap: the most significant set bit in map is the maximum - * satp mode that is supported. It may be chosen by the user and must respect - * what qemu implements (valid_1_10_32/64) and what the hw is capable of - * (supported bitmap below). - * - * init is a 16-bit bitmap used to make sure the user selected a correct - * configuration as per the specification. - * - * supported is a 16-bit bitmap used to reflect the hw capabilities. - */ -typedef struct { - uint16_t map, init, supported; -} RISCVSATPMap; - -struct RISCVCPUConfig { - bool ext_zba; - bool ext_zbb; - bool ext_zbc; - bool ext_zbkb; - bool ext_zbkc; - bool ext_zbkx; - bool ext_zbs; - bool ext_zca; - bool ext_zcb; - bool ext_zcd; - bool ext_zce; - bool ext_zcf; - bool ext_zcmp; - bool ext_zcmt; - bool ext_zk; - bool ext_zkn; - bool ext_zknd; - bool ext_zkne; - bool ext_zknh; - bool ext_zkr; - bool ext_zks; - bool ext_zksed; - bool ext_zksh; - bool ext_zkt; - bool ext_ifencei; - bool ext_icsr; - bool ext_icbom; - bool ext_icboz; - bool ext_zicond; - bool ext_zihintpause; - bool ext_smstateen; - bool ext_sstc; - bool ext_svadu; - bool ext_svinval; - bool ext_svnapot; - bool ext_svpbmt; - bool ext_zdinx; - bool ext_zawrs; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zfinx; - bool ext_zhinx; - bool ext_zhinxmin; - bool ext_zve32f; - bool ext_zve64f; - bool ext_zve64d; - bool ext_zmmul; - bool ext_zvfh; - bool ext_zvfhmin; - bool ext_smaia; - bool ext_ssaia; - bool ext_sscofpmf; - bool rvv_ta_all_1s; - bool rvv_ma_all_1s; - - uint32_t mvendorid; - uint64_t marchid; - uint64_t mimpid; - - /* Vendor-specific custom extensions */ - bool ext_xtheadba; - bool ext_xtheadbb; - bool ext_xtheadbs; - bool ext_xtheadcmo; - bool ext_xtheadcondmov; - bool ext_xtheadfmemidx; - bool ext_xtheadfmv; - bool ext_xtheadmac; - bool ext_xtheadmemidx; - bool ext_xtheadmempair; - bool ext_xtheadsync; - bool ext_XVentanaCondOps; - - uint8_t pmu_num; - char *priv_spec; - char *user_spec; - char *bext_spec; - char *vext_spec; - uint16_t vlen; - uint16_t elen; - uint16_t cbom_blocksize; - uint16_t cboz_blocksize; - bool mmu; - bool pmp; - bool epmp; - bool debug; - bool misa_w; - - bool short_isa_string; - -#ifndef CONFIG_USER_ONLY - RISCVSATPMap satp_mode; -#endif -}; - -typedef struct RISCVCPUConfig RISCVCPUConfig; - -/* * RISCVCPU: * @env: #CPURISCVState * @@ -546,6 +436,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool probe, uintptr_t retaddr); char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(void); +void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h new file mode 100644 index 0000000000..c4a627d335 --- /dev/null +++ b/target/riscv/cpu_cfg.h @@ -0,0 +1,136 @@ +/* + * QEMU RISC-V CPU CFG + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * Copyright (c) 2021-2023 PLCT Lab + * + * 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/>. + */ + +#ifndef RISCV_CPU_CFG_H +#define RISCV_CPU_CFG_H + +/* + * map is a 16-bit bitmap: the most significant set bit in map is the maximum + * satp mode that is supported. It may be chosen by the user and must respect + * what qemu implements (valid_1_10_32/64) and what the hw is capable of + * (supported bitmap below). + * + * init is a 16-bit bitmap used to make sure the user selected a correct + * configuration as per the specification. + * + * supported is a 16-bit bitmap used to reflect the hw capabilities. + */ +typedef struct { + uint16_t map, init, supported; +} RISCVSATPMap; + +struct RISCVCPUConfig { + bool ext_zba; + bool ext_zbb; + bool ext_zbc; + bool ext_zbkb; + bool ext_zbkc; + bool ext_zbkx; + bool ext_zbs; + bool ext_zca; + bool ext_zcb; + bool ext_zcd; + bool ext_zce; + bool ext_zcf; + bool ext_zcmp; + bool ext_zcmt; + bool ext_zk; + bool ext_zkn; + bool ext_zknd; + bool ext_zkne; + bool ext_zknh; + bool ext_zkr; + bool ext_zks; + bool ext_zksed; + bool ext_zksh; + bool ext_zkt; + bool ext_ifencei; + bool ext_icsr; + bool ext_icbom; + bool ext_icboz; + bool ext_zicond; + bool ext_zihintpause; + bool ext_smstateen; + bool ext_sstc; + bool ext_svadu; + bool ext_svinval; + bool ext_svnapot; + bool ext_svpbmt; + bool ext_zdinx; + bool ext_zawrs; + bool ext_zfh; + bool ext_zfhmin; + bool ext_zfinx; + bool ext_zhinx; + bool ext_zhinxmin; + bool ext_zve32f; + bool ext_zve64f; + bool ext_zve64d; + bool ext_zmmul; + bool ext_zvfh; + bool ext_zvfhmin; + bool ext_smaia; + bool ext_ssaia; + bool ext_sscofpmf; + bool rvv_ta_all_1s; + bool rvv_ma_all_1s; + + uint32_t mvendorid; + uint64_t marchid; + uint64_t mimpid; + + /* Vendor-specific custom extensions */ + bool ext_xtheadba; + bool ext_xtheadbb; + bool ext_xtheadbs; + bool ext_xtheadcmo; + bool ext_xtheadcondmov; + bool ext_xtheadfmemidx; + bool ext_xtheadfmv; + bool ext_xtheadmac; + bool ext_xtheadmemidx; + bool ext_xtheadmempair; + bool ext_xtheadsync; + bool ext_XVentanaCondOps; + + uint8_t pmu_num; + char *priv_spec; + char *user_spec; + char *bext_spec; + char *vext_spec; + uint16_t vlen; + uint16_t elen; + uint16_t cbom_blocksize; + uint16_t cboz_blocksize; + bool mmu; + bool pmp; + bool epmp; + bool debug; + bool misa_w; + + bool short_isa_string; + +#ifndef CONFIG_USER_ONLY + RISCVSATPMap satp_mode; +#endif +}; + +typedef struct RISCVCPUConfig RISCVCPUConfig; +#endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 56381aaf26..90cef9856d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -120,6 +120,12 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, vs = MIN(vs, get_field(env->mstatus_hs, MSTATUS_VS)); } + /* With Zfinx, floating point is enabled/disabled by Smstateen. */ + if (!riscv_has_ext(env, RVF)) { + fs = (smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR) == RISCV_EXCP_NONE) + ? EXT_STATUS_DIRTY : EXT_STATUS_DISABLED; + } + if (cpu->cfg.debug && !icount_enabled()) { flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER, env->itrigger_enabled); } @@ -128,7 +134,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, flags = FIELD_DP32(flags, TB_FLAGS, FS, fs); flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); - if (env->cur_pmmask < (env->xl == MXL_RV32 ? UINT32_MAX : UINT64_MAX)) { + if (env->cur_pmmask != 0) { flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1); } if (env->cur_pmbase != 0) { @@ -140,7 +146,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, void riscv_cpu_update_mask(CPURISCVState *env) { - target_ulong mask = -1, base = 0; + target_ulong mask = 0, base = 0; /* * TODO: Current RVJ spec does not specify * how the extension interacts with XLEN. @@ -688,39 +694,30 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * * @env: CPURISCVState * @prot: The returned protection attributes - * @tlb_size: TLB page size containing addr. It could be modified after PMP - * permission checking. NULL if not set TLB page for addr. * @addr: The physical address to be checked permission * @access_type: The type of MMU access * @mode: Indicates current privilege level. */ -static int get_physical_address_pmp(CPURISCVState *env, int *prot, - target_ulong *tlb_size, hwaddr addr, +static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, int size, MMUAccessType access_type, int mode) { pmp_priv_t pmp_priv; - int pmp_index = -1; + bool pmp_has_privs; if (!riscv_cpu_cfg(env)->pmp) { *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TRANSLATE_SUCCESS; } - pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type, - &pmp_priv, mode); - if (pmp_index < 0) { + pmp_has_privs = pmp_hart_has_privs(env, addr, size, 1 << access_type, + &pmp_priv, mode); + if (!pmp_has_privs) { *prot = 0; return TRANSLATE_PMP_FAIL; } *prot = pmp_priv_to_page_prot(pmp_priv); - if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) { - target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); - target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; - - *tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea); - } return TRANSLATE_SUCCESS; } @@ -909,7 +906,7 @@ restart: } int pmp_prot; - int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr, + int pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, sizeof(target_ulong), MMU_DATA_LOAD, PRV_S); if (pmp_ret != TRANSLATE_SUCCESS) { @@ -1305,8 +1302,9 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, prot &= prot2; if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + ret = get_physical_address_pmp(env, &prot_pmp, pa, size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" @@ -1338,8 +1336,9 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, __func__, address, ret, pa, prot); if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + ret = get_physical_address_pmp(env, &prot_pmp, pa, size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4451bd1263..58499b5afc 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -82,6 +82,10 @@ static RISCVException fs(CPURISCVState *env, int csrno) !riscv_cpu_cfg(env)->ext_zfinx) { return RISCV_EXCP_ILLEGAL_INST; } + + if (!env->debugger && !riscv_cpu_fp_enabled(env)) { + return smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR); + } #endif return RISCV_EXCP_NONE; } @@ -1324,8 +1328,15 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mstatus = set_field(mstatus, MSTATUS64_SXL, xl); } env->mstatus = mstatus; - env->xl = cpu_recompute_xl(env); + /* + * Except in debug mode, UXL/SXL can only be modified by higher + * privilege mode. So xl will not be changed in normal mode. + */ + if (env->debugger) { + env->xl = cpu_recompute_xl(env); + riscv_cpu_update_mask(env); + } return RISCV_EXCP_NONE; } @@ -1387,39 +1398,18 @@ static RISCVException read_misa(CPURISCVState *env, int csrno, static RISCVException write_misa(CPURISCVState *env, int csrno, target_ulong val) { + RISCVCPU *cpu = env_archcpu(env); + uint32_t orig_misa_ext = env->misa_ext; + Error *local_err = NULL; + if (!riscv_cpu_cfg(env)->misa_w) { /* drop write to misa */ return RISCV_EXCP_NONE; } - /* 'I' or 'E' must be present */ - if (!(val & (RVI | RVE))) { - /* It is not, drop write to misa */ - return RISCV_EXCP_NONE; - } - - /* 'E' excludes all other extensions */ - if (val & RVE) { - /* - * when we support 'E' we can do "val = RVE;" however - * for now we just drop writes if 'E' is present. - */ - return RISCV_EXCP_NONE; - } - - /* - * misa.MXL writes are not supported by QEMU. - * Drop writes to those bits. - */ - /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* 'D' depends on 'F', so clear 'D' if 'F' is not present */ - if ((val & RVD) && !(val & RVF)) { - val &= ~RVD; - } - /* * Suppress 'C' if next instruction is not aligned * TODO: this should check next_pc @@ -1428,18 +1418,36 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, val &= ~RVC; } + /* Disable RVG if any of its dependencies are disabled */ + if (!(val & RVI && val & RVM && val & RVA && + val & RVF && val & RVD)) { + val &= ~RVG; + } + /* If nothing changed, do nothing. */ if (val == env->misa_ext) { return RISCV_EXCP_NONE; } - if (!(val & RVF)) { + env->misa_ext = val; + riscv_cpu_validate_set_extensions(cpu, &local_err); + if (local_err != NULL) { + /* Rollback on validation error */ + qemu_log_mask(LOG_GUEST_ERROR, "Unable to write MISA ext value " + "0x%x, keeping existing MISA ext 0x%x\n", + env->misa_ext, orig_misa_ext); + + env->misa_ext = orig_misa_ext; + + return RISCV_EXCP_NONE; + } + + if (!(env->misa_ext & RVF)) { env->mstatus &= ~MSTATUS_FS; } /* flush translation cache */ tb_flush(env_cpu(env)); - env->misa_ext = val; env->xl = riscv_cpu_mxl(env); return RISCV_EXCP_NONE; } @@ -2100,6 +2108,9 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, target_ulong new_val) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } return write_mstateen(env, csrno, wr_mask, new_val); } @@ -2173,6 +2184,10 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + return write_hstateen(env, csrno, wr_mask, new_val); } @@ -2259,6 +2274,10 @@ static RISCVException write_sstateen0(CPURISCVState *env, int csrno, { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + return write_sstateen(env, csrno, wr_mask, new_val); } diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 528baa1652..dc14d7fc7a 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -108,7 +108,7 @@ static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY decode_save_opc(ctx); - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); gen_helper_wfi(cpu_env); return true; #else diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 2c51e01c40..6bdb55ef43 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -31,9 +31,11 @@ } \ } while (0) -#define REQUIRE_ZCD(ctx) do { \ - if (!ctx->cfg_ptr->ext_zcd) { \ - return false; \ +#define REQUIRE_ZCD_OR_DC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcd) { \ + if (!has_ext(ctx, RVD) || !has_ext(ctx, RVC)) { \ + return false; \ + } \ } \ } while (0) @@ -67,13 +69,13 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) static bool trans_c_fld(DisasContext *ctx, arg_fld *a) { - REQUIRE_ZCD(ctx); + REQUIRE_ZCD_OR_DC(ctx); return trans_fld(ctx, a); } static bool trans_c_fsd(DisasContext *ctx, arg_fsd *a) { - REQUIRE_ZCD(ctx); + REQUIRE_ZCD_OR_DC(ctx); return trans_fsd(ctx, a); } diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index b2de4fcf3f..a0da7391c7 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -19,9 +19,10 @@ */ #define REQUIRE_FPU do {\ - if (ctx->mstatus_fs == EXT_STATUS_DISABLED) \ - if (!ctx->cfg_ptr->ext_zfinx) \ - return false; \ + if (ctx->mstatus_fs == EXT_STATUS_DISABLED) { \ + ctx->virt_inst_excp = ctx->virt_enabled && ctx->cfg_ptr->ext_zfinx; \ + return false; \ + } \ } while (0) #define REQUIRE_ZFINX_OR_F(ctx) do {\ @@ -30,10 +31,12 @@ } \ } while (0) -#define REQUIRE_ZCF(ctx) do { \ - if (!ctx->cfg_ptr->ext_zcf) { \ - return false; \ - } \ +#define REQUIRE_ZCF_OR_FC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcf) { \ + if (!has_ext(ctx, RVF) || !has_ext(ctx, RVC)) { \ + return false; \ + } \ + } \ } while (0) static bool trans_flw(DisasContext *ctx, arg_flw *a) @@ -69,13 +72,13 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) static bool trans_c_flw(DisasContext *ctx, arg_flw *a) { - REQUIRE_ZCF(ctx); + REQUIRE_ZCF_OR_FC(ctx); return trans_flw(ctx, a); } static bool trans_c_fsw(DisasContext *ctx, arg_fsw *a) { - REQUIRE_ZCF(ctx); + REQUIRE_ZCF_OR_FC(ctx); return trans_fsw(ctx, a); } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 2031e9931e..297142208e 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -38,7 +38,9 @@ static bool trans_lui(DisasContext *ctx, arg_lui *a) static bool trans_auipc(DisasContext *ctx, arg_auipc *a) { - gen_set_gpri(ctx, a->rd, a->imm + ctx->base.pc_next); + TCGv target_pc = dest_gpr(ctx, a->rd); + gen_pc_plus_diff(target_pc, ctx, a->imm); + gen_set_gpr(ctx, a->rd, target_pc); return true; } @@ -51,25 +53,33 @@ static bool trans_jal(DisasContext *ctx, arg_jal *a) static bool trans_jalr(DisasContext *ctx, arg_jalr *a) { TCGLabel *misaligned = NULL; + TCGv target_pc = tcg_temp_new(); + TCGv succ_pc = dest_gpr(ctx, a->rd); - tcg_gen_addi_tl(cpu_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); - tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); + tcg_gen_addi_tl(target_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); + tcg_gen_andi_tl(target_pc, target_pc, (target_ulong)-2); - gen_set_pc(ctx, cpu_pc); - if (!ctx->cfg_ptr->ext_zca) { + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target_pc, target_pc); + } + + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { TCGv t0 = tcg_temp_new(); misaligned = gen_new_label(); - tcg_gen_andi_tl(t0, cpu_pc, 0x2); + tcg_gen_andi_tl(t0, target_pc, 0x2); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); } - gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, a->rd, succ_pc); + + tcg_gen_mov_tl(cpu_pc, target_pc); lookup_and_goto_ptr(ctx); if (misaligned) { gen_set_label(misaligned); - gen_exception_inst_addr_mis(ctx); + gen_exception_inst_addr_mis(ctx, target_pc); } ctx->base.is_jmp = DISAS_NORETURN; @@ -153,6 +163,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) TCGLabel *l = gen_new_label(); TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN); TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN); + target_ulong orig_pc_save = ctx->pc_save; if (get_xl(ctx) == MXL_RV128) { TCGv src1h = get_gprh(ctx, a->rs1); @@ -165,16 +176,21 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) } else { tcg_gen_brcond_tl(cond, src1, src2, l); } - gen_goto_tb(ctx, 1, ctx->pc_succ_insn); + gen_goto_tb(ctx, 1, ctx->cur_insn_len); + ctx->pc_save = orig_pc_save; gen_set_label(l); /* branch taken */ - if (!ctx->cfg_ptr->ext_zca && ((ctx->base.pc_next + a->imm) & 0x3)) { + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca && + (a->imm & 0x3)) { /* misaligned */ - gen_exception_inst_addr_mis(ctx); + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, a->imm); + gen_exception_inst_addr_mis(ctx, target_pc); } else { - gen_goto_tb(ctx, 0, ctx->base.pc_next + a->imm); + gen_goto_tb(ctx, 0, a->imm); } + ctx->pc_save = -1; ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -767,7 +783,7 @@ static bool trans_pause(DisasContext *ctx, arg_pause *a) * PAUSE is a no-op in QEMU, * end the TB and return to main loop */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; @@ -791,7 +807,7 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) * FENCE_I is a no-op in QEMU, * however we need to end the translation block */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -802,7 +818,7 @@ static bool do_csr_post(DisasContext *ctx) /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ decode_save_opc(ctx); /* We may have changed important cpu state -- exit to main loop. */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 6c07eebc52..c2f7527f53 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -169,7 +169,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) gen_set_gpr(s, rd, dst); mark_vs_dirty(s); - gen_set_pc_imm(s, s->pc_succ_insn); + gen_update_pc(s, s->cur_insn_len); lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; return true; @@ -188,7 +188,7 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) gen_helper_vsetvl(dst, cpu_env, s1, s2); gen_set_gpr(s, rd, dst); mark_vs_dirty(s); - gen_set_pc_imm(s, s->pc_succ_insn); + gen_update_pc(s, s->cur_insn_len); lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/insn_trans/trans_rvzawrs.c.inc b/target/riscv/insn_trans/trans_rvzawrs.c.inc index 8254e7dfe2..32efbff4d5 100644 --- a/target/riscv/insn_trans/trans_rvzawrs.c.inc +++ b/target/riscv/insn_trans/trans_rvzawrs.c.inc @@ -33,7 +33,7 @@ static bool trans_wrs(DisasContext *ctx) /* Clear the load reservation (if any). */ tcg_gen_movi_tl(load_res, -1); - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index a727169a4b..8d8a64f493 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -202,8 +202,8 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) } if (ret) { - TCGv ret_addr = get_gpr(ctx, xRA, EXT_NONE); - gen_set_pc(ctx, ret_addr); + TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN); + tcg_gen_mov_tl(cpu_pc, ret_addr); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; } @@ -297,12 +297,14 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) * Update pc to current for the non-unwinding exception * that might come from cpu_ld*_code() in the helper. */ - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + gen_update_pc(ctx, 0); gen_helper_cm_jalt(cpu_pc, cpu_env, tcg_constant_i32(a->index)); /* c.jt vs c.jalt depends on the index. */ if (a->index >= 32) { - gen_set_gpri(ctx, xRA, ctx->pc_succ_insn); + TCGv succ_pc = dest_gpr(ctx, xRA); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, xRA, succ_pc); } tcg_gen_lookup_and_goto_ptr(); diff --git a/target/riscv/insn_trans/trans_xthead.c.inc b/target/riscv/insn_trans/trans_xthead.c.inc index 3e13b1d74d..da093a4cec 100644 --- a/target/riscv/insn_trans/trans_xthead.c.inc +++ b/target/riscv/insn_trans/trans_xthead.c.inc @@ -999,7 +999,7 @@ static void gen_th_sync_local(DisasContext *ctx) * Emulate out-of-order barriers with pipeline flush * by exiting the translation block. */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; } diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 1f5aca42e8..9d8db493e6 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -26,10 +26,9 @@ #include "trace.h" #include "exec/exec-all.h" -static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, +static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, uint8_t val); static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); /* * Accessor method to extract address matching type 'a field' from cfg reg @@ -83,7 +82,7 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) * Accessor to set the cfg reg for a specific PMP/HART * Bounds checks and relevant lock bit. */ -static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) +static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { if (pmp_index < MAX_RISCV_PMPS) { bool locked = true; @@ -119,14 +118,17 @@ static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) if (locked) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); - } else { + } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { env->pmp_state.pmp[pmp_index].cfg_reg = val; - pmp_update_rule(env, pmp_index); + pmp_update_rule_addr(env, pmp_index); + return true; } } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - out of bounds\n"); } + + return false; } static void pmp_decode_napot(target_ulong a, target_ulong *sa, @@ -206,18 +208,6 @@ void pmp_update_rule_nums(CPURISCVState *env) } } -/* - * Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' - * end address values. - * This function is called relatively infrequently whereas the check that - * an address is within a pmp rule is called often, so optimise that one - */ -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) -{ - pmp_update_rule_addr(env, pmp_index); - pmp_update_rule_nums(env); -} - static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) { @@ -236,37 +226,34 @@ static int pmp_is_in_range(CPURISCVState *env, int pmp_index, /* * Check if the address has required RWX privs when no PMP entry is matched. */ -static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, +static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs, pmp_priv_t *allowed_privs, target_ulong mode) { bool ret; - if (riscv_cpu_cfg(env)->epmp) { - if (MSECCFG_MMWP_ISSET(env)) { - /* - * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set - * so we default to deny all, even for M-mode. - */ + if (MSECCFG_MMWP_ISSET(env)) { + /* + * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set + * so we default to deny all, even for M-mode. + */ + *allowed_privs = 0; + return false; + } else if (MSECCFG_MML_ISSET(env)) { + /* + * The Machine Mode Lockdown (mseccfg.MML) bit is set + * so we can only execute code in M-mode with an applicable + * rule. Other modes are disabled. + */ + if (mode == PRV_M && !(privs & PMP_EXEC)) { + ret = true; + *allowed_privs = PMP_READ | PMP_WRITE; + } else { + ret = false; *allowed_privs = 0; - return false; - } else if (MSECCFG_MML_ISSET(env)) { - /* - * The Machine Mode Lockdown (mseccfg.MML) bit is set - * so we can only execute code in M-mode with an applicable - * rule. Other modes are disabled. - */ - if (mode == PRV_M && !(privs & PMP_EXEC)) { - ret = true; - *allowed_privs = PMP_READ | PMP_WRITE; - } else { - ret = false; - *allowed_privs = 0; - } - - return ret; } + + return ret; } if (!riscv_cpu_cfg(env)->pmp || (mode == PRV_M)) { @@ -296,26 +283,21 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, /* * Check if the address has required RWX privs to complete desired operation - * Return PMP rule index if a pmp rule match - * Return MAX_RISCV_PMPS if default match - * Return negtive value if no match + * Return true if a pmp rule match or default match + * Return false if no match */ -int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, - pmp_priv_t *allowed_privs, target_ulong mode) +bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, + target_ulong size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, target_ulong mode) { int i = 0; - int ret = -1; int pmp_size = 0; target_ulong s = 0; target_ulong e = 0; /* Short cut if no rules */ if (0 == pmp_get_num_rules(env)) { - if (pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode)) { - ret = MAX_RISCV_PMPS; - } + return pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } if (size == 0) { @@ -344,8 +326,8 @@ int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, if ((s + e) == 1) { qemu_log_mask(LOG_GUEST_ERROR, "pmp violation - access is partially inside\n"); - ret = -1; - break; + *allowed_privs = 0; + return false; } /* fully inside */ @@ -452,20 +434,12 @@ int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, * defined with PMP must be used. We shouldn't fallback on * finding default privileges. */ - ret = i; - break; + return (privs & *allowed_privs) == privs; } } /* No rule matched */ - if (ret == -1) { - if (pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode)) { - ret = MAX_RISCV_PMPS; - } - } - - return ret; + return pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } /* @@ -477,16 +451,20 @@ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, int i; uint8_t cfg_val; int pmpcfg_nums = 2 << riscv_cpu_mxl(env); + bool modified = false; trace_pmpcfg_csr_write(env->mhartid, reg_index, val); for (i = 0; i < pmpcfg_nums; i++) { cfg_val = (val >> 8 * i) & 0xff; - pmp_write_cfg(env, (reg_index * 4) + i, cfg_val); + modified |= pmp_write_cfg(env, (reg_index * 4) + i, cfg_val); } /* If PMP permission of any addr has been changed, flush TLB pages. */ - tlb_flush(env_cpu(env)); + if (modified) { + pmp_update_rule_nums(env); + tlb_flush(env_cpu(env)); + } } @@ -517,6 +495,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val) { trace_pmpaddr_csr_write(env->mhartid, addr_index, val); + bool is_next_cfg_tor = false; if (addr_index < MAX_RISCV_PMPS) { /* @@ -525,9 +504,9 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, */ if (addr_index + 1 < MAX_RISCV_PMPS) { uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; + is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); - if (pmp_cfg & PMP_LOCK && - PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg)) { + if (pmp_cfg & PMP_LOCK && is_next_cfg_tor) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - pmpcfg + 1 locked\n"); return; @@ -535,8 +514,14 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, } if (!pmp_is_locked(env, addr_index)) { - env->pmp_state.pmp[addr_index].addr_reg = val; - pmp_update_rule(env, addr_index); + if (env->pmp_state.pmp[addr_index].addr_reg != val) { + env->pmp_state.pmp[addr_index].addr_reg = val; + pmp_update_rule_addr(env, addr_index); + if (is_next_cfg_tor) { + pmp_update_rule_addr(env, addr_index + 1); + } + tlb_flush(env_cpu(env)); + } } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - locked\n"); @@ -585,8 +570,15 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) } } - /* Sticky bits */ - val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + if (riscv_cpu_cfg(env)->epmp) { + /* Sticky bits */ + val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) { + tlb_flush(env_cpu(env)); + } + } else { + val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB); + } env->mseccfg = val; } @@ -601,28 +593,67 @@ target_ulong mseccfg_csr_read(CPURISCVState *env) } /* - * Calculate the TLB size if the start address or the end address of - * PMP entry is presented in the TLB page. + * Calculate the TLB size. + * It's possible that PMP regions only cover partial of the TLB page, and + * this may split the page into regions with different permissions. + * For example if PMP0 is (0x80000008~0x8000000F, R) and PMP1 is (0x80000000 + * ~0x80000FFF, RWX), then region 0x80000008~0x8000000F has R permission, and + * the other regions in this page have RWX permissions. + * A write access to 0x80000000 will match PMP1. However we cannot cache the + * translation result in the TLB since this will make the write access to + * 0x80000008 bypass the check of PMP0. + * To avoid this we return a size of 1 (which means no caching) if the PMP + * region only covers partial of the TLB page. */ -target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, - target_ulong tlb_sa, target_ulong tlb_ea) +target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr) { - target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa; - target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea; + target_ulong pmp_sa; + target_ulong pmp_ea; + target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); + target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; + int i; - if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) { + /* + * If PMP is not supported or there are no PMP rules, the TLB page will not + * be split into regions with different permissions by PMP so we set the + * size to TARGET_PAGE_SIZE. + */ + if (!riscv_cpu_cfg(env)->pmp || !pmp_get_num_rules(env)) { return TARGET_PAGE_SIZE; - } else { + } + + for (i = 0; i < MAX_RISCV_PMPS; i++) { + if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) { + continue; + } + + pmp_sa = env->pmp_state.addr[i].sa; + pmp_ea = env->pmp_state.addr[i].ea; + /* - * At this point we have a tlb_size that is the smallest possible size - * That fits within a TARGET_PAGE_SIZE and the PMP region. - * - * If the size is less then TARGET_PAGE_SIZE we drop the size to 1. - * This means the result isn't cached in the TLB and is only used for - * a single translation. + * Only the first PMP entry that covers (whole or partial of) the TLB + * page really matters: + * If it covers the whole TLB page, set the size to TARGET_PAGE_SIZE, + * since the following PMP entries have lower priority and will not + * affect the permissions of the page. + * If it only covers partial of the TLB page, set the size to 1 since + * the allowed permissions of the region may be different from other + * region of the page. */ - return 1; + if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) { + return TARGET_PAGE_SIZE; + } else if ((pmp_sa >= tlb_sa && pmp_sa <= tlb_ea) || + (pmp_ea >= tlb_sa && pmp_ea <= tlb_ea)) { + return 1; + } } + + /* + * If no PMP entry matches the TLB page, the TLB page will also not be + * split into regions with different permissions by PMP so we set the size + * to TARGET_PAGE_SIZE. + */ + return TARGET_PAGE_SIZE; } /* diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index b296ea1fc6..cf5c99f8e6 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -72,12 +72,11 @@ target_ulong mseccfg_csr_read(CPURISCVState *env); void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val); target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); -int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, - pmp_priv_t *allowed_privs, - target_ulong mode); -target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, - target_ulong tlb_sa, target_ulong tlb_ea); +bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, + target_ulong size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, + target_ulong mode); +target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr); void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_nums(CPURISCVState *env); uint32_t pmp_get_num_rules(CPURISCVState *env); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 933b11c50d..8a33da811e 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -59,8 +59,8 @@ typedef enum { typedef struct DisasContext { DisasContextBase base; - /* pc_succ_insn points to the instruction following base.pc_next */ - target_ulong pc_succ_insn; + target_ulong cur_insn_len; + target_ulong pc_save; target_ulong priv_ver; RISCVMXL misa_mxl_max; RISCVMXL xl; @@ -224,26 +224,34 @@ static void decode_save_opc(DisasContext *ctx) ctx->insn_start = NULL; } -static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) +static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, + target_long diff) { - if (get_xl(ctx) == MXL_RV32) { - dest = (int32_t)dest; + target_ulong dest = ctx->base.pc_next + diff; + + assert(ctx->pc_save != -1); + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(target, cpu_pc, dest - ctx->pc_save); + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target, target); + } + } else { + if (get_xl(ctx) == MXL_RV32) { + dest = (int32_t)dest; + } + tcg_gen_movi_tl(target, dest); } - tcg_gen_movi_tl(cpu_pc, dest); } -static void gen_set_pc(DisasContext *ctx, TCGv dest) +static void gen_update_pc(DisasContext *ctx, target_long diff) { - if (get_xl(ctx) == MXL_RV32) { - tcg_gen_ext32s_tl(cpu_pc, dest); - } else { - tcg_gen_mov_tl(cpu_pc, dest); - } + gen_pc_plus_diff(cpu_pc, ctx, diff); + ctx->pc_save = ctx->base.pc_next + diff; } static void generate_exception(DisasContext *ctx, int excp) { - gen_set_pc_imm(ctx, ctx->base.pc_next); + gen_update_pc(ctx, 0); gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); ctx->base.is_jmp = DISAS_NORETURN; } @@ -259,9 +267,9 @@ static void gen_exception_illegal(DisasContext *ctx) } } -static void gen_exception_inst_addr_mis(DisasContext *ctx) +static void gen_exception_inst_addr_mis(DisasContext *ctx, TCGv target) { - tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); + tcg_gen_st_tl(target, cpu_env, offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); } @@ -285,18 +293,33 @@ static void exit_tb(DisasContext *ctx) tcg_gen_exit_tb(NULL, 0); } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) { + target_ulong dest = ctx->base.pc_next + diff; + /* * Under itrigger, instruction executes one by one like singlestep, * direct block chain benefits will be small. */ if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) { - tcg_gen_goto_tb(n); - gen_set_pc_imm(ctx, dest); + /* + * For pcrel, the pc must always be up-to-date on entry to + * the linked TB, so that it can use simple additions for all + * further adjustments. For !pcrel, the linked TB is compiled + * to know its full virtual address, so we can delay the + * update to pc to the unlinked path. A long chain of links + * can thus avoid many updates to the PC. + */ + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + gen_update_pc(ctx, diff); + tcg_gen_goto_tb(n); + } else { + tcg_gen_goto_tb(n); + gen_update_pc(ctx, diff); + } tcg_gen_exit_tb(ctx->base.tb, n); } else { - gen_set_pc_imm(ctx, dest); + gen_update_pc(ctx, diff); lookup_and_goto_ptr(ctx); } } @@ -547,19 +570,22 @@ static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { - target_ulong next_pc; + TCGv succ_pc = dest_gpr(ctx, rd); /* check misaligned: */ - next_pc = ctx->base.pc_next + imm; - if (!ctx->cfg_ptr->ext_zca) { - if ((next_pc & 0x3) != 0) { - gen_exception_inst_addr_mis(ctx); + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if ((imm & 0x3) != 0) { + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, imm); + gen_exception_inst_addr_mis(ctx, target_pc); return; } } - gen_set_gpri(ctx, rd, ctx->pc_succ_insn); - gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, rd, succ_pc); + + gen_goto_tb(ctx, 0, imm); /* must use this for safety */ ctx->base.is_jmp = DISAS_NORETURN; } @@ -1117,15 +1143,16 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) }; ctx->virt_inst_excp = false; + ctx->cur_insn_len = insn_len(opcode); /* Check for compressed insn */ - if (insn_len(opcode) == 2) { + if (ctx->cur_insn_len == 2) { ctx->opcode = opcode; - ctx->pc_succ_insn = ctx->base.pc_next + 2; /* * The Zca extension is added as way to refer to instructions in the C * extension that do not include the floating-point loads and stores */ - if (ctx->cfg_ptr->ext_zca && decode_insn16(ctx, opcode)) { + if ((has_ext(ctx, RVC) || ctx->cfg_ptr->ext_zca) && + decode_insn16(ctx, opcode)) { return; } } else { @@ -1134,7 +1161,6 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) translator_lduw(env, &ctx->base, ctx->base.pc_next + 2)); ctx->opcode = opcode32; - ctx->pc_succ_insn = ctx->base.pc_next + 4; for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { if (decoders[i].guard_func(ctx) && @@ -1154,7 +1180,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); uint32_t tb_flags = ctx->base.tb->flags; - ctx->pc_succ_insn = ctx->base.pc_first; + ctx->pc_save = ctx->base.pc_first; ctx->priv = FIELD_EX32(tb_flags, TB_FLAGS, PRIV); ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); ctx->mstatus_fs = FIELD_EX32(tb_flags, TB_FLAGS, FS); @@ -1189,8 +1215,13 @@ static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + target_ulong pc_next = ctx->base.pc_next; + + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_next &= ~TARGET_PAGE_MASK; + } - tcg_gen_insn_start(ctx->base.pc_next, 0); + tcg_gen_insn_start(pc_next, 0); ctx->insn_start = tcg_last_op(); } @@ -1202,7 +1233,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) ctx->ol = ctx->xl; decode_opc(env, ctx, opcode16); - ctx->base.pc_next = ctx->pc_succ_insn; + ctx->base.pc_next += ctx->cur_insn_len; /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { @@ -1229,7 +1260,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: - gen_goto_tb(ctx, 0, ctx->base.pc_next); + gen_goto_tb(ctx, 0, 0); break; case DISAS_NORETURN: break; diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index f4d0438988..1e06e7447c 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -169,7 +169,7 @@ static inline uint32_t vext_get_total_elems(CPURISCVState *env, uint32_t desc, static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) { - return (addr & env->cur_pmmask) | env->cur_pmbase; + return (addr & ~env->cur_pmmask) | env->cur_pmbase; } /* @@ -264,26 +264,21 @@ GEN_VEXT_ST_ELEM(ste_h, int16_t, H2, stw) GEN_VEXT_ST_ELEM(ste_w, int32_t, H4, stl) GEN_VEXT_ST_ELEM(ste_d, int64_t, H8, stq) -static void vext_set_tail_elems_1s(CPURISCVState *env, target_ulong vl, - void *vd, uint32_t desc, uint32_t nf, +static void vext_set_tail_elems_1s(target_ulong vl, void *vd, + uint32_t desc, uint32_t nf, uint32_t esz, uint32_t max_elems) { - uint32_t total_elems = vext_get_total_elems(env, desc, esz); - uint32_t vlenb = riscv_cpu_cfg(env)->vlen >> 3; uint32_t vta = vext_vta(desc); - uint32_t registers_used; int k; + if (vta == 0) { + return; + } + for (k = 0; k < nf; ++k) { vext_set_elems_1s(vd, vta, (k * max_elems + vl) * esz, (k * max_elems + max_elems) * esz); } - - if (nf * max_elems % total_elems != 0) { - registers_used = ((nf * max_elems) * esz + (vlenb - 1)) / vlenb; - vext_set_elems_1s(vd, vta, (nf * max_elems) * esz, - registers_used * vlenb); - } } /* @@ -319,7 +314,7 @@ vext_ldst_stride(void *vd, void *v0, target_ulong base, } env->vstart = 0; - vext_set_tail_elems_1s(env, env->vl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_STRIDE(NAME, ETYPE, LOAD_FN) \ @@ -378,12 +373,12 @@ vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, } env->vstart = 0; - vext_set_tail_elems_1s(env, evl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems); } /* * masked unit-stride load and store operation will be a special case of - * stride, stride = NF * sizeof (MTYPE) + * stride, stride = NF * sizeof (ETYPE) */ #define GEN_VEXT_LD_US(NAME, ETYPE, LOAD_FN) \ @@ -499,7 +494,7 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, } env->vstart = 0; - vext_set_tail_elems_1s(env, env->vl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_INDEX(NAME, ETYPE, INDEX_FN, LOAD_FN) \ @@ -629,7 +624,7 @@ ProbeSuccess: } env->vstart = 0; - vext_set_tail_elems_1s(env, env->vl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LDFF(NAME, ETYPE, LOAD_FN) \ @@ -655,10 +650,6 @@ GEN_VEXT_LDFF(vle64ff_v, int64_t, lde_d) #define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) #define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) -/* Unsigned min/max */ -#define DO_MAXU(N, M) DO_MAX((UMTYPE)N, (UMTYPE)M) -#define DO_MINU(N, M) DO_MIN((UMTYPE)N, (UMTYPE)M) - /* * load and store whole register instructions */ |