diff --git a/xen/arch/x86/cpu/mcheck/mce_intel.c b/xen/arch/x86/cpu/mcheck/mce_intel.c index 03160b48f8..aee9e32c9d 100644 --- a/xen/arch/x86/cpu/mcheck/mce_intel.c +++ b/xen/arch/x86/cpu/mcheck/mce_intel.c @@ -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 = diff --git a/xen/arch/x86/domain_build.c b/xen/arch/x86/domain_build.c index a740791e56..c4b4a66921 100644 --- a/xen/arch/x86/domain_build.c +++ b/xen/arch/x86/domain_build.c @@ -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)); diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 5207b5ac2d..0a0df9ef9d 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -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); diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c index 5dc6204d58..064fb0d86b 100644 --- a/xen/arch/x86/mm/mem_sharing.c +++ b/xen/arch/x86/mm/mem_sharing.c @@ -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; } diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index d4d7b26396..8f575d088f 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -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); diff --git a/xen/arch/x86/mm/paging.c b/xen/arch/x86/mm/paging.c index 77ed35f02a..4cec628ef9 100644 --- a/xen/arch/x86/mm/paging.c +++ b/xen/arch/x86/mm/paging.c @@ -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 diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index 49f8349cd2..9a449b97e8 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -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) ) { diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h index 94724b2002..355c889187 100644 --- a/xen/arch/x86/mm/shadow/private.h +++ b/xen/arch/x86/mm/shadow/private.h @@ -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; diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 946b340340..fda92163d4 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -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]; diff --git a/xen/common/domctl.c b/xen/common/domctl.c index 0c417c7fea..9d7c271d4f 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -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)); } diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index e56cb6f1e7..a28da459ec 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -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); } diff --git a/xen/common/memory.c b/xen/common/memory.c index 11ccdf2fcd..acd131e994 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -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); } diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c index e307e9d9fd..ec6d03ef9b 100644 --- a/xen/drivers/passthrough/iommu.c +++ b/xen/drivers/passthrough/iommu.c @@ -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) diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h index b0c5bb1b8f..5c3ab88c3a 100644 --- a/xen/include/asm-x86/mm.h +++ b/xen/include/asm-x86/mm.h @@ -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)])