[SUGGESTION] KVM: RISC-V: detect gstage mode hierarchy
Radim Krčmář
radim.krcmar at oss.qualcomm.com
Thu Jan 29 12:25:08 PST 2026
2026-01-29T15:27:35+00:00, Radim Krčmář <radim.krcmar at oss.qualcomm.com>:
> (I'll reply with a patch later.)
Something like this would avoid a bit of technical debt.
The solution could be even more generic by returning a bitmap of
supported modes, but that would be larger refactoring...
Feel free to use it in the series, but beware: only compile-tested.
It's late and hope it at least boots. :)
Thanks.
---8<---
RISC-V ISA does not require a hierarchy of standard hgatp mode (i.e.
57x4 does not depend on 48x4 onr 39x4), yet future patches want to
assume that the hierarchy exists, for simplicity.
Only accept a hgatp mode if all narrower modes are supported as well.
All sensible RISC-V implementations should have the hierarchy, since
it's very cheap to add the narrower modes.
Signed-off-by: Radim Krčmář <rkrcmar at oss.qualcomm.com>
---
The hunk that removes the default values at the beginning of gstage.c
should have been a separate patch, sorry.
---
arch/riscv/include/asm/kvm_gstage.h | 2 +-
arch/riscv/kvm/gstage.c | 66 ++++++++++++-----------------
arch/riscv/kvm/main.c | 6 ++-
3 files changed, 32 insertions(+), 42 deletions(-)
diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
index 595e2183173e..18db474ce583 100644
--- a/arch/riscv/include/asm/kvm_gstage.h
+++ b/arch/riscv/include/asm/kvm_gstage.h
@@ -67,6 +67,6 @@ void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage,
void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end);
-void kvm_riscv_gstage_mode_detect(void);
+unsigned long kvm_riscv_gstage_mode_detect(void);
#endif
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index b67d60d722c2..678b304553bc 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -11,13 +11,8 @@
#include <linux/pgtable.h>
#include <asm/kvm_gstage.h>
-#ifdef CONFIG_64BIT
-unsigned long kvm_riscv_gstage_mode __ro_after_init = HGATP_MODE_SV39X4;
-unsigned long kvm_riscv_gstage_pgd_levels __ro_after_init = 3;
-#else
-unsigned long kvm_riscv_gstage_mode __ro_after_init = HGATP_MODE_SV32X4;
-unsigned long kvm_riscv_gstage_pgd_levels __ro_after_init = 2;
-#endif
+unsigned long kvm_riscv_gstage_mode __ro_after_init;
+unsigned long kvm_riscv_gstage_pgd_levels __ro_after_init;
#define gstage_pte_leaf(__ptep) \
(pte_val(*(__ptep)) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC))
@@ -313,47 +308,38 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
}
}
-void __init kvm_riscv_gstage_mode_detect(void)
+static inline unsigned long __init __kvm_riscv_gstage_mode_detect(void)
{
#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_mode = HGATP_MODE_SV57X4;
- kvm_riscv_gstage_pgd_levels = 5;
- goto done;
- }
-
- /* 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) {
- kvm_riscv_gstage_mode = HGATP_MODE_SV48X4;
- kvm_riscv_gstage_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_mode = HGATP_MODE_SV39X4;
- kvm_riscv_gstage_pgd_levels = 3;
- goto done;
- }
+ if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) != HGATP_MODE_SV39X4)
+ return HGATP_MODE_OFF;
+
+ csr_write(CSR_HGATP, HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT);
+ if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) != HGATP_MODE_SV48X4)
+ return HGATP_MODE_SV39X4;
+
+ csr_write(CSR_HGATP, HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT);
+ if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) != HGATP_MODE_SV57X4)
+ return HGATP_MODE_SV48X4;
+
+ return HGATP_MODE_SV57X4;
#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_mode = HGATP_MODE_SV32X4;
- kvm_riscv_gstage_pgd_levels = 2;
- goto done;
- }
+ if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) != HGATP_MODE_SV32X4)
+ return HGATP_MODE_OFF;
+
+ return HGATP_MODE_SV32X4;
#endif
+}
- /* KVM depends on !HGATP_MODE_OFF */
- kvm_riscv_gstage_mode = HGATP_MODE_OFF;
- kvm_riscv_gstage_pgd_levels = 0;
+/* We could probably omit the HGATP write and fence. */
+unsigned long __init kvm_riscv_gstage_mode_detect(void)
+{
+ unsigned long gstage_mode = __kvm_riscv_gstage_mode_detect();
-done:
csr_write(CSR_HGATP, 0);
kvm_riscv_local_hfence_gvma_all();
+
+ return gstage_mode;
}
diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c
index 45536af521f0..58fd6ae8e04a 100644
--- a/arch/riscv/kvm/main.c
+++ b/arch/riscv/kvm/main.c
@@ -104,19 +104,23 @@ static int __init riscv_kvm_init(void)
if (rc && rc != -ENODEV)
return rc;
- kvm_riscv_gstage_mode_detect();
+ kvm_riscv_gstage_mode = kvm_riscv_gstage_mode_detect();
switch (kvm_riscv_gstage_mode) {
case HGATP_MODE_SV32X4:
str = "Sv32x4";
+ kvm_riscv_gstage_pgd_levels = 2;
break;
case HGATP_MODE_SV39X4:
str = "Sv39x4";
+ kvm_riscv_gstage_pgd_levels = 3;
break;
case HGATP_MODE_SV48X4:
str = "Sv48x4";
+ kvm_riscv_gstage_pgd_levels = 4;
break;
case HGATP_MODE_SV57X4:
str = "Sv57x4";
+ kvm_riscv_gstage_pgd_levels = 5;
break;
default:
kvm_riscv_nacl_exit();
--
2.51.0
More information about the linux-riscv
mailing list