Hi Andrew, On Thu, Dec 1, 2022 at 3:47 PM Andrew Jones <ajo...@ventanamicro.com> wrote:
> On Thu, Dec 01, 2022 at 10:36:23AM +0100, Alexandre Ghiti wrote: > > 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 "mbare", "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 > > > > 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,sv32=on # Can't enable 32-bit satp mode in 64-bit > > -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 # sv39 must be supported if higher modes are > > > > In addition, we now correctly set the device-tree entry 'mmu-type' using > > those new properties. > > > > Co-Developed-by: Ludovic Henry <ludo...@rivosinc.com> > > Signed-off-by: Ludovic Henry <ludo...@rivosinc.com> > > Signed-off-by: Alexandre Ghiti <alexgh...@rivosinc.com> > > --- > > v3: > > - Free sv_name as pointed by Bin > > - Replace satp-mode with boolean properties as suggested by Andrew > > - Removed RB from Atish as the patch considerably changed > > > > v2: > > - Use error_setg + return as suggested by Alistair > > - Add RB from Atish > > - Fixed checkpatch issues missed in v1 > > - Replaced Ludovic email address with the rivos one > > > > hw/riscv/virt.c | 16 ++-- > > target/riscv/cpu.c | 164 ++++++++++++++++++++++++++++++++++++++++ > > target/riscv/cpu.h | 8 ++ > > target/riscv/cpu_bits.h | 1 + > > target/riscv/csr.c | 8 +- > > 5 files changed, 186 insertions(+), 11 deletions(-) > > > > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > > index a5bc7353b4..bb7c739a74 100644 > > --- a/hw/riscv/virt.c > > +++ b/hw/riscv/virt.c > > @@ -228,7 +228,7 @@ static void create_fdt_socket_cpus(RISCVVirtState > *s, int socket, > > int cpu; > > uint32_t cpu_phandle; > > MachineState *mc = MACHINE(s); > > - char *name, *cpu_name, *core_name, *intc_name; > > + char *name, *cpu_name, *core_name, *intc_name, *sv_name; > > > > for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { > > cpu_phandle = (*phandle)++; > > @@ -236,14 +236,12 @@ static void create_fdt_socket_cpus(RISCVVirtState > *s, int socket, > > cpu_name = g_strdup_printf("/cpus/cpu@%d", > > s->soc[socket].hartid_base + cpu); > > qemu_fdt_add_subnode(mc->fdt, cpu_name); > > - if (riscv_feature(&s->soc[socket].harts[cpu].env, > > - RISCV_FEATURE_MMU)) { > > - qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", > > - (is_32_bit) ? "riscv,sv32" : > "riscv,sv48"); > > - } else { > > - qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", > > - "riscv,none"); > > - } > > + > > + sv_name = g_strdup_printf("riscv,%s", > > + > s->soc[socket].harts[cpu].cfg.satp_mode_str); > > + qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", sv_name); > > + g_free(sv_name); > > + > > name = riscv_isa_string(&s->soc[socket].harts[cpu]); > > qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name); > > g_free(name); > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c > > index d14e95c9dc..51c06ed057 100644 > > --- a/target/riscv/cpu.c > > +++ b/target/riscv/cpu.c > > @@ -907,6 +907,66 @@ static void riscv_cpu_realize(DeviceState *dev, > Error **errp) > > } > > #endif > > > > + /* > > + * Either a cpu sets its supported satp_mode in XXX_cpu_init > > + * or the user sets this value using satp_mode property. > > using the sv* and mbare properties. > > > + */ > > + bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; > > + > > + cpu->cfg.satp_mode = VM_1_10_UNDEF; > > Could probably just use -1 here instead of introducing VM_1_10_UNDEF. > > > + > > + if (rv32) { > > + if (cpu->cfg.sv32 == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("sv32"); > > No need to allocate memory, satp_mode_str = "sv32". > > Also I'm not sure we need to keep mode_str in CPUConfig. Providing a > function with a switch on VM_1_10_SV* cases to get it should be enough > for its one usecase. > > > + cpu->cfg.satp_mode = VM_1_10_SV32; > > + } else if (cpu->cfg.mbare == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("none"); > > + cpu->cfg.satp_mode = VM_1_10_MBARE; > > + } > > + } else { > > + if (cpu->cfg.sv64 == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("sv64"); > > + cpu->cfg.satp_mode = VM_1_10_SV64; > > + } else if (cpu->cfg.sv57 == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("sv57"); > > + cpu->cfg.satp_mode = VM_1_10_SV57; > > + } else if (cpu->cfg.sv48 == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("sv48"); > > + cpu->cfg.satp_mode = VM_1_10_SV48; > > + } else if (cpu->cfg.sv39 == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("sv39"); > > + cpu->cfg.satp_mode = VM_1_10_SV39; > > + } else if (cpu->cfg.mbare == ON_OFF_AUTO_ON) { > > + cpu->cfg.satp_mode_str = g_strdup("none"); > > + cpu->cfg.satp_mode = VM_1_10_MBARE; > > + } > > + } > > + > > + /* > > + * If unset by both the user and the cpu, we fallback to sv32 for > 32-bit > > + * or sv57 for 64-bit when a MMU is present, and bare otherwise. > > + */ > > + if (cpu->cfg.satp_mode == VM_1_10_UNDEF) { > > + if (riscv_feature(&cpu->env, RISCV_FEATURE_MMU)) { > > + if (rv32) { > > + cpu->cfg.satp_mode_str = g_strdup("sv32"); > > + cpu->cfg.satp_mode = VM_1_10_SV32; > > + } else { > > + cpu->cfg.satp_mode_str = g_strdup("sv57"); > > + cpu->cfg.satp_mode = VM_1_10_SV57; > > + } > > + } else { > > + cpu->cfg.satp_mode_str = g_strdup("none"); > > + cpu->cfg.satp_mode = VM_1_10_MBARE; > > + } > > + } > > + > > + 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); > > @@ -915,6 +975,102 @@ static void riscv_cpu_realize(DeviceState *dev, > Error **errp) > > mcc->parent_realize(dev, errp); > > } > > > > +void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) > > +{ > > + bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; > > + > > + /* First, get rid of 32-bit/64-bit incompatibilities */ > > + if (rv32) { > > + if (cpu->cfg.sv39 == ON_OFF_AUTO_ON > > + || cpu->cfg.sv48 == ON_OFF_AUTO_ON > > + || cpu->cfg.sv57 == ON_OFF_AUTO_ON > > + || cpu->cfg.sv64 == ON_OFF_AUTO_ON) { > > + error_setg(errp, "cannot enable 64-bit satp modes " > > + "(sv39/sv48/sv57/sv64)"); > > + error_append_hint(errp, "cpu is in 32-bit mode, 64-bit satp > modes " > > + "can't be enabled\n"); > > + return; > > + } > > + } else { > > + if (cpu->cfg.sv32 == ON_OFF_AUTO_ON) { > > + error_setg(errp, "cannot enable 32-bit satp mode (sv32)"); > > + error_append_hint(errp, "cpu is in 64-bit mode, 32-bit satp > mode " > > + "can't be enabled\n"); > > + return; > > + } > > + } > > + > > + /* > > + * Then make sure the user did not ask for an invalid configuration > as per > > + * the specification. > > + */ > > + switch (cpu->cfg.satp_mode) { > > + case VM_1_10_SV32: > > + if (cpu->cfg.mbare == ON_OFF_AUTO_OFF) { > > + error_setg(errp, "cannot disable mbare satp mode"); > > + error_append_hint(errp, "mbare satp mode must be enabled if > sv32 " > > + "is enabled\n"); > > + return; > > + } > > + > > + break; > > + case VM_1_10_SV39: > > + if (cpu->cfg.mbare == ON_OFF_AUTO_OFF) { > > + error_setg(errp, "cannot disable mbare satp mode"); > > + error_append_hint(errp, "mbare satp mode must be enabled if > sv39 " > > + "is enabled\n"); > > + return; > > + } > > + > > + break; > > + case VM_1_10_SV48: > > + if (cpu->cfg.mbare == ON_OFF_AUTO_OFF > > + || cpu->cfg.sv39 == ON_OFF_AUTO_OFF) { > > + error_setg(errp, "cannot disable mbare/sv39 satp modes"); > > + error_append_hint(errp, "mbare/sv39 satp modes must be > enabled if " > > + "sv48 is enabled\n"); > > Combined errors like this make the user look to see which one it is. I > think we can we reorganize this switch to fall through from largest to > smallest allowing the checks for smaller widths and mbare to be shared. > If a user has more than one problem then they'll only see an error for the > larger first, but then they'll try again and see the next one. In each > case they'll see exactly what needs to be fixed, though. > > > + return; > > + } > > + > > + break; > > + case VM_1_10_SV57: > > + if (cpu->cfg.mbare == ON_OFF_AUTO_OFF > > + || cpu->cfg.sv39 == ON_OFF_AUTO_OFF > > + || cpu->cfg.sv48 == ON_OFF_AUTO_OFF) { > > + error_setg(errp, "cannot disable mbare/sv39/sv48 satp > modes"); > > + error_append_hint(errp, "mbare/sv39/sv48 satp modes must be > " > > + "enabled if sv57 is enabled\n"); > > + return; > > + } > > + > > + break; > > + case VM_1_10_SV64: > > + if (cpu->cfg.mbare == ON_OFF_AUTO_OFF > > + || cpu->cfg.sv39 == ON_OFF_AUTO_OFF > > + || cpu->cfg.sv48 == ON_OFF_AUTO_OFF > > + || cpu->cfg.sv57 == ON_OFF_AUTO_OFF) { > > + error_setg(errp, "cannot disable mbare/sv39/sv48/sv57 satp " > > + "modes"); > > + error_append_hint(errp, "mbare/sv39/sv48/sv57 satp modes > must be " > > + "enabled if sv57 is enabled\n"); > > + return; > > + } > > + > > + break; > > + } > > +} > > + > > +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) > > +{ > > + Error *local_err = NULL; > > + > > + riscv_cpu_satp_mode_finalize(cpu, &local_err); > > + if (local_err != NULL) { > > + error_propagate(errp, local_err); > > + return; > > + } > > +} > > + > > #ifndef CONFIG_USER_ONLY > > static void riscv_cpu_set_irq(void *opaque, int irq, int level) > > { > > @@ -1094,6 +1250,14 @@ static Property riscv_cpu_properties[] = { > > > > DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, cfg.rvv_ta_all_1s, > false), > > DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, cfg.rvv_ma_all_1s, > false), > > + > > + DEFINE_PROP_ON_OFF_AUTO("mbare", RISCVCPU, cfg.mbare, > ON_OFF_AUTO_AUTO), > > + DEFINE_PROP_ON_OFF_AUTO("sv32", RISCVCPU, cfg.sv32, > ON_OFF_AUTO_AUTO), > > + DEFINE_PROP_ON_OFF_AUTO("sv39", RISCVCPU, cfg.sv39, > ON_OFF_AUTO_AUTO), > > + DEFINE_PROP_ON_OFF_AUTO("sv48", RISCVCPU, cfg.sv48, > ON_OFF_AUTO_AUTO), > > + DEFINE_PROP_ON_OFF_AUTO("sv57", RISCVCPU, cfg.sv57, > ON_OFF_AUTO_AUTO), > > + DEFINE_PROP_ON_OFF_AUTO("sv64", RISCVCPU, cfg.sv64, > ON_OFF_AUTO_AUTO), > > I'm not sure what types of issues may arise mixing booleans and OnOffAutos > in a future cpu-model-expansion. I also think we can simplify things by > using arm's sve* boolean properties as a pattern. For that, each property > is a boolean which shares the same get and set accessors. The set accessor > not only sets the property to true/false, but also tracks that the user > did the setting, allowing for sanity checks at finalize time. > I can't find the sve* properties you're talking about, can you point them to me? Thanks, Alex > > Using a pair of bitmaps for the sv properties, where VM_1_10_SV* are used > for the bit numbers, should work well. Validating input will likely reduce > to some bitmap comparing operations. It would also drop all the extra cfg > state. In fact, one of the temporary bitmaps could use the satp_mode > member, before the final result gets written to it. So, only a single > extra uint8_t for the other bitmap is needed. > > > + > > DEFINE_PROP_END_OF_LIST(), > > }; > > > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > > index 3a9e25053f..dcdde1e0b7 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 "qapi/qapi-types-common.h" > > > > #define TCG_GUEST_DEFAULT_MO 0 > > > > @@ -480,6 +481,10 @@ struct RISCVCPUConfig { > > bool debug; > > > > bool short_isa_string; > > + > > + OnOffAuto mbare, sv32, sv39, sv48, sv57, sv64; > > + uint8_t satp_mode; > > + char *satp_mode_str; > > }; > > > > typedef struct RISCVCPUConfig RISCVCPUConfig; > > @@ -789,4 +794,7 @@ void riscv_set_csr_ops(int csrno, > riscv_csr_operations *ops); > > > > void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); > > > > +void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp); > > +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp); > > + > > #endif /* RISCV_CPU_H */ > > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h > > index d8f5f0abed..3e67a815d5 100644 > > --- a/target/riscv/cpu_bits.h > > +++ b/target/riscv/cpu_bits.h > > @@ -590,6 +590,7 @@ typedef enum { > > #define VM_1_10_SV48 9 > > #define VM_1_10_SV57 10 > > #define VM_1_10_SV64 11 > > +#define VM_1_10_UNDEF 16 > > > > /* Page table entry (PTE) fields */ > > #define PTE_V 0x001 /* Valid */ > > diff --git a/target/riscv/csr.c b/target/riscv/csr.c > > index 5c9a7ee287..d2aab1627e 100644 > > --- a/target/riscv/csr.c > > +++ b/target/riscv/csr.c > > @@ -1109,10 +1109,14 @@ static RISCVException read_mstatus(CPURISCVState > *env, int csrno, > > > > static int validate_vm(CPURISCVState *env, target_ulong vm) > > { > > + vm &= 0xf; > > + > > if (riscv_cpu_mxl(env) == MXL_RV32) { > > - return valid_vm_1_10_32[vm & 0xf]; > > + return valid_vm_1_10_32[vm] && > > + (vm <= RISCV_CPU(env_cpu(env))->cfg.satp_mode); > > } else { > > - return valid_vm_1_10_64[vm & 0xf]; > > + return valid_vm_1_10_64[vm] && > > + (vm <= RISCV_CPU(env_cpu(env))->cfg.satp_mode); > > } > > } > > > > -- > > 2.37.2 > > > > Thanks, > drew >