[PATCH v2 09/11] xen/riscv: introduce p2m_gpa_bits

Oleksii Kurochko posted 11 patches 3 hours ago
[PATCH v2 09/11] xen/riscv: introduce p2m_gpa_bits
Posted by Oleksii Kurochko 3 hours ago
p2m_gpa_bits is used by common/device-tree/domain-build.c thereby when
CONFIG_DOMAIN_BUILD_HELPERS=y it is necessary to have p2m_gpa_bits properly
defined as it is going to be used to find unused regions.

Introduce default_gstage_mode to have ability to limit p2m_gpa_bits before
p2m_init() is being called as it will be too late.

Limit p2m_gpa_bits in guest_mm_init() as it could be that default G-stage
MMU mode uses less VA wide bits than IOMMU, so p2m_gpa_bits should be
restricted more so that dom0less code uses the correct GPA bits.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
---
Changes in v2:
 - New patch.
---
 xen/arch/riscv/include/asm/p2m.h | 10 ++++++++--
 xen/arch/riscv/p2m.c             | 34 ++++++++++++++++++++++++++++----
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/xen/arch/riscv/include/asm/p2m.h b/xen/arch/riscv/include/asm/p2m.h
index 54ea67990f06..76b30af8dacb 100644
--- a/xen/arch/riscv/include/asm/p2m.h
+++ b/xen/arch/riscv/include/asm/p2m.h
@@ -32,10 +32,13 @@
  */
 #define P2M_LEVEL_ORDER(lvl) XEN_PT_LEVEL_ORDER(lvl)
 
-#define P2M_ROOT_EXTRA_BITS(p2m, lvl) (2 * ((lvl) == P2M_ROOT_LEVEL(p2m)))
+#define P2M_ROOT_EXTRA_BITS 2
+
+#define P2M_LEVEL_EXTRA_BITS(p2m, lvl) \
+    (P2M_ROOT_EXTRA_BITS * ((lvl) == P2M_ROOT_LEVEL(p2m)))
 
 #define P2M_PAGETABLE_ENTRIES(p2m, lvl) \
-    (BIT(PAGETABLE_ORDER + P2M_ROOT_EXTRA_BITS(p2m, lvl), UL))
+    (BIT(PAGETABLE_ORDER + P2M_LEVEL_EXTRA_BITS(p2m, lvl), UL))
 
 #define P2M_TABLE_OFFSET(p2m, lvl) (P2M_PAGETABLE_ENTRIES(p2m, lvl) - 1UL)
 
@@ -44,6 +47,9 @@
 #define P2M_LEVEL_MASK(p2m, lvl) \
     (P2M_TABLE_OFFSET(p2m, lvl) << P2M_GFN_LEVEL_SHIFT(lvl))
 
+/* Holds the bit size of GPAs in p2m tables */
+extern unsigned int p2m_gpa_bits;
+
 #define paddr_bits PADDR_BITS
 
 /* Get host p2m table */
diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c
index 11beaeead5ac..cd682d6586c7 100644
--- a/xen/arch/riscv/p2m.c
+++ b/xen/arch/riscv/p2m.c
@@ -51,6 +51,24 @@ static struct gstage_mode_desc __ro_after_init max_gstage_mode = {
     .name = "Bare",
 };
 
+static struct gstage_mode_desc __ro_after_init default_gstage_mode = {
+    .mode = HGATP_MODE_SV39X4,
+    .paging_levels = 2,
+    .name = "Sv39x4",
+};
+
+/*
+ * Set to the maximum configured support for GPA bits, so the number of GPA
+ * bits can be restricted by an external entity (e.g. IOMMU) and the
+ * restriction must happen before the call of guest_mm_init().
+ *
+ * The widest G-stage mode defined by the RISC-V specification is Sv57x4,
+ * which yields 59-bit GPAs: Sv57 maps 57-bit VAs onto 56-bit PAs (PADDR_BITS),
+ * and the G-stage "x4" extension widens the address space by a further 2 bits,
+ * hence PADDR_BITS + 1 + P2M_ROOT_EXTRA_BITS.
+ */
+unsigned int __ro_after_init p2m_gpa_bits = PADDR_BITS + P2M_ROOT_EXTRA_BITS + 1;
+
 static void p2m_free_page(struct p2m_domain *p2m, struct page_info *pg);
 
 static inline void p2m_free_metadata_page(struct p2m_domain *p2m,
@@ -191,8 +209,13 @@ static void __init gstage_mode_detect(void)
 
 void __init guest_mm_init(void)
 {
+    unsigned int gpa_bits;
+    unsigned int paging_levels = default_gstage_mode.paging_levels;
+
     gstage_mode_detect();
 
+    ASSERT(default_gstage_mode.paging_levels <= max_gstage_mode.paging_levels);
+
     vmid_init();
 
     /*
@@ -226,6 +249,11 @@ void __init guest_mm_init(void)
      * so it could be that we polluted local TLB so flush all guest TLB.
      */
     local_hfence_gvma_all();
+
+    gpa_bits = P2M_GFN_LEVEL_SHIFT(paging_levels + 1) + P2M_ROOT_EXTRA_BITS;
+
+    if ( gpa_bits < p2m_gpa_bits )
+        p2m_gpa_bits = gpa_bits;
 }
 
 /*
@@ -363,9 +391,7 @@ int p2m_init(struct domain *d)
 #endif
 
     /* TODO: don't hardcode used for a domain g-stage mode. */
-    p2m->mode.mode = HGATP_MODE_SV39X4;
-    p2m->mode.paging_levels = 2;
-    safe_strcpy(p2m->mode.name, "Sv39x4");
+    p2m->mode = default_gstage_mode;
 
     return 0;
 }
@@ -1304,7 +1330,7 @@ static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
 {
     unsigned int level = P2M_ROOT_LEVEL(p2m);
     unsigned int gfn_limit_bits =
-        P2M_LEVEL_ORDER(level + 1) + P2M_ROOT_EXTRA_BITS(p2m, level);
+        P2M_LEVEL_ORDER(level + 1) + P2M_LEVEL_EXTRA_BITS(p2m, level);
     pte_t entry, *table;
     int rc;
     mfn_t mfn = INVALID_MFN;
-- 
2.53.0