]> xenbits.xensource.com Git - people/dariof/xen.git/commitdiff
M2P translation cannot be handled through flat table with only one slot per MFN
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 17 Dec 2009 06:27:56 +0000 (06:27 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 17 Dec 2009 06:27:56 +0000 (06:27 +0000)
when an MFN is shared. However, all existing calls can either infer the GFN (for
example p2m table destructor) or will not need to know GFN for shared pages.
This patch identifies and fixes all the M2P accessors, either by removing the
translation altogether or by making the relevant modifications. Shared MFNs have
a special value of SHARED_M2P_ENTRY stored in their M2P table slot.

Signed-off-by: Grzegorz Milos <Grzegorz.Milos@citrix.com>
14 files changed:
xen/arch/x86/cpu/mcheck/mce_intel.c
xen/arch/x86/domain_build.c
xen/arch/x86/mm.c
xen/arch/x86/mm/mem_sharing.c
xen/arch/x86/mm/p2m.c
xen/arch/x86/mm/paging.c
xen/arch/x86/mm/shadow/multi.c
xen/arch/x86/mm/shadow/private.h
xen/arch/x86/traps.c
xen/common/domctl.c
xen/common/grant_table.c
xen/common/memory.c
xen/drivers/passthrough/iommu.c
xen/include/asm-x86/mm.h

index 03160b48f8af242344b0049a11462a6fc0bd3151..aee9e32c9d87b454ae3b39bbf31061103b52d146 100644 (file)
@@ -356,6 +356,10 @@ static void intel_UCR_handler(struct mcinfo_bank *bank,
                       /* Fill vMCE# injection and vMCE# MSR virtualization "
                        * "related data */
                       bank->mc_domid = result->owner;
+                      /* XXX: Cannot handle shared pages yet 
+                       * (this should identify all domains and gfn mapping to
+                       *  the mfn in question) */
+                      BUG_ON( result->owner == DOMID_COW );
                       if ( result->owner != DOMID_XEN ) {
                           d = get_domain_by_id(result->owner);
                           gfn =
index a740791e569f1b46b7fce0af001b12deadc1daf8..c4b4a66921385ede51132b16c3b816e65ec1869d 100644 (file)
@@ -931,6 +931,7 @@ int __init construct_dom0(
     page_list_for_each ( page, &d->page_list )
     {
         mfn = page_to_mfn(page);
+        BUG_ON(SHARED_M2P(get_gpfn_from_mfn(mfn)));
         if ( get_gpfn_from_mfn(mfn) >= count )
         {
             BUG_ON(is_pv_32bit_domain(d));
index 5207b5ac2dcda55b2b9c831fa548225a8d7ac42a..0a0df9ef9d93c5c4852ac541a1c0d864e3ff2596 100644 (file)
@@ -2138,7 +2138,9 @@ int free_page_type(struct page_info *page, unsigned long type,
 
         gmfn = mfn_to_gmfn(owner, page_to_mfn(page));
         ASSERT(VALID_M2P(gmfn));
-        shadow_remove_all_shadows(owner->vcpu[0], _mfn(gmfn));
+        /* Page sharing not supported for shadowed domains */
+        if(!SHARED_M2P(gmfn))
+            shadow_remove_all_shadows(owner->vcpu[0], _mfn(gmfn));
     }
 
     if ( !(type & PGT_partial) )
@@ -4234,12 +4236,22 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg)
             spin_unlock(&d->grant_table->lock);
             break;
         case XENMAPSPACE_gmfn:
-            xatp.idx = gmfn_to_mfn(d, xatp.idx);
+        {
+            p2m_type_t p2mt;
+
+            xatp.idx = mfn_x(gfn_to_mfn_unshare(d, xatp.idx, &p2mt, 0));
+            /* If the page is still shared, exit early */
+            if ( p2m_is_shared(p2mt) )
+            {
+                rcu_unlock_domain(d);
+                return -ENOMEM;
+            }
             if ( !get_page_from_pagenr(xatp.idx, d) )
                 break;
             mfn = xatp.idx;
             page = mfn_to_page(mfn);
             break;
+        }
         default:
             break;
         }
@@ -4268,6 +4280,7 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg)
 
         /* Unmap from old location, if any. */
         gpfn = get_gpfn_from_mfn(mfn);
+        ASSERT( gpfn != SHARED_M2P_ENTRY );
         if ( gpfn != INVALID_M2P_ENTRY )
             guest_physmap_remove_page(d, gpfn, mfn, 0);
 
index 5dc6204d5882f40699c54c131166459b394edb80..064fb0d86bbd62fae09cdf0594b4292c3b51bdf1 100644 (file)
@@ -216,6 +216,9 @@ int mem_sharing_nominate_page(struct domain *d,
         goto out;
     }
 
+    /* Update m2p entry to SHARED_M2P_ENTRY */
+    set_gpfn_from_mfn(mfn_x(mfn), SHARED_M2P_ENTRY);
+
     ret = 0;
 
 out:
@@ -260,6 +263,8 @@ private_page_found:
         printk("Could not change p2m type.\n");
         BUG();
     }
+    /* Update m2p entry */
+    set_gpfn_from_mfn(mfn_x(page_to_mfn(page)), gfn);
 
     return 0;
 }
index d4d7b263969ed982be72d153e92db64966dcf0dd..8f575d088f38eff02577649f87859f951a1391be 100644 (file)
@@ -1601,6 +1601,8 @@ int p2m_alloc_table(struct domain *d,
     {
         mfn = page_to_mfn(page);
         gfn = get_gpfn_from_mfn(mfn_x(mfn));
+        /* Pages should not be shared that early */
+        ASSERT(gfn != SHARED_M2P_ENTRY);
         page_count++;
         if (
 #ifdef __x86_64__
@@ -1712,6 +1714,13 @@ static void audit_p2m(struct domain *d)
             continue;
         }
 
+        if ( gfn == SHARED_P2M_ENTRY)
+        {
+            P2M_PRINTK("shared mfn (%lx) on domain page list!\n",
+                    mfn);
+            continue;
+        }
+
         p2mfn = gfn_to_mfn_type_foreign(d, gfn, &type, p2m_query);
         if ( mfn_x(p2mfn) != mfn )
         {
@@ -1803,7 +1812,9 @@ static void audit_p2m(struct domain *d)
                         for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++)
                         {
                             m2pfn = get_gpfn_from_mfn(mfn+i1);
-                            if ( m2pfn != (gfn + i1) )
+                            /* Allow shared M2Ps */
+                            if ( (m2pfn != (gfn + i1)) &&
+                                 (m2pfn != SHARED_M2P_ENTRY) )
                             {
                                 pmbad++;
                                 P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx"
@@ -1834,7 +1845,8 @@ static void audit_p2m(struct domain *d)
                         m2pfn = get_gpfn_from_mfn(mfn);
                         if ( m2pfn != gfn &&
                              type != p2m_mmio_direct &&
-                             !p2m_is_grant(type) )
+                             !p2m_is_grant(type) &&
+                             !p2m_is_shared(type) )
                         {
                             pmbad++;
                             printk("mismatch: gfn %#lx -> mfn %#lx"
@@ -2137,12 +2149,11 @@ void p2m_change_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt)
     l1_pgentry_t *l1e;
     l2_pgentry_t *l2e;
     mfn_t l1mfn, l2mfn;
-    int i1, i2;
+    unsigned long i1, i2, i3;
     l3_pgentry_t *l3e;
-    int i3;
 #if CONFIG_PAGING_LEVELS == 4
     l4_pgentry_t *l4e;
-    int i4;
+    unsigned long i4;
 #endif /* CONFIG_PAGING_LEVELS == 4 */
 
     BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt));
@@ -2193,7 +2204,10 @@ void p2m_change_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt)
                     if ( p2m_flags_to_type(flags) != ot )
                         continue;
                     mfn = l2e_get_pfn(l2e[i2]);
-                    gfn = get_gpfn_from_mfn(mfn);
+                    /* Do not use get_gpfn_from_mfn because it may return 
+                       SHARED_M2P_ENTRY */
+                    gfn = (i2 + (i3 + (i4 * L3_PAGETABLE_ENTRIES))
+                           * L2_PAGETABLE_ENTRIES) * L1_PAGETABLE_ENTRIES; 
                     flags = p2m_type_to_flags(nt);
                     l1e_content = l1e_from_pfn(mfn, flags | _PAGE_PSE);
                     paging_write_p2m_entry(d, gfn, (l1_pgentry_t *)&l2e[i2],
@@ -2210,7 +2224,8 @@ void p2m_change_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt)
                     if ( p2m_flags_to_type(flags) != ot )
                         continue;
                     mfn = l1e_get_pfn(l1e[i1]);
-                    gfn = get_gpfn_from_mfn(mfn);
+                    gfn = i1 + (i2 + (i3 + (i4 * L3_PAGETABLE_ENTRIES))
+                           * L2_PAGETABLE_ENTRIES) * L1_PAGETABLE_ENTRIES; 
                     /* create a new 1le entry with the new type */
                     flags = p2m_type_to_flags(nt);
                     l1e_content = l1e_from_pfn(mfn, flags);
index 77ed35f02a25e98156fe43c3bc79f4fd9939d3f3..4cec628ef95f83f782f7751832a0ccce28838a13 100644 (file)
@@ -280,6 +280,8 @@ void paging_mark_dirty(struct domain *d, unsigned long guest_mfn)
 
     /* We /really/ mean PFN here, even for non-translated guests. */
     pfn = get_gpfn_from_mfn(mfn_x(gmfn));
+    /* Shared MFNs should NEVER be marked dirty */
+    BUG_ON(SHARED_M2P(pfn));
 
     /*
      * Values with the MSB set denote MFNs that aren't really part of the
index 49f8349cd2b15e9bb3694c0ac5686146ea454f77..9a449b97e8520fe5c384e67d0f7de132f82019f6 100644 (file)
@@ -1070,6 +1070,8 @@ static inline void shadow_vram_get_l1e(shadow_l1e_t new_sl1e,
         return;
 
     gfn = mfn_to_gfn(d, mfn);
+    /* Page sharing not supported on shadow PTs */
+    BUG_ON(SHARED_M2P(gfn));
 
     if ( (gfn >= dirty_vram->begin_pfn) && (gfn < dirty_vram->end_pfn) )
     {
@@ -1099,6 +1101,8 @@ static inline void shadow_vram_put_l1e(shadow_l1e_t old_sl1e,
         return;
 
     gfn = mfn_to_gfn(d, mfn);
+    /* Page sharing not supported on shadow PTs */
+    BUG_ON(SHARED_M2P(gfn));
 
     if ( (gfn >= dirty_vram->begin_pfn) && (gfn < dirty_vram->end_pfn) )
     {
index 94724b2002732dcbb5ef54efd9ae3b004f3c374e..355c889187e49f9882d89c578f51c7d35525c863 100644 (file)
@@ -565,6 +565,8 @@ sh_mfn_is_dirty(struct domain *d, mfn_t gmfn)
 
     /* We /really/ mean PFN here, even for non-translated guests. */
     pfn = get_gpfn_from_mfn(mfn_x(gmfn));
+    /* Page sharing not supported for shadow domains */
+    BUG_ON(SHARED_M2P(pfn));
     if ( unlikely(!VALID_M2P(pfn)) )
         return 0;
     
index 946b3403404601dbd2ecd0dfa8a04ff32bc277bc..fda92163d484a61f1a9efa423ec3a73cb1e6fdd9 100644 (file)
@@ -2088,15 +2088,27 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
             break;
             
         case 3: /* Read CR3 */
+        {
+            unsigned long mfn;
+            
             if ( !is_pv_32on64_vcpu(v) )
+            {
+                mfn = pagetable_get_pfn(v->arch.guest_table);
                 *reg = xen_pfn_to_cr3(mfn_to_gmfn(
-                    v->domain, pagetable_get_pfn(v->arch.guest_table)));
+                    v->domain, mfn));
+            }
 #ifdef CONFIG_COMPAT
             else
+            {
+                mfn = l4e_get_pfn(*(l4_pgentry_t *)__va(pagetable_get_paddr(v->arch.guest_table)));
                 *reg = compat_pfn_to_cr3(mfn_to_gmfn(
-                    v->domain, l4e_get_pfn(*(l4_pgentry_t *)__va(pagetable_get_paddr(v->arch.guest_table)))));
+                    v->domain, mfn));
+            }
 #endif
-            break;
+            /* PTs should not be shared */
+            BUG_ON(page_get_owner(mfn_to_page(mfn)) == dom_cow);
+        }
+        break;
 
         case 4: /* Read CR4 */
             *reg = v->arch.guest_context.ctrlreg[4];
index 0c417c7feaddc24187643fce17ce3c0a246486e2..9d7c271d4fc1422a5114e2e3ae59447f70ae3aeb 100644 (file)
@@ -137,6 +137,7 @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info)
     info->tot_pages         = d->tot_pages;
     info->max_pages         = d->max_pages;
     info->shared_info_frame = mfn_to_gmfn(d, __pa(d->shared_info)>>PAGE_SHIFT);
+    BUG_ON(SHARED_M2P(info->shared_info_frame));
 
     memcpy(info->handle, d->handle, sizeof(xen_domain_handle_t));
 }
index e56cb6f1e744217957bb1e84388156ce00c6a4e7..a28da459ecbdb65e8375f92ffefd8bbcc1fa9cd3 100644 (file)
@@ -1195,6 +1195,8 @@ gnttab_setup_table(
     for ( i = 0; i < op.nr_frames; i++ )
     {
         gmfn = gnttab_shared_gmfn(d, d->grant_table, i);
+        /* Grant tables cannot be shared */
+        BUG_ON(SHARED_M2P(gmfn));
         (void)copy_to_guest_offset(op.frame_list, i, &gmfn, 1);
     }
 
index 11ccdf2fcdb8947bd37fea479667044a800d594d..acd131e994fc074fc1186461df63c102224eb820 100644 (file)
@@ -22,6 +22,7 @@
 #include <xen/tmem.h>
 #include <asm/current.h>
 #include <asm/hardirq.h>
+#include <asm/p2m.h>
 #include <xen/numa.h>
 #include <public/memory.h>
 #include <xsm/xsm.h>
@@ -151,9 +152,10 @@ out:
 int guest_remove_page(struct domain *d, unsigned long gmfn)
 {
     struct page_info *page;
+    p2m_type_t p2mt;
     unsigned long mfn;
 
-    mfn = gmfn_to_mfn(d, gmfn);
+    mfn = mfn_x(gfn_to_mfn(d, gmfn, &p2mt)); 
     if ( unlikely(!mfn_valid(mfn)) )
     {
         gdprintk(XENLOG_INFO, "Domain %u page number %lx invalid\n",
@@ -162,6 +164,15 @@ int guest_remove_page(struct domain *d, unsigned long gmfn)
     }
             
     page = mfn_to_page(mfn);
+    /* If gmfn is shared, just drop the guest reference (which may or may not
+     * free the page) */
+    if(p2m_is_shared(p2mt))
+    {
+        put_page_and_type(page);
+        guest_physmap_remove_page(d, gmfn, mfn, 0);
+        return 1;
+    }
+
     if ( unlikely(!get_page(page, d)) )
     {
         gdprintk(XENLOG_INFO, "Bad page free for domain %u\n", d->domain_id);
@@ -319,7 +330,15 @@ static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
 
             for ( k = 0; k < (1UL << exch.in.extent_order); k++ )
             {
-                mfn = gmfn_to_mfn(d, gmfn + k);
+                p2m_type_t p2mt;
+
+                /* Shared pages cannot be exchanged */
+                mfn = mfn_x(gfn_to_mfn_unshare(d, gmfn + k, &p2mt, 0));
+                if ( p2m_is_shared(p2mt) )
+                {
+                    rc = -ENOMEM;
+                    goto fail; 
+                }
                 if ( unlikely(!mfn_valid(mfn)) )
                 {
                     rc = -EINVAL;
@@ -358,10 +377,15 @@ static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
         /* Destroy final reference to each input page. */
         while ( (page = page_list_remove_head(&in_chunk_list)) )
         {
+            unsigned long gfn;
+
             if ( !test_and_clear_bit(_PGC_allocated, &page->count_info) )
                 BUG();
             mfn = page_to_mfn(page);
-            guest_physmap_remove_page(d, mfn_to_gmfn(d, mfn), mfn, 0);
+            gfn = mfn_to_gmfn(d, mfn);
+            /* Pages were unshared above */
+            BUG_ON(SHARED_M2P(gfn));
+            guest_physmap_remove_page(d, gfn, mfn, 0);
             put_page(page);
         }
 
index e307e9d9fdd3639762c7cce079b42cdf7a764284..ec6d03ef9bfaefb9afe8624599afbb62f0933fc5 100644 (file)
@@ -168,6 +168,7 @@ static int iommu_populate_page_table(struct domain *d)
         if ( is_hvm_domain(d) ||
             (page->u.inuse.type_info & PGT_type_mask) == PGT_writable_page )
         {
+            BUG_ON(SHARED_M2P(mfn_to_gmfn(d, page_to_mfn(page))));
             rc = hd->platform_ops->map_page(
                 d, mfn_to_gmfn(d, page_to_mfn(page)), page_to_mfn(page));
             if (rc)
index b0c5bb1b8f174acecd683e43f27abf31883ece41..5c3ab88c3a43135af642444c026c6f2d31c472de 100644 (file)
@@ -438,15 +438,27 @@ TYPE_SAFE(unsigned long,mfn);
 #define machine_to_phys_mapping  ((unsigned long *)RDWR_MPT_VIRT_START)
 #define INVALID_M2P_ENTRY        (~0UL)
 #define VALID_M2P(_e)            (!((_e) & (1UL<<(BITS_PER_LONG-1))))
+#define SHARED_M2P_ENTRY         (~0UL - 1UL)
+#define SHARED_M2P(_e)           ((_e) == SHARED_M2P_ENTRY)
 
 #ifdef CONFIG_COMPAT
 #define compat_machine_to_phys_mapping ((unsigned int *)RDWR_COMPAT_MPT_VIRT_START)
-#define set_gpfn_from_mfn(mfn, pfn) \
+#define set_gpfn_from_mfn(mfn, pfn) ({                         \
+    struct domain *d = page_get_owner(__mfn_to_page(mfn));     \
+    unsigned long entry = (d && (d == dom_cow)) ?              \
+        SHARED_M2P_ENTRY : (pfn);                              \
     ((void)((mfn) >= (RDWR_COMPAT_MPT_VIRT_END - RDWR_COMPAT_MPT_VIRT_START) / 4 || \
-            (compat_machine_to_phys_mapping[(mfn)] = (unsigned int)(pfn))), \
-     machine_to_phys_mapping[(mfn)] = (pfn))
+            (compat_machine_to_phys_mapping[(mfn)] = (unsigned int)(entry))), \
+     machine_to_phys_mapping[(mfn)] = (entry));                \
+    })
 #else
-#define set_gpfn_from_mfn(mfn, pfn) (machine_to_phys_mapping[(mfn)] = (pfn))
+#define set_gpfn_from_mfn(mfn, pfn) ({                         \
+    struct domain *d = page_get_owner(__mfn_to_page(mfn));     \
+    if(d && (d == dom_cow))                                    \
+        machine_to_phys_mapping[(mfn)] = SHARED_M2P_ENTRY;     \
+    else                                                       \
+        machine_to_phys_mapping[(mfn)] = (pfn);                \
+    })
 #endif
 #define get_gpfn_from_mfn(mfn)      (machine_to_phys_mapping[(mfn)])