From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5B6BBC4332F for ; Fri, 16 Dec 2022 09:33:14 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1p674u-00084S-Dc; Fri, 16 Dec 2022 04:32:28 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1p674p-00083u-FD for qemu-devel@nongnu.org; Fri, 16 Dec 2022 04:32:23 -0500 Received: from mail-lf1-x132.google.com ([2a00:1450:4864:20::132]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1p674l-0008Ty-Ej for qemu-devel@nongnu.org; Fri, 16 Dec 2022 04:32:23 -0500 Received: by mail-lf1-x132.google.com with SMTP id b3so2630589lfv.2 for ; Fri, 16 Dec 2022 01:32:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sifive.com; s=google; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=yi4dvs5rADxhwT2aNfVqBnI628LFztDJCP8VQMpQH9c=; b=EuBaxG/L09ZzRFr6dpW7EvlSnkfQu6PfR9fw5iWX5/UdfI8EJlty5bnROVd0Zx3rEi AfDPGw3psZhi6cO9GM+Zg68fFMctFF/+RdnuvS79icAKC9FYXX0v0wgUnHs34uIx1MkY Jn3RvNBATtimdWmac90YZgHCPffqYdudZFCMObu5XQV5qDGOaJj8XFPUfEM6fTUriM2h jD6KyBFtiH5FWzYdxfXt9h3GlU3Ike7+hVdbkBQJ56jvmbp5AmMV3e4/JNG8xbTU3u7W bPVqd6I2nppPq9b2ETiUwtlfPSPytim6RuoCPrqrKvlb/iRD2fkPqPFgXdbBaY48SKd7 SlBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=yi4dvs5rADxhwT2aNfVqBnI628LFztDJCP8VQMpQH9c=; b=6ytPX4mM7lC3woYlhDNAVL5Evn6RcolLa25z7oQ/dujJ9JaTJPN5spXNqGv7xcTvHh C7S8FRB/WdASgPvWOiglRzPSFiXvLcK+QO7RC/zS2SKg2ulsy2qJSq4lfTHgkQDh3mxb FR0xy7OCyFdPw2y1y/9c8r8HzjelgeKMUgo+gRmcmd9h6Hp6wreEHb8GqWyxi0O3w/O/ tkfD4FldTqIiAkSPTJ4JZ7mIOC21XXc3APR5i5zFd95JsjfM864TOf1Kc4U06lmic1T/ ZoIbS9+2yY4EGXehWmi5WGIN5wUZFAghlWuIgA39Q2bTrMqLLg3Vs9QPDarKeRPQluQZ bD7w== X-Gm-Message-State: ANoB5pmf1/y3eMGSPe6M6XRhmS6A/9szxokQ5qc6r9qXUNcIVS0aeknY QEb8md3tOhHCcxCHcDSAiUVFiktLivbqWgQg+sU= X-Google-Smtp-Source: AA0mqf6OgzG/riuxY4u/goopwwrcF7tbo73v8HV92PqoY6Bi827F30s+7XjS69GC3enLFUCChbLsCQ== X-Received: by 2002:ac2:5fe5:0:b0:4a4:68b7:f895 with SMTP id s5-20020ac25fe5000000b004a468b7f895mr13464367lfg.57.1671183137173; Fri, 16 Dec 2022 01:32:17 -0800 (PST) Received: from mail-lf1-f47.google.com (mail-lf1-f47.google.com. [209.85.167.47]) by smtp.gmail.com with ESMTPSA id a1-20020a056512200100b004b097f2c73dsm168910lfb.253.2022.12.16.01.32.16 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 16 Dec 2022 01:32:16 -0800 (PST) Received: by mail-lf1-f47.google.com with SMTP id z26so2583501lfu.8; Fri, 16 Dec 2022 01:32:16 -0800 (PST) X-Received: by 2002:a05:6512:230b:b0:4b6:ef9b:c51a with SMTP id o11-20020a056512230b00b004b6ef9bc51amr1842626lfu.471.1671183136013; Fri, 16 Dec 2022 01:32:16 -0800 (PST) MIME-Version: 1.0 References: <20221212102250.3365948-1-alexghiti@rivosinc.com> In-Reply-To: <20221212102250.3365948-1-alexghiti@rivosinc.com> From: Frank Chang Date: Fri, 16 Dec 2022 17:32:04 +0800 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: [PATCH v4] riscv: Allow user to set the satp mode To: Alexandre Ghiti Cc: Palmer Dabbelt , Alistair Francis , Bin Meng , Andrew Jones , qemu-riscv@nongnu.org, qemu-devel@nongnu.org, Ludovic Henry Content-Type: multipart/alternative; boundary="0000000000002d41f805efeea572" Received-SPF: pass client-ip=2a00:1450:4864:20::132; envelope-from=frank.chang@sifive.com; helo=mail-lf1-x132.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org --0000000000002d41f805efeea572 Content-Type: text/plain; charset="UTF-8" Hi Alexandre, Thanks for the contribution. This is really helpful. It seems like if we want to specify the SATP mode for the "named" CPUs, we have to do, e.g.: cpu->cfg.satp_mode.map |= (1 << idx_satp_mode_from_str("sv39")); in each CPU's init function. Can we add another helper function to wrap this for the "named" CPUs? Regards, Frank Chang On Mon, Dec 12, 2022 at 6:23 PM 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 > Signed-off-by: Ludovic Henry > Signed-off-by: Alexandre Ghiti > --- > v4: > - Use custom boolean properties instead of OnOffAuto properties, based > on ARMVQMap, as suggested by Andrew > > 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 | 20 +++-- > target/riscv/cpu.c | 217 +++++++++++++++++++++++++++++++++++++++++++-- > target/riscv/cpu.h | 25 ++++++ > target/riscv/csr.c | 13 ++- > 4 files changed, 256 insertions(+), 19 deletions(-) > > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > index a5bc7353b4..9bb5ba7366 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -228,7 +228,8 @@ 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; > + uint8_t satp_mode_max; > + 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 +237,15 @@ 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"); > - } > + > + satp_mode_max = satp_mode_max_from_map( > + s->soc[socket].harts[cpu].cfg.satp_mode.map, > + is_32_bit); > + sv_name = g_strdup_printf("riscv,%s", > + satp_mode_str(satp_mode_max, > is_32_bit)); > + 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..639231ce2e 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" > @@ -199,7 +200,7 @@ static const char * const riscv_intr_names[] = { > "reserved" > }; > > -static void register_cpu_props(DeviceState *dev); > +static void register_cpu_props(Object *obj); > > const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) > { > @@ -237,7 +238,7 @@ static void riscv_any_cpu_init(Object *obj) > set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU); > #endif > set_priv_version(env, PRIV_VERSION_1_12_0); > - register_cpu_props(DEVICE(obj)); > + register_cpu_props(obj); > } > > #if defined(TARGET_RISCV64) > @@ -246,7 +247,7 @@ static void rv64_base_cpu_init(Object *obj) > CPURISCVState *env = &RISCV_CPU(obj)->env; > /* We set this in the realise function */ > set_misa(env, MXL_RV64, 0); > - register_cpu_props(DEVICE(obj)); > + register_cpu_props(obj); > /* Set latest version of privileged specification */ > set_priv_version(env, PRIV_VERSION_1_12_0); > } > @@ -279,7 +280,7 @@ static void rv128_base_cpu_init(Object *obj) > CPURISCVState *env = &RISCV_CPU(obj)->env; > /* We set this in the realise function */ > set_misa(env, MXL_RV128, 0); > - register_cpu_props(DEVICE(obj)); > + register_cpu_props(obj); > /* Set latest version of privileged specification */ > set_priv_version(env, PRIV_VERSION_1_12_0); > } > @@ -289,7 +290,7 @@ static void rv32_base_cpu_init(Object *obj) > CPURISCVState *env = &RISCV_CPU(obj)->env; > /* We set this in the realise function */ > set_misa(env, MXL_RV32, 0); > - register_cpu_props(DEVICE(obj)); > + register_cpu_props(obj); > /* Set latest version of privileged specification */ > set_priv_version(env, PRIV_VERSION_1_12_0); > } > @@ -342,7 +343,7 @@ static void riscv_host_cpu_init(Object *obj) > #elif defined(TARGET_RISCV64) > set_misa(env, MXL_RV64, 0); > #endif > - register_cpu_props(DEVICE(obj)); > + register_cpu_props(obj); > } > #endif > > @@ -612,6 +613,71 @@ static void riscv_cpu_disas_set_info(CPUState *s, > disassemble_info *info) > } > } > > +#define OFFSET_SATP_MODE_64 16 > + > +static uint8_t idx_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 + OFFSET_SATP_MODE_64; > + } > + > + if (!strncmp(satp_mode_str, "sv48", 4)) { > + return VM_1_10_SV48 + OFFSET_SATP_MODE_64; > + } > + > + if (!strncmp(satp_mode_str, "sv57", 4)) { > + return VM_1_10_SV57 + OFFSET_SATP_MODE_64; > + } > + > + if (!strncmp(satp_mode_str, "sv64", 4)) { > + return VM_1_10_SV64 + OFFSET_SATP_MODE_64; > + } > + > + /* Will never get there */ > + return -1; > +} > + > +uint8_t satp_mode_max_from_map(uint32_t map, bool is_32_bit) > +{ > + return is_32_bit ? > + (31 - __builtin_clz(map & 0xFFFF)) : (31 - __builtin_clz(map >> > 16)); > +} > + > +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"; > + } > + } > + > + return NULL; > +} > + > static void riscv_cpu_realize(DeviceState *dev, Error **errp) > { > CPUState *cs = CPU(dev); > @@ -907,6 +973,30 @@ static void riscv_cpu_realize(DeviceState *dev, Error > **errp) > } > #endif > > + bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; > + > + /* > + * 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.map == 0) { > + if (riscv_feature(&cpu->env, RISCV_FEATURE_MMU)) { > + if (rv32) { > + cpu->cfg.satp_mode.map |= (1 << > idx_satp_mode_from_str("sv32")); > + } else { > + cpu->cfg.satp_mode.map |= (1 << > idx_satp_mode_from_str("sv57")); > + } > + } else { > + cpu->cfg.satp_mode.map |= (1 << > idx_satp_mode_from_str("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 +1005,115 @@ static void riscv_cpu_realize(DeviceState *dev, > Error **errp) > mcc->parent_realize(dev, errp); > } > > +static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + RISCVSATPMap *satp_map = opaque; > + uint8_t idx_satp = idx_satp_mode_from_str(name); > + bool value; > + > + value = (satp_map->map & (1 << idx_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 idx_satp = idx_satp_mode_from_str(name); > + bool value; > + > + if (!visit_type_bool(v, name, &value, errp)) { > + return; > + } > + > + if (value) { > + satp_map->map |= 1 << idx_satp; > + } > + > + satp_map->init |= 1 << idx_satp; > +} > + > +static void riscv_add_satp_mode_properties(Object *obj) > +{ > + RISCVCPU *cpu = RISCV_CPU(obj); > + > + object_property_add(obj, "mbare", "bool", cpu_riscv_get_satp, > + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); > + object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp, > + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); > + 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); > +} > + > +#define error_append_or_setg(errp, str, ...) ({ \ > + if (*errp) \ > + error_append_hint(errp, str"\n", ##__VA_ARGS__);\ > + else \ > + error_setg(errp, str, ##__VA_ARGS__); \ > + }) > + > +void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) > +{ > + bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; > + > + /* Get rid of 32-bit/64-bit incompatibility */ > + if (rv32) { > + if (cpu->cfg.satp_mode.map >= (1 << OFFSET_SATP_MODE_64)) > + error_append_or_setg(errp, "cannot enable 64-bit satp modes " > + "(sv39/sv48/sv57/sv64) if cpu is in > 32-bit " > + "mode"); > + } else { > + if (cpu->cfg.satp_mode.map & (1 << VM_1_10_SV32)) > + error_append_or_setg(errp, "cannot enable 32-bit satp mode > (sv32) " > + "if cpu is in 64-bit mode"); > + } > + > + /* > + * Then make sure the user did not ask for an invalid configuration > as per > + * the specification. > + */ > + if (rv32) { > + if (cpu->cfg.satp_mode.map & (1 << VM_1_10_SV32)) { > + if (!(cpu->cfg.satp_mode.map & (1 << VM_1_10_MBARE)) && > + (cpu->cfg.satp_mode.init & (1 << VM_1_10_MBARE))) > + error_append_or_setg(errp, "cannot disable mbare satp > mode if " > + "sv32 is enabled"); > + } > + } else { > + uint8_t satp_mode_max; > + > + satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map, > false); > + > + for (int i = satp_mode_max - 1; i >= 0; --i) { > + if (!(cpu->cfg.satp_mode.map & (1 << (i + > OFFSET_SATP_MODE_64))) && > + (cpu->cfg.satp_mode.init & (1 << (i + > OFFSET_SATP_MODE_64)))) > + error_append_or_setg(errp, "cannot disable %s satp mode > if %s " > + "is enabled", > + satp_mode_str(i, false), > + satp_mode_str(satp_mode_max, false)); > + } > + } > +} > + > +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) > { > @@ -1070,13 +1269,16 @@ static Property riscv_cpu_extensions[] = { > DEFINE_PROP_END_OF_LIST(), > }; > > -static void register_cpu_props(DeviceState *dev) > +static void register_cpu_props(Object *obj) > { > Property *prop; > + DeviceState *dev = DEVICE(obj); > > for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { > qdev_property_add_static(dev, prop); > } > + > + riscv_add_satp_mode_properties(obj); > } > > static Property riscv_cpu_properties[] = { > @@ -1094,6 +1296,7 @@ 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_END_OF_LIST(), > }; > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > index 3a9e25053f..1717b33321 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 > > @@ -407,6 +408,22 @@ struct RISCVCPUClass { > DeviceReset parent_reset; > }; > > +/* > + * map and init are divided into two 16bit bitmaps: the upper one is for > rv64 > + * and the lower one is for rv32, this is because the value for sv32 (ie. > 1) > + * may be reused later for another purpose for rv64 (see the > specification which > + * states that it is "reserved for standard use"). > + * > + * In a 16bit bitmap in map, the most significant set bit is the maximum > + * satp mode that is supported. > + * > + * Both 16bit bitmaps in init are used to make sure the user selected a > correct > + * combination as per the specification. > + */ > +typedef struct { > + uint32_t map, init; > +} RISCVSATPMap; > + > struct RISCVCPUConfig { > bool ext_i; > bool ext_e; > @@ -480,6 +497,8 @@ struct RISCVCPUConfig { > bool debug; > > bool short_isa_string; > + > + RISCVSATPMap satp_mode; > }; > > typedef struct RISCVCPUConfig RISCVCPUConfig; > @@ -789,4 +808,10 @@ void riscv_set_csr_ops(int csrno, > riscv_csr_operations *ops); > > void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); > > +uint8_t satp_mode_max_from_map(uint32_t map, bool is_32_bit); > +const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); > + > +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/csr.c b/target/riscv/csr.c > index 5c9a7ee287..5c732653b2 100644 > --- a/target/riscv/csr.c > +++ b/target/riscv/csr.c > @@ -1109,10 +1109,17 @@ static RISCVException read_mstatus(CPURISCVState > *env, int csrno, > > static int validate_vm(CPURISCVState *env, target_ulong vm) > { > - if (riscv_cpu_mxl(env) == MXL_RV32) { > - return valid_vm_1_10_32[vm & 0xf]; > + uint8_t satp_mode_max; > + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); > + bool is_32_bit = riscv_cpu_mxl(env) == MXL_RV32; > + > + vm &= 0xf; > + satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map, > is_32_bit); > + > + if (is_32_bit) { > + return valid_vm_1_10_32[vm] && (vm <= satp_mode_max); > } else { > - return valid_vm_1_10_64[vm & 0xf]; > + return valid_vm_1_10_64[vm] && (vm <= satp_mode_max); > } > } > > -- > 2.37.2 > > > --0000000000002d41f805efeea572 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Alexandre,

Thanks for the contributi= on. This is really helpful.

It seems like if we wa= nt to specify the SATP mode for the "named" CPUs,
we ha= ve to do, e.g.:
cpu->cfg.satp_mode.map |=3D (1 << idx_sa= tp_mode_from_str("sv39"));
in each CPU's init f= unction.

Can we add another helper function to wra= p this for the "named" CPUs?

Regards,
Frank Chang


On Mon, Dec 12, 2022 at 6:23 PM Ale= xandre Ghiti <alexghiti@rivosi= nc.com> wrote:
RISC-V specifies multiple sizes for addressable memory and Linux prob= es 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 th= e
"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" a= s follows:
-cpu rv64,sv57=3Don # Linux will boot using sv57 scheme
-cpu rv64,sv39=3Don # Linux will boot using sv39 scheme

We take the highest level set by the user:
-cpu rv64,sv48=3Don,sv57=3Don # Linux will boot using sv57 scheme

We make sure that invalid configurations are rejected:
-cpu rv64,sv32=3Don # Can't enable 32-bit satp mode in 64-bit
-cpu rv64,sv39=3Doff,sv48=3Don # sv39 must be supported if higher modes are=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0# enabled

We accept "redundant" configurations:
-cpu rv64,sv48=3Don,sv57=3Doff # 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 <ludovic@rivosinc.com>
Signed-off-by: Ludovic Henry <ludovic@rivosinc.com>
Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com>
---
v4:
- Use custom boolean properties instead of OnOffAuto properties, based
=C2=A0 on ARMVQMap, as suggested by Andrew

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

=C2=A0hw/riscv/virt.c=C2=A0 =C2=A0 |=C2=A0 20 +++--
=C2=A0target/riscv/cpu.c | 217 +++++++++++++++++++++++++++++++++++++++++++-= -
=C2=A0target/riscv/cpu.h |=C2=A0 25 ++++++
=C2=A0target/riscv/csr.c |=C2=A0 13 ++-
=C2=A04 files changed, 256 insertions(+), 19 deletions(-)

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index a5bc7353b4..9bb5ba7366 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -228,7 +228,8 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, i= nt socket,
=C2=A0 =C2=A0 =C2=A0int cpu;
=C2=A0 =C2=A0 =C2=A0uint32_t cpu_phandle;
=C2=A0 =C2=A0 =C2=A0MachineState *mc =3D MACHINE(s);
-=C2=A0 =C2=A0 char *name, *cpu_name, *core_name, *intc_name;
+=C2=A0 =C2=A0 uint8_t satp_mode_max;
+=C2=A0 =C2=A0 char *name, *cpu_name, *core_name, *intc_name, *sv_name;

=C2=A0 =C2=A0 =C2=A0for (cpu =3D s->soc[socket].num_harts - 1; cpu >= =3D 0; cpu--) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_phandle =3D (*phandle)++;
@@ -236,14 +237,15 @@ static void create_fdt_socket_cpus(RISCVVirtState *s,= int socket,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_name =3D g_strdup_printf("/cpus/= cpu@%d",
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->soc[socket].hartid_ba= se + cpu);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_fdt_add_subnode(mc->fdt, cpu_name= );
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_feature(&s->soc[socket].harts= [cpu].env,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 RISCV_FEATURE_MMU)) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_fdt_setprop_string(mc->f= dt, cpu_name, "mmu-type",
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (is_32_bit) ? "ri= scv,sv32" : "riscv,sv48");
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_fdt_setprop_string(mc->f= dt, cpu_name, "mmu-type",
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "riscv,none"= );
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 satp_mode_max =3D satp_mode_max_from_map(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 s->soc[socket].harts[cpu].cfg.satp_mode.map, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 is_32_bit);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sv_name =3D g_strdup_printf("riscv,%s&quo= t;,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 satp_mode_str(satp_mode_max, = is_32_bit));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_fdt_setprop_string(mc->fdt, cpu_name, = "mmu-type", sv_name);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_free(sv_name);
+
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0name =3D riscv_isa_string(&s->soc[= socket].harts[cpu]);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_fdt_setprop_string(mc->fdt, cpu_n= ame, "riscv,isa", name);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0g_free(name);
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index d14e95c9dc..639231ce2e 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -27,6 +27,7 @@
=C2=A0#include "time_helper.h"
=C2=A0#include "exec/exec-all.h"
=C2=A0#include "qapi/error.h"
+#include "qapi/visitor.h"
=C2=A0#include "qemu/error-report.h"
=C2=A0#include "hw/qdev-properties.h"
=C2=A0#include "migration/vmstate.h"
@@ -199,7 +200,7 @@ static const char * const riscv_intr_names[] =3D {
=C2=A0 =C2=A0 =C2=A0"reserved"
=C2=A0};

-static void register_cpu_props(DeviceState *dev);
+static void register_cpu_props(Object *obj);

=C2=A0const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) =C2=A0{
@@ -237,7 +238,7 @@ static void riscv_any_cpu_init(Object *obj)
=C2=A0 =C2=A0 =C2=A0set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | R= VC | RVU);
=C2=A0#endif
=C2=A0 =C2=A0 =C2=A0set_priv_version(env, PRIV_VERSION_1_12_0);
-=C2=A0 =C2=A0 register_cpu_props(DEVICE(obj));
+=C2=A0 =C2=A0 register_cpu_props(obj);
=C2=A0}

=C2=A0#if defined(TARGET_RISCV64)
@@ -246,7 +247,7 @@ static void rv64_base_cpu_init(Object *obj)
=C2=A0 =C2=A0 =C2=A0CPURISCVState *env =3D &RISCV_CPU(obj)->env;
=C2=A0 =C2=A0 =C2=A0/* We set this in the realise function */
=C2=A0 =C2=A0 =C2=A0set_misa(env, MXL_RV64, 0);
-=C2=A0 =C2=A0 register_cpu_props(DEVICE(obj));
+=C2=A0 =C2=A0 register_cpu_props(obj);
=C2=A0 =C2=A0 =C2=A0/* Set latest version of privileged specification */ =C2=A0 =C2=A0 =C2=A0set_priv_version(env, PRIV_VERSION_1_12_0);
=C2=A0}
@@ -279,7 +280,7 @@ static void rv128_base_cpu_init(Object *obj)
=C2=A0 =C2=A0 =C2=A0CPURISCVState *env =3D &RISCV_CPU(obj)->env;
=C2=A0 =C2=A0 =C2=A0/* We set this in the realise function */
=C2=A0 =C2=A0 =C2=A0set_misa(env, MXL_RV128, 0);
-=C2=A0 =C2=A0 register_cpu_props(DEVICE(obj));
+=C2=A0 =C2=A0 register_cpu_props(obj);
=C2=A0 =C2=A0 =C2=A0/* Set latest version of privileged specification */ =C2=A0 =C2=A0 =C2=A0set_priv_version(env, PRIV_VERSION_1_12_0);
=C2=A0}
@@ -289,7 +290,7 @@ static void rv32_base_cpu_init(Object *obj)
=C2=A0 =C2=A0 =C2=A0CPURISCVState *env =3D &RISCV_CPU(obj)->env;
=C2=A0 =C2=A0 =C2=A0/* We set this in the realise function */
=C2=A0 =C2=A0 =C2=A0set_misa(env, MXL_RV32, 0);
-=C2=A0 =C2=A0 register_cpu_props(DEVICE(obj));
+=C2=A0 =C2=A0 register_cpu_props(obj);
=C2=A0 =C2=A0 =C2=A0/* Set latest version of privileged specification */ =C2=A0 =C2=A0 =C2=A0set_priv_version(env, PRIV_VERSION_1_12_0);
=C2=A0}
@@ -342,7 +343,7 @@ static void riscv_host_cpu_init(Object *obj)
=C2=A0#elif defined(TARGET_RISCV64)
=C2=A0 =C2=A0 =C2=A0set_misa(env, MXL_RV64, 0);
=C2=A0#endif
-=C2=A0 =C2=A0 register_cpu_props(DEVICE(obj));
+=C2=A0 =C2=A0 register_cpu_props(obj);
=C2=A0}
=C2=A0#endif

@@ -612,6 +613,71 @@ static void riscv_cpu_disas_set_info(CPUState *s, disa= ssemble_info *info)
=C2=A0 =C2=A0 =C2=A0}
=C2=A0}

+#define OFFSET_SATP_MODE_64=C2=A0 =C2=A0 =C2=A016
+
+static uint8_t idx_satp_mode_from_str(const char *satp_mode_str)
+{
+=C2=A0 =C2=A0 if (!strncmp(satp_mode_str, "mbare", 5)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return VM_1_10_MBARE;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (!strncmp(satp_mode_str, "sv32", 4)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return VM_1_10_SV32;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (!strncmp(satp_mode_str, "sv39", 4)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return VM_1_10_SV39 + OFFSET_SATP_MODE_64;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (!strncmp(satp_mode_str, "sv48", 4)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return VM_1_10_SV48 + OFFSET_SATP_MODE_64;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (!strncmp(satp_mode_str, "sv57", 4)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return VM_1_10_SV57 + OFFSET_SATP_MODE_64;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (!strncmp(satp_mode_str, "sv64", 4)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return VM_1_10_SV64 + OFFSET_SATP_MODE_64;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* Will never get there */
+=C2=A0 =C2=A0 return -1;
+}
+
+uint8_t satp_mode_max_from_map(uint32_t map, bool is_32_bit)
+{
+=C2=A0 =C2=A0 return is_32_bit ?
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (31 - __builtin_clz(map & 0xFFFF)) : (31 -= __builtin_clz(map >> 16));
+}
+
+const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit)
+{
+=C2=A0 =C2=A0 if (is_32_bit) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (satp_mode) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_SV32:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "sv32";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_MBARE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "none";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (satp_mode) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_SV64:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "sv64";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_SV57:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "sv57";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_SV48:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "sv48";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_SV39:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "sv39";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case VM_1_10_MBARE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return "none";
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 return NULL;
+}
+
=C2=A0static void riscv_cpu_realize(DeviceState *dev, Error **errp)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0CPUState *cs =3D CPU(dev);
@@ -907,6 +973,30 @@ static void riscv_cpu_realize(DeviceState *dev, Error = **errp)
=C2=A0 =C2=A0 =C2=A0 }
=C2=A0#endif

+=C2=A0 =C2=A0 bool rv32 =3D riscv_cpu_mxl(&cpu->env) =3D=3D MXL_RV3= 2;
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* If unset by both the user and the cpu, we fallback t= o sv32 for 32-bit
+=C2=A0 =C2=A0 =C2=A0* or sv57 for 64-bit when a MMU is present, and bare o= therwise.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 if (cpu->cfg.satp_mode.map =3D=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_feature(&cpu->env, RISCV_FEAT= URE_MMU)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (rv32) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu->cfg.satp_m= ode.map |=3D (1 << idx_satp_mode_from_str("sv32"));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu->cfg.satp_m= ode.map |=3D (1 << idx_satp_mode_from_str("sv57"));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu->cfg.satp_mode.map |=3D (= 1 << idx_satp_mode_from_str("mbare"));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 riscv_cpu_finalize_features(cpu, &local_err);
+=C2=A0 =C2=A0 if (local_err !=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_propagate(errp, local_err);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
=C2=A0 =C2=A0 =C2=A0riscv_cpu_register_gdb_regs_for_features(cs);

=C2=A0 =C2=A0 =C2=A0qemu_init_vcpu(cs);
@@ -915,6 +1005,115 @@ static void riscv_cpu_realize(DeviceState *dev, Erro= r **errp)
=C2=A0 =C2=A0 =C2=A0mcc->parent_realize(dev, errp);
=C2=A0}

+static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name,<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0void *opaque, Error **errp)
+{
+=C2=A0 =C2=A0 RISCVSATPMap *satp_map =3D opaque;
+=C2=A0 =C2=A0 uint8_t idx_satp =3D idx_satp_mode_from_str(name);
+=C2=A0 =C2=A0 bool value;
+
+=C2=A0 =C2=A0 value =3D (satp_map->map & (1 << idx_satp)); +
+=C2=A0 =C2=A0 visit_type_bool(v, name, &value, errp);
+}
+
+static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name,<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0void *opaque, Error **errp)
+{
+=C2=A0 =C2=A0 RISCVSATPMap *satp_map =3D opaque;
+=C2=A0 =C2=A0 uint8_t idx_satp =3D idx_satp_mode_from_str(name);
+=C2=A0 =C2=A0 bool value;
+
+=C2=A0 =C2=A0 if (!visit_type_bool(v, name, &value, errp)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (value) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 satp_map->map |=3D 1 << idx_satp;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 satp_map->init |=3D 1 << idx_satp;
+}
+
+static void riscv_add_satp_mode_properties(Object *obj)
+{
+=C2=A0 =C2=A0 RISCVCPU *cpu =3D RISCV_CPU(obj);
+
+=C2=A0 =C2=A0 object_property_add(obj, "mbare", "bool"= , cpu_riscv_get_satp,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+=C2=A0 =C2=A0 object_property_add(obj, "sv32", "bool",= cpu_riscv_get_satp,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+=C2=A0 =C2=A0 object_property_add(obj, "sv39", "bool",= cpu_riscv_get_satp,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+=C2=A0 =C2=A0 object_property_add(obj, "sv48", "bool",= cpu_riscv_get_satp,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+=C2=A0 =C2=A0 object_property_add(obj, "sv57", "bool",= cpu_riscv_get_satp,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+=C2=A0 =C2=A0 object_property_add(obj, "sv64", "bool",= cpu_riscv_get_satp,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+}
+
+#define error_append_or_setg(errp, str, ...) ({=C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0\
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (*errp)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_append_hint(errp, str"= ;\n", ##__VA_ARGS__);\
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_setg(errp, str, ##__VA_ARG= S__);=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0\
+=C2=A0 =C2=A0 })
+
+void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
+{
+=C2=A0 =C2=A0 bool rv32 =3D riscv_cpu_mxl(&cpu->env) =3D=3D MXL_RV3= 2;
+
+=C2=A0 =C2=A0 /* Get rid of 32-bit/64-bit incompatibility */
+=C2=A0 =C2=A0 if (rv32) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (cpu->cfg.satp_mode.map >=3D (1 <&= lt; OFFSET_SATP_MODE_64))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_append_or_setg(errp, "= ;cannot enable 64-bit satp modes "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"(sv39/sv48/sv57/sv64) if= cpu is in 32-bit "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"mode");
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (cpu->cfg.satp_mode.map & (1 <<= ; VM_1_10_SV32))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_append_or_setg(errp, "= ;cannot enable 32-bit satp mode (sv32) "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"if cpu is in 64-bit mode= ");
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Then make sure the user did not ask for an invalid c= onfiguration as per
+=C2=A0 =C2=A0 =C2=A0* the specification.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 if (rv32) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (cpu->cfg.satp_mode.map & (1 <<= ; VM_1_10_SV32)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!(cpu->cfg.satp_mode.map = & (1 << VM_1_10_MBARE)) &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cpu->cfg= .satp_mode.init & (1 << VM_1_10_MBARE)))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_append_or_se= tg(errp, "cannot disable mbare satp mode if "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"sv32 is en= abled");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 uint8_t satp_mode_max;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 satp_mode_max =3D satp_mode_max_from_map(cpu-&= gt;cfg.satp_mode.map, false);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for (int i =3D satp_mode_max - 1; i >=3D 0;= --i) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!(cpu->cfg.satp_mode.map = & (1 << (i + OFFSET_SATP_MODE_64))) &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cpu->cfg= .satp_mode.init & (1 << (i + OFFSET_SATP_MODE_64))))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_append_or_se= tg(errp, "cannot disable %s satp mode if %s "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"is enabled= ",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0satp_mode_str(i,= false),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0satp_mode_str(sa= tp_mode_max, false));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+}
+
+void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
+{
+=C2=A0 =C2=A0 Error *local_err =3D NULL;
+
+=C2=A0 =C2=A0 riscv_cpu_satp_mode_finalize(cpu, &local_err);
+=C2=A0 =C2=A0 if (local_err !=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_propagate(errp, local_err);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+}
+
=C2=A0#ifndef CONFIG_USER_ONLY
=C2=A0static void riscv_cpu_set_irq(void *opaque, int irq, int level)
=C2=A0{
@@ -1070,13 +1269,16 @@ static Property riscv_cpu_extensions[] =3D {
=C2=A0 =C2=A0 =C2=A0DEFINE_PROP_END_OF_LIST(),
=C2=A0};

-static void register_cpu_props(DeviceState *dev)
+static void register_cpu_props(Object *obj)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0Property *prop;
+=C2=A0 =C2=A0 DeviceState *dev =3D DEVICE(obj);

=C2=A0 =C2=A0 =C2=A0for (prop =3D riscv_cpu_extensions; prop && pro= p->name; prop++) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qdev_property_add_static(dev, prop);
=C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 riscv_add_satp_mode_properties(obj);
=C2=A0}

=C2=A0static Property riscv_cpu_properties[] =3D {
@@ -1094,6 +1296,7 @@ static Property riscv_cpu_properties[] =3D {

=C2=A0 =C2=A0 =C2=A0DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, c= fg.rvv_ta_all_1s, false),
=C2=A0 =C2=A0 =C2=A0DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, c= fg.rvv_ma_all_1s, false),
+
=C2=A0 =C2=A0 =C2=A0DEFINE_PROP_END_OF_LIST(),
=C2=A0};

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 3a9e25053f..1717b33321 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -27,6 +27,7 @@
=C2=A0#include "qom/object.h"
=C2=A0#include "qemu/int128.h"
=C2=A0#include "cpu_bits.h"
+#include "qapi/qapi-types-common.h"

=C2=A0#define TCG_GUEST_DEFAULT_MO 0

@@ -407,6 +408,22 @@ struct RISCVCPUClass {
=C2=A0 =C2=A0 =C2=A0DeviceReset parent_reset;
=C2=A0};

+/*
+ * map and init are divided into two 16bit bitmaps: the upper one is for r= v64
+ * and the lower one is for rv32, this is because the value for sv32 (ie. = 1)
+ * may be reused later for another purpose for rv64 (see the specification= which
+ * states that it is "reserved for standard use").
+ *
+ * In a 16bit bitmap in map, the most significant set bit is the maximum + * satp mode that is supported.
+ *
+ * Both 16bit bitmaps in init are used to make sure the user selected a co= rrect
+ * combination as per the specification.
+ */
+typedef struct {
+=C2=A0 =C2=A0 uint32_t map, init;
+} RISCVSATPMap;
+
=C2=A0struct RISCVCPUConfig {
=C2=A0 =C2=A0 =C2=A0bool ext_i;
=C2=A0 =C2=A0 =C2=A0bool ext_e;
@@ -480,6 +497,8 @@ struct RISCVCPUConfig {
=C2=A0 =C2=A0 =C2=A0bool debug;

=C2=A0 =C2=A0 =C2=A0bool short_isa_string;
+
+=C2=A0 =C2=A0 RISCVSATPMap satp_mode;
=C2=A0};

=C2=A0typedef struct RISCVCPUConfig RISCVCPUConfig;
@@ -789,4 +808,10 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations= *ops);

=C2=A0void riscv_cpu_register_gdb_regs_for_features(CPUState *cs);

+uint8_t satp_mode_max_from_map(uint32_t map, bool is_32_bit);
+const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit);
+
+void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp);
+void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp);
+
=C2=A0#endif /* RISCV_CPU_H */
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 5c9a7ee287..5c732653b2 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1109,10 +1109,17 @@ static RISCVException read_mstatus(CPURISCVState *e= nv, int csrno,

=C2=A0static int validate_vm(CPURISCVState *env, target_ulong vm)
=C2=A0{
-=C2=A0 =C2=A0 if (riscv_cpu_mxl(env) =3D=3D MXL_RV32) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return valid_vm_1_10_32[vm & 0xf];
+=C2=A0 =C2=A0 uint8_t satp_mode_max;
+=C2=A0 =C2=A0 RISCVCPU *cpu =3D RISCV_CPU(env_cpu(env));
+=C2=A0 =C2=A0 bool is_32_bit =3D riscv_cpu_mxl(env) =3D=3D MXL_RV32;
+
+=C2=A0 =C2=A0 vm &=3D 0xf;
+=C2=A0 =C2=A0 satp_mode_max =3D satp_mode_max_from_map(cpu->cfg.satp_mo= de.map, is_32_bit);
+
+=C2=A0 =C2=A0 if (is_32_bit) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return valid_vm_1_10_32[vm] && (vm <= ;=3D satp_mode_max);
=C2=A0 =C2=A0 =C2=A0} else {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return valid_vm_1_10_64[vm & 0xf];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return valid_vm_1_10_64[vm] && (vm <= ;=3D satp_mode_max);
=C2=A0 =C2=A0 =C2=A0}
=C2=A0}

--
2.37.2


--0000000000002d41f805efeea572--