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

[PATCH v5 25/45] x86/mm: introduce populate_perdomain_mapping()



From: Roger Pau Monné <roger.pau@xxxxxxxxxx>

The per-domain area has central machinery for building its page-tables
and for managing the backing pages it owns itself:
create_perdomain_mapping() / destroy_perdomain_mapping(), used by the
mapcache and the compat argument-translation area.  What the interface
lacks is a way to install a caller's own pages at a chosen address,
which a per-vCPU state window, introduced by a later patch, needs: its
users allocate their pages themselves and map them there.

Introduce populate_perdomain_mapping(v, va, mfn, nr, flags) for this.
It writes the given MFNs, with the given page-table flags, into v's view
of the per-domain area, walking the per-domain page-table structures
with transient mappings, so v need not be current.  Callers don't need
to know where the page-tables live, how the area is structured, or
whether v has a per-domain area of its own or shares its domain's.

We require the range to already have been populated down to the L1
tables by create_perdomain_mapping().  TLB flushing is left to the
caller.  A present entry not owned by the area (!_PAGE_AVAIL0) is
replaced.  A present entry owned by the area (_PAGE_AVAIL0, installed
by create_perdomain_mapping() itself) is never expected: such a page is
referenced only by the mapping, and no caller replaces area-owned
backing.  Should one ever do so, the page is leaked with a message
rather than freed.

The function is not for contexts with interrupts off: its users map
their pages when allocating them.

Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
Assisted-by: Claude Code:claude-fable-5, Claude Code:claude-opus-4-8, Claude 
Code:claude-opus-5-5
Signed-off-by: George Dunlap <gwd@xxxxxxxxxxxxxx>
---
Changes in v5:
- Comment above populate_perdomain_mapping(): the range lies within one
  sub-area (one PERDOMAIN_VIRT_SLOT()), not one "per-domain slot".
- Message: "whether v has a per-domain area of its own or shares its
  domain's".

Changes in v4:
- Re-introduced into the series.
- HVM only: the users are the state window's, all of which map their
  pages at allocation time, so drop the linear-mapping fast path and the
  IRQs-off walk that context-switch callers needed, along with the
  map_domain_page_irqoff() variants.
---
 xen/arch/x86/include/asm/mm.h |  3 ++
 xen/arch/x86/mm.c             | 62 +++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h
index af43ae01b0..6d50e17259 100644
--- a/xen/arch/x86/include/asm/mm.h
+++ b/xen/arch/x86/include/asm/mm.h
@@ -620,6 +620,9 @@ int compat_arch_memory_op(unsigned long cmd, 
XEN_GUEST_HANDLE_PARAM(void) arg);
 int create_perdomain_mapping(struct vcpu *v, unsigned long va,
                              unsigned int nr, l1_pgentry_t **pl1tab,
                              struct page_info **ppg);
+void populate_perdomain_mapping(const struct vcpu *v, unsigned long va,
+                                const mfn_t *mfn, unsigned int nr,
+                                unsigned int flags);
 void destroy_perdomain_mapping(const struct vcpu *v, unsigned long va,
                                unsigned int nr);
 void free_perdomain_mappings(struct domain *d);
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 8d09a8e2c8..b10cc03840 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -6555,6 +6555,68 @@ int create_perdomain_mapping(struct vcpu *v, unsigned 
long va,
     return rc;
 }
 
+/*
+ * Map @nr pages, @mfn[0..nr-1], at consecutive pages from @va in v's view of
+ * the per-domain area, with page-table @flags.  The range must lie within a
+ * single sub-area (one PERDOMAIN_VIRT_SLOT()), and must already have been
+ * plumbed down to the L1 tables by create_perdomain_mapping().  Present
+ * entries are replaced, and no TLB flushing is done.
+ *
+ * The walk maps the per-domain page-table pages transiently, so v need not
+ * be current.  It is not for contexts with interrupts disabled.
+ */
+void populate_perdomain_mapping(const struct vcpu *v, unsigned long va,
+                                const mfn_t *mfn, unsigned int nr,
+                                unsigned int flags)
+{
+    struct page_info *l3_pg = perdomain_l3(v);
+    const l3_pgentry_t *l3tab;
+    const l2_pgentry_t *l2tab;
+    l1_pgentry_t *l1tab = NULL;
+    unsigned int i;
+
+    ASSERT(va >= PERDOMAIN_VIRT_START &&
+           va < PERDOMAIN_VIRT_SLOT(PERDOMAIN_SLOTS));
+    ASSERT(!nr || !l3_table_offset(va ^ (va + nr * PAGE_SIZE - 1)));
+    ASSERT(local_irq_is_enabled());
+
+    if ( !nr )
+        return;
+
+    BUG_ON(!l3_pg);
+    l3tab = __map_domain_page(l3_pg);
+    BUG_ON(!(l3e_get_flags(l3tab[l3_table_offset(va)]) & _PAGE_PRESENT));
+    l2tab = map_l2t_from_l3e(l3tab[l3_table_offset(va)]);
+    unmap_domain_page(l3tab);
+
+    for ( i = 0; i < nr; i++, va += PAGE_SIZE )
+    {
+        l1_pgentry_t *pl1e;
+
+        if ( !l1tab || !l1_table_offset(va) )
+        {
+            const l2_pgentry_t *pl2e = &l2tab[l2_table_offset(va)];
+
+            unmap_domain_page(l1tab);
+            BUG_ON(!(l2e_get_flags(*pl2e) & _PAGE_PRESENT));
+            l1tab = map_l1t_from_l2e(*pl2e);
+        }
+
+        pl1e = &l1tab[l1_table_offset(va)];
+        if ( perdomain_l1e_needs_freeing(*pl1e) )
+        {
+            printk(XENLOG_ERR
+                   "%pv: leaking area-owned page %"PRI_mfn" at %#lx\n",
+                   v, mfn_x(l1e_get_mfn(*pl1e)), va);
+            ASSERT_UNREACHABLE();
+        }
+        l1e_write(pl1e, l1e_from_mfn(mfn[i], flags));
+    }
+
+    unmap_domain_page(l1tab);
+    unmap_domain_page(l2tab);
+}
+
 void destroy_perdomain_mapping(const struct vcpu *v, unsigned long va,
                                unsigned int nr)
 {
-- 
2.56.0




 


Rackspace

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