mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 08:13:54 -06:00
riscv: Allow user to set the satp mode
RISC-V specifies multiple sizes for addressable memory and Linux probes for the machine's support at startup via the satp CSR register (done in csr.c:validate_vm). As per the specification, sv64 must support sv57, which in turn must support sv48...etc. So we can restrict machine support by simply setting the "highest" supported mode and the bare mode is always supported. You can set the satp mode using the new properties "sv32", "sv39", "sv48", "sv57" and "sv64" as follows: -cpu rv64,sv57=on # Linux will boot using sv57 scheme -cpu rv64,sv39=on # Linux will boot using sv39 scheme -cpu rv64,sv57=off # Linux will boot using sv48 scheme -cpu rv64 # Linux will boot using sv57 scheme by default We take the highest level set by the user: -cpu rv64,sv48=on,sv57=on # Linux will boot using sv57 scheme We make sure that invalid configurations are rejected: -cpu rv64,sv39=off,sv48=on # sv39 must be supported if higher modes are # enabled We accept "redundant" configurations: -cpu rv64,sv48=on,sv57=off # Linux will boot using sv48 scheme And contradictory configurations: -cpu rv64,sv48=on,sv48=off # Linux will boot using sv39 scheme Co-Developed-by: Ludovic Henry <ludovic@rivosinc.com> Signed-off-by: Ludovic Henry <ludovic@rivosinc.com> Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Bin Meng <bmeng@tinylab.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Message-ID: <20230303131252.892893-4-alexghiti@rivosinc.com> Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
parent
bf1a6abec4
commit
6f23aaeb9b
3 changed files with 240 additions and 7 deletions
|
@ -28,6 +28,7 @@
|
|||
#include "time_helper.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
@ -249,6 +250,82 @@ 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)
|
||||
{
|
||||
if (!strncmp(satp_mode_str, "mbare", 5)) {
|
||||
return VM_1_10_MBARE;
|
||||
}
|
||||
|
||||
if (!strncmp(satp_mode_str, "sv32", 4)) {
|
||||
return VM_1_10_SV32;
|
||||
}
|
||||
|
||||
if (!strncmp(satp_mode_str, "sv39", 4)) {
|
||||
return VM_1_10_SV39;
|
||||
}
|
||||
|
||||
if (!strncmp(satp_mode_str, "sv48", 4)) {
|
||||
return VM_1_10_SV48;
|
||||
}
|
||||
|
||||
if (!strncmp(satp_mode_str, "sv57", 4)) {
|
||||
return VM_1_10_SV57;
|
||||
}
|
||||
|
||||
if (!strncmp(satp_mode_str, "sv64", 4)) {
|
||||
return VM_1_10_SV64;
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
uint8_t satp_mode_max_from_map(uint32_t map)
|
||||
{
|
||||
/* map here has at least one bit set, so no problem with clz */
|
||||
return 31 - __builtin_clz(map);
|
||||
}
|
||||
|
||||
const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit)
|
||||
{
|
||||
if (is_32_bit) {
|
||||
switch (satp_mode) {
|
||||
case VM_1_10_SV32:
|
||||
return "sv32";
|
||||
case VM_1_10_MBARE:
|
||||
return "none";
|
||||
}
|
||||
} else {
|
||||
switch (satp_mode) {
|
||||
case VM_1_10_SV64:
|
||||
return "sv64";
|
||||
case VM_1_10_SV57:
|
||||
return "sv57";
|
||||
case VM_1_10_SV48:
|
||||
return "sv48";
|
||||
case VM_1_10_SV39:
|
||||
return "sv39";
|
||||
case VM_1_10_MBARE:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Sets the satp mode to the max supported */
|
||||
static void set_satp_mode_default_map(RISCVCPU *cpu)
|
||||
{
|
||||
bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32;
|
||||
|
||||
if (riscv_feature(&cpu->env, RISCV_FEATURE_MMU)) {
|
||||
cpu->cfg.satp_mode.map |= (1 << (rv32 ? VM_1_10_SV32 : VM_1_10_SV57));
|
||||
} else {
|
||||
cpu->cfg.satp_mode.map |= (1 << VM_1_10_MBARE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void riscv_any_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
|
@ -918,6 +995,87 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
|
|||
set_misa(env, env->misa_mxl, ext);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
|
||||
{
|
||||
bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32;
|
||||
const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64;
|
||||
uint8_t satp_mode_max;
|
||||
|
||||
if (cpu->cfg.satp_mode.map == 0) {
|
||||
if (cpu->cfg.satp_mode.init == 0) {
|
||||
/* If unset by the user, we fallback to the default satp mode. */
|
||||
set_satp_mode_default_map(cpu);
|
||||
} else {
|
||||
/*
|
||||
* Find the lowest level that was disabled and then enable the
|
||||
* first valid level below which can be found in
|
||||
* valid_vm_1_10_32/64.
|
||||
*/
|
||||
for (int i = 1; i < 16; ++i) {
|
||||
if ((cpu->cfg.satp_mode.init & (1 << i)) && valid_vm[i]) {
|
||||
for (int j = i - 1; j >= 0; --j) {
|
||||
if (valid_vm[j]) {
|
||||
cpu->cfg.satp_mode.map |= (1 << j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the configuration asked is supported by qemu */
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if ((cpu->cfg.satp_mode.map & (1 << i)) && !valid_vm[i]) {
|
||||
error_setg(errp, "satp_mode %s is not valid",
|
||||
satp_mode_str(i, rv32));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the user did not ask for an invalid configuration as per
|
||||
* the specification.
|
||||
*/
|
||||
satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map);
|
||||
|
||||
if (!rv32) {
|
||||
for (int i = satp_mode_max - 1; i >= 0; --i) {
|
||||
if (!(cpu->cfg.satp_mode.map & (1 << i)) &&
|
||||
(cpu->cfg.satp_mode.init & (1 << i)) &&
|
||||
valid_vm[i]) {
|
||||
error_setg(errp, "cannot disable %s satp mode if %s "
|
||||
"is enabled", satp_mode_str(i, false),
|
||||
satp_mode_str(satp_mode_max, false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally expand the map so that all valid modes are set */
|
||||
for (int i = satp_mode_max - 1; i >= 0; --i) {
|
||||
if (valid_vm[i]) {
|
||||
cpu->cfg.satp_mode.map |= (1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
Error *local_err = NULL;
|
||||
|
||||
riscv_cpu_satp_mode_finalize(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
|
@ -1017,6 +1175,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
#endif
|
||||
|
||||
riscv_cpu_finalize_features(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
riscv_cpu_register_gdb_regs_for_features(cs);
|
||||
|
||||
qemu_init_vcpu(cs);
|
||||
|
@ -1026,6 +1190,52 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
RISCVSATPMap *satp_map = opaque;
|
||||
uint8_t satp = satp_mode_from_str(name);
|
||||
bool value;
|
||||
|
||||
value = satp_map->map & (1 << satp);
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
RISCVSATPMap *satp_map = opaque;
|
||||
uint8_t satp = satp_mode_from_str(name);
|
||||
bool value;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
satp_map->map = deposit32(satp_map->map, satp, 1, value);
|
||||
satp_map->init |= 1 << satp;
|
||||
}
|
||||
|
||||
static void riscv_add_satp_mode_properties(Object *obj)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
|
||||
if (cpu->env.misa_mxl == MXL_RV32) {
|
||||
object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp,
|
||||
cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
|
||||
} else {
|
||||
object_property_add(obj, "sv39", "bool", cpu_riscv_get_satp,
|
||||
cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
|
||||
object_property_add(obj, "sv48", "bool", cpu_riscv_get_satp,
|
||||
cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
|
||||
object_property_add(obj, "sv57", "bool", cpu_riscv_get_satp,
|
||||
cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
|
||||
object_property_add(obj, "sv64", "bool", cpu_riscv_get_satp,
|
||||
cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(opaque);
|
||||
|
@ -1246,6 +1456,10 @@ static void register_cpu_props(Object *obj)
|
|||
for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
|
||||
qdev_property_add_static(dev, prop);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
riscv_add_satp_mode_properties(obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Property riscv_cpu_properties[] = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue