From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
supported by the host and record them in a bitmask. Keep tracking the
maximum supported G-stage page table level for existing internal users.
Also provide lightweight helpers to retrieve the supported-mode bitmask
and validate a requested HGATP.MODE against it.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
arch/riscv/include/asm/kvm_gstage.h | 37 +++++++++++++++++++++++++++
arch/riscv/kvm/gstage.c | 39 ++++++++++++++++-------------
2 files changed, 58 insertions(+), 18 deletions(-)
diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
index b12605fbca44..c0c5a8b99056 100644
--- a/arch/riscv/include/asm/kvm_gstage.h
+++ b/arch/riscv/include/asm/kvm_gstage.h
@@ -30,6 +30,7 @@ struct kvm_gstage_mapping {
#endif
extern unsigned long kvm_riscv_gstage_max_pgd_levels;
+extern u32 kvm_riscv_gstage_mode_mask;
#define kvm_riscv_gstage_pgd_xbits 2
#define kvm_riscv_gstage_pgd_size (1UL << (HGATP_PAGE_SHIFT + kvm_riscv_gstage_pgd_xbits))
@@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
void kvm_riscv_gstage_mode_detect(void);
+enum kvm_riscv_hgatp_mode_bit {
+ HGATP_MODE_SV39X4_BIT = 0,
+ HGATP_MODE_SV48X4_BIT = 1,
+ HGATP_MODE_SV57X4_BIT = 2,
+};
+
+static inline u32 kvm_riscv_get_hgatp_mode_mask(void)
+{
+ return kvm_riscv_gstage_mode_mask;
+}
+
+static inline bool kvm_riscv_hgatp_mode_is_valid(unsigned long mode)
+{
+#ifdef CONFIG_64BIT
+ u32 bit;
+
+ switch (mode) {
+ case HGATP_MODE_SV39X4:
+ bit = HGATP_MODE_SV39X4_BIT;
+ break;
+ case HGATP_MODE_SV48X4:
+ bit = HGATP_MODE_SV48X4_BIT;
+ break;
+ case HGATP_MODE_SV57X4:
+ bit = HGATP_MODE_SV57X4_BIT;
+ break;
+ default:
+ return false;
+ }
+
+ return kvm_riscv_gstage_mode_mask & BIT(bit);
+#else
+ return false;
+#endif
+}
+
#endif
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 2d0045f502d1..edbabdac57d8 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -16,6 +16,8 @@ unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3;
#else
unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 2;
#endif
+/* Bitmask of supported HGATP.MODE (see HGATP_MODE_*_BIT). */
+u32 kvm_riscv_gstage_mode_mask __ro_after_init;
#define gstage_pte_leaf(__ptep) \
(pte_val(*(__ptep)) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC))
@@ -315,42 +317,43 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
}
}
+static bool __init kvm_riscv_hgatp_mode_supported(unsigned long mode)
+{
+ csr_write(CSR_HGATP, mode << HGATP_MODE_SHIFT);
+ return ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == mode);
+}
+
void __init kvm_riscv_gstage_mode_detect(void)
{
+ kvm_riscv_gstage_mode_mask = 0;
+ kvm_riscv_gstage_max_pgd_levels = 0;
+
#ifdef CONFIG_64BIT
- /* Try Sv57x4 G-stage mode */
- csr_write(CSR_HGATP, HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT);
- if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV57X4) {
- kvm_riscv_gstage_max_pgd_levels = 5;
- goto done;
+ /* Try Sv39x4 G-stage mode */
+ if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV39X4)) {
+ kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV39X4_BIT);
+ kvm_riscv_gstage_max_pgd_levels = 3;
}
/* Try Sv48x4 G-stage mode */
- csr_write(CSR_HGATP, HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT);
- if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV48X4) {
+ if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV48X4)) {
+ kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV48X4_BIT);
kvm_riscv_gstage_max_pgd_levels = 4;
- goto done;
}
- /* Try Sv39x4 G-stage mode */
- csr_write(CSR_HGATP, HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT);
- if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV39X4) {
- kvm_riscv_gstage_max_pgd_levels = 3;
- goto done;
+ /* Try Sv57x4 G-stage mode */
+ if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV57X4)) {
+ kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV57X4_BIT);
+ kvm_riscv_gstage_max_pgd_levels = 5;
}
#else /* CONFIG_32BIT */
/* Try Sv32x4 G-stage mode */
csr_write(CSR_HGATP, HGATP_MODE_SV32X4 << HGATP_MODE_SHIFT);
if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV32X4) {
kvm_riscv_gstage_max_pgd_levels = 2;
- goto done;
}
#endif
- /* KVM depends on !HGATP_MODE_OFF */
- kvm_riscv_gstage_max_pgd_levels = 0;
-
-done:
csr_write(CSR_HGATP, 0);
kvm_riscv_local_hfence_gvma_all();
}
--
2.50.1
2026-02-02T22:07:14+08:00, <fangyu.yu@linux.alibaba.com>:
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
> supported by the host and record them in a bitmask. Keep tracking the
> maximum supported G-stage page table level for existing internal users.
>
> Also provide lightweight helpers to retrieve the supported-mode bitmask
> and validate a requested HGATP.MODE against it.
>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
> +enum kvm_riscv_hgatp_mode_bit {
> + HGATP_MODE_SV39X4_BIT = 0,
> + HGATP_MODE_SV48X4_BIT = 1,
> + HGATP_MODE_SV57X4_BIT = 2,
I think it's a bit awkward to pass 9 when selecting the hgatp mode, but
then look for bit 0 when detecting it...
Why not to use the RVI defined values for this UABI as well?
There are only 16 possible hgatp.mode values, so we're fine storing them
in a bitmap even on RV32.
Thanks.
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>
>> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
>> supported by the host and record them in a bitmask. Keep tracking the
>> maximum supported G-stage page table level for existing internal users.
>>
>> Also provide lightweight helpers to retrieve the supported-mode bitmask
>> and validate a requested HGATP.MODE against it.
>>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
>> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>> +enum kvm_riscv_hgatp_mode_bit {
>> + HGATP_MODE_SV39X4_BIT = 0,
>> + HGATP_MODE_SV48X4_BIT = 1,
>> + HGATP_MODE_SV57X4_BIT = 2,
>
>I think it's a bit awkward to pass 9 when selecting the hgatp mode, but
>then look for bit 0 when detecting it...
>Why not to use the RVI defined values for this UABI as well?
>
>There are only 16 possible hgatp.mode values, so we're fine storing them
>in a bitmap even on RV32.
I think this is a good point.
Using logical bits 0/1/2 is indeed less intuitive than testing
BIT(HGATP_MODE_SV39X4) when userspace passes the architectural HGATP.MODE
encoding.
However, if we use “HGATP.MODE encoding as bit index”, we need to export
those encodings to userspace. Today HGATP_MODE_* are not part of the
UAPI, so userspace would need to hardcode magic numbers.
So if we go with this approach, I’ll add UAPI definitions for the HGATP
mode encodings (e.g. #define KVM_RISCV_HGATP_MODE_SV39X4_BIT 8, etc.) and
then define the returned bitmask as BIT(mode).
>
>Thanks.
>
Thanks,
Fangyu
On Tue, Feb 03, 2026 at 10:24:22PM +0800, fangyu.yu@linux.alibaba.com wrote:
> >> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> >>
> >> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
> >> supported by the host and record them in a bitmask. Keep tracking the
> >> maximum supported G-stage page table level for existing internal users.
> >>
> >> Also provide lightweight helpers to retrieve the supported-mode bitmask
> >> and validate a requested HGATP.MODE against it.
> >>
> >> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> >> ---
> >> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
> >> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
> >> +enum kvm_riscv_hgatp_mode_bit {
> >> + HGATP_MODE_SV39X4_BIT = 0,
> >> + HGATP_MODE_SV48X4_BIT = 1,
> >> + HGATP_MODE_SV57X4_BIT = 2,
> >
> >I think it's a bit awkward to pass 9 when selecting the hgatp mode, but
> >then look for bit 0 when detecting it...
> >Why not to use the RVI defined values for this UABI as well?
> >
> >There are only 16 possible hgatp.mode values, so we're fine storing them
> >in a bitmap even on RV32.
>
> I think this is a good point.
>
> Using logical bits 0/1/2 is indeed less intuitive than testing
> BIT(HGATP_MODE_SV39X4) when userspace passes the architectural HGATP.MODE
> encoding.
>
> However, if we use “HGATP.MODE encoding as bit index”, we need to export
> those encodings to userspace. Today HGATP_MODE_* are not part of the
> UAPI, so userspace would need to hardcode magic numbers.
>
> So if we go with this approach, I’ll add UAPI definitions for the HGATP
> mode encodings (e.g. #define KVM_RISCV_HGATP_MODE_SV39X4_BIT 8, etc.) and
> then define the returned bitmask as BIT(mode).
The best part of Radim's suggestion is that there is no need to add the
bits to UAPI. We can write in the documentation for the capability that
the mode values match the spec. kvm userspace can then just look at the
spec to determine those values and create its own defines (which QEMU,
for example, has certainly already done).
Thanks,
drew
>> >> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> >>
>> >> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
>> >> supported by the host and record them in a bitmask. Keep tracking the
>> >> maximum supported G-stage page table level for existing internal users.
>> >>
>> >> Also provide lightweight helpers to retrieve the supported-mode bitmask
>> >> and validate a requested HGATP.MODE against it.
>> >>
>> >> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> >> ---
>> >> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
>> >> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>> >> +enum kvm_riscv_hgatp_mode_bit {
>> >> + HGATP_MODE_SV39X4_BIT = 0,
>> >> + HGATP_MODE_SV48X4_BIT = 1,
>> >> + HGATP_MODE_SV57X4_BIT = 2,
>> >
>> >I think it's a bit awkward to pass 9 when selecting the hgatp mode, but
>> >then look for bit 0 when detecting it...
>> >Why not to use the RVI defined values for this UABI as well?
>> >
>> >There are only 16 possible hgatp.mode values, so we're fine storing them
>> >in a bitmap even on RV32.
>>
>> I think this is a good point.
>>
>> Using logical bits 0/1/2 is indeed less intuitive than testing
>> BIT(HGATP_MODE_SV39X4) when userspace passes the architectural HGATP.MODE
>> encoding.
>>
>> However, if we use “HGATP.MODE encoding as bit index”, we need to export
>> those encodings to userspace. Today HGATP_MODE_* are not part of the
>> UAPI, so userspace would need to hardcode magic numbers.
>>
>> So if we go with this approach, I’ll add UAPI definitions for the HGATP
>> mode encodings (e.g. #define KVM_RISCV_HGATP_MODE_SV39X4_BIT 8, etc.) and
>> then define the returned bitmask as BIT(mode).
>
>The best part of Radim's suggestion is that there is no need to add the
>bits to UAPI. We can write in the documentation for the capability that
>the mode values match the spec. kvm userspace can then just look at the
>spec to determine those values and create its own defines (which QEMU,
>for example, has certainly already done).
Makes sense, thanks.
If we use the architectural HGATP.MODE encoding as the bit index, we can
indeed avoid adding any extra *_BIT or mode constants to the UAPI.
Not sure why my replies didn’t go through yesterday.
Thanks for the review. I’ll incorporate this feedback as well as your
other suggestions and address them in the next revision of the series.
>
>Thanks,
>drew
Thanks,
Fangyu
>> >> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> >>
>> >> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
>> >> supported by the host and record them in a bitmask. Keep tracking the
>> >> maximum supported G-stage page table level for existing internal users.
>> >>
>> >> Also provide lightweight helpers to retrieve the supported-mode bitmask
>> >> and validate a requested HGATP.MODE against it.
>> >>
>> >> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> >> ---
>> >> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
>> >> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>> >> +enum kvm_riscv_hgatp_mode_bit {
>> >> + HGATP_MODE_SV39X4_BIT = 0,
>> >> + HGATP_MODE_SV48X4_BIT = 1,
>> >> + HGATP_MODE_SV57X4_BIT = 2,
>> >
>> >I think it's a bit awkward to pass 9 when selecting the hgatp mode, but
>> >then look for bit 0 when detecting it...
>> >Why not to use the RVI defined values for this UABI as well?
>> >
>> >There are only 16 possible hgatp.mode values, so we're fine storing them
>> >in a bitmap even on RV32.
>>
>> I think this is a good point.
>>
>> Using logical bits 0/1/2 is indeed less intuitive than testing
>> BIT(HGATP_MODE_SV39X4) when userspace passes the architectural HGATP.MODE
>> encoding.
>>
>> However, if we use “HGATP.MODE encoding as bit index”, we need to export
>> those encodings to userspace. Today HGATP_MODE_* are not part of the
>> UAPI, so userspace would need to hardcode magic numbers.
>>
>> So if we go with this approach, I’ll add UAPI definitions for the HGATP
>> mode encodings (e.g. #define KVM_RISCV_HGATP_MODE_SV39X4_BIT 8, etc.) and
>> then define the returned bitmask as BIT(mode).
>
>The best part of Radim's suggestion is that there is no need to add the
>bits to UAPI. We can write in the documentation for the capability that
>the mode values match the spec. kvm userspace can then just look at the
>spec to determine those values and create its own defines (which QEMU,
>for example, has certainly already done).
Makes sense, thanks.
If we use the architectural HGATP.MODE encoding as the bit index, we can
indeed avoid adding any extra *_BIT or mode constants to the UAPI.
Not sure why my replies didn’t go through yesterday.
Thanks for the review. I’ll incorporate this feedback as well as your
other suggestions and address them in the next revision of the series.
>
>Thanks,
>drew
Thanks,
Fangyu
On Mon, Feb 02, 2026 at 10:07:14PM +0800, fangyu.yu@linux.alibaba.com wrote:
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
> supported by the host and record them in a bitmask. Keep tracking the
> maximum supported G-stage page table level for existing internal users.
>
> Also provide lightweight helpers to retrieve the supported-mode bitmask
> and validate a requested HGATP.MODE against it.
>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
> arch/riscv/include/asm/kvm_gstage.h | 37 +++++++++++++++++++++++++++
> arch/riscv/kvm/gstage.c | 39 ++++++++++++++++-------------
> 2 files changed, 58 insertions(+), 18 deletions(-)
>
> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
> index b12605fbca44..c0c5a8b99056 100644
> --- a/arch/riscv/include/asm/kvm_gstage.h
> +++ b/arch/riscv/include/asm/kvm_gstage.h
> @@ -30,6 +30,7 @@ struct kvm_gstage_mapping {
> #endif
>
> extern unsigned long kvm_riscv_gstage_max_pgd_levels;
> +extern u32 kvm_riscv_gstage_mode_mask;
>
> #define kvm_riscv_gstage_pgd_xbits 2
> #define kvm_riscv_gstage_pgd_size (1UL << (HGATP_PAGE_SHIFT + kvm_riscv_gstage_pgd_xbits))
> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>
> void kvm_riscv_gstage_mode_detect(void);
>
> +enum kvm_riscv_hgatp_mode_bit {
> + HGATP_MODE_SV39X4_BIT = 0,
> + HGATP_MODE_SV48X4_BIT = 1,
> + HGATP_MODE_SV57X4_BIT = 2,
> +};
These should be defined in the UAPI, as I see the last patch of the series
does. No need to define them twice.
> +
> +static inline u32 kvm_riscv_get_hgatp_mode_mask(void)
> +{
> + return kvm_riscv_gstage_mode_mask;
> +}
> +
> +static inline bool kvm_riscv_hgatp_mode_is_valid(unsigned long mode)
> +{
> +#ifdef CONFIG_64BIT
> + u32 bit;
> +
> + switch (mode) {
> + case HGATP_MODE_SV39X4:
> + bit = HGATP_MODE_SV39X4_BIT;
> + break;
> + case HGATP_MODE_SV48X4:
> + bit = HGATP_MODE_SV48X4_BIT;
> + break;
> + case HGATP_MODE_SV57X4:
> + bit = HGATP_MODE_SV57X4_BIT;
> + break;
> + default:
> + return false;
> + }
> +
> + return kvm_riscv_gstage_mode_mask & BIT(bit);
> +#else
> + return false;
> +#endif
It seems like we're going out of our way to only provide the capability
for rv64. While the cap isn't useful for rv32, having #ifdefs in KVM and
additional paths in kvm userspace is probably worse than just having a
useless HGATP_MODE_SV32X4_BIT that rv32 userspace can set.
> +}
> +
> #endif
> diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
> index 2d0045f502d1..edbabdac57d8 100644
> --- a/arch/riscv/kvm/gstage.c
> +++ b/arch/riscv/kvm/gstage.c
> @@ -16,6 +16,8 @@ unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3;
> #else
> unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 2;
> #endif
> +/* Bitmask of supported HGATP.MODE (see HGATP_MODE_*_BIT). */
> +u32 kvm_riscv_gstage_mode_mask __ro_after_init;
>
> #define gstage_pte_leaf(__ptep) \
> (pte_val(*(__ptep)) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC))
> @@ -315,42 +317,43 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
> }
> }
>
> +static bool __init kvm_riscv_hgatp_mode_supported(unsigned long mode)
> +{
> + csr_write(CSR_HGATP, mode << HGATP_MODE_SHIFT);
> + return ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == mode);
> +}
> +
> void __init kvm_riscv_gstage_mode_detect(void)
> {
> + kvm_riscv_gstage_mode_mask = 0;
> + kvm_riscv_gstage_max_pgd_levels = 0;
> +
> #ifdef CONFIG_64BIT
> - /* Try Sv57x4 G-stage mode */
> - csr_write(CSR_HGATP, HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT);
> - if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV57X4) {
> - kvm_riscv_gstage_max_pgd_levels = 5;
> - goto done;
> + /* Try Sv39x4 G-stage mode */
> + if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV39X4)) {
> + kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV39X4_BIT);
> + kvm_riscv_gstage_max_pgd_levels = 3;
> }
>
> /* Try Sv48x4 G-stage mode */
> - csr_write(CSR_HGATP, HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT);
> - if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV48X4) {
> + if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV48X4)) {
> + kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV48X4_BIT);
> kvm_riscv_gstage_max_pgd_levels = 4;
> - goto done;
> }
>
> - /* Try Sv39x4 G-stage mode */
> - csr_write(CSR_HGATP, HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT);
> - if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV39X4) {
> - kvm_riscv_gstage_max_pgd_levels = 3;
> - goto done;
> + /* Try Sv57x4 G-stage mode */
> + if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV57X4)) {
> + kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV57X4_BIT);
> + kvm_riscv_gstage_max_pgd_levels = 5;
> }
> #else /* CONFIG_32BIT */
> /* Try Sv32x4 G-stage mode */
> csr_write(CSR_HGATP, HGATP_MODE_SV32X4 << HGATP_MODE_SHIFT);
> if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV32X4) {
Can use kvm_riscv_hgatp_mode_supported() here too.
> kvm_riscv_gstage_max_pgd_levels = 2;
> - goto done;
> }
> #endif
>
> - /* KVM depends on !HGATP_MODE_OFF */
> - kvm_riscv_gstage_max_pgd_levels = 0;
> -
> -done:
> csr_write(CSR_HGATP, 0);
> kvm_riscv_local_hfence_gvma_all();
> }
> --
> 2.50.1
>
Thanks,
drew
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>
>> Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
>> supported by the host and record them in a bitmask. Keep tracking the
>> maximum supported G-stage page table level for existing internal users.
>>
>> Also provide lightweight helpers to retrieve the supported-mode bitmask
>> and validate a requested HGATP.MODE against it.
>>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>> arch/riscv/include/asm/kvm_gstage.h | 37 +++++++++++++++++++++++++++
>> arch/riscv/kvm/gstage.c | 39 ++++++++++++++++-------------
>> 2 files changed, 58 insertions(+), 18 deletions(-)
>>
>> diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
>> index b12605fbca44..c0c5a8b99056 100644
>> --- a/arch/riscv/include/asm/kvm_gstage.h
>> +++ b/arch/riscv/include/asm/kvm_gstage.h
>> @@ -30,6 +30,7 @@ struct kvm_gstage_mapping {
>> #endif
>>
>> extern unsigned long kvm_riscv_gstage_max_pgd_levels;
>> +extern u32 kvm_riscv_gstage_mode_mask;
>>
>> #define kvm_riscv_gstage_pgd_xbits 2
>> #define kvm_riscv_gstage_pgd_size (1UL << (HGATP_PAGE_SHIFT + kvm_riscv_gstage_pgd_xbits))
>> @@ -75,4 +76,40 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>>
>> void kvm_riscv_gstage_mode_detect(void);
>>
>> +enum kvm_riscv_hgatp_mode_bit {
>> + HGATP_MODE_SV39X4_BIT = 0,
>> + HGATP_MODE_SV48X4_BIT = 1,
>> + HGATP_MODE_SV57X4_BIT = 2,
>> +};
>
>These should be defined in the UAPI, as I see the last patch of the series
>does. No need to define them twice.
ok, I'll drop the duplicated enum from the non-UAPI header and reuse the UAPI
definitions instead.
>
>> +
>> +static inline u32 kvm_riscv_get_hgatp_mode_mask(void)
>> +{
>> + return kvm_riscv_gstage_mode_mask;
>> +}
>> +
>> +static inline bool kvm_riscv_hgatp_mode_is_valid(unsigned long mode)
>> +{
>> +#ifdef CONFIG_64BIT
>> + u32 bit;
>> +
>> + switch (mode) {
>> + case HGATP_MODE_SV39X4:
>> + bit = HGATP_MODE_SV39X4_BIT;
>> + break;
>> + case HGATP_MODE_SV48X4:
>> + bit = HGATP_MODE_SV48X4_BIT;
>> + break;
>> + case HGATP_MODE_SV57X4:
>> + bit = HGATP_MODE_SV57X4_BIT;
>> + break;
>> + default:
>> + return false;
>> + }
>> +
>> + return kvm_riscv_gstage_mode_mask & BIT(bit);
>> +#else
>> + return false;
>> +#endif
>
>It seems like we're going out of our way to only provide the capability
>for rv64. While the cap isn't useful for rv32, having #ifdefs in KVM and
>additional paths in kvm userspace is probably worse than just having a
>useless HGATP_MODE_SV32X4_BIT that rv32 userspace can set.
>
Agreed.
I'll drop the CONFIG_64BIT conditional and make the mode validation work
for both RV32 and RV64. On RV32 we'll define/accept an HGATP_MODE_SV32X4
bit.
>> +}
>> +
>> #endif
>> diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
>> index 2d0045f502d1..edbabdac57d8 100644
>> --- a/arch/riscv/kvm/gstage.c
>> +++ b/arch/riscv/kvm/gstage.c
>> @@ -16,6 +16,8 @@ unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3;
>> #else
>> unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 2;
>> #endif
>> +/* Bitmask of supported HGATP.MODE (see HGATP_MODE_*_BIT). */
>> +u32 kvm_riscv_gstage_mode_mask __ro_after_init;
>>
>> #define gstage_pte_leaf(__ptep) \
>> (pte_val(*(__ptep)) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC))
>> @@ -315,42 +317,43 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>> }
>> }
>>
>> +static bool __init kvm_riscv_hgatp_mode_supported(unsigned long mode)
>> +{
>> + csr_write(CSR_HGATP, mode << HGATP_MODE_SHIFT);
>> + return ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == mode);
>> +}
>> +
>> void __init kvm_riscv_gstage_mode_detect(void)
>> {
>> + kvm_riscv_gstage_mode_mask = 0;
>> + kvm_riscv_gstage_max_pgd_levels = 0;
>> +
>> #ifdef CONFIG_64BIT
>> - /* Try Sv57x4 G-stage mode */
>> - csr_write(CSR_HGATP, HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT);
>> - if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV57X4) {
>> - kvm_riscv_gstage_max_pgd_levels = 5;
>> - goto done;
>> + /* Try Sv39x4 G-stage mode */
>> + if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV39X4)) {
>> + kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV39X4_BIT);
>> + kvm_riscv_gstage_max_pgd_levels = 3;
>> }
>>
>> /* Try Sv48x4 G-stage mode */
>> - csr_write(CSR_HGATP, HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT);
>> - if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV48X4) {
>> + if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV48X4)) {
>> + kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV48X4_BIT);
>> kvm_riscv_gstage_max_pgd_levels = 4;
>> - goto done;
>> }
>>
>> - /* Try Sv39x4 G-stage mode */
>> - csr_write(CSR_HGATP, HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT);
>> - if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV39X4) {
>> - kvm_riscv_gstage_max_pgd_levels = 3;
>> - goto done;
>> + /* Try Sv57x4 G-stage mode */
>> + if (kvm_riscv_hgatp_mode_supported(HGATP_MODE_SV57X4)) {
>> + kvm_riscv_gstage_mode_mask |= BIT(HGATP_MODE_SV57X4_BIT);
>> + kvm_riscv_gstage_max_pgd_levels = 5;
>> }
>> #else /* CONFIG_32BIT */
>> /* Try Sv32x4 G-stage mode */
>> csr_write(CSR_HGATP, HGATP_MODE_SV32X4 << HGATP_MODE_SHIFT);
>> if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV32X4) {
>
>Can use kvm_riscv_hgatp_mode_supported() here too.
Sure.
I'll switch the 32-bit path to use kvm_riscv_hgatp_mode_supported() as well,
so both RV32 and RV64 use the same helper to probe HGATP mode support.
>
>> kvm_riscv_gstage_max_pgd_levels = 2;
>> - goto done;
>> }
>> #endif
>>
>> - /* KVM depends on !HGATP_MODE_OFF */
>> - kvm_riscv_gstage_max_pgd_levels = 0;
>> -
>> -done:
>> csr_write(CSR_HGATP, 0);
>> kvm_riscv_local_hfence_gvma_all();
>> }
>> --
>> 2.50.1
>>
>
>Thanks,
>drew
>
Thanks,
Fangyu
© 2016 - 2026 Red Hat, Inc.