[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [V8 PATCH 5/8] pvh dom0: Add and remove foreign pages
In this patch, a new function, p2m_add_foreign(), is added to map pages from foreign guest into dom0 for domU creation. Such pages are typed p2m_map_foreign. Note, it is the nature of such pages that a refcnt is held during their stay in the p2m. The refcnt is added and released in the low level ept function atomic_write_ept_entry for convenience. Finally, p2m_teardown adds a call to allow for the cleanup of foreign pages during it's destruction. Signed-off-by: Mukesh Rathor <mukesh.rathor@xxxxxxxxxx> --- xen/arch/x86/mm.c | 5 +++ xen/arch/x86/mm/p2m-ept.c | 34 ++++++++++++++--- xen/arch/x86/mm/p2m-pt.c | 9 ++++- xen/arch/x86/mm/p2m.c | 95 +++++++++++++++++++++++++++++++++++++++++++++-- xen/include/asm-x86/p2m.h | 3 ++ 5 files changed, 135 insertions(+), 11 deletions(-) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index fdc5ed3..fcd3178 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -4583,6 +4583,11 @@ int xenmem_add_to_physmap_one( page = mfn_to_page(mfn); break; } + case XENMAPSPACE_gmfn_foreign: + { + rc = p2m_add_foreign(d, idx, gpfn, foreign_domid); + return rc; + } default: break; } diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c index 08d1d72..beb7288 100644 --- a/xen/arch/x86/mm/p2m-ept.c +++ b/xen/arch/x86/mm/p2m-ept.c @@ -36,8 +36,6 @@ #define atomic_read_ept_entry(__pepte) \ ( (ept_entry_t) { .epte = read_atomic(&(__pepte)->epte) } ) -#define atomic_write_ept_entry(__pepte, __epte) \ - write_atomic(&(__pepte)->epte, (__epte).epte) #define is_epte_present(ept_entry) ((ept_entry)->epte & 0x7) #define is_epte_superpage(ept_entry) ((ept_entry)->sp) @@ -46,6 +44,30 @@ static inline bool_t is_epte_valid(ept_entry_t *e) return (e->epte != 0 && e->sa_p2mt != p2m_invalid); } +static inline void atomic_write_ept_entry(ept_entry_t *entryptr, + const ept_entry_t *new) +{ + unsigned long oldmfn = 0; + + if ( p2m_is_foreign(new->sa_p2mt) ) + { + struct page_info *page; + struct domain *fdom; + + ASSERT(mfn_valid(new->mfn)); + page = mfn_to_page(new->mfn); + fdom = page_get_owner(page); + get_page(page, fdom); + } + if ( p2m_is_foreign(entryptr->sa_p2mt) ) + oldmfn = entryptr->mfn; + + write_atomic(&entryptr->epte, new->epte); + + if ( oldmfn ) + put_page(mfn_to_page(oldmfn)); +} + static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type, p2m_access_t access) { /* First apply type permissions */ @@ -378,7 +400,7 @@ ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); } - atomic_write_ept_entry(ept_entry, new_entry); + atomic_write_ept_entry(ept_entry, &new_entry); } else { @@ -398,7 +420,7 @@ ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, /* now install the newly split ept sub-tree */ /* NB: please make sure domian is paused and no in-fly VT-d DMA. */ - atomic_write_ept_entry(ept_entry, split_ept_entry); + atomic_write_ept_entry(ept_entry, &split_ept_entry); /* then move to the level we want to make real changes */ for ( ; i > target; i-- ) @@ -428,7 +450,7 @@ ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); - atomic_write_ept_entry(ept_entry, new_entry); + atomic_write_ept_entry(ept_entry, &new_entry); } /* Track the highest gfn for which we have ever had a valid mapping */ @@ -665,7 +687,7 @@ static void ept_change_entry_type_page(mfn_t ept_page_mfn, int ept_page_level, e.sa_p2mt = nt; ept_p2m_type_to_flags(&e, nt, e.access); - atomic_write_ept_entry(&epte[i], e); + atomic_write_ept_entry(&epte[i], &e); } } diff --git a/xen/arch/x86/mm/p2m-pt.c b/xen/arch/x86/mm/p2m-pt.c index 09b60ce..766fd67 100644 --- a/xen/arch/x86/mm/p2m-pt.c +++ b/xen/arch/x86/mm/p2m-pt.c @@ -276,7 +276,7 @@ p2m_next_level(struct p2m_domain *p2m, mfn_t *table_mfn, void **table, return 1; } -// Returns 0 on error (out of memory) +// Returns 0 on error (out of memory, or unimplemented p2m type) static int p2m_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma) @@ -312,6 +312,13 @@ p2m_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, __trace_var(TRC_MEM_SET_P2M_ENTRY, 0, sizeof(t), &t); } + if ( p2m_is_foreign(p2mt) ) + { + /* pvh fixme: foreign types are only supported on ept at present */ + gdprintk(XENLOG_WARNING, "Unimplemented foreign p2m type.\n"); + return 0; + } + if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, L4_PAGETABLE_SHIFT - PAGE_SHIFT, L4_PAGETABLE_ENTRIES, PGT_l3_page_table) ) diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index 7050f6a..52dd1e4 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -36,6 +36,7 @@ #include <xen/event.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/svm/amd-iommu-proto.h> +#include <xsm/xsm.h> #include "mm-locks.h" @@ -275,14 +276,19 @@ struct page_info *get_page_from_gfn_p2m( /* Fast path: look up and get out */ p2m_read_lock(p2m); mfn = __get_gfn_type_access(p2m, gfn, t, a, 0, NULL, 0); - if ( (p2m_is_ram(*t) || p2m_is_grant(*t)) + if ( (p2m_is_ram(*t) || p2m_is_grant(*t) || p2m_is_foreign(*t) ) && mfn_valid(mfn) && !((q & P2M_UNSHARE) && p2m_is_shared(*t)) ) { page = mfn_to_page(mfn); - if ( !get_page(page, d) - /* Page could be shared */ - && !get_page(page, dom_cow) ) + if ( p2m_is_foreign(*t) ) + { + struct domain *fdom = page_get_owner_and_reference(page); + ASSERT(fdom && fdom != d); + } + else if ( !get_page(page, d) + /* Page could be shared */ + && !get_page(page, dom_cow) ) page = NULL; } p2m_read_unlock(p2m); @@ -447,6 +453,10 @@ void p2m_teardown(struct p2m_domain *p2m) d = p2m->domain; p2m_lock(p2m); + /* pvh: we must release refcnt on all foreign pages */ + if ( is_pvh_domain(d) ) + p2m_change_entry_type_global(d, p2m_map_foreign, p2m_invalid); + ASSERT(atomic_read(&d->shr_pages) == 0); p2m->phys_table = pagetable_null(); @@ -1710,6 +1720,83 @@ out_p2m_audit: #endif /* P2M_AUDIT */ /* + * Add frames from foreign domain to target domain's physmap. Similar to + * XENMAPSPACE_gmfn but the frame is foreign being mapped into current, + * and is not removed from foreign domain. + * Usage: libxl on pvh dom0 creating a guest and doing privcmd_ioctl_mmap. + * Side Effect: the mfn for fgfn will be refcounted in lower level routines + * so it is not lost while mapped here. The refcnt is released + * via the XENMEM_remove_from_physmap path. + * Returns: 0 ==> success + */ +int p2m_add_foreign(struct domain *tdom, unsigned long fgfn, + unsigned long gpfn, domid_t fdid) +{ + int rc = -ESRCH; + p2m_type_t p2mt, p2mt_prev; + unsigned long prev_mfn, mfn; + struct page_info *page = NULL; + struct domain *fdom = NULL; + + /* xentrace mapping pages from xen heap are owned by DOMID_XEN */ + if ( (fdid == DOMID_XEN && (fdom = rcu_lock_domain(dom_xen)) == NULL) || + (fdid != DOMID_XEN && (fdom = rcu_lock_domain_by_id(fdid)) == NULL) ) + goto out; + + if ( (rc = xsm_map_gmfn_foreign(XSM_TARGET, tdom, fdom)) ) + goto out; + + rc = -EINVAL; + if ( tdom == fdom || !tdom || !fdom || !is_pvh_domain(tdom) ) + goto out; + + /* following will take a refcnt on the mfn */ + page = get_page_from_gfn(fdom, fgfn, &p2mt, P2M_ALLOC); + if ( !page || !p2m_is_valid(p2mt) ) + { + if ( page ) + put_page(page); + goto out; + } + mfn = mfn_x(page_to_mfn(page)); + + /* Remove previously mapped page if it is present. */ + prev_mfn = mfn_x(get_gfn(tdom, gpfn, &p2mt_prev)); + if ( mfn_valid(_mfn(prev_mfn)) ) + { + if ( is_xen_heap_mfn(prev_mfn) ) + /* Xen heap frames are simply unhooked from this phys slot */ + guest_physmap_remove_page(tdom, gpfn, prev_mfn, 0); + else + /* Normal domain memory is freed, to avoid leaking memory. */ + guest_remove_page(tdom, gpfn); + } + /* + * Create the new mapping. Can't use guest_physmap_add_page() because it + * will update the m2p table which will result in mfn -> gpfn of dom0 + * and not fgfn of domU. + */ + if ( set_foreign_p2m_entry(tdom, gpfn, _mfn(mfn)) == 0 ) + gdprintk(XENLOG_WARNING, "set_foreign_p2m_entry failed. " + "gpfn:%lx mfn:%lx fgfn:%lx td:%d fd:%d\n", + gpfn, mfn, fgfn, tdom->domain_id, fdom->domain_id); + else + rc = 0; + + put_page(page); + + /* + * We must do this put_gfn after set_foreign_p2m_entry so another cpu + * doesn't populate the gpfn before us. + */ + put_gfn(tdom, gpfn); + +out: + if ( fdom ) + rcu_unlock_domain(fdom); + return rc; +} +/* * Local variables: * mode: C * c-file-style: "BSD" diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index ad2383e..9e691f2 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -512,6 +512,9 @@ p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn, int set_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn); int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn); +/* Add foreign mapping to the guest's p2m table. */ +int p2m_add_foreign(struct domain *tdom, unsigned long fgfn, + unsigned long gpfn, domid_t foreign_domid); /* * Populate-on-demand -- 1.8.3.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |