[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v4 19/30] x86/mm: run guest contexts on the sparse directmap view



In init_xen_l4_slots(), if d->arch.asi.sparse_dmap is set, point the
directmap slots at the sparse view, copying from idle_pg_table only the
Xen slots below them.  A root page-table not shared with a PV guest gets
no extended directmap, which the sparse view does not cover.

d->arch.asi.sparse_dmap is decided at domain creation: PV domains all
have a mapcache, HVM domains need per-vCPU page-tables for one.  No
option asks for the sparse view yet.

The sparse view is built before any domain is created, so the root
page-tables of every domain using it carry it from their creation,
dom0's included.  Dom0's builders reach dom0's memory only through guest
virtual addresses and map_domain_page().  In PV shim mode,
replace_va_mapping() reads the guest's L4 through the directmap; map it
with map_domain_page() instead, as the lower levels already are.  Shadow
L4s, dom0=shadow's included, get the sparse view from
init_xen_l4_slots().

The two directmaps differ where the sparse view leaves memory out, so no
translation of the full directmap may remain usable on page-tables
carrying the sparse view.  Directmap mappings stay global nevertheless:
every CR3 write which can move a CPU onto the sparse view goes through
switch_cr3_cr4(), which flushes the old page-tables' translations,
global entries included, before it returns.  The CR3 writes which keep
entries -- a PV vCPU's switch between its kernel and user page-tables,
XPTI's entry and exit -- stay within one vCPU's page-tables, which carry
the same directmap, or go to XPTI's per-CPU root, which maps only Xen's
own per-CPU pages there.  Removals from the sparse view are flushed on
every CPU, global entries included.  PV guests running with global
pages, a PV dom0 not using the sparse view among them, so keep their
directmap TLB entries across switches between guest kernel and user
mode.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: George Dunlap <gwd@xxxxxxxxxxxxxx>
---
Changes in v4:
- d->arch.vcpu_pt and d->arch.sparse_dmap are now d->arch.asi.vcpu_pt
  and d->arch.asi.sparse_dmap.
- Drop sparse_dmap_active and sparse_dmap_activate(): root page-tables
  carry the sparse view from their creation, dom0's included (Roger).
- The sparse view's slots are now marked NX in "x86/mm: build and
  maintain a sparse view of the directmap".
- PV shim: replace_va_mapping() maps the guest's L4 with
  map_domain_page().
- Give sparse_dmap_wanted() its hardware-domain case (Roger).
- init_xen_l4_slots() takes the directmap slots from the sparse view,
  rather than copying the full directmap's and overwriting them;
  sparse_dmap_install() goes (Roger).
- Message: drop the sentence on NX (Roger).

Changes in v3:
- New in this version.
---
 xen/arch/x86/include/asm/sparse-directmap.h |  9 +++++++
 xen/arch/x86/mm.c                           | 28 +++++++++++++++++++--
 xen/arch/x86/pv/shim.c                      |  4 ++-
 xen/arch/x86/spec_ctrl.c                    |  7 ++++++
 4 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/xen/arch/x86/include/asm/sparse-directmap.h 
b/xen/arch/x86/include/asm/sparse-directmap.h
index ea14d44334..2f8b94f4c1 100644
--- a/xen/arch/x86/include/asm/sparse-directmap.h
+++ b/xen/arch/x86/include/asm/sparse-directmap.h
@@ -31,6 +31,14 @@ static inline bool sparse_dmap_configured(void)
     return opt_sparse_dmap_pv || opt_sparse_dmap_hvm || opt_sparse_dmap_hwdom;
 }
 
+/* Whether a new domain of the given type is to use the sparse view. */
+static inline bool sparse_dmap_wanted(bool pv, bool hwdom)
+{
+    return sparse_dmap_root &&
+           (hwdom ? opt_sparse_dmap_hwdom
+                  : pv ? opt_sparse_dmap_pv : opt_sparse_dmap_hvm);
+}
+
 void sparse_dmap_init(void);
 int sparse_dmap_mirror_map(unsigned long virt, mfn_t mfn, unsigned long nr,
                            pte_attr_t flags);
@@ -42,6 +50,7 @@ int sparse_dmap_drop_pagetable(mfn_t mfn);
 #else /* !CONFIG_SPARSE_DIRECTMAP */
 
 static inline bool sparse_dmap_configured(void) { return false; }
+static inline bool sparse_dmap_wanted(bool pv, bool hwdom) { return false; }
 static inline void sparse_dmap_init(void) {}
 static inline int sparse_dmap_mirror_map(unsigned long virt, mfn_t mfn,
                                          unsigned long nr, pte_attr_t flags)
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 7e3009251c..7b5a355e6e 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -1703,7 +1703,8 @@ struct page_info *perdomain_l3(const struct vcpu *v)
  * l4t, l4mfn, and v are mandatory, but l4mfn doesn't need to be the mfn under
  * *l4t.  All other parameters are optional and will either fill or zero the
  * appropriate slots.  Pagetables not shared with guests will gain the
- * extended directmap.
+ * extended directmap, unless the domain runs on the sparse view of the
+ * directmap, which replaces the directmap slots.
  *
  * v is the vCPU the table is private to or, for a table the domain's vCPUs
  * share, any of them: slot 260 gets v's per-domain area (perdomain_l3()),
@@ -1715,7 +1716,7 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn,
     const struct domain *d = v->domain;
     /*
      * PV vcpus need a shortened directmap.  HVM and Idle vcpus get the full
-     * directmap.
+     * directmap, unless the domain runs on the sparse view (below).
      */
     bool short_directmap = !paging_mode_external(d);
 
@@ -1750,6 +1751,29 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn,
             l4t[l4_table_offset(PERDOMAIN_VIRT_START)];
 
     /* Slot 261-: text/data/bss, RW M2P, vmap, frametable, directmap. */
+#ifdef CONFIG_SPARSE_DIRECTMAP
+    if ( d->arch.asi.sparse_dmap )
+    {
+        unsigned int i = l4_table_offset(SPARSE_DMAP_START);
+
+        memcpy(&l4t[l4_table_offset(XEN_VIRT_START)],
+               &idle_pg_table[l4_table_offset(XEN_VIRT_START)],
+               (i - l4_table_offset(XEN_VIRT_START)) * sizeof(*l4t));
+
+        /*
+         * Every slot the sparse view covers gets its L3, whatever the full
+         * directmap has there, so that memory added later is covered too.  A
+         * PV root has guest slots above; any other root loses the rest of the
+         * directmap, which the sparse view does not cover.
+         */
+        for ( ; i < l4_table_offset(SPARSE_DMAP_END); i++ )
+            l4t[i] = sparse_dmap_root[i];
+        if ( !short_directmap )
+            for ( ; i < l4_table_offset(DIRECTMAP_VIRT_END); i++ )
+                l4t[i] = l4e_empty();
+    }
+    else
+#endif
 #ifndef NDEBUG
     if ( short_directmap &&
          unlikely(root_pgt_pv_xen_slots < ROOT_PAGETABLE_PV_XEN_SLOTS) )
diff --git a/xen/arch/x86/pv/shim.c b/xen/arch/x86/pv/shim.c
index 41000b0523..2a47dbc56c 100644
--- a/xen/arch/x86/pv/shim.c
+++ b/xen/arch/x86/pv/shim.c
@@ -148,12 +148,14 @@ const struct platform_bad_page *__init 
pv_shim_reserved_pages(unsigned int *size
 static void __init replace_va_mapping(struct domain *d, l4_pgentry_t *l4start,
                                       unsigned long va, mfn_t mfn)
 {
-    l4_pgentry_t l4e = l4start[l4_table_offset(va)];
+    const l4_pgentry_t *l4t = map_domain_page(virt_to_mfn(l4start));
+    l4_pgentry_t l4e = l4t[l4_table_offset(va)];
     l3_pgentry_t l3e = l3e_from_l4e(l4e, l3_table_offset(va));
     l2_pgentry_t l2e = l2e_from_l3e(l3e, l2_table_offset(va));
     l1_pgentry_t *pl1e = map_l1t_from_l2e(l2e) + l1_table_offset(va);
     struct page_info *page = mfn_to_page(l1e_get_mfn(*pl1e));
 
+    unmap_domain_page(l4t);
     put_page_and_type(page);
 
     *pl1e = l1e_from_mfn(mfn, (!is_pv_32bit_domain(d) ? L1_PROT
diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c
index 66c878212a..32e6ba28a3 100644
--- a/xen/arch/x86/spec_ctrl.c
+++ b/xen/arch/x86/spec_ctrl.c
@@ -1943,6 +1943,13 @@ void spec_ctrl_init_domain(struct domain *d)
 
     d->arch.asi.vcpu_pt = !pv && (is_hardware_domain(d) ? opt_pvh_vcpu_pt_hwdom
                                                         : opt_vcpu_pt_hvm);
+
+    /*
+     * The sparse directmap view needs a mapcache: PV domains all have one,
+     * HVM domains through per-vCPU page-tables.
+     */
+    d->arch.asi.sparse_dmap = (pv || d->arch.asi.vcpu_pt) &&
+                              sparse_dmap_wanted(pv, is_hardware_domain(d));
 }
 
 void __init init_speculation_mitigations(void)
-- 
2.55.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.