Re: [PATCH v8 3/5] riscv: Allow user to set the satp mode

2023-01-31 Thread Alexandre Ghiti
On Wed, Jan 25, 2023 at 5:52 PM Andrew Jones  wrote:
>
> On Wed, Jan 25, 2023 at 05:20:08PM +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 "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 
> > Signed-off-by: Ludovic Henry 
> > Signed-off-by: Alexandre Ghiti 
> > ---
> >  target/riscv/cpu.c | 206 +
> >  target/riscv/cpu.h |  19 +
> >  target/riscv/csr.c |  12 ++-
> >  3 files changed, 230 insertions(+), 7 deletions(-)
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index 7181b34f86..54494a72be 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -27,6 +27,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"
> > @@ -229,6 +230,81 @@ static void set_vext_version(CPURISCVState *env, int 
> > vext_ver)
> >  env->vext_ver = vext_ver;
> >  }
> >
> > +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(RISCVCPU *cpu)
> > +{
> > +bool rv32 = riscv_cpu_mxl(>env) == MXL_RV32;
> > +
> > +if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
> > +cpu->cfg.satp_mode.map |=
> > +(1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));
> > +} else {
> > +cpu->cfg.satp_mode.map |= (1 << satp_mode_from_str("mbare"));
> > +}
> > +}
> > +
> >  static void riscv_any_cpu_init(Object *obj)
> >  {
> >  CPURISCVState *env = _CPU(obj)->env;
> > @@ -619,6 +695,82 @@ static void riscv_cpu_disas_set_info(CPUState *s, 
> > disassemble_info *info)
> >  }
> >  }
> >
> > +static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
> > +{
> > +bool rv32 = riscv_cpu_mxl(>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, 

Re: [PATCH v8 3/5] riscv: Allow user to set the satp mode

2023-01-31 Thread Alexandre Ghiti
Hi Bin,

On Mon, Jan 30, 2023 at 5:22 AM Bin Meng  wrote:
>
> On Thu, Jan 26, 2023 at 12:23 AM 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 "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 
> > Signed-off-by: Ludovic Henry 
> > Signed-off-by: Alexandre Ghiti 
> > ---
> >  target/riscv/cpu.c | 206 +
> >  target/riscv/cpu.h |  19 +
> >  target/riscv/csr.c |  12 ++-
> >  3 files changed, 230 insertions(+), 7 deletions(-)
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index 7181b34f86..54494a72be 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -27,6 +27,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"
> > @@ -229,6 +230,81 @@ static void set_vext_version(CPURISCVState *env, int 
> > vext_ver)
> >  env->vext_ver = vext_ver;
> >  }
> >
> > +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(RISCVCPU *cpu)
>
> This function is better named as set_satp_mode_default_map
>

Indeed, fixed in v9.

> > +{
> > +bool rv32 = riscv_cpu_mxl(>env) == MXL_RV32;
> > +
> > +if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
> > +cpu->cfg.satp_mode.map |=
> > +(1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));
> > +} else {
> > +cpu->cfg.satp_mode.map |= (1 << satp_mode_from_str("mbare"));
> > +}
>
> I believe the "mbare" bit should always be set, so this can be:
>
> cpu->cfg.satp_mode.map = 1 << satp_mode_from_str("mbare");
> if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
>cpu->cfg.satp_mode.map |=
> (1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));

Actually, at the end of riscv_cpu_satp_mode_finalize, the map is
"expanded" so that all supported modes are set in the bitmap.

>
> > +}
> > +
> >  static void riscv_any_cpu_init(Object *obj)
> >  {
> >  CPURISCVState *env = 

Re: [PATCH v8 3/5] riscv: Allow user to set the satp mode

2023-01-29 Thread Bin Meng
On Thu, Jan 26, 2023 at 12:23 AM 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 "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 
> Signed-off-by: Ludovic Henry 
> Signed-off-by: Alexandre Ghiti 
> ---
>  target/riscv/cpu.c | 206 +
>  target/riscv/cpu.h |  19 +
>  target/riscv/csr.c |  12 ++-
>  3 files changed, 230 insertions(+), 7 deletions(-)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 7181b34f86..54494a72be 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -27,6 +27,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"
> @@ -229,6 +230,81 @@ static void set_vext_version(CPURISCVState *env, int 
> vext_ver)
>  env->vext_ver = vext_ver;
>  }
>
> +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(RISCVCPU *cpu)

This function is better named as set_satp_mode_default_map

> +{
> +bool rv32 = riscv_cpu_mxl(>env) == MXL_RV32;
> +
> +if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
> +cpu->cfg.satp_mode.map |=
> +(1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));
> +} else {
> +cpu->cfg.satp_mode.map |= (1 << satp_mode_from_str("mbare"));
> +}

I believe the "mbare" bit should always be set, so this can be:

cpu->cfg.satp_mode.map = 1 << satp_mode_from_str("mbare");
if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
   cpu->cfg.satp_mode.map |=
(1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));

> +}
> +
>  static void riscv_any_cpu_init(Object *obj)
>  {
>  CPURISCVState *env = _CPU(obj)->env;
> @@ -619,6 +695,82 @@ static void riscv_cpu_disas_set_info(CPUState *s, 
> disassemble_info *info)
>  }
>  }
>
> +static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
> +{
> +bool rv32 = riscv_cpu_mxl(>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 

Re: [PATCH v8 3/5] riscv: Allow user to set the satp mode

2023-01-25 Thread Andrew Jones
On Wed, Jan 25, 2023 at 05:20:08PM +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 "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 
> Signed-off-by: Ludovic Henry 
> Signed-off-by: Alexandre Ghiti 
> ---
>  target/riscv/cpu.c | 206 +
>  target/riscv/cpu.h |  19 +
>  target/riscv/csr.c |  12 ++-
>  3 files changed, 230 insertions(+), 7 deletions(-)
> 
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 7181b34f86..54494a72be 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -27,6 +27,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"
> @@ -229,6 +230,81 @@ static void set_vext_version(CPURISCVState *env, int 
> vext_ver)
>  env->vext_ver = vext_ver;
>  }
>  
> +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(RISCVCPU *cpu)
> +{
> +bool rv32 = riscv_cpu_mxl(>env) == MXL_RV32;
> +
> +if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
> +cpu->cfg.satp_mode.map |=
> +(1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));
> +} else {
> +cpu->cfg.satp_mode.map |= (1 << satp_mode_from_str("mbare"));
> +}
> +}
> +
>  static void riscv_any_cpu_init(Object *obj)
>  {
>  CPURISCVState *env = _CPU(obj)->env;
> @@ -619,6 +695,82 @@ static void riscv_cpu_disas_set_info(CPUState *s, 
> disassemble_info *info)
>  }
>  }
>  
> +static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
> +{
> +bool rv32 = riscv_cpu_mxl(>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(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.
> + */
> +

[PATCH v8 3/5] riscv: Allow user to set the satp mode

2023-01-25 Thread Alexandre Ghiti
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 
Signed-off-by: Ludovic Henry 
Signed-off-by: Alexandre Ghiti 
---
 target/riscv/cpu.c | 206 +
 target/riscv/cpu.h |  19 +
 target/riscv/csr.c |  12 ++-
 3 files changed, 230 insertions(+), 7 deletions(-)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 7181b34f86..54494a72be 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -27,6 +27,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"
@@ -229,6 +230,81 @@ static void set_vext_version(CPURISCVState *env, int 
vext_ver)
 env->vext_ver = vext_ver;
 }
 
+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(RISCVCPU *cpu)
+{
+bool rv32 = riscv_cpu_mxl(>env) == MXL_RV32;
+
+if (riscv_feature(>env, RISCV_FEATURE_MMU)) {
+cpu->cfg.satp_mode.map |=
+(1 << satp_mode_from_str(rv32 ? "sv32" : "sv57"));
+} else {
+cpu->cfg.satp_mode.map |= (1 << satp_mode_from_str("mbare"));
+}
+}
+
 static void riscv_any_cpu_init(Object *obj)
 {
 CPURISCVState *env = _CPU(obj)->env;
@@ -619,6 +695,82 @@ static void riscv_cpu_disas_set_info(CPUState *s, 
disassemble_info *info)
 }
 }
 
+static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
+{
+bool rv32 = riscv_cpu_mxl(>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(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;
+}
+