[PATCH] target/riscv: add hgatp-specific WARL legalization

Shunchao Hu posted 1 patch 6 days, 4 hours ago
There is a newer version of this series
target/riscv/cpu_bits.h |  8 ++++++++
target/riscv/tcg/csr.c  | 43 +++++++++++++++++++++++++++++++++++++++++--
2 files changed, 49 insertions(+), 2 deletions(-)
[PATCH] target/riscv: add hgatp-specific WARL legalization
Posted by Shunchao Hu 6 days, 4 hours ago
According to the Hypervisor Extension in RISC-V spec:
- VMIDMAX is 7 bits for RV32 and 14 bits for RV64
- the fields of hgatp are WARL in the normal way
- the lowest two bits of the physical page number
  (PPN) in hgatp always read as zeros

These behaviors differ from those of the satp CSR,
so hgatp cannot reuse legalize_xatp(). Add a dedicated
legalize_hgatp() helper to implement the hgatp-specific
WARL semantics.

Co-authored-by: Tianze Wu <wutianze@ict.ac.cn>
Signed-off-by: Tianze Wu <wutianze@ict.ac.cn>
Signed-off-by: Shunchao Hu <hushunchao@bosc.ac.cn>
---
 target/riscv/cpu_bits.h |  8 ++++++++
 target/riscv/tcg/csr.c  | 43 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index c01050ce2b..c4a914411b 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -711,6 +711,14 @@ typedef enum {
 #define SATP64_ASID         0x0FFFF00000000000ULL
 #define SATP64_PPN          0x00000FFFFFFFFFFFULL
 
+/* hgatp CSR field masks */
+#define HGATP32_MODE        SATP32_MODE
+#define HGATP32_VMID        0x1FC00000
+#define HGATP32_PPN         SATP32_PPN
+#define HGATP64_MODE        SATP64_MODE
+#define HGATP64_VMID        0x03FFF00000000000ULL
+#define HGATP64_PPN         SATP64_PPN
+
 /* RNMI mnstatus CSR mask */
 #define MNSTATUS_NMIE       0x00000008
 #define MNSTATUS_MNPV       0x00000080
diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index 061bc9db77..ef08600ca7 100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -5050,17 +5050,56 @@ static RISCVException read_hgeip(CPURISCVState *env, int csrno,
     return RISCV_EXCP_NONE;
 }
 
+static target_ulong hgatp_mask(CPURISCVState *env)
+{
+    target_ulong mask;
+
+    if (riscv_cpu_mxl(env) == MXL_RV32) {
+        mask = HGATP32_MODE | HGATP32_VMID | HGATP32_PPN;
+    } else {
+        mask = HGATP64_MODE | HGATP64_VMID | HGATP64_PPN;
+    }
+
+    /* G-stage x4 root page tables are always 16 KiB aligned. */
+    return mask & ~(target_ulong)3;
+}
+
+static target_ulong legalize_hgatp(CPURISCVState *env,
+                                   target_ulong old_hgatp,
+                                   target_ulong val)
+{
+    target_ulong mode_mask = riscv_cpu_mxl(env) == MXL_RV32 ?
+                             HGATP32_MODE : HGATP64_MODE;
+    target_ulong hgatp = val & hgatp_mask(env);
+    target_ulong mode = get_field(hgatp, mode_mask);
+
+    /*
+     * Unlike satp, an unsupported hgatp.MODE does not discard the whole
+     * write.  Keep the old MODE while accepting the other WARL fields.
+     */
+    if (!validate_vm(env, mode)) {
+        hgatp = set_field(hgatp, mode_mask,
+                          get_field(old_hgatp, mode_mask));
+    }
+
+    if (hgatp != old_hgatp) {
+        tlb_flush(env_cpu(env));
+    }
+
+    return hgatp;
+}
+
 static RISCVException read_hgatp(CPURISCVState *env, int csrno,
                                  target_ulong *val)
 {
-    *val = env->hgatp;
+    *val = env->hgatp & hgatp_mask(env);
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException write_hgatp(CPURISCVState *env, int csrno,
                                   target_ulong val, uintptr_t ra)
 {
-    env->hgatp = legalize_xatp(env, env->hgatp, val);
+    env->hgatp = legalize_hgatp(env, env->hgatp, val);
     return RISCV_EXCP_NONE;
 }
 

---
base-commit: c1c18d1e640b64292859ce9f30f3c344edfb0294
change-id: 20260920-riscv-hgatp-warl-1e48fd321188

Best regards,
--  
Shunchao Hu <hushunchao@bosc.ac.cn>
Re: [PATCH] target/riscv: add hgatp-specific WARL legalization
Posted by Chao Liu 4 days, 10 hours ago
On Sun, Sep 20, 2026 at 10:39:39PM +0800, Shunchao Hu wrote:
> According to the Hypervisor Extension in RISC-V spec:
> - VMIDMAX is 7 bits for RV32 and 14 bits for RV64
> - the fields of hgatp are WARL in the normal way
> - the lowest two bits of the physical page number
>   (PPN) in hgatp always read as zeros
> 
> These behaviors differ from those of the satp CSR,
> so hgatp cannot reuse legalize_xatp(). Add a dedicated
> legalize_hgatp() helper to implement the hgatp-specific
> WARL semantics.
> 
> Co-authored-by: Tianze Wu <wutianze@ict.ac.cn>
> Signed-off-by: Tianze Wu <wutianze@ict.ac.cn>
> Signed-off-by: Shunchao Hu <hushunchao@bosc.ac.cn>
> ---
>  target/riscv/cpu_bits.h |  8 ++++++++
>  target/riscv/tcg/csr.c  | 43 +++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 49 insertions(+), 2 deletions(-)
> 
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index c01050ce2b..c4a914411b 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -711,6 +711,14 @@ typedef enum {
>  #define SATP64_ASID         0x0FFFF00000000000ULL
>  #define SATP64_PPN          0x00000FFFFFFFFFFFULL
>  
> +/* hgatp CSR field masks */
> +#define HGATP32_MODE        SATP32_MODE
> +#define HGATP32_VMID        0x1FC00000
> +#define HGATP32_PPN         SATP32_PPN
> +#define HGATP64_MODE        SATP64_MODE
> +#define HGATP64_VMID        0x03FFF00000000000ULL
> +#define HGATP64_PPN         SATP64_PPN
> +
>  /* RNMI mnstatus CSR mask */
>  #define MNSTATUS_NMIE       0x00000008
>  #define MNSTATUS_MNPV       0x00000080
> diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
> index 061bc9db77..ef08600ca7 100644
> --- a/target/riscv/tcg/csr.c
> +++ b/target/riscv/tcg/csr.c
> @@ -5050,17 +5050,56 @@ static RISCVException read_hgeip(CPURISCVState *env, int csrno,
>      return RISCV_EXCP_NONE;
>  }
>  
> +static target_ulong hgatp_mask(CPURISCVState *env)
> +{
> +    target_ulong mask;
> +
> +    if (riscv_cpu_mxl(env) == MXL_RV32) {
> +        mask = HGATP32_MODE | HGATP32_VMID | HGATP32_PPN;
> +    } else {
> +        mask = HGATP64_MODE | HGATP64_VMID | HGATP64_PPN;
> +    }
> +
> +    /* G-stage x4 root page tables are always 16 KiB aligned. */
> +    return mask & ~(target_ulong)3;
> +}
> +
> +static target_ulong legalize_hgatp(CPURISCVState *env,
> +                                   target_ulong old_hgatp,
> +                                   target_ulong val)
> +{
> +    target_ulong mode_mask = riscv_cpu_mxl(env) == MXL_RV32 ?
> +                             HGATP32_MODE : HGATP64_MODE;
> +    target_ulong hgatp = val & hgatp_mask(env);
> +    target_ulong mode = get_field(hgatp, mode_mask);
> +
> +    /*
> +     * Unlike satp, an unsupported hgatp.MODE does not discard the whole
> +     * write.  Keep the old MODE while accepting the other WARL fields.
> +     */
> +    if (!validate_vm(env, mode)) {
> +        hgatp = set_field(hgatp, mode_mask,
> +                          get_field(old_hgatp, mode_mask));
> +    }
Looks good to me. One thing I want to note is that the RISC-V H Extension Spec
does not actually define how an unsupported hgatp.MODE write should be handled.
It only says that the write must not be ignored ad it is for satp, and similar
WARL, see:

    A write to hgatp with an unsupported MODE value is not ignored as it is for satp.
    Instead, the fields of hgatp are WARL in the normal way, when so indicated [1].

So keeping the old MODE is a valid choice, but not the only one. Could we make
the comment a bit more neutral?

[1]: https://docs.riscv.org/reference/isa/v20260120/priv/hypervisor.html#norm:hgatp-mode_warl

Thanks,
Chao
> +
> +    if (hgatp != old_hgatp) {
> +        tlb_flush(env_cpu(env));
> +    }
> +
> +    return hgatp;
> +}
> +
>  static RISCVException read_hgatp(CPURISCVState *env, int csrno,
>                                   target_ulong *val)
>  {
> -    *val = env->hgatp;
> +    *val = env->hgatp & hgatp_mask(env);
>      return RISCV_EXCP_NONE;
>  }
>  
>  static RISCVException write_hgatp(CPURISCVState *env, int csrno,
>                                    target_ulong val, uintptr_t ra)
>  {
> -    env->hgatp = legalize_xatp(env, env->hgatp, val);
> +    env->hgatp = legalize_hgatp(env, env->hgatp, val);
>      return RISCV_EXCP_NONE;
>  }
>  
> 
> ---
> base-commit: c1c18d1e640b64292859ce9f30f3c344edfb0294
> change-id: 20260920-riscv-hgatp-warl-1e48fd321188
> 
> Best regards,
> --  
> Shunchao Hu <hushunchao@bosc.ac.cn>
>
Re: [PATCH] target/riscv: add hgatp-specific WARL legalization
Posted by 胡顺超 1 day, 5 hours ago
> So keeping the old MODE is a valid choice, but not the only one. Could we make
> the comment a bit more neutral?

Agree. Keeping the old mode value is just one valid choice.I'll reword the comment
accordingly in v2.

Thanks,
Shunchao