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 <[email protected]>
> Signed-off-by: Tianze Wu <[email protected]>
> Signed-off-by: Shunchao Hu <[email protected]>
> ---
>  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 <[email protected]>
> 

Reply via email to