|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |