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

Re: [Xen-devel] [PATCH 14/18 V2]: PVH xen: add xenmem_add_foreign_to_pmap()



At 17:58 -0700 on 15 Mar (1363370311), Mukesh Rathor wrote:
>  In this patch, a new function, xenmem_add_foreign_to_pmap(), is added
>  to map pages from foreign guest into current dom0 for domU creation.
>  Also, allow XENMEM_remove_from_physmap to remove p2m_map_foreign
>  pages. Note, in this path, we must release the refcount that was taken
>  during the map phase.
> 
> Changes in V2:
>   - Move the XENMEM_remove_from_physmap changes here instead of prev patch
>   - Move grant changes from this to one of the next patches.
>   - In xenmem_add_foreign_to_pmap(), do locked get_gfn
>   - Fail the mappings for qemu mapping pages for memory not there.
> 
> Signed-off-by: Mukesh Rathor <mukesh.rathor@xxxxxxxxxx>
> ---
>  xen/arch/x86/mm.c   |   74 ++++++++++++++++++++++++++++++++++++++++++++++++--
>  xen/common/memory.c |   44 +++++++++++++++++++++++++++---
>  2 files changed, 110 insertions(+), 8 deletions(-)
> 
> diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
> index 6603752..dbac811 100644
> --- a/xen/arch/x86/mm.c
> +++ b/xen/arch/x86/mm.c
> @@ -2656,7 +2656,7 @@ static struct domain *get_pg_owner(domid_t domid)
>          goto out;
>      }
>  
> -    if ( unlikely(paging_mode_translate(curr)) )
> +    if ( !is_pvh_domain(curr) && unlikely(paging_mode_translate(curr)) )
>      {
>          MEM_LOG("Cannot mix foreign mappings with translated domains");
>          goto out;
> @@ -4192,7 +4192,7 @@ long do_update_descriptor(u64 pa, u64 desc)
>      page = get_page_from_gfn(dom, gmfn, NULL, P2M_ALLOC);
>      if ( (((unsigned int)pa % sizeof(struct desc_struct)) != 0) ||
>           !page ||
> -         !check_descriptor(dom, &d) )
> +         (!is_pvh_domain(dom) && !check_descriptor(dom, &d)) )

Is this change related to xenmem_add_foreign_to_pmap() ?

>      {
>          if ( page )
>              put_page(page);
> @@ -4266,6 +4266,66 @@ static int handle_iomem_range(unsigned long s, 
> unsigned long e, void *p)
>      return 0;
>  }
>  
> +/* 
> + * Add frames from foreign domain to current 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 so it is not lost
> + *              while mapped here. The refcnt is released in do_memory_op() 
> + *              via XENMEM_remove_from_physmap.
> + * Returns: 0 ==> success
> + */
> +static int xenmem_add_foreign_to_pmap(domid_t foreign_domid, 
> +                                      unsigned long fgfn, unsigned long gpfn)
> +{
> +    p2m_type_t p2mt, p2mt_prev;
> +    int rc = -EINVAL;
> +    unsigned long prev_mfn, mfn = 0;
> +    struct domain *fdom, *currd = current->domain;
> +
> +    if ( (fdom = get_pg_owner(foreign_domid)) == NULL )
> +        return -EPERM;
> +
> +    mfn = mfn_x(get_gfn_query(fdom, fgfn, &p2mt));
> +    if ( !mfn_valid(mfn) || !p2m_is_valid(p2mt) )
> +        goto out_rc;
> +
> +    if ( !get_page_from_pagenr(mfn, fdom) )
> +        goto out_rc;

I think you can use get_page_from_gfn() here instead of doing the
translation and the get_page() by hand.  That way you don't need to
worry about the put_gfn().  Which is just as well, as otherwise the
second get_gfn() might deadlock if fdom == currd.

> +    /* Remove previously mapped page if it is present. */
> +    prev_mfn = mfn_x(get_gfn(currd, gpfn, &p2mt_prev));
> +    if ( mfn_valid(prev_mfn) )
> +    {
> +        if ( is_xen_heap_mfn(prev_mfn) )
> +            /* Xen heap frames are simply unhooked from this phys slot */
> +            guest_physmap_remove_page(currd, gpfn, prev_mfn, 0);
> +        else
> +            /* Normal domain memory is freed, to avoid leaking memory. */
> +            guest_remove_page(currd, gpfn);
> +    }
> +    put_gfn(currd, gpfn);

Again, without the p2m lock held, another CPU could populate gpfn
between here and set_foreign_p2m_entry().

> +    /* 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(currd, gpfn, _mfn(mfn)) == 0 ) {
> +
> +        printk("guest_physmap_add_page failed. gpfn:%lx mfn:%lx fgfn:%lx\n", 
> +               gpfn, mfn, fgfn);
> +        put_page(mfn_to_page(mfn));
> +        goto out_rc;
> +    }
> +    rc = 0;
> +
> +out_rc:
> +    put_gfn(fdom, fgfn);
> +    put_pg_owner(fdom);
> +    return rc;
> +}
> +
>  static int xenmem_add_to_physmap_once(
>      struct domain *d,
>      const struct xen_add_to_physmap *xatp,
> @@ -4328,6 +4388,14 @@ static int xenmem_add_to_physmap_once(
>              page = mfn_to_page(mfn);
>              break;
>          }
> +
> +        case XENMAPSPACE_gmfn_foreign:
> +        {
> +            rc = xenmem_add_foreign_to_pmap(foreign_domid, xatp->idx, 
> +                                            xatp->gpfn);
> +            return rc;

I don't see any access control on this hypercall.  I'd expect to see an
IS_PRIV() somewhere, or equivalent xsm runes. 

Also, is it intended to be restricted to PVH domains or available to HVM
domains too?  If it's for everyone, the unwind code below shouldn't gate
on is_pvh_vcpu(current).

> diff --git a/xen/common/memory.c b/xen/common/memory.c
> index 68501d1..91a56b6 100644
> --- a/xen/common/memory.c
> +++ b/xen/common/memory.c
> @@ -675,9 +675,12 @@ long do_memory_op(unsigned long cmd, 
> XEN_GUEST_HANDLE_PARAM(void) arg)
>  
>      case XENMEM_remove_from_physmap:
>      {
> +        unsigned long argmfn, foreign_mfn = INVALID_MFN;
>          struct xen_remove_from_physmap xrfp;
>          struct page_info *page;
> -        struct domain *d;
> +        struct domain *d, *foreign_dom = NULL;
> +        p2m_type_t p2mt, tp;
> +        int valid_pvh_pg, is_curr_pvh = is_pvh_vcpu(current);
>  
>          if ( copy_from_guest(&xrfp, arg, 1) )
>              return -EFAULT;
> @@ -695,14 +698,45 @@ long do_memory_op(unsigned long cmd, 
> XEN_GUEST_HANDLE_PARAM(void) arg)
>  
>          domain_lock(d);
>  
> -        page = get_page_from_gfn(d, xrfp.gpfn, NULL, P2M_ALLOC);
> -        if ( page )
> +        /* PVH note: if PVH, the gfn could be mapped to a mfn from foreign
> +         * domain by the user space tool during domain creation. We need to
> +         * check for that, free it up from the p2m, and release refcnt on it.
> +         * In such a case, page would be NULL. */
> +
> +        page = get_page_from_gfn(d, xrfp.gpfn, &p2mt, P2M_ALLOC);
> +        valid_pvh_pg = is_curr_pvh && 
> +                       (p2m_is_mmio(p2mt) || p2m_is_foreign(p2mt));

If you're testing for PVH, it should be 'd', not 'current', but I think
that test is unnecessary anyway.

And as I think I said last time, you should be testing fpr
p2m_mmio_direct() here.

> +
> +        if ( page || valid_pvh_pg)
>          {
> -            guest_physmap_remove_page(d, xrfp.gpfn, page_to_mfn(page), 0);
> -            put_page(page);
> +            argmfn = page ? page_to_mfn(page) : INVALID_MFN;
> +
> +            if ( is_curr_pvh && p2m_is_foreign(p2mt) )

Again, testing 'current' is wrong here.

> +            {
> +                foreign_mfn = mfn_x(get_gfn_query_unlocked(d, xrfp.gpfn, 
> &tp));
> +                foreign_dom = page_get_owner(mfn_to_page(foreign_mfn));

That's not safe for MMIO mappings! mfn_to_page() might not give you a
valid pointer unless the MFN is a RAM page.  I think it would be best to
handle MMIO and foreign separately here, just for clarity.

> +                ASSERT(p2m_is_mmio(tp) || p2m_is_foreign(tp));

If you want to guarantee that the mapping is still the same as the one
you saw at the top, you should use get_gfn() at the top, and not
put_gfn() until you've removed the mapping.

> +            }
> +
> +            guest_physmap_remove_page(d, xrfp.gpfn, argmfn, 0);
> +            if (page)
> +                put_page(page);
> +
> +            /* if pages were mapped from foreign domain via 
> +             * xenmem_add_foreign_to_pmap(), we must drop a refcnt here */
> +            if ( is_curr_pvh && p2m_is_foreign(p2mt) )
> +            {
> +                ASSERT( d != foreign_dom );

You'll need to make sure that the foreign-map op doesn't accept
DOMID_SELF, then.

Cheers,

Tim.

> +                put_page(mfn_to_page(foreign_mfn));
> +            }
>          }
>          else
> +        {
> +            if ( is_curr_pvh )
> +                gdprintk(XENLOG_WARNING, "%s: Domain:%u gmfn:%lx invalid\n",
> +                         __func__, current->domain->domain_id, xrfp.gpfn);
>              rc = -ENOENT;
> +        }
>  
>          domain_unlock(d);
>  
> -- 
> 1.7.2.3
> 
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> http://lists.xen.org/xen-devel

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

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