[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] x86/mm/p2m: break into common, pt-implementation and pod parts.
# HG changeset patch # User Tim Deegan <Tim.Deegan@xxxxxxxxxx> # Date 1304676935 -3600 # Node ID 19452acd23045f40c4e18437f0a60f016757e5bd # Parent d9982136d8fa5e7ffb0d6ddb23668a7040c71d5b x86/mm/p2m: break into common, pt-implementation and pod parts. Start to make a clearer distinction between generic p2m functions and the implementation of the datastructure as an x86 pagetable. Also move the EPT datastructure implementation into x86/mm/ to match, and split the PoD admin code into its own file. This is just code motion, except for splitting the p2m_initialise function into a pt-specific part and a common part. Signed-off-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx> --- diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/Makefile --- a/xen/arch/x86/mm/Makefile Mon May 09 15:00:57 2011 +0100 +++ b/xen/arch/x86/mm/Makefile Fri May 06 11:15:35 2011 +0100 @@ -2,7 +2,7 @@ subdir-y += hap obj-y += paging.o -obj-y += p2m.o +obj-y += p2m.o p2m-pt.o p2m-ept.o p2m-pod.o obj-y += guest_walk_2.o obj-y += guest_walk_3.o obj-$(x86_64) += guest_walk_4.o diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/hap/Makefile --- a/xen/arch/x86/mm/hap/Makefile Mon May 09 15:00:57 2011 +0100 +++ b/xen/arch/x86/mm/hap/Makefile Fri May 06 11:15:35 2011 +0100 @@ -2,7 +2,6 @@ obj-y += guest_walk_2level.o obj-y += guest_walk_3level.o obj-$(x86_64) += guest_walk_4level.o -obj-y += p2m-ept.o obj-y += nested_hap.o guest_walk_%level.o: guest_walk.c Makefile diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/hap/p2m-ept.c --- a/xen/arch/x86/mm/hap/p2m-ept.c Mon May 09 15:00:57 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,911 +0,0 @@ -/* - * ept-p2m.c: use the EPT page table as p2m - * Copyright (c) 2007, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - */ - -#include <xen/config.h> -#include <xen/domain_page.h> -#include <xen/sched.h> -#include <asm/current.h> -#include <asm/paging.h> -#include <asm/types.h> -#include <asm/domain.h> -#include <asm/p2m.h> -#include <asm/hvm/vmx/vmx.h> -#include <asm/hvm/vmx/vmcs.h> -#include <xen/iommu.h> -#include <asm/mtrr.h> -#include <asm/hvm/cacheattr.h> -#include <xen/keyhandler.h> -#include <xen/softirq.h> - -#define atomic_read_ept_entry(__pepte) \ - ( (ept_entry_t) { .epte = atomic_read64(&(__pepte)->epte) } ) -#define atomic_write_ept_entry(__pepte, __epte) \ - atomic_write64(&(__pepte)->epte, (__epte).epte) - -#define is_epte_present(ept_entry) ((ept_entry)->epte & 0x7) -#define is_epte_superpage(ept_entry) ((ept_entry)->sp) - -/* Non-ept "lock-and-check" wrapper */ -static int ept_pod_check_and_populate(struct p2m_domain *p2m, unsigned long gfn, - ept_entry_t *entry, int order, - p2m_query_t q) -{ - /* Only take the lock if we don't already have it. Otherwise it - * wouldn't be safe to do p2m lookups with the p2m lock held */ - int do_locking = !p2m_locked_by_me(p2m); - int r; - - if ( do_locking ) - p2m_lock(p2m); - - /* Check to make sure this is still PoD */ - if ( entry->sa_p2mt != p2m_populate_on_demand ) - { - if ( do_locking ) - p2m_unlock(p2m); - return 0; - } - - r = p2m_pod_demand_populate(p2m, gfn, order, q); - - if ( do_locking ) - p2m_unlock(p2m); - - return r; -} - -static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type, p2m_access_t access) -{ - /* First apply type permissions */ - switch(type) - { - case p2m_invalid: - case p2m_mmio_dm: - case p2m_populate_on_demand: - case p2m_ram_paging_out: - case p2m_ram_paged: - case p2m_ram_paging_in: - case p2m_ram_paging_in_start: - default: - entry->r = entry->w = entry->x = 0; - break; - case p2m_ram_rw: - entry->r = entry->w = entry->x = 1; - break; - case p2m_mmio_direct: - entry->r = entry->x = 1; - entry->w = !rangeset_contains_singleton(mmio_ro_ranges, - entry->mfn); - break; - case p2m_ram_logdirty: - case p2m_ram_ro: - case p2m_ram_shared: - entry->r = entry->x = 1; - entry->w = 0; - break; - case p2m_grant_map_rw: - entry->r = entry->w = 1; - entry->x = 0; - break; - case p2m_grant_map_ro: - entry->r = 1; - entry->w = entry->x = 0; - break; - } - - - /* Then restrict with access permissions */ - switch (access) - { - case p2m_access_n: - entry->r = entry->w = entry->x = 0; - break; - case p2m_access_r: - entry->w = entry->x = 0; - break; - case p2m_access_w: - entry->r = entry->x = 0; - break; - case p2m_access_x: - entry->r = entry->w = 0; - break; - case p2m_access_rx: - case p2m_access_rx2rw: - entry->w = 0; - break; - case p2m_access_wx: - entry->r = 0; - break; - case p2m_access_rw: - entry->x = 0; - break; - case p2m_access_rwx: - break; - } - -} - -#define GUEST_TABLE_MAP_FAILED 0 -#define GUEST_TABLE_NORMAL_PAGE 1 -#define GUEST_TABLE_SUPER_PAGE 2 -#define GUEST_TABLE_POD_PAGE 3 - -/* Fill in middle levels of ept table */ -static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) -{ - struct page_info *pg; - - pg = p2m_alloc_ptp(p2m, 0); - if ( pg == NULL ) - return 0; - - ept_entry->epte = 0; - ept_entry->mfn = page_to_mfn(pg); - ept_entry->access = p2m->default_access; - - ept_entry->r = ept_entry->w = ept_entry->x = 1; - - return 1; -} - -/* free ept sub tree behind an entry */ -void ept_free_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry, int level) -{ - /* End if the entry is a leaf entry. */ - if ( level == 0 || !is_epte_present(ept_entry) || - is_epte_superpage(ept_entry) ) - return; - - if ( level > 1 ) - { - ept_entry_t *epte = map_domain_page(ept_entry->mfn); - for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) - ept_free_entry(p2m, epte + i, level - 1); - unmap_domain_page(epte); - } - - p2m_free_ptp(p2m, mfn_to_page(ept_entry->mfn)); -} - -static int ept_split_super_page(struct p2m_domain *p2m, ept_entry_t *ept_entry, - int level, int target) -{ - ept_entry_t new_ept, *table; - uint64_t trunk; - int rv = 1; - - /* End if the entry is a leaf entry or reaches the target level. */ - if ( level == 0 || level == target ) - return rv; - - ASSERT(is_epte_superpage(ept_entry)); - - if ( !ept_set_middle_entry(p2m, &new_ept) ) - return 0; - - table = map_domain_page(new_ept.mfn); - trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); - - for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) - { - ept_entry_t *epte = table + i; - - epte->epte = 0; - epte->emt = ept_entry->emt; - epte->ipat = ept_entry->ipat; - epte->sp = (level > 1) ? 1 : 0; - epte->access = ept_entry->access; - epte->sa_p2mt = ept_entry->sa_p2mt; - epte->mfn = ept_entry->mfn + i * trunk; - epte->rsvd2_snp = ( iommu_enabled && iommu_snoop ) ? 1 : 0; - - ept_p2m_type_to_flags(epte, epte->sa_p2mt, epte->access); - - if ( (level - 1) == target ) - continue; - - ASSERT(is_epte_superpage(epte)); - - if ( !(rv = ept_split_super_page(p2m, epte, level - 1, target)) ) - break; - } - - unmap_domain_page(table); - - /* Even failed we should install the newly allocated ept page. */ - *ept_entry = new_ept; - - return rv; -} - -/* Take the currently mapped table, find the corresponding gfn entry, - * and map the next table, if available. If the entry is empty - * and read_only is set, - * Return values: - * 0: Failed to map. Either read_only was set and the entry was - * empty, or allocating a new page failed. - * GUEST_TABLE_NORMAL_PAGE: next level mapped normally - * GUEST_TABLE_SUPER_PAGE: - * The next entry points to a superpage, and caller indicates - * that they are going to the superpage level, or are only doing - * a read. - * GUEST_TABLE_POD: - * The next entry is marked populate-on-demand. - */ -static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, - ept_entry_t **table, unsigned long *gfn_remainder, - int next_level) -{ - unsigned long mfn; - ept_entry_t *ept_entry, e; - u32 shift, index; - - shift = next_level * EPT_TABLE_ORDER; - - index = *gfn_remainder >> shift; - - /* index must be falling into the page */ - ASSERT(index < EPT_PAGETABLE_ENTRIES); - - ept_entry = (*table) + index; - - /* ept_next_level() is called (sometimes) without a lock. Read - * the entry once, and act on the "cached" entry after that to - * avoid races. */ - e = atomic_read_ept_entry(ept_entry); - - if ( !is_epte_present(&e) ) - { - if ( e.sa_p2mt == p2m_populate_on_demand ) - return GUEST_TABLE_POD_PAGE; - - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - - if ( !ept_set_middle_entry(p2m, ept_entry) ) - return GUEST_TABLE_MAP_FAILED; - else - e = atomic_read_ept_entry(ept_entry); /* Refresh */ - } - - /* The only time sp would be set here is if we had hit a superpage */ - if ( is_epte_superpage(&e) ) - return GUEST_TABLE_SUPER_PAGE; - - mfn = e.mfn; - unmap_domain_page(*table); - *table = map_domain_page(mfn); - *gfn_remainder &= (1UL << shift) - 1; - return GUEST_TABLE_NORMAL_PAGE; -} - -/* - * ept_set_entry() computes 'need_modify_vtd_table' for itself, - * by observing whether any gfn->mfn translations are modified. - */ -static int -ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma) -{ - ept_entry_t *table, *ept_entry = NULL; - unsigned long gfn_remainder = gfn; - unsigned long offset = 0; - u32 index; - int i, target = order / EPT_TABLE_ORDER; - int rv = 0; - int ret = 0; - bool_t direct_mmio = (p2mt == p2m_mmio_direct); - uint8_t ipat = 0; - int need_modify_vtd_table = 1; - int vtd_pte_present = 0; - int needs_sync = 1; - struct domain *d = p2m->domain; - ept_entry_t old_entry = { .epte = 0 }; - - /* - * the caller must make sure: - * 1. passing valid gfn and mfn at order boundary. - * 2. gfn not exceeding guest physical address width. - * 3. passing a valid order. - */ - if ( ((gfn | mfn_x(mfn)) & ((1UL << order) - 1)) || - ((u64)gfn >> ((ept_get_wl(d) + 1) * EPT_TABLE_ORDER)) || - (order % EPT_TABLE_ORDER) ) - return 0; - - ASSERT((target == 2 && hvm_hap_has_1gb(d)) || - (target == 1 && hvm_hap_has_2mb(d)) || - (target == 0)); - - table = map_domain_page(ept_get_asr(d)); - - ASSERT(table != NULL); - - for ( i = ept_get_wl(d); i > target; i-- ) - { - ret = ept_next_level(p2m, 0, &table, &gfn_remainder, i); - if ( !ret ) - goto out; - else if ( ret != GUEST_TABLE_NORMAL_PAGE ) - break; - } - - ASSERT(ret != GUEST_TABLE_POD_PAGE || i != target); - - index = gfn_remainder >> (i * EPT_TABLE_ORDER); - offset = gfn_remainder & ((1UL << (i * EPT_TABLE_ORDER)) - 1); - - ept_entry = table + index; - - /* In case VT-d uses same page table, this flag is needed by VT-d */ - vtd_pte_present = is_epte_present(ept_entry) ? 1 : 0; - - /* - * If we're here with i > target, we must be at a leaf node, and - * we need to break up the superpage. - * - * If we're here with i == target and i > 0, we need to check to see - * if we're replacing a non-leaf entry (i.e., pointing to an N-1 table) - * with a leaf entry (a 1GiB or 2MiB page), and handle things appropriately. - */ - - if ( i == target ) - { - /* We reached the target level. */ - ept_entry_t new_entry = { .epte = 0 }; - - /* No need to flush if the old entry wasn't valid */ - if ( !is_epte_present(ept_entry) ) - needs_sync = 0; - - /* If we're replacing a non-leaf entry with a leaf entry (1GiB or 2MiB), - * the intermediate tables will be freed below after the ept flush - * - * Read-then-write is OK because we hold the p2m lock. */ - old_entry = *ept_entry; - - if ( mfn_valid(mfn_x(mfn)) || direct_mmio || p2m_is_paged(p2mt) || - (p2mt == p2m_ram_paging_in_start) ) - { - /* Construct the new entry, and then write it once */ - new_entry.emt = epte_get_entry_emt(p2m->domain, gfn, mfn, &ipat, - direct_mmio); - - new_entry.ipat = ipat; - new_entry.sp = order ? 1 : 0; - new_entry.sa_p2mt = p2mt; - new_entry.access = p2ma; - new_entry.rsvd2_snp = (iommu_enabled && iommu_snoop); - - new_entry.mfn = mfn_x(mfn); - - if ( old_entry.mfn == new_entry.mfn ) - need_modify_vtd_table = 0; - - ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); - } - - atomic_write_ept_entry(ept_entry, new_entry); - } - else - { - /* We need to split the original page. */ - ept_entry_t split_ept_entry; - ept_entry_t new_entry = { .epte = 0 }; - - ASSERT(is_epte_superpage(ept_entry)); - - split_ept_entry = atomic_read_ept_entry(ept_entry); - - if ( !ept_split_super_page(p2m, &split_ept_entry, i, target) ) - { - ept_free_entry(p2m, &split_ept_entry, i); - goto out; - } - - /* 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); - - /* then move to the level we want to make real changes */ - for ( ; i > target; i-- ) - ept_next_level(p2m, 0, &table, &gfn_remainder, i); - - ASSERT(i == target); - - index = gfn_remainder >> (i * EPT_TABLE_ORDER); - offset = gfn_remainder & ((1UL << (i * EPT_TABLE_ORDER)) - 1); - - ept_entry = table + index; - - new_entry.emt = epte_get_entry_emt(d, gfn, mfn, &ipat, direct_mmio); - new_entry.ipat = ipat; - new_entry.sp = i ? 1 : 0; - new_entry.sa_p2mt = p2mt; - new_entry.access = p2ma; - new_entry.rsvd2_snp = (iommu_enabled && iommu_snoop); - - /* the caller should take care of the previous page */ - new_entry.mfn = mfn_x(mfn); - - /* Safe to read-then-write because we hold the p2m lock */ - if ( ept_entry->mfn == new_entry.mfn ) - need_modify_vtd_table = 0; - - ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); - - atomic_write_ept_entry(ept_entry, new_entry); - } - - /* Track the highest gfn for which we have ever had a valid mapping */ - if ( mfn_valid(mfn_x(mfn)) && - (gfn + (1UL << order) - 1 > p2m->max_mapped_pfn) ) - p2m->max_mapped_pfn = gfn + (1UL << order) - 1; - - /* Success */ - rv = 1; - -out: - unmap_domain_page(table); - - if ( needs_sync ) - ept_sync_domain(p2m->domain); - - if ( rv && iommu_enabled && need_iommu(p2m->domain) && need_modify_vtd_table ) - { - if ( iommu_hap_pt_share ) - iommu_pte_flush(d, gfn, (u64*)ept_entry, order, vtd_pte_present); - else - { - if ( p2mt == p2m_ram_rw ) - { - if ( order > 0 ) - { - for ( i = 0; i < (1 << order); i++ ) - iommu_map_page( - p2m->domain, gfn - offset + i, mfn_x(mfn) - offset + i, - IOMMUF_readable | IOMMUF_writable); - } - else if ( !order ) - iommu_map_page( - p2m->domain, gfn, mfn_x(mfn), IOMMUF_readable | IOMMUF_writable); - } - else - { - if ( order > 0 ) - { - for ( i = 0; i < (1 << order); i++ ) - iommu_unmap_page(p2m->domain, gfn - offset + i); - } - else if ( !order ) - iommu_unmap_page(p2m->domain, gfn); - } - } - } - - /* Release the old intermediate tables, if any. This has to be the - last thing we do, after the ept_sync_domain() and removal - from the iommu tables, so as to avoid a potential - use-after-free. */ - if ( is_epte_present(&old_entry) ) - ept_free_entry(p2m, &old_entry, target); - - return rv; -} - -/* Read ept p2m entries */ -static mfn_t ept_get_entry(struct p2m_domain *p2m, - unsigned long gfn, p2m_type_t *t, p2m_access_t* a, - p2m_query_t q) -{ - struct domain *d = p2m->domain; - ept_entry_t *table = map_domain_page(ept_get_asr(d)); - unsigned long gfn_remainder = gfn; - ept_entry_t *ept_entry; - u32 index; - int i; - int ret = 0; - mfn_t mfn = _mfn(INVALID_MFN); - - *t = p2m_mmio_dm; - *a = p2m_access_n; - - /* This pfn is higher than the highest the p2m map currently holds */ - if ( gfn > p2m->max_mapped_pfn ) - goto out; - - /* Should check if gfn obeys GAW here. */ - - for ( i = ept_get_wl(d); i > 0; i-- ) - { - retry: - ret = ept_next_level(p2m, 1, &table, &gfn_remainder, i); - if ( !ret ) - goto out; - else if ( ret == GUEST_TABLE_POD_PAGE ) - { - if ( q == p2m_query ) - { - *t = p2m_populate_on_demand; - goto out; - } - - /* Populate this superpage */ - ASSERT(i == 1); - - index = gfn_remainder >> ( i * EPT_TABLE_ORDER); - ept_entry = table + index; - - if ( !ept_pod_check_and_populate(p2m, gfn, - ept_entry, 9, q) ) - goto retry; - else - goto out; - } - else if ( ret == GUEST_TABLE_SUPER_PAGE ) - break; - } - - index = gfn_remainder >> (i * EPT_TABLE_ORDER); - ept_entry = table + index; - - if ( ept_entry->sa_p2mt == p2m_populate_on_demand ) - { - if ( q == p2m_query ) - { - *t = p2m_populate_on_demand; - goto out; - } - - ASSERT(i == 0); - - if ( ept_pod_check_and_populate(p2m, gfn, - ept_entry, 0, q) ) - goto out; - } - - /* Need to check for all-zeroes because typecode 0 is p2m_ram and an - * entirely empty entry shouldn't have RAM type. */ - if ( ept_entry->epte != 0 && ept_entry->sa_p2mt != p2m_invalid ) - { - *t = ept_entry->sa_p2mt; - *a = ept_entry->access; - - mfn = _mfn(ept_entry->mfn); - if ( i ) - { - /* - * We may meet super pages, and to split into 4k pages - * to emulate p2m table - */ - unsigned long split_mfn = mfn_x(mfn) + - (gfn_remainder & - ((1 << (i * EPT_TABLE_ORDER)) - 1)); - mfn = _mfn(split_mfn); - } - } - -out: - unmap_domain_page(table); - return mfn; -} - -/* WARNING: Only caller doesn't care about PoD pages. So this function will - * always return 0 for PoD pages, not populate them. If that becomes necessary, - * pass a p2m_query_t type along to distinguish. */ -static ept_entry_t ept_get_entry_content(struct p2m_domain *p2m, - unsigned long gfn, int *level) -{ - ept_entry_t *table = map_domain_page(ept_get_asr(p2m->domain)); - unsigned long gfn_remainder = gfn; - ept_entry_t *ept_entry; - ept_entry_t content = { .epte = 0 }; - u32 index; - int i; - int ret=0; - - /* This pfn is higher than the highest the p2m map currently holds */ - if ( gfn > p2m->max_mapped_pfn ) - goto out; - - for ( i = ept_get_wl(p2m->domain); i > 0; i-- ) - { - ret = ept_next_level(p2m, 1, &table, &gfn_remainder, i); - if ( !ret || ret == GUEST_TABLE_POD_PAGE ) - goto out; - else if ( ret == GUEST_TABLE_SUPER_PAGE ) - break; - } - - index = gfn_remainder >> (i * EPT_TABLE_ORDER); - ept_entry = table + index; - content = *ept_entry; - *level = i; - - out: - unmap_domain_page(table); - return content; -} - -void ept_walk_table(struct domain *d, unsigned long gfn) -{ - struct p2m_domain *p2m = p2m_get_hostp2m(d); - ept_entry_t *table = map_domain_page(ept_get_asr(d)); - unsigned long gfn_remainder = gfn; - - int i; - - gdprintk(XENLOG_ERR, "Walking EPT tables for domain %d gfn %lx\n", - d->domain_id, gfn); - - /* This pfn is higher than the highest the p2m map currently holds */ - if ( gfn > p2m->max_mapped_pfn ) - { - gdprintk(XENLOG_ERR, " gfn exceeds max_mapped_pfn %lx\n", - p2m->max_mapped_pfn); - goto out; - } - - for ( i = ept_get_wl(d); i >= 0; i-- ) - { - ept_entry_t *ept_entry, *next; - u32 index; - - /* Stolen from ept_next_level */ - index = gfn_remainder >> (i*EPT_TABLE_ORDER); - ept_entry = table + index; - - gdprintk(XENLOG_ERR, " epte %"PRIx64"\n", ept_entry->epte); - - if ( (i == 0) || !is_epte_present(ept_entry) || - is_epte_superpage(ept_entry) ) - goto out; - else - { - gfn_remainder &= (1UL << (i*EPT_TABLE_ORDER)) - 1; - - next = map_domain_page(ept_entry->mfn); - - unmap_domain_page(table); - - table = next; - } - } - -out: - unmap_domain_page(table); - return; -} - -static mfn_t ept_get_entry_current(struct p2m_domain *p2m, - unsigned long gfn, p2m_type_t *t, p2m_access_t *a, - p2m_query_t q) -{ - return ept_get_entry(p2m, gfn, t, a, q); -} - -/* - * To test if the new emt type is the same with old, - * return 1 to not to reset ept entry. - */ -static int need_modify_ept_entry(struct p2m_domain *p2m, unsigned long gfn, - mfn_t mfn, uint8_t o_ipat, uint8_t o_emt, - p2m_type_t p2mt) -{ - uint8_t ipat; - uint8_t emt; - bool_t direct_mmio = (p2mt == p2m_mmio_direct); - - emt = epte_get_entry_emt(p2m->domain, gfn, mfn, &ipat, direct_mmio); - - if ( (emt == o_emt) && (ipat == o_ipat) ) - return 0; - - return 1; -} - -void ept_change_entry_emt_with_range(struct domain *d, - unsigned long start_gfn, - unsigned long end_gfn) -{ - unsigned long gfn; - ept_entry_t e; - mfn_t mfn; - int order = 0; - struct p2m_domain *p2m = p2m_get_hostp2m(d); - - p2m_lock(p2m); - for ( gfn = start_gfn; gfn <= end_gfn; gfn++ ) - { - int level = 0; - uint64_t trunk = 0; - - e = ept_get_entry_content(p2m, gfn, &level); - if ( !p2m_has_emt(e.sa_p2mt) ) - continue; - - order = 0; - mfn = _mfn(e.mfn); - - if ( is_epte_superpage(&e) ) - { - while ( level ) - { - trunk = (1UL << (level * EPT_TABLE_ORDER)) - 1; - if ( !(gfn & trunk) && (gfn + trunk <= end_gfn) ) - { - /* gfn assigned with 2M or 1G, and the end covers more than - * the super page areas. - * Set emt for super page. - */ - order = level * EPT_TABLE_ORDER; - if ( need_modify_ept_entry(p2m, gfn, mfn, - e.ipat, e.emt, e.sa_p2mt) ) - ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access); - gfn += trunk; - break; - } - level--; - } - } - else /* gfn assigned with 4k */ - { - if ( need_modify_ept_entry(p2m, gfn, mfn, e.ipat, e.emt, e.sa_p2mt) ) - ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access); - } - } - p2m_unlock(p2m); -} - -/* - * Walk the whole p2m table, changing any entries of the old type - * to the new type. This is used in hardware-assisted paging to - * quickly enable or diable log-dirty tracking - */ -static void ept_change_entry_type_page(mfn_t ept_page_mfn, int ept_page_level, - p2m_type_t ot, p2m_type_t nt) -{ - ept_entry_t e, *epte = map_domain_page(mfn_x(ept_page_mfn)); - - for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) - { - if ( !is_epte_present(epte + i) ) - continue; - - if ( (ept_page_level > 0) && !is_epte_superpage(epte + i) ) - ept_change_entry_type_page(_mfn(epte[i].mfn), - ept_page_level - 1, ot, nt); - else - { - e = atomic_read_ept_entry(&epte[i]); - if ( e.sa_p2mt != ot ) - continue; - - e.sa_p2mt = nt; - ept_p2m_type_to_flags(&e, nt, e.access); - atomic_write_ept_entry(&epte[i], e); - } - } - - unmap_domain_page(epte); -} - -static void ept_change_entry_type_global(struct p2m_domain *p2m, - p2m_type_t ot, p2m_type_t nt) -{ - struct domain *d = p2m->domain; - if ( ept_get_asr(d) == 0 ) - return; - - BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); - BUG_ON(ot != nt && (ot == p2m_mmio_direct || nt == p2m_mmio_direct)); - - ept_change_entry_type_page(_mfn(ept_get_asr(d)), ept_get_wl(d), ot, nt); - - ept_sync_domain(d); -} - -void ept_p2m_init(struct domain *d) -{ - struct p2m_domain *p2m = p2m_get_hostp2m(d); - p2m->set_entry = ept_set_entry; - p2m->get_entry = ept_get_entry; - p2m->get_entry_current = ept_get_entry_current; - p2m->change_entry_type_global = ept_change_entry_type_global; -} - -static void ept_dump_p2m_table(unsigned char key) -{ - struct domain *d; - ept_entry_t *table, *ept_entry; - mfn_t mfn; - int order; - int i; - int is_pod; - int ret = 0; - unsigned long index; - unsigned long gfn, gfn_remainder; - unsigned long record_counter = 0; - struct p2m_domain *p2m; - - for_each_domain(d) - { - if ( !hap_enabled(d) ) - continue; - - p2m = p2m_get_hostp2m(d); - printk("\ndomain%d EPT p2m table: \n", d->domain_id); - - for ( gfn = 0; gfn <= p2m->max_mapped_pfn; gfn += (1 << order) ) - { - gfn_remainder = gfn; - mfn = _mfn(INVALID_MFN); - table = map_domain_page(ept_get_asr(d)); - - for ( i = ept_get_wl(d); i > 0; i-- ) - { - ret = ept_next_level(p2m, 1, &table, &gfn_remainder, i); - if ( ret != GUEST_TABLE_NORMAL_PAGE ) - break; - } - - order = i * EPT_TABLE_ORDER; - - if ( ret == GUEST_TABLE_MAP_FAILED ) - goto out; - - index = gfn_remainder >> order; - ept_entry = table + index; - if ( ept_entry->sa_p2mt != p2m_invalid ) - { - ( ept_entry->sa_p2mt == p2m_populate_on_demand ) ? - ( mfn = _mfn(INVALID_MFN), is_pod = 1 ) : - ( mfn = _mfn(ept_entry->mfn), is_pod = 0 ); - - printk("gfn: %-16lx mfn: %-16lx order: %2d is_pod: %d\n", - gfn, mfn_x(mfn), order, is_pod); - - if ( !(record_counter++ % 100) ) - process_pending_softirqs(); - } -out: - unmap_domain_page(table); - } - } -} - -static struct keyhandler ept_p2m_table = { - .diagnostic = 0, - .u.fn = ept_dump_p2m_table, - .desc = "dump ept p2m table" -}; - -void setup_ept_dump(void) -{ - register_keyhandler('D', &ept_p2m_table); -} - -/* - * Local variables: - * mode: C - * c-set-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/p2m-ept.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/mm/p2m-ept.c Fri May 06 11:15:35 2011 +0100 @@ -0,0 +1,910 @@ +/* + * ept-p2m.c: use the EPT page table as p2m + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <xen/config.h> +#include <xen/domain_page.h> +#include <xen/sched.h> +#include <asm/current.h> +#include <asm/paging.h> +#include <asm/types.h> +#include <asm/domain.h> +#include <asm/p2m.h> +#include <asm/hvm/vmx/vmx.h> +#include <asm/hvm/vmx/vmcs.h> +#include <xen/iommu.h> +#include <asm/mtrr.h> +#include <asm/hvm/cacheattr.h> +#include <xen/keyhandler.h> +#include <xen/softirq.h> + +#define atomic_read_ept_entry(__pepte) \ + ( (ept_entry_t) { .epte = atomic_read64(&(__pepte)->epte) } ) +#define atomic_write_ept_entry(__pepte, __epte) \ + atomic_write64(&(__pepte)->epte, (__epte).epte) + +#define is_epte_present(ept_entry) ((ept_entry)->epte & 0x7) +#define is_epte_superpage(ept_entry) ((ept_entry)->sp) + +/* Non-ept "lock-and-check" wrapper */ +static int ept_pod_check_and_populate(struct p2m_domain *p2m, unsigned long gfn, + ept_entry_t *entry, int order, + p2m_query_t q) +{ + /* Only take the lock if we don't already have it. Otherwise it + * wouldn't be safe to do p2m lookups with the p2m lock held */ + int do_locking = !p2m_locked_by_me(p2m); + int r; + + if ( do_locking ) + p2m_lock(p2m); + + /* Check to make sure this is still PoD */ + if ( entry->sa_p2mt != p2m_populate_on_demand ) + { + if ( do_locking ) + p2m_unlock(p2m); + return 0; + } + + r = p2m_pod_demand_populate(p2m, gfn, order, q); + + if ( do_locking ) + p2m_unlock(p2m); + + return r; +} + +static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type, p2m_access_t access) +{ + /* First apply type permissions */ + switch(type) + { + case p2m_invalid: + case p2m_mmio_dm: + case p2m_populate_on_demand: + case p2m_ram_paging_out: + case p2m_ram_paged: + case p2m_ram_paging_in: + case p2m_ram_paging_in_start: + default: + entry->r = entry->w = entry->x = 0; + break; + case p2m_ram_rw: + entry->r = entry->w = entry->x = 1; + break; + case p2m_mmio_direct: + entry->r = entry->x = 1; + entry->w = !rangeset_contains_singleton(mmio_ro_ranges, + entry->mfn); + break; + case p2m_ram_logdirty: + case p2m_ram_ro: + case p2m_ram_shared: + entry->r = entry->x = 1; + entry->w = 0; + break; + case p2m_grant_map_rw: + entry->r = entry->w = 1; + entry->x = 0; + break; + case p2m_grant_map_ro: + entry->r = 1; + entry->w = entry->x = 0; + break; + } + + + /* Then restrict with access permissions */ + switch (access) + { + case p2m_access_n: + entry->r = entry->w = entry->x = 0; + break; + case p2m_access_r: + entry->w = entry->x = 0; + break; + case p2m_access_w: + entry->r = entry->x = 0; + break; + case p2m_access_x: + entry->r = entry->w = 0; + break; + case p2m_access_rx: + case p2m_access_rx2rw: + entry->w = 0; + break; + case p2m_access_wx: + entry->r = 0; + break; + case p2m_access_rw: + entry->x = 0; + break; + case p2m_access_rwx: + break; + } + +} + +#define GUEST_TABLE_MAP_FAILED 0 +#define GUEST_TABLE_NORMAL_PAGE 1 +#define GUEST_TABLE_SUPER_PAGE 2 +#define GUEST_TABLE_POD_PAGE 3 + +/* Fill in middle levels of ept table */ +static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) +{ + struct page_info *pg; + + pg = p2m_alloc_ptp(p2m, 0); + if ( pg == NULL ) + return 0; + + ept_entry->epte = 0; + ept_entry->mfn = page_to_mfn(pg); + ept_entry->access = p2m->default_access; + + ept_entry->r = ept_entry->w = ept_entry->x = 1; + + return 1; +} + +/* free ept sub tree behind an entry */ +void ept_free_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry, int level) +{ + /* End if the entry is a leaf entry. */ + if ( level == 0 || !is_epte_present(ept_entry) || + is_epte_superpage(ept_entry) ) + return; + + if ( level > 1 ) + { + ept_entry_t *epte = map_domain_page(ept_entry->mfn); + for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) + ept_free_entry(p2m, epte + i, level - 1); + unmap_domain_page(epte); + } + + p2m_free_ptp(p2m, mfn_to_page(ept_entry->mfn)); +} + +static int ept_split_super_page(struct p2m_domain *p2m, ept_entry_t *ept_entry, + int level, int target) +{ + ept_entry_t new_ept, *table; + uint64_t trunk; + int rv = 1; + + /* End if the entry is a leaf entry or reaches the target level. */ + if ( level == 0 || level == target ) + return rv; + + ASSERT(is_epte_superpage(ept_entry)); + + if ( !ept_set_middle_entry(p2m, &new_ept) ) + return 0; + + table = map_domain_page(new_ept.mfn); + trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); + + for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) + { + ept_entry_t *epte = table + i; + + epte->epte = 0; + epte->emt = ept_entry->emt; + epte->ipat = ept_entry->ipat; + epte->sp = (level > 1) ? 1 : 0; + epte->access = ept_entry->access; + epte->sa_p2mt = ept_entry->sa_p2mt; + epte->mfn = ept_entry->mfn + i * trunk; + epte->rsvd2_snp = ( iommu_enabled && iommu_snoop ) ? 1 : 0; + + ept_p2m_type_to_flags(epte, epte->sa_p2mt, epte->access); + + if ( (level - 1) == target ) + continue; + + ASSERT(is_epte_superpage(epte)); + + if ( !(rv = ept_split_super_page(p2m, epte, level - 1, target)) ) + break; + } + + unmap_domain_page(table); + + /* Even failed we should install the newly allocated ept page. */ + *ept_entry = new_ept; + + return rv; +} + +/* Take the currently mapped table, find the corresponding gfn entry, + * and map the next table, if available. If the entry is empty + * and read_only is set, + * Return values: + * 0: Failed to map. Either read_only was set and the entry was + * empty, or allocating a new page failed. + * GUEST_TABLE_NORMAL_PAGE: next level mapped normally + * GUEST_TABLE_SUPER_PAGE: + * The next entry points to a superpage, and caller indicates + * that they are going to the superpage level, or are only doing + * a read. + * GUEST_TABLE_POD: + * The next entry is marked populate-on-demand. + */ +static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, + ept_entry_t **table, unsigned long *gfn_remainder, + int next_level) +{ + unsigned long mfn; + ept_entry_t *ept_entry, e; + u32 shift, index; + + shift = next_level * EPT_TABLE_ORDER; + + index = *gfn_remainder >> shift; + + /* index must be falling into the page */ + ASSERT(index < EPT_PAGETABLE_ENTRIES); + + ept_entry = (*table) + index; + + /* ept_next_level() is called (sometimes) without a lock. Read + * the entry once, and act on the "cached" entry after that to + * avoid races. */ + e = atomic_read_ept_entry(ept_entry); + + if ( !is_epte_present(&e) ) + { + if ( e.sa_p2mt == p2m_populate_on_demand ) + return GUEST_TABLE_POD_PAGE; + + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + + if ( !ept_set_middle_entry(p2m, ept_entry) ) + return GUEST_TABLE_MAP_FAILED; + else + e = atomic_read_ept_entry(ept_entry); /* Refresh */ + } + + /* The only time sp would be set here is if we had hit a superpage */ + if ( is_epte_superpage(&e) ) + return GUEST_TABLE_SUPER_PAGE; + + mfn = e.mfn; + unmap_domain_page(*table); + *table = map_domain_page(mfn); + *gfn_remainder &= (1UL << shift) - 1; + return GUEST_TABLE_NORMAL_PAGE; +} + +/* + * ept_set_entry() computes 'need_modify_vtd_table' for itself, + * by observing whether any gfn->mfn translations are modified. + */ +static int +ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, + unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma) +{ + ept_entry_t *table, *ept_entry = NULL; + unsigned long gfn_remainder = gfn; + unsigned long offset = 0; + u32 index; + int i, target = order / EPT_TABLE_ORDER; + int rv = 0; + int ret = 0; + bool_t direct_mmio = (p2mt == p2m_mmio_direct); + uint8_t ipat = 0; + int need_modify_vtd_table = 1; + int vtd_pte_present = 0; + int needs_sync = 1; + struct domain *d = p2m->domain; + ept_entry_t old_entry = { .epte = 0 }; + + /* + * the caller must make sure: + * 1. passing valid gfn and mfn at order boundary. + * 2. gfn not exceeding guest physical address width. + * 3. passing a valid order. + */ + if ( ((gfn | mfn_x(mfn)) & ((1UL << order) - 1)) || + ((u64)gfn >> ((ept_get_wl(d) + 1) * EPT_TABLE_ORDER)) || + (order % EPT_TABLE_ORDER) ) + return 0; + + ASSERT((target == 2 && hvm_hap_has_1gb(d)) || + (target == 1 && hvm_hap_has_2mb(d)) || + (target == 0)); + + table = map_domain_page(ept_get_asr(d)); + + ASSERT(table != NULL); + + for ( i = ept_get_wl(d); i > target; i-- ) + { + ret = ept_next_level(p2m, 0, &table, &gfn_remainder, i); + if ( !ret ) + goto out; + else if ( ret != GUEST_TABLE_NORMAL_PAGE ) + break; + } + + ASSERT(ret != GUEST_TABLE_POD_PAGE || i != target); + + index = gfn_remainder >> (i * EPT_TABLE_ORDER); + offset = gfn_remainder & ((1UL << (i * EPT_TABLE_ORDER)) - 1); + + ept_entry = table + index; + + /* In case VT-d uses same page table, this flag is needed by VT-d */ + vtd_pte_present = is_epte_present(ept_entry) ? 1 : 0; + + /* + * If we're here with i > target, we must be at a leaf node, and + * we need to break up the superpage. + * + * If we're here with i == target and i > 0, we need to check to see + * if we're replacing a non-leaf entry (i.e., pointing to an N-1 table) + * with a leaf entry (a 1GiB or 2MiB page), and handle things appropriately. + */ + + if ( i == target ) + { + /* We reached the target level. */ + ept_entry_t new_entry = { .epte = 0 }; + + /* No need to flush if the old entry wasn't valid */ + if ( !is_epte_present(ept_entry) ) + needs_sync = 0; + + /* If we're replacing a non-leaf entry with a leaf entry (1GiB or 2MiB), + * the intermediate tables will be freed below after the ept flush + * + * Read-then-write is OK because we hold the p2m lock. */ + old_entry = *ept_entry; + + if ( mfn_valid(mfn_x(mfn)) || direct_mmio || p2m_is_paged(p2mt) || + (p2mt == p2m_ram_paging_in_start) ) + { + /* Construct the new entry, and then write it once */ + new_entry.emt = epte_get_entry_emt(p2m->domain, gfn, mfn, &ipat, + direct_mmio); + + new_entry.ipat = ipat; + new_entry.sp = order ? 1 : 0; + new_entry.sa_p2mt = p2mt; + new_entry.access = p2ma; + new_entry.rsvd2_snp = (iommu_enabled && iommu_snoop); + + new_entry.mfn = mfn_x(mfn); + + if ( old_entry.mfn == new_entry.mfn ) + need_modify_vtd_table = 0; + + ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); + } + + atomic_write_ept_entry(ept_entry, new_entry); + } + else + { + /* We need to split the original page. */ + ept_entry_t split_ept_entry; + ept_entry_t new_entry = { .epte = 0 }; + + ASSERT(is_epte_superpage(ept_entry)); + + split_ept_entry = atomic_read_ept_entry(ept_entry); + + if ( !ept_split_super_page(p2m, &split_ept_entry, i, target) ) + { + ept_free_entry(p2m, &split_ept_entry, i); + goto out; + } + + /* 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); + + /* then move to the level we want to make real changes */ + for ( ; i > target; i-- ) + ept_next_level(p2m, 0, &table, &gfn_remainder, i); + + ASSERT(i == target); + + index = gfn_remainder >> (i * EPT_TABLE_ORDER); + offset = gfn_remainder & ((1UL << (i * EPT_TABLE_ORDER)) - 1); + + ept_entry = table + index; + + new_entry.emt = epte_get_entry_emt(d, gfn, mfn, &ipat, direct_mmio); + new_entry.ipat = ipat; + new_entry.sp = i ? 1 : 0; + new_entry.sa_p2mt = p2mt; + new_entry.access = p2ma; + new_entry.rsvd2_snp = (iommu_enabled && iommu_snoop); + + /* the caller should take care of the previous page */ + new_entry.mfn = mfn_x(mfn); + + /* Safe to read-then-write because we hold the p2m lock */ + if ( ept_entry->mfn == new_entry.mfn ) + need_modify_vtd_table = 0; + + ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); + + atomic_write_ept_entry(ept_entry, new_entry); + } + + /* Track the highest gfn for which we have ever had a valid mapping */ + if ( mfn_valid(mfn_x(mfn)) && + (gfn + (1UL << order) - 1 > p2m->max_mapped_pfn) ) + p2m->max_mapped_pfn = gfn + (1UL << order) - 1; + + /* Success */ + rv = 1; + +out: + unmap_domain_page(table); + + if ( needs_sync ) + ept_sync_domain(p2m->domain); + + if ( rv && iommu_enabled && need_iommu(p2m->domain) && need_modify_vtd_table ) + { + if ( iommu_hap_pt_share ) + iommu_pte_flush(d, gfn, (u64*)ept_entry, order, vtd_pte_present); + else + { + if ( p2mt == p2m_ram_rw ) + { + if ( order > 0 ) + { + for ( i = 0; i < (1 << order); i++ ) + iommu_map_page( + p2m->domain, gfn - offset + i, mfn_x(mfn) - offset + i, + IOMMUF_readable | IOMMUF_writable); + } + else if ( !order ) + iommu_map_page( + p2m->domain, gfn, mfn_x(mfn), IOMMUF_readable | IOMMUF_writable); + } + else + { + if ( order > 0 ) + { + for ( i = 0; i < (1 << order); i++ ) + iommu_unmap_page(p2m->domain, gfn - offset + i); + } + else if ( !order ) + iommu_unmap_page(p2m->domain, gfn); + } + } + } + + /* Release the old intermediate tables, if any. This has to be the + last thing we do, after the ept_sync_domain() and removal + from the iommu tables, so as to avoid a potential + use-after-free. */ + if ( is_epte_present(&old_entry) ) + ept_free_entry(p2m, &old_entry, target); + + return rv; +} + +/* Read ept p2m entries */ +static mfn_t ept_get_entry(struct p2m_domain *p2m, + unsigned long gfn, p2m_type_t *t, p2m_access_t* a, + p2m_query_t q) +{ + struct domain *d = p2m->domain; + ept_entry_t *table = map_domain_page(ept_get_asr(d)); + unsigned long gfn_remainder = gfn; + ept_entry_t *ept_entry; + u32 index; + int i; + int ret = 0; + mfn_t mfn = _mfn(INVALID_MFN); + + *t = p2m_mmio_dm; + *a = p2m_access_n; + + /* This pfn is higher than the highest the p2m map currently holds */ + if ( gfn > p2m->max_mapped_pfn ) + goto out; + + /* Should check if gfn obeys GAW here. */ + + for ( i = ept_get_wl(d); i > 0; i-- ) + { + retry: + ret = ept_next_level(p2m, 1, &table, &gfn_remainder, i); + if ( !ret ) + goto out; + else if ( ret == GUEST_TABLE_POD_PAGE ) + { + if ( q == p2m_query ) + { + *t = p2m_populate_on_demand; + goto out; + } + + /* Populate this superpage */ + ASSERT(i == 1); + + index = gfn_remainder >> ( i * EPT_TABLE_ORDER); + ept_entry = table + index; + + if ( !ept_pod_check_and_populate(p2m, gfn, + ept_entry, 9, q) ) + goto retry; + else + goto out; + } + else if ( ret == GUEST_TABLE_SUPER_PAGE ) + break; + } + + index = gfn_remainder >> (i * EPT_TABLE_ORDER); + ept_entry = table + index; + + if ( ept_entry->sa_p2mt == p2m_populate_on_demand ) + { + if ( q == p2m_query ) + { + *t = p2m_populate_on_demand; + goto out; + } + + ASSERT(i == 0); + + if ( ept_pod_check_and_populate(p2m, gfn, + ept_entry, 0, q) ) + goto out; + } + + /* Need to check for all-zeroes because typecode 0 is p2m_ram and an + * entirely empty entry shouldn't have RAM type. */ + if ( ept_entry->epte != 0 && ept_entry->sa_p2mt != p2m_invalid ) + { + *t = ept_entry->sa_p2mt; + *a = ept_entry->access; + + mfn = _mfn(ept_entry->mfn); + if ( i ) + { + /* + * We may meet super pages, and to split into 4k pages + * to emulate p2m table + */ + unsigned long split_mfn = mfn_x(mfn) + + (gfn_remainder & + ((1 << (i * EPT_TABLE_ORDER)) - 1)); + mfn = _mfn(split_mfn); + } + } + +out: + unmap_domain_page(table); + return mfn; +} + +/* WARNING: Only caller doesn't care about PoD pages. So this function will + * always return 0 for PoD pages, not populate them. If that becomes necessary, + * pass a p2m_query_t type along to distinguish. */ +static ept_entry_t ept_get_entry_content(struct p2m_domain *p2m, + unsigned long gfn, int *level) +{ + ept_entry_t *table = map_domain_page(ept_get_asr(p2m->domain)); + unsigned long gfn_remainder = gfn; + ept_entry_t *ept_entry; + ept_entry_t content = { .epte = 0 }; + u32 index; + int i; + int ret=0; + + /* This pfn is higher than the highest the p2m map currently holds */ + if ( gfn > p2m->max_mapped_pfn ) + goto out; + + for ( i = ept_get_wl(p2m->domain); i > 0; i-- ) + { + ret = ept_next_level(p2m, 1, &table, &gfn_remainder, i); + if ( !ret || ret == GUEST_TABLE_POD_PAGE ) + goto out; + else if ( ret == GUEST_TABLE_SUPER_PAGE ) + break; + } + + index = gfn_remainder >> (i * EPT_TABLE_ORDER); + ept_entry = table + index; + content = *ept_entry; + *level = i; + + out: + unmap_domain_page(table); + return content; +} + +void ept_walk_table(struct domain *d, unsigned long gfn) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + ept_entry_t *table = map_domain_page(ept_get_asr(d)); + unsigned long gfn_remainder = gfn; + + int i; + + gdprintk(XENLOG_ERR, "Walking EPT tables for domain %d gfn %lx\n", + d->domain_id, gfn); + + /* This pfn is higher than the highest the p2m map currently holds */ + if ( gfn > p2m->max_mapped_pfn ) + { + gdprintk(XENLOG_ERR, " gfn exceeds max_mapped_pfn %lx\n", + p2m->max_mapped_pfn); + goto out; + } + + for ( i = ept_get_wl(d); i >= 0; i-- ) + { + ept_entry_t *ept_entry, *next; + u32 index; + + /* Stolen from ept_next_level */ + index = gfn_remainder >> (i*EPT_TABLE_ORDER); + ept_entry = table + index; + + gdprintk(XENLOG_ERR, " epte %"PRIx64"\n", ept_entry->epte); + + if ( (i == 0) || !is_epte_present(ept_entry) || + is_epte_superpage(ept_entry) ) + goto out; + else + { + gfn_remainder &= (1UL << (i*EPT_TABLE_ORDER)) - 1; + + next = map_domain_page(ept_entry->mfn); + + unmap_domain_page(table); + + table = next; + } + } + +out: + unmap_domain_page(table); + return; +} + +static mfn_t ept_get_entry_current(struct p2m_domain *p2m, + unsigned long gfn, p2m_type_t *t, p2m_access_t *a, + p2m_query_t q) +{ + return ept_get_entry(p2m, gfn, t, a, q); +} + +/* + * To test if the new emt type is the same with old, + * return 1 to not to reset ept entry. + */ +static int need_modify_ept_entry(struct p2m_domain *p2m, unsigned long gfn, + mfn_t mfn, uint8_t o_ipat, uint8_t o_emt, + p2m_type_t p2mt) +{ + uint8_t ipat; + uint8_t emt; + bool_t direct_mmio = (p2mt == p2m_mmio_direct); + + emt = epte_get_entry_emt(p2m->domain, gfn, mfn, &ipat, direct_mmio); + + if ( (emt == o_emt) && (ipat == o_ipat) ) + return 0; + + return 1; +} + +void ept_change_entry_emt_with_range(struct domain *d, + unsigned long start_gfn, + unsigned long end_gfn) +{ + unsigned long gfn; + ept_entry_t e; + mfn_t mfn; + int order = 0; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + p2m_lock(p2m); + for ( gfn = start_gfn; gfn <= end_gfn; gfn++ ) + { + int level = 0; + uint64_t trunk = 0; + + e = ept_get_entry_content(p2m, gfn, &level); + if ( !p2m_has_emt(e.sa_p2mt) ) + continue; + + order = 0; + mfn = _mfn(e.mfn); + + if ( is_epte_superpage(&e) ) + { + while ( level ) + { + trunk = (1UL << (level * EPT_TABLE_ORDER)) - 1; + if ( !(gfn & trunk) && (gfn + trunk <= end_gfn) ) + { + /* gfn assigned with 2M or 1G, and the end covers more than + * the super page areas. + * Set emt for super page. + */ + order = level * EPT_TABLE_ORDER; + if ( need_modify_ept_entry(p2m, gfn, mfn, + e.ipat, e.emt, e.sa_p2mt) ) + ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access); + gfn += trunk; + break; + } + level--; + } + } + else /* gfn assigned with 4k */ + { + if ( need_modify_ept_entry(p2m, gfn, mfn, e.ipat, e.emt, e.sa_p2mt) ) + ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access); + } + } + p2m_unlock(p2m); +} + +/* + * Walk the whole p2m table, changing any entries of the old type + * to the new type. This is used in hardware-assisted paging to + * quickly enable or diable log-dirty tracking + */ +static void ept_change_entry_type_page(mfn_t ept_page_mfn, int ept_page_level, + p2m_type_t ot, p2m_type_t nt) +{ + ept_entry_t e, *epte = map_domain_page(mfn_x(ept_page_mfn)); + + for ( int i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) + { + if ( !is_epte_present(epte + i) ) + continue; + + if ( (ept_page_level > 0) && !is_epte_superpage(epte + i) ) + ept_change_entry_type_page(_mfn(epte[i].mfn), + ept_page_level - 1, ot, nt); + else + { + e = atomic_read_ept_entry(&epte[i]); + if ( e.sa_p2mt != ot ) + continue; + + e.sa_p2mt = nt; + ept_p2m_type_to_flags(&e, nt, e.access); + atomic_write_ept_entry(&epte[i], e); + } + } + + unmap_domain_page(epte); +} + +static void ept_change_entry_type_global(struct p2m_domain *p2m, + p2m_type_t ot, p2m_type_t nt) +{ + struct domain *d = p2m->domain; + if ( ept_get_asr(d) == 0 ) + return; + + BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); + BUG_ON(ot != nt && (ot == p2m_mmio_direct || nt == p2m_mmio_direct)); + + ept_change_entry_type_page(_mfn(ept_get_asr(d)), ept_get_wl(d), ot, nt); + + ept_sync_domain(d); +} + +void ept_p2m_init(struct p2m_domain *p2m) +{ + p2m->set_entry = ept_set_entry; + p2m->get_entry = ept_get_entry; + p2m->get_entry_current = ept_get_entry_current; + p2m->change_entry_type_global = ept_change_entry_type_global; +} + +static void ept_dump_p2m_table(unsigned char key) +{ + struct domain *d; + ept_entry_t *table, *ept_entry; + mfn_t mfn; + int order; + int i; + int is_pod; + int ret = 0; + unsigned long index; + unsigned long gfn, gfn_remainder; + unsigned long record_counter = 0; + struct p2m_domain *p2m; + + for_each_domain(d) + { + if ( !hap_enabled(d) ) + continue; + + p2m = p2m_get_hostp2m(d); + printk("\ndomain%d EPT p2m table: \n", d->domain_id); + + for ( gfn = 0; gfn <= p2m->max_mapped_pfn; gfn += (1 << order) ) + { + gfn_remainder = gfn; + mfn = _mfn(INVALID_MFN); + table = map_domain_page(ept_get_asr(d)); + + for ( i = ept_get_wl(d); i > 0; i-- ) + { + ret = ept_next_level(p2m, 1, &table, &gfn_remainder, i); + if ( ret != GUEST_TABLE_NORMAL_PAGE ) + break; + } + + order = i * EPT_TABLE_ORDER; + + if ( ret == GUEST_TABLE_MAP_FAILED ) + goto out; + + index = gfn_remainder >> order; + ept_entry = table + index; + if ( ept_entry->sa_p2mt != p2m_invalid ) + { + ( ept_entry->sa_p2mt == p2m_populate_on_demand ) ? + ( mfn = _mfn(INVALID_MFN), is_pod = 1 ) : + ( mfn = _mfn(ept_entry->mfn), is_pod = 0 ); + + printk("gfn: %-16lx mfn: %-16lx order: %2d is_pod: %d\n", + gfn, mfn_x(mfn), order, is_pod); + + if ( !(record_counter++ % 100) ) + process_pending_softirqs(); + } +out: + unmap_domain_page(table); + } + } +} + +static struct keyhandler ept_p2m_table = { + .diagnostic = 0, + .u.fn = ept_dump_p2m_table, + .desc = "dump ept p2m table" +}; + +void setup_ept_dump(void) +{ + register_keyhandler('D', &ept_p2m_table); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/p2m-pod.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/mm/p2m-pod.c Fri May 06 11:15:35 2011 +0100 @@ -0,0 +1,1151 @@ +/****************************************************************************** + * arch/x86/mm/p2m-pod.c + * + * Populate-on-demand p2m entries. + * + * Copyright (c) 2009-2011 Citrix Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <asm/domain.h> +#include <asm/page.h> +#include <asm/paging.h> +#include <asm/p2m.h> +#include <asm/hvm/vmx/vmx.h> /* ept_p2m_init() */ +#include <xen/iommu.h> +#include <asm/mem_event.h> +#include <public/mem_event.h> +#include <asm/mem_sharing.h> +#include <xen/event.h> +#include <asm/hvm/nestedhvm.h> +#include <asm/hvm/svm/amd-iommu-proto.h> + +/* Printouts */ +#define P2M_PRINTK(_f, _a...) \ + debugtrace_printk("p2m: %s(): " _f, __func__, ##_a) +#define P2M_ERROR(_f, _a...) \ + printk("pg error: %s(): " _f, __func__, ##_a) +#if P2M_DEBUGGING +#define P2M_DEBUG(_f, _a...) \ + debugtrace_printk("p2mdebug: %s(): " _f, __func__, ##_a) +#else +#define P2M_DEBUG(_f, _a...) do { (void)(_f); } while(0) +#endif + + +/* Override macros from asm/page.h to make them work with mfn_t */ +#undef mfn_to_page +#define mfn_to_page(_m) __mfn_to_page(mfn_x(_m)) +#undef mfn_valid +#define mfn_valid(_mfn) __mfn_valid(mfn_x(_mfn)) +#undef page_to_mfn +#define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg)) + +#if P2M_AUDIT +extern void audit_p2m(struct p2m_domain *p2m, int strict_m2p); +#else +# define audit_p2m(_p2m, _m2p) do { (void)(_p2m),(_m2p); } while (0) +#endif /* P2M_AUDIT */ + +#define SUPERPAGE_PAGES (1UL << 9) +#define superpage_aligned(_x) (((_x)&(SUPERPAGE_PAGES-1))==0) + +/* + * Populate-on-demand functionality + */ + +static int +p2m_pod_cache_add(struct p2m_domain *p2m, + struct page_info *page, + unsigned long order) +{ + int i; + struct page_info *p; + struct domain *d = p2m->domain; + +#ifndef NDEBUG + mfn_t mfn; + + mfn = page_to_mfn(page); + + /* Check to make sure this is a contiguous region */ + if( mfn_x(mfn) & ((1 << order) - 1) ) + { + printk("%s: mfn %lx not aligned order %lu! (mask %lx)\n", + __func__, mfn_x(mfn), order, ((1UL << order) - 1)); + return -1; + } + + for(i=0; i < 1 << order ; i++) { + struct domain * od; + + p = mfn_to_page(_mfn(mfn_x(mfn) + i)); + od = page_get_owner(p); + if(od != d) + { + printk("%s: mfn %lx expected owner d%d, got owner d%d!\n", + __func__, mfn_x(mfn), d->domain_id, + od?od->domain_id:-1); + return -1; + } + } +#endif + + ASSERT(p2m_locked_by_me(p2m)); + + /* + * Pages from domain_alloc and returned by the balloon driver aren't + * guaranteed to be zero; but by reclaiming zero pages, we implicitly + * promise to provide zero pages. So we scrub pages before using. + */ + for ( i = 0; i < (1 << order); i++ ) + { + char *b = map_domain_page(mfn_x(page_to_mfn(page)) + i); + clear_page(b); + unmap_domain_page(b); + } + + spin_lock(&d->page_alloc_lock); + + /* First, take all pages off the domain list */ + for(i=0; i < 1 << order ; i++) + { + p = page + i; + page_list_del(p, &d->page_list); + } + + /* Then add the first one to the appropriate populate-on-demand list */ + switch(order) + { + case 9: + page_list_add_tail(page, &p2m->pod.super); /* lock: page_alloc */ + p2m->pod.count += 1 << order; + break; + case 0: + page_list_add_tail(page, &p2m->pod.single); /* lock: page_alloc */ + p2m->pod.count += 1; + break; + default: + BUG(); + } + + /* Ensure that the PoD cache has never been emptied. + * This may cause "zombie domains" since the page will never be freed. */ + BUG_ON( d->arch.relmem != RELMEM_not_started ); + + spin_unlock(&d->page_alloc_lock); + + return 0; +} + +/* Get a page of size order from the populate-on-demand cache. Will break + * down 2-meg pages into singleton pages automatically. Returns null if + * a superpage is requested and no superpages are available. Must be called + * with the d->page_lock held. */ +static struct page_info * p2m_pod_cache_get(struct p2m_domain *p2m, + unsigned long order) +{ + struct page_info *p = NULL; + int i; + + if ( order == 9 && page_list_empty(&p2m->pod.super) ) + { + return NULL; + } + else if ( order == 0 && page_list_empty(&p2m->pod.single) ) + { + unsigned long mfn; + struct page_info *q; + + BUG_ON( page_list_empty(&p2m->pod.super) ); + + /* Break up a superpage to make single pages. NB count doesn't + * need to be adjusted. */ + p = page_list_remove_head(&p2m->pod.super); + mfn = mfn_x(page_to_mfn(p)); + + for ( i=0; i<SUPERPAGE_PAGES; i++ ) + { + q = mfn_to_page(_mfn(mfn+i)); + page_list_add_tail(q, &p2m->pod.single); + } + } + + switch ( order ) + { + case 9: + BUG_ON( page_list_empty(&p2m->pod.super) ); + p = page_list_remove_head(&p2m->pod.super); + p2m->pod.count -= 1 << order; /* Lock: page_alloc */ + break; + case 0: + BUG_ON( page_list_empty(&p2m->pod.single) ); + p = page_list_remove_head(&p2m->pod.single); + p2m->pod.count -= 1; + break; + default: + BUG(); + } + + /* Put the pages back on the domain page_list */ + for ( i = 0 ; i < (1 << order); i++ ) + { + BUG_ON(page_get_owner(p + i) != p2m->domain); + page_list_add_tail(p + i, &p2m->domain->page_list); + } + + return p; +} + +/* Set the size of the cache, allocating or freeing as necessary. */ +static int +p2m_pod_set_cache_target(struct p2m_domain *p2m, unsigned long pod_target, int preemptible) +{ + struct domain *d = p2m->domain; + int ret = 0; + + /* Increasing the target */ + while ( pod_target > p2m->pod.count ) + { + struct page_info * page; + int order; + + if ( (pod_target - p2m->pod.count) >= SUPERPAGE_PAGES ) + order = 9; + else + order = 0; + retry: + page = alloc_domheap_pages(d, order, 0); + if ( unlikely(page == NULL) ) + { + if ( order == 9 ) + { + /* If we can't allocate a superpage, try singleton pages */ + order = 0; + goto retry; + } + + printk("%s: Unable to allocate domheap page for pod cache. target %lu cachesize %d\n", + __func__, pod_target, p2m->pod.count); + ret = -ENOMEM; + goto out; + } + + p2m_pod_cache_add(p2m, page, order); + + if ( hypercall_preempt_check() && preemptible ) + { + ret = -EAGAIN; + goto out; + } + } + + /* Decreasing the target */ + /* We hold the p2m lock here, so we don't need to worry about + * cache disappearing under our feet. */ + while ( pod_target < p2m->pod.count ) + { + struct page_info * page; + int order, i; + + /* Grab the lock before checking that pod.super is empty, or the last + * entries may disappear before we grab the lock. */ + spin_lock(&d->page_alloc_lock); + + if ( (p2m->pod.count - pod_target) > SUPERPAGE_PAGES + && !page_list_empty(&p2m->pod.super) ) + order = 9; + else + order = 0; + + page = p2m_pod_cache_get(p2m, order); + + ASSERT(page != NULL); + + spin_unlock(&d->page_alloc_lock); + + /* Then free them */ + for ( i = 0 ; i < (1 << order) ; i++ ) + { + /* Copied from common/memory.c:guest_remove_page() */ + if ( unlikely(!get_page(page+i, d)) ) + { + gdprintk(XENLOG_INFO, "Bad page free for domain %u\n", d->domain_id); + ret = -EINVAL; + goto out; + } + + if ( test_and_clear_bit(_PGT_pinned, &(page+i)->u.inuse.type_info) ) + put_page_and_type(page+i); + + if ( test_and_clear_bit(_PGC_allocated, &(page+i)->count_info) ) + put_page(page+i); + + put_page(page+i); + + if ( hypercall_preempt_check() && preemptible ) + { + ret = -EAGAIN; + goto out; + } + } + } + +out: + return ret; +} + +/* + * The "right behavior" here requires some careful thought. First, some + * definitions: + * + M: static_max + * + B: number of pages the balloon driver has ballooned down to. + * + P: Number of populated pages. + * + T: Old target + * + T': New target + * + * The following equations should hold: + * 0 <= P <= T <= B <= M + * d->arch.p2m->pod.entry_count == B - P + * d->tot_pages == P + d->arch.p2m->pod.count + * + * Now we have the following potential cases to cover: + * B <T': Set the PoD cache size equal to the number of outstanding PoD + * entries. The balloon driver will deflate the balloon to give back + * the remainder of the ram to the guest OS. + * T <T'<B : Increase PoD cache size. + * T'<T<=B : Here we have a choice. We can decrease the size of the cache, + * get the memory right away. However, that means every time we + * reduce the memory target we risk the guest attempting to populate the + * memory before the balloon driver has reached its new target. Safer to + * never reduce the cache size here, but only when the balloon driver frees + * PoD ranges. + * + * If there are many zero pages, we could reach the target also by doing + * zero sweeps and marking the ranges PoD; but the balloon driver will have + * to free this memory eventually anyway, so we don't actually gain that much + * by doing so. + * + * NB that the equation (B<T') may require adjustment to the cache + * size as PoD pages are freed as well; i.e., freeing a PoD-backed + * entry when pod.entry_count == pod.count requires us to reduce both + * pod.entry_count and pod.count. + */ +int +p2m_pod_set_mem_target(struct domain *d, unsigned long target) +{ + unsigned pod_target; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + int ret = 0; + unsigned long populated; + + p2m_lock(p2m); + + /* P == B: Nothing to do. */ + if ( p2m->pod.entry_count == 0 ) + goto out; + + /* Don't do anything if the domain is being torn down */ + if ( d->is_dying ) + goto out; + + /* T' < B: Don't reduce the cache size; let the balloon driver + * take care of it. */ + if ( target < d->tot_pages ) + goto out; + + populated = d->tot_pages - p2m->pod.count; + + pod_target = target - populated; + + /* B < T': Set the cache size equal to # of outstanding entries, + * let the balloon driver fill in the rest. */ + if ( pod_target > p2m->pod.entry_count ) + pod_target = p2m->pod.entry_count; + + ASSERT( pod_target >= p2m->pod.count ); + + ret = p2m_pod_set_cache_target(p2m, pod_target, 1/*preemptible*/); + +out: + p2m_unlock(p2m); + + return ret; +} + +void +p2m_pod_empty_cache(struct domain *d) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + struct page_info *page; + + /* After this barrier no new PoD activities can happen. */ + BUG_ON(!d->is_dying); + spin_barrier(&p2m->lock); + + spin_lock(&d->page_alloc_lock); + + while ( (page = page_list_remove_head(&p2m->pod.super)) ) + { + int i; + + for ( i = 0 ; i < SUPERPAGE_PAGES ; i++ ) + { + BUG_ON(page_get_owner(page + i) != d); + page_list_add_tail(page + i, &d->page_list); + } + + p2m->pod.count -= SUPERPAGE_PAGES; + } + + while ( (page = page_list_remove_head(&p2m->pod.single)) ) + { + BUG_ON(page_get_owner(page) != d); + page_list_add_tail(page, &d->page_list); + + p2m->pod.count -= 1; + } + + BUG_ON(p2m->pod.count != 0); + + spin_unlock(&d->page_alloc_lock); +} + +int +p2m_pod_offline_or_broken_hit(struct page_info *p) +{ + struct domain *d; + struct p2m_domain *p2m; + struct page_info *q, *tmp; + unsigned long mfn, bmfn; + + if ( !(d = page_get_owner(p)) || !(p2m = p2m_get_hostp2m(d)) ) + return 0; + + spin_lock(&d->page_alloc_lock); + bmfn = mfn_x(page_to_mfn(p)); + page_list_for_each_safe(q, tmp, &p2m->pod.super) + { + mfn = mfn_x(page_to_mfn(q)); + if ( (bmfn >= mfn) && ((bmfn - mfn) < SUPERPAGE_PAGES) ) + { + unsigned long i; + page_list_del(q, &p2m->pod.super); + for ( i = 0; i < SUPERPAGE_PAGES; i++) + { + q = mfn_to_page(_mfn(mfn + i)); + page_list_add_tail(q, &p2m->pod.single); + } + page_list_del(p, &p2m->pod.single); + p2m->pod.count--; + goto pod_hit; + } + } + + page_list_for_each_safe(q, tmp, &p2m->pod.single) + { + mfn = mfn_x(page_to_mfn(q)); + if ( mfn == bmfn ) + { + page_list_del(p, &p2m->pod.single); + p2m->pod.count--; + goto pod_hit; + } + } + + spin_unlock(&d->page_alloc_lock); + return 0; + +pod_hit: + page_list_add_tail(p, &d->arch.relmem_list); + spin_unlock(&d->page_alloc_lock); + return 1; +} + +void +p2m_pod_offline_or_broken_replace(struct page_info *p) +{ + struct domain *d; + struct p2m_domain *p2m; + + if ( !(d = page_get_owner(p)) || !(p2m = p2m_get_hostp2m(d)) ) + return; + + free_domheap_page(p); + + p = alloc_domheap_page(d, 0); + if ( unlikely(!p) ) + return; + + p2m_lock(p2m); + p2m_pod_cache_add(p2m, p, 0); + p2m_unlock(p2m); + return; +} + +/* This function is needed for two reasons: + * + To properly handle clearing of PoD entries + * + To "steal back" memory being freed for the PoD cache, rather than + * releasing it. + * + * Once both of these functions have been completed, we can return and + * allow decrease_reservation() to handle everything else. + */ +int +p2m_pod_decrease_reservation(struct domain *d, + xen_pfn_t gpfn, + unsigned int order) +{ + int ret=0; + int i; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + int steal_for_cache = 0; + int pod = 0, nonpod = 0, ram = 0; + + + /* If we don't have any outstanding PoD entries, let things take their + * course */ + if ( p2m->pod.entry_count == 0 ) + goto out; + + /* Figure out if we need to steal some freed memory for our cache */ + steal_for_cache = ( p2m->pod.entry_count > p2m->pod.count ); + + p2m_lock(p2m); + audit_p2m(p2m, 1); + + if ( unlikely(d->is_dying) ) + goto out_unlock; + + /* See what's in here. */ + /* FIXME: Add contiguous; query for PSE entries? */ + for ( i=0; i<(1<<order); i++) + { + p2m_type_t t; + + gfn_to_mfn_query(p2m, gpfn + i, &t); + + if ( t == p2m_populate_on_demand ) + pod++; + else + { + nonpod++; + if ( p2m_is_ram(t) ) + ram++; + } + } + + /* No populate-on-demand? Don't need to steal anything? Then we're done!*/ + if(!pod && !steal_for_cache) + goto out_unlock; + + if ( !nonpod ) + { + /* All PoD: Mark the whole region invalid and tell caller + * we're done. */ + set_p2m_entry(p2m, gpfn, _mfn(INVALID_MFN), order, p2m_invalid, p2m->default_access); + p2m->pod.entry_count-=(1<<order); /* Lock: p2m */ + BUG_ON(p2m->pod.entry_count < 0); + ret = 1; + goto out_entry_check; + } + + /* FIXME: Steal contig 2-meg regions for cache */ + + /* Process as long as: + * + There are PoD entries to handle, or + * + There is ram left, and we want to steal it + */ + for ( i=0; + i<(1<<order) && (pod>0 || (steal_for_cache && ram > 0)); + i++) + { + mfn_t mfn; + p2m_type_t t; + + mfn = gfn_to_mfn_query(p2m, gpfn + i, &t); + if ( t == p2m_populate_on_demand ) + { + set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid, p2m->default_access); + p2m->pod.entry_count--; /* Lock: p2m */ + BUG_ON(p2m->pod.entry_count < 0); + pod--; + } + else if ( steal_for_cache && p2m_is_ram(t) ) + { + struct page_info *page; + + ASSERT(mfn_valid(mfn)); + + page = mfn_to_page(mfn); + + set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid, p2m->default_access); + set_gpfn_from_mfn(mfn_x(mfn), INVALID_M2P_ENTRY); + + p2m_pod_cache_add(p2m, page, 0); + + steal_for_cache = ( p2m->pod.entry_count > p2m->pod.count ); + + nonpod--; + ram--; + } + } + + /* If there are no more non-PoD entries, tell decrease_reservation() that + * there's nothing left to do. */ + if ( nonpod == 0 ) + ret = 1; + +out_entry_check: + /* If we've reduced our "liabilities" beyond our "assets", free some */ + if ( p2m->pod.entry_count < p2m->pod.count ) + { + p2m_pod_set_cache_target(p2m, p2m->pod.entry_count, 0/*can't preempt*/); + } + +out_unlock: + audit_p2m(p2m, 1); + p2m_unlock(p2m); + +out: + return ret; +} + +void +p2m_pod_dump_data(struct p2m_domain *p2m) +{ + printk(" PoD entries=%d cachesize=%d\n", + p2m->pod.entry_count, p2m->pod.count); +} + + +/* Search for all-zero superpages to be reclaimed as superpages for the + * PoD cache. Must be called w/ p2m lock held, page_alloc lock not held. */ +static int +p2m_pod_zero_check_superpage(struct p2m_domain *p2m, unsigned long gfn) +{ + mfn_t mfn, mfn0 = _mfn(INVALID_MFN); + p2m_type_t type, type0 = 0; + unsigned long * map = NULL; + int ret=0, reset = 0; + int i, j; + int max_ref = 1; + struct domain *d = p2m->domain; + + if ( !superpage_aligned(gfn) ) + goto out; + + /* Allow an extra refcount for one shadow pt mapping in shadowed domains */ + if ( paging_mode_shadow(d) ) + max_ref++; + + /* Look up the mfns, checking to make sure they're the same mfn + * and aligned, and mapping them. */ + for ( i=0; i<SUPERPAGE_PAGES; i++ ) + { + + mfn = gfn_to_mfn_query(p2m, gfn + i, &type); + + if ( i == 0 ) + { + mfn0 = mfn; + type0 = type; + } + + /* Conditions that must be met for superpage-superpage: + * + All gfns are ram types + * + All gfns have the same type + * + All of the mfns are allocated to a domain + * + None of the mfns are used as pagetables, or allocated via xenheap + * + The first mfn is 2-meg aligned + * + All the other mfns are in sequence + * Adding for good measure: + * + None of the mfns are likely to be mapped elsewhere (refcount + * 2 or less for shadow, 1 for hap) + */ + if ( !p2m_is_ram(type) + || type != type0 + || ( (mfn_to_page(mfn)->count_info & PGC_allocated) == 0 ) + || ( (mfn_to_page(mfn)->count_info & (PGC_page_table|PGC_xen_heap)) != 0 ) + || ( (mfn_to_page(mfn)->count_info & PGC_xen_heap ) != 0 ) + || ( (mfn_to_page(mfn)->count_info & PGC_count_mask) > max_ref ) + || !( ( i == 0 && superpage_aligned(mfn_x(mfn0)) ) + || ( i != 0 && mfn_x(mfn) == (mfn_x(mfn0) + i) ) ) ) + goto out; + } + + /* Now, do a quick check to see if it may be zero before unmapping. */ + for ( i=0; i<SUPERPAGE_PAGES; i++ ) + { + /* Quick zero-check */ + map = map_domain_page(mfn_x(mfn0) + i); + + for ( j=0; j<16; j++ ) + if( *(map+j) != 0 ) + break; + + unmap_domain_page(map); + + if ( j < 16 ) + goto out; + + } + + /* Try to remove the page, restoring old mapping if it fails. */ + set_p2m_entry(p2m, gfn, + _mfn(POPULATE_ON_DEMAND_MFN), 9, + p2m_populate_on_demand, p2m->default_access); + + /* Make none of the MFNs are used elsewhere... for example, mapped + * via the grant table interface, or by qemu. Allow one refcount for + * being allocated to the domain. */ + for ( i=0; i < SUPERPAGE_PAGES; i++ ) + { + mfn = _mfn(mfn_x(mfn0) + i); + if ( (mfn_to_page(mfn)->count_info & PGC_count_mask) > 1 ) + { + reset = 1; + goto out_reset; + } + } + + /* Finally, do a full zero-check */ + for ( i=0; i < SUPERPAGE_PAGES; i++ ) + { + map = map_domain_page(mfn_x(mfn0) + i); + + for ( j=0; j<PAGE_SIZE/sizeof(*map); j++ ) + if( *(map+j) != 0 ) + { + reset = 1; + break; + } + + unmap_domain_page(map); + + if ( reset ) + goto out_reset; + } + + if ( tb_init_done ) + { + struct { + u64 gfn, mfn; + int d:16,order:16; + } t; + + t.gfn = gfn; + t.mfn = mfn_x(mfn); + t.d = d->domain_id; + t.order = 9; + + __trace_var(TRC_MEM_POD_ZERO_RECLAIM, 0, sizeof(t), &t); + } + + /* Finally! We've passed all the checks, and can add the mfn superpage + * back on the PoD cache, and account for the new p2m PoD entries */ + p2m_pod_cache_add(p2m, mfn_to_page(mfn0), 9); + p2m->pod.entry_count += SUPERPAGE_PAGES; + +out_reset: + if ( reset ) + set_p2m_entry(p2m, gfn, mfn0, 9, type0, p2m->default_access); + +out: + return ret; +} + +static void +p2m_pod_zero_check(struct p2m_domain *p2m, unsigned long *gfns, int count) +{ + mfn_t mfns[count]; + p2m_type_t types[count]; + unsigned long * map[count]; + struct domain *d = p2m->domain; + + int i, j; + int max_ref = 1; + + /* Allow an extra refcount for one shadow pt mapping in shadowed domains */ + if ( paging_mode_shadow(d) ) + max_ref++; + + /* First, get the gfn list, translate to mfns, and map the pages. */ + for ( i=0; i<count; i++ ) + { + mfns[i] = gfn_to_mfn_query(p2m, gfns[i], types + i); + /* If this is ram, and not a pagetable or from the xen heap, and probably not mapped + elsewhere, map it; otherwise, skip. */ + if ( p2m_is_ram(types[i]) + && ( (mfn_to_page(mfns[i])->count_info & PGC_allocated) != 0 ) + && ( (mfn_to_page(mfns[i])->count_info & (PGC_page_table|PGC_xen_heap)) == 0 ) + && ( (mfn_to_page(mfns[i])->count_info & PGC_count_mask) <= max_ref ) ) + map[i] = map_domain_page(mfn_x(mfns[i])); + else + map[i] = NULL; + } + + /* Then, go through and check for zeroed pages, removing write permission + * for those with zeroes. */ + for ( i=0; i<count; i++ ) + { + if(!map[i]) + continue; + + /* Quick zero-check */ + for ( j=0; j<16; j++ ) + if( *(map[i]+j) != 0 ) + break; + + if ( j < 16 ) + { + unmap_domain_page(map[i]); + map[i] = NULL; + continue; + } + + /* Try to remove the page, restoring old mapping if it fails. */ + set_p2m_entry(p2m, gfns[i], + _mfn(POPULATE_ON_DEMAND_MFN), 0, + p2m_populate_on_demand, p2m->default_access); + + /* See if the page was successfully unmapped. (Allow one refcount + * for being allocated to a domain.) */ + if ( (mfn_to_page(mfns[i])->count_info & PGC_count_mask) > 1 ) + { + unmap_domain_page(map[i]); + map[i] = NULL; + + set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i], p2m->default_access); + + continue; + } + } + + /* Now check each page for real */ + for ( i=0; i < count; i++ ) + { + if(!map[i]) + continue; + + for ( j=0; j<PAGE_SIZE/sizeof(*map[i]); j++ ) + if( *(map[i]+j) != 0 ) + break; + + unmap_domain_page(map[i]); + + /* See comment in p2m_pod_zero_check_superpage() re gnttab + * check timing. */ + if ( j < PAGE_SIZE/sizeof(*map[i]) ) + { + set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i], p2m->default_access); + } + else + { + if ( tb_init_done ) + { + struct { + u64 gfn, mfn; + int d:16,order:16; + } t; + + t.gfn = gfns[i]; + t.mfn = mfn_x(mfns[i]); + t.d = d->domain_id; + t.order = 0; + + __trace_var(TRC_MEM_POD_ZERO_RECLAIM, 0, sizeof(t), &t); + } + + /* Add to cache, and account for the new p2m PoD entry */ + p2m_pod_cache_add(p2m, mfn_to_page(mfns[i]), 0); + p2m->pod.entry_count++; + } + } + +} + +#define POD_SWEEP_LIMIT 1024 +static void +p2m_pod_emergency_sweep_super(struct p2m_domain *p2m) +{ + unsigned long i, start, limit; + + if ( p2m->pod.reclaim_super == 0 ) + { + p2m->pod.reclaim_super = (p2m->pod.max_guest>>9)<<9; + p2m->pod.reclaim_super -= SUPERPAGE_PAGES; + } + + start = p2m->pod.reclaim_super; + limit = (start > POD_SWEEP_LIMIT) ? (start - POD_SWEEP_LIMIT) : 0; + + for ( i=p2m->pod.reclaim_super ; i > 0 ; i -= SUPERPAGE_PAGES ) + { + p2m_pod_zero_check_superpage(p2m, i); + /* Stop if we're past our limit and we have found *something*. + * + * NB that this is a zero-sum game; we're increasing our cache size + * by increasing our 'debt'. Since we hold the p2m lock, + * (entry_count - count) must remain the same. */ + if ( !page_list_empty(&p2m->pod.super) && i < limit ) + break; + } + + p2m->pod.reclaim_super = i ? i - SUPERPAGE_PAGES : 0; +} + +#define POD_SWEEP_STRIDE 16 +static void +p2m_pod_emergency_sweep(struct p2m_domain *p2m) +{ + unsigned long gfns[POD_SWEEP_STRIDE]; + unsigned long i, j=0, start, limit; + p2m_type_t t; + + + if ( p2m->pod.reclaim_single == 0 ) + p2m->pod.reclaim_single = p2m->pod.max_guest; + + start = p2m->pod.reclaim_single; + limit = (start > POD_SWEEP_LIMIT) ? (start - POD_SWEEP_LIMIT) : 0; + + /* FIXME: Figure out how to avoid superpages */ + for ( i=p2m->pod.reclaim_single; i > 0 ; i-- ) + { + gfn_to_mfn_query(p2m, i, &t ); + if ( p2m_is_ram(t) ) + { + gfns[j] = i; + j++; + BUG_ON(j > POD_SWEEP_STRIDE); + if ( j == POD_SWEEP_STRIDE ) + { + p2m_pod_zero_check(p2m, gfns, j); + j = 0; + } + } + /* Stop if we're past our limit and we have found *something*. + * + * NB that this is a zero-sum game; we're increasing our cache size + * by re-increasing our 'debt'. Since we hold the p2m lock, + * (entry_count - count) must remain the same. */ + if ( p2m->pod.count > 0 && i < limit ) + break; + } + + if ( j ) + p2m_pod_zero_check(p2m, gfns, j); + + p2m->pod.reclaim_single = i ? i - 1 : i; + +} + +int +p2m_pod_demand_populate(struct p2m_domain *p2m, unsigned long gfn, + unsigned int order, + p2m_query_t q) +{ + struct domain *d = p2m->domain; + struct page_info *p = NULL; /* Compiler warnings */ + unsigned long gfn_aligned; + mfn_t mfn; + int i; + + ASSERT(p2m_locked_by_me(p2m)); + + /* This check is done with the p2m lock held. This will make sure that + * even if d->is_dying changes under our feet, p2m_pod_empty_cache() + * won't start until we're done. */ + if ( unlikely(d->is_dying) ) + goto out_fail; + + /* Because PoD does not have cache list for 1GB pages, it has to remap + * 1GB region to 2MB chunks for a retry. */ + if ( order == 18 ) + { + gfn_aligned = (gfn >> order) << order; + /* Note that we are supposed to call set_p2m_entry() 512 times to + * split 1GB into 512 2MB pages here. But We only do once here because + * set_p2m_entry() should automatically shatter the 1GB page into + * 512 2MB pages. The rest of 511 calls are unnecessary. + */ + set_p2m_entry(p2m, gfn_aligned, _mfn(POPULATE_ON_DEMAND_MFN), 9, + p2m_populate_on_demand, p2m->default_access); + audit_p2m(p2m, 1); + p2m_unlock(p2m); + return 0; + } + + /* Once we've ballooned down enough that we can fill the remaining + * PoD entries from the cache, don't sweep even if the particular + * list we want to use is empty: that can lead to thrashing zero pages + * through the cache for no good reason. */ + if ( p2m->pod.entry_count > p2m->pod.count ) + { + + /* If we're low, start a sweep */ + if ( order == 9 && page_list_empty(&p2m->pod.super) ) + p2m_pod_emergency_sweep_super(p2m); + + if ( page_list_empty(&p2m->pod.single) && + ( ( order == 0 ) + || (order == 9 && page_list_empty(&p2m->pod.super) ) ) ) + p2m_pod_emergency_sweep(p2m); + } + + /* Keep track of the highest gfn demand-populated by a guest fault */ + if ( q == p2m_guest && gfn > p2m->pod.max_guest ) + p2m->pod.max_guest = gfn; + + spin_lock(&d->page_alloc_lock); + + if ( p2m->pod.count == 0 ) + goto out_of_memory; + + /* Get a page f/ the cache. A NULL return value indicates that the + * 2-meg range should be marked singleton PoD, and retried */ + if ( (p = p2m_pod_cache_get(p2m, order)) == NULL ) + goto remap_and_retry; + + mfn = page_to_mfn(p); + + BUG_ON((mfn_x(mfn) & ((1 << order)-1)) != 0); + + spin_unlock(&d->page_alloc_lock); + + gfn_aligned = (gfn >> order) << order; + + set_p2m_entry(p2m, gfn_aligned, mfn, order, p2m_ram_rw, p2m->default_access); + + for( i = 0; i < (1UL << order); i++ ) + { + set_gpfn_from_mfn(mfn_x(mfn) + i, gfn_aligned + i); + paging_mark_dirty(d, mfn_x(mfn) + i); + } + + p2m->pod.entry_count -= (1 << order); /* Lock: p2m */ + BUG_ON(p2m->pod.entry_count < 0); + + if ( tb_init_done ) + { + struct { + u64 gfn, mfn; + int d:16,order:16; + } t; + + t.gfn = gfn; + t.mfn = mfn_x(mfn); + t.d = d->domain_id; + t.order = order; + + __trace_var(TRC_MEM_POD_POPULATE, 0, sizeof(t), &t); + } + + return 0; +out_of_memory: + spin_unlock(&d->page_alloc_lock); + + printk("%s: Out of populate-on-demand memory! tot_pages %" PRIu32 " pod_entries %" PRIi32 "\n", + __func__, d->tot_pages, p2m->pod.entry_count); + domain_crash(d); +out_fail: + return -1; +remap_and_retry: + BUG_ON(order != 9); + spin_unlock(&d->page_alloc_lock); + + /* Remap this 2-meg region in singleton chunks */ + gfn_aligned = (gfn>>order)<<order; + for(i=0; i<(1<<order); i++) + set_p2m_entry(p2m, gfn_aligned+i, _mfn(POPULATE_ON_DEMAND_MFN), 0, + p2m_populate_on_demand, p2m->default_access); + if ( tb_init_done ) + { + struct { + u64 gfn; + int d:16; + } t; + + t.gfn = gfn; + t.d = d->domain_id; + + __trace_var(TRC_MEM_POD_SUPERPAGE_SPLINTER, 0, sizeof(t), &t); + } + + return 0; +} + + +int +guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, + unsigned int order) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + unsigned long i; + p2m_type_t ot; + mfn_t omfn; + int pod_count = 0; + int rc = 0; + + BUG_ON(!paging_mode_translate(d)); + + rc = p2m_gfn_check_limit(d, gfn, order); + if ( rc != 0 ) + return rc; + + p2m_lock(p2m); + audit_p2m(p2m, 1); + + P2M_DEBUG("mark pod gfn=%#lx\n", gfn); + + /* Make sure all gpfns are unused */ + for ( i = 0; i < (1UL << order); i++ ) + { + omfn = gfn_to_mfn_query(p2m, gfn + i, &ot); + if ( p2m_is_ram(ot) ) + { + printk("%s: gfn_to_mfn returned type %d!\n", + __func__, ot); + rc = -EBUSY; + goto out; + } + else if ( ot == p2m_populate_on_demand ) + { + /* Count how man PoD entries we'll be replacing if successful */ + pod_count++; + } + } + + /* Now, actually do the two-way mapping */ + if ( !set_p2m_entry(p2m, gfn, _mfn(POPULATE_ON_DEMAND_MFN), order, + p2m_populate_on_demand, p2m->default_access) ) + rc = -EINVAL; + else + { + p2m->pod.entry_count += 1 << order; /* Lock: p2m */ + p2m->pod.entry_count -= pod_count; + BUG_ON(p2m->pod.entry_count < 0); + } + + audit_p2m(p2m, 1); + p2m_unlock(p2m); + +out: + return rc; +} + diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/p2m-pt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/mm/p2m-pt.c Fri May 06 11:15:35 2011 +0100 @@ -0,0 +1,1301 @@ +/****************************************************************************** + * arch/x86/mm/p2m-pt.c + * + * Implementation of p2m datastructures as pagetables, for use by + * NPT and shadow-pagetable code + * + * Parts of this code are Copyright (c) 2009-2011 by Citrix Systems, Inc. + * Parts of this code are Copyright (c) 2007 by Advanced Micro Devices. + * Parts of this code are Copyright (c) 2006-2007 by XenSource Inc. + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <asm/domain.h> +#include <asm/page.h> +#include <asm/paging.h> +#include <asm/p2m.h> +#include <xen/iommu.h> +#include <asm/mem_event.h> +#include <public/mem_event.h> +#include <asm/mem_sharing.h> +#include <xen/event.h> +#include <xen/trace.h> +#include <asm/hvm/nestedhvm.h> +#include <asm/hvm/svm/amd-iommu-proto.h> + +/* Debugging and auditing of the P2M code? */ +#define P2M_AUDIT 0 +#define P2M_DEBUGGING 0 + +/* Printouts */ +#define P2M_PRINTK(_f, _a...) \ + debugtrace_printk("p2m: %s(): " _f, __func__, ##_a) +#define P2M_ERROR(_f, _a...) \ + printk("pg error: %s(): " _f, __func__, ##_a) +#if P2M_DEBUGGING +#define P2M_DEBUG(_f, _a...) \ + debugtrace_printk("p2mdebug: %s(): " _f, __func__, ##_a) +#else +#define P2M_DEBUG(_f, _a...) do { (void)(_f); } while(0) +#endif + + +/* Override macros from asm/page.h to make them work with mfn_t */ +#undef mfn_to_page +#define mfn_to_page(_m) __mfn_to_page(mfn_x(_m)) +#undef mfn_valid +#define mfn_valid(_mfn) __mfn_valid(mfn_x(_mfn)) +#undef page_to_mfn +#define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg)) + + +/* PTE flags for the various types of p2m entry */ +#define P2M_BASE_FLAGS \ + (_PAGE_PRESENT | _PAGE_USER | _PAGE_DIRTY | _PAGE_ACCESSED) + +#define SUPERPAGE_PAGES (1UL << 9) +#define superpage_aligned(_x) (((_x)&(SUPERPAGE_PAGES-1))==0) + +unsigned long p2m_type_to_flags(p2m_type_t t, mfn_t mfn) +{ + unsigned long flags; +#ifdef __x86_64__ + /* + * AMD IOMMU: When we share p2m table with iommu, bit 9 - bit 11 will be + * used for iommu hardware to encode next io page level. Bit 59 - bit 62 + * are used for iommu flags, We could not use these bits to store p2m types. + */ + flags = (unsigned long)(t & 0x7f) << 12; +#else + flags = (t & 0x7UL) << 9; +#endif +#ifndef HAVE_GRANT_MAP_P2M + BUG_ON(p2m_is_grant(t)); +#endif + switch(t) + { + case p2m_invalid: + default: + return flags; + case p2m_ram_rw: + case p2m_grant_map_rw: + return flags | P2M_BASE_FLAGS | _PAGE_RW; + case p2m_ram_logdirty: + return flags | P2M_BASE_FLAGS; + case p2m_ram_ro: + case p2m_grant_map_ro: + return flags | P2M_BASE_FLAGS; + case p2m_ram_shared: + return flags | P2M_BASE_FLAGS; + case p2m_mmio_dm: + return flags; + case p2m_mmio_direct: + if ( !rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) + flags |= _PAGE_RW; + return flags | P2M_BASE_FLAGS | _PAGE_PCD; + case p2m_populate_on_demand: + return flags; + } +} + +#if P2M_AUDIT +void audit_p2m(struct p2m_domain *p2m, int strict_m2p); +#else +# define audit_p2m(_p2m, _m2p) do { (void)(_p2m),(_m2p); } while (0) +#endif /* P2M_AUDIT */ + +// Find the next level's P2M entry, checking for out-of-range gfn's... +// Returns NULL on error. +// +l1_pgentry_t * +p2m_find_entry(void *table, unsigned long *gfn_remainder, + unsigned long gfn, uint32_t shift, uint32_t max) +{ + u32 index; + + index = *gfn_remainder >> shift; + if ( index >= max ) + { + P2M_DEBUG("gfn=0x%lx out of range " + "(gfn_remainder=0x%lx shift=%d index=0x%x max=0x%x)\n", + gfn, *gfn_remainder, shift, index, max); + return NULL; + } + *gfn_remainder &= (1 << shift) - 1; + return (l1_pgentry_t *)table + index; +} + +struct page_info * +p2m_alloc_ptp(struct p2m_domain *p2m, unsigned long type) +{ + struct page_info *pg; + + ASSERT(p2m); + ASSERT(p2m->domain); + ASSERT(p2m->domain->arch.paging.alloc_page); + pg = p2m->domain->arch.paging.alloc_page(p2m->domain); + if (pg == NULL) + return NULL; + + page_list_add_tail(pg, &p2m->pages); + pg->u.inuse.type_info = type | 1 | PGT_validated; + + return pg; +} + +void +p2m_free_ptp(struct p2m_domain *p2m, struct page_info *pg) +{ + ASSERT(pg); + ASSERT(p2m); + ASSERT(p2m->domain); + ASSERT(p2m->domain->arch.paging.free_page); + + page_list_del(pg, &p2m->pages); + p2m->domain->arch.paging.free_page(p2m->domain, pg); + + return; +} + +/* Free intermediate tables from a p2m sub-tree */ +void +p2m_free_entry(struct p2m_domain *p2m, l1_pgentry_t *p2m_entry, int page_order) +{ + /* End if the entry is a leaf entry. */ + if ( page_order == 0 + || !(l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) + || (l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) + return; + + if ( page_order > 9 ) + { + l1_pgentry_t *l3_table = map_domain_page(l1e_get_pfn(*p2m_entry)); + for ( int i = 0; i < L3_PAGETABLE_ENTRIES; i++ ) + p2m_free_entry(p2m, l3_table + i, page_order - 9); + unmap_domain_page(l3_table); + } + + p2m_free_ptp(p2m, mfn_to_page(_mfn(l1e_get_pfn(*p2m_entry)))); +} + +// Walk one level of the P2M table, allocating a new table if required. +// Returns 0 on error. +// + +/* AMD IOMMU: Convert next level bits and r/w bits into 24 bits p2m flags */ +#define iommu_nlevel_to_flags(nl, f) ((((nl) & 0x7) << 9 )|(((f) & 0x3) << 21)) + +static void p2m_add_iommu_flags(l1_pgentry_t *p2m_entry, + unsigned int nlevel, unsigned int flags) +{ +#if CONFIG_PAGING_LEVELS == 4 + if ( iommu_hap_pt_share ) + l1e_add_flags(*p2m_entry, iommu_nlevel_to_flags(nlevel, flags)); +#endif +} + +static int +p2m_next_level(struct p2m_domain *p2m, mfn_t *table_mfn, void **table, + unsigned long *gfn_remainder, unsigned long gfn, u32 shift, + u32 max, unsigned long type) +{ + l1_pgentry_t *l1_entry; + l1_pgentry_t *p2m_entry; + l1_pgentry_t new_entry; + void *next; + int i; + + if ( !(p2m_entry = p2m_find_entry(*table, gfn_remainder, gfn, + shift, max)) ) + return 0; + + /* PoD: Not present doesn't imply empty. */ + if ( !l1e_get_flags(*p2m_entry) ) + { + struct page_info *pg; + + pg = p2m_alloc_ptp(p2m, type); + if ( pg == NULL ) + return 0; + + new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), + __PAGE_HYPERVISOR | _PAGE_USER); + + switch ( type ) { + case PGT_l3_page_table: + p2m_add_iommu_flags(&new_entry, 3, IOMMUF_readable|IOMMUF_writable); + p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 4); + break; + case PGT_l2_page_table: +#if CONFIG_PAGING_LEVELS == 3 + /* for PAE mode, PDPE only has PCD/PWT/P bits available */ + new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), _PAGE_PRESENT); +#endif + p2m_add_iommu_flags(&new_entry, 2, IOMMUF_readable|IOMMUF_writable); + p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 3); + break; + case PGT_l1_page_table: + p2m_add_iommu_flags(&new_entry, 1, IOMMUF_readable|IOMMUF_writable); + p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 2); + break; + default: + BUG(); + break; + } + } + + ASSERT(l1e_get_flags(*p2m_entry) & (_PAGE_PRESENT|_PAGE_PSE)); + + /* split 1GB pages into 2MB pages */ + if ( type == PGT_l2_page_table && (l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) + { + unsigned long flags, pfn; + struct page_info *pg; + + pg = p2m_alloc_ptp(p2m, PGT_l2_page_table); + if ( pg == NULL ) + return 0; + + flags = l1e_get_flags(*p2m_entry); + pfn = l1e_get_pfn(*p2m_entry); + + l1_entry = map_domain_page(mfn_x(page_to_mfn(pg))); + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + { + new_entry = l1e_from_pfn(pfn + (i * L1_PAGETABLE_ENTRIES), flags); + p2m_add_iommu_flags(&new_entry, 1, IOMMUF_readable|IOMMUF_writable); + p2m->write_p2m_entry(p2m, gfn, + l1_entry+i, *table_mfn, new_entry, 2); + } + unmap_domain_page(l1_entry); + new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), + __PAGE_HYPERVISOR|_PAGE_USER); //disable PSE + p2m_add_iommu_flags(&new_entry, 2, IOMMUF_readable|IOMMUF_writable); + p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 3); + } + + + /* split single 2MB large page into 4KB page in P2M table */ + if ( type == PGT_l1_page_table && (l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) + { + unsigned long flags, pfn; + struct page_info *pg; + + pg = p2m_alloc_ptp(p2m, PGT_l1_page_table); + if ( pg == NULL ) + return 0; + + /* New splintered mappings inherit the flags of the old superpage, + * with a little reorganisation for the _PAGE_PSE_PAT bit. */ + flags = l1e_get_flags(*p2m_entry); + pfn = l1e_get_pfn(*p2m_entry); + if ( pfn & 1 ) /* ==> _PAGE_PSE_PAT was set */ + pfn -= 1; /* Clear it; _PAGE_PSE becomes _PAGE_PAT */ + else + flags &= ~_PAGE_PSE; /* Clear _PAGE_PSE (== _PAGE_PAT) */ + + l1_entry = __map_domain_page(pg); + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + { + new_entry = l1e_from_pfn(pfn + i, flags); + p2m_add_iommu_flags(&new_entry, 0, 0); + p2m->write_p2m_entry(p2m, gfn, + l1_entry+i, *table_mfn, new_entry, 1); + } + unmap_domain_page(l1_entry); + + new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), + __PAGE_HYPERVISOR|_PAGE_USER); + p2m_add_iommu_flags(&new_entry, 1, IOMMUF_readable|IOMMUF_writable); + p2m->write_p2m_entry(p2m, gfn, + p2m_entry, *table_mfn, new_entry, 2); + } + + *table_mfn = _mfn(l1e_get_pfn(*p2m_entry)); + next = map_domain_page(mfn_x(*table_mfn)); + unmap_domain_page(*table); + *table = next; + + return 1; +} + +// Returns 0 on error (out of memory) +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) +{ + // XXX -- this might be able to be faster iff current->domain == d + mfn_t table_mfn = pagetable_get_mfn(p2m_get_pagetable(p2m)); + void *table =map_domain_page(mfn_x(table_mfn)); + unsigned long i, gfn_remainder = gfn; + l1_pgentry_t *p2m_entry; + l1_pgentry_t entry_content; + l2_pgentry_t l2e_content; + l3_pgentry_t l3e_content; + int rv=0; + unsigned int iommu_pte_flags = (p2mt == p2m_ram_rw) ? + IOMMUF_readable|IOMMUF_writable: + 0; + unsigned long old_mfn = 0; + + if ( tb_init_done ) + { + struct { + u64 gfn, mfn; + int p2mt; + int d:16,order:16; + } t; + + t.gfn = gfn; + t.mfn = mfn_x(mfn); + t.p2mt = p2mt; + t.d = p2m->domain->domain_id; + t.order = page_order; + + __trace_var(TRC_MEM_SET_P2M_ENTRY, 0, sizeof(t), &t); + } + +#if CONFIG_PAGING_LEVELS >= 4 + if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, + L4_PAGETABLE_SHIFT - PAGE_SHIFT, + L4_PAGETABLE_ENTRIES, PGT_l3_page_table) ) + goto out; +#endif + /* + * Try to allocate 1GB page table if this feature is supported. + */ + if ( page_order == 18 ) + { + l1_pgentry_t old_entry = l1e_empty(); + p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, + L3_PAGETABLE_SHIFT - PAGE_SHIFT, + L3_PAGETABLE_ENTRIES); + ASSERT(p2m_entry); + if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) && + !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) + { + /* We're replacing a non-SP page with a superpage. Make sure to + * handle freeing the table properly. */ + old_entry = *p2m_entry; + } + + ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct); + l3e_content = mfn_valid(mfn) + ? l3e_from_pfn(mfn_x(mfn), + p2m_type_to_flags(p2mt, mfn) | _PAGE_PSE) + : l3e_empty(); + entry_content.l1 = l3e_content.l3; + + if ( entry_content.l1 != 0 ) + { + p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags); + old_mfn = l1e_get_pfn(*p2m_entry); + } + + p2m->write_p2m_entry(p2m, gfn, p2m_entry, table_mfn, entry_content, 3); + /* NB: paging_write_p2m_entry() handles tlb flushes properly */ + + /* Free old intermediate tables if necessary */ + if ( l1e_get_flags(old_entry) & _PAGE_PRESENT ) + p2m_free_entry(p2m, &old_entry, page_order); + } + /* + * When using PAE Xen, we only allow 33 bits of pseudo-physical + * address in translated guests (i.e. 8 GBytes). This restriction + * comes from wanting to map the P2M table into the 16MB RO_MPT hole + * in Xen's address space for translated PV guests. + * When using AMD's NPT on PAE Xen, we are restricted to 4GB. + */ + else if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, + L3_PAGETABLE_SHIFT - PAGE_SHIFT, + ((CONFIG_PAGING_LEVELS == 3) + ? (hap_enabled(p2m->domain) ? 4 : 8) + : L3_PAGETABLE_ENTRIES), + PGT_l2_page_table) ) + goto out; + + if ( page_order == 0 ) + { + if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, + L2_PAGETABLE_SHIFT - PAGE_SHIFT, + L2_PAGETABLE_ENTRIES, PGT_l1_page_table) ) + goto out; + + p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, + 0, L1_PAGETABLE_ENTRIES); + ASSERT(p2m_entry); + + if ( mfn_valid(mfn) || (p2mt == p2m_mmio_direct) ) + entry_content = l1e_from_pfn(mfn_x(mfn), + p2m_type_to_flags(p2mt, mfn)); + else + entry_content = l1e_empty(); + + if ( entry_content.l1 != 0 ) + { + p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags); + old_mfn = l1e_get_pfn(*p2m_entry); + } + /* level 1 entry */ + p2m->write_p2m_entry(p2m, gfn, p2m_entry, table_mfn, entry_content, 1); + /* NB: paging_write_p2m_entry() handles tlb flushes properly */ + } + else if ( page_order == 9 ) + { + l1_pgentry_t old_entry = l1e_empty(); + p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, + L2_PAGETABLE_SHIFT - PAGE_SHIFT, + L2_PAGETABLE_ENTRIES); + ASSERT(p2m_entry); + + /* FIXME: Deal with 4k replaced by 2meg pages */ + if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) && + !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) + { + /* We're replacing a non-SP page with a superpage. Make sure to + * handle freeing the table properly. */ + old_entry = *p2m_entry; + } + + ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct); + if ( mfn_valid(mfn) || p2m_is_magic(p2mt) ) + l2e_content = l2e_from_pfn(mfn_x(mfn), + p2m_type_to_flags(p2mt, mfn) | + _PAGE_PSE); + else + l2e_content = l2e_empty(); + + entry_content.l1 = l2e_content.l2; + + if ( entry_content.l1 != 0 ) + { + p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags); + old_mfn = l1e_get_pfn(*p2m_entry); + } + + p2m->write_p2m_entry(p2m, gfn, p2m_entry, table_mfn, entry_content, 2); + /* NB: paging_write_p2m_entry() handles tlb flushes properly */ + + /* Free old intermediate tables if necessary */ + if ( l1e_get_flags(old_entry) & _PAGE_PRESENT ) + p2m_free_entry(p2m, &old_entry, page_order); + } + + /* Track the highest gfn for which we have ever had a valid mapping */ + if ( mfn_valid(mfn) + && (gfn + (1UL << page_order) - 1 > p2m->max_mapped_pfn) ) + p2m->max_mapped_pfn = gfn + (1UL << page_order) - 1; + + if ( iommu_enabled && need_iommu(p2m->domain) ) + { + if ( iommu_hap_pt_share ) + { + if ( old_mfn && (old_mfn != mfn_x(mfn)) ) + amd_iommu_flush_pages(p2m->domain, gfn, page_order); + } + else + { + if ( p2mt == p2m_ram_rw ) + for ( i = 0; i < (1UL << page_order); i++ ) + iommu_map_page(p2m->domain, gfn+i, mfn_x(mfn)+i, + IOMMUF_readable|IOMMUF_writable); + else + for ( int i = 0; i < (1UL << page_order); i++ ) + iommu_unmap_page(p2m->domain, gfn+i); + } + } + + /* Success */ + rv = 1; + +out: + unmap_domain_page(table); + return rv; +} + + +/* Non-ept "lock-and-check" wrapper */ +static int p2m_pod_check_and_populate(struct p2m_domain *p2m, unsigned long gfn, + l1_pgentry_t *p2m_entry, int order, + p2m_query_t q) +{ + /* Only take the lock if we don't already have it. Otherwise it + * wouldn't be safe to do p2m lookups with the p2m lock held */ + int do_locking = !p2m_locked_by_me(p2m); + int r; + + if ( do_locking ) + p2m_lock(p2m); + + audit_p2m(p2m, 1); + + /* Check to make sure this is still PoD */ + if ( p2m_flags_to_type(l1e_get_flags(*p2m_entry)) != p2m_populate_on_demand ) + { + if ( do_locking ) + p2m_unlock(p2m); + return 0; + } + + r = p2m_pod_demand_populate(p2m, gfn, order, q); + + audit_p2m(p2m, 1); + if ( do_locking ) + p2m_unlock(p2m); + + return r; +} + + +static mfn_t +p2m_gfn_to_mfn(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *t, p2m_access_t *a, + p2m_query_t q) +{ + mfn_t mfn; + paddr_t addr = ((paddr_t)gfn) << PAGE_SHIFT; + l2_pgentry_t *l2e; + l1_pgentry_t *l1e; + + ASSERT(paging_mode_translate(p2m->domain)); + + /* XXX This is for compatibility with the old model, where anything not + * XXX marked as RAM was considered to be emulated MMIO space. + * XXX Once we start explicitly registering MMIO regions in the p2m + * XXX we will return p2m_invalid for unmapped gfns */ + *t = p2m_mmio_dm; + /* Not implemented except with EPT */ + *a = p2m_access_rwx; + + mfn = pagetable_get_mfn(p2m_get_pagetable(p2m)); + + if ( gfn > p2m->max_mapped_pfn ) + /* This pfn is higher than the highest the p2m map currently holds */ + return _mfn(INVALID_MFN); + +#if CONFIG_PAGING_LEVELS >= 4 + { + l4_pgentry_t *l4e = map_domain_page(mfn_x(mfn)); + l4e += l4_table_offset(addr); + if ( (l4e_get_flags(*l4e) & _PAGE_PRESENT) == 0 ) + { + unmap_domain_page(l4e); + return _mfn(INVALID_MFN); + } + mfn = _mfn(l4e_get_pfn(*l4e)); + unmap_domain_page(l4e); + } +#endif + { + l3_pgentry_t *l3e = map_domain_page(mfn_x(mfn)); +#if CONFIG_PAGING_LEVELS == 3 + /* On PAE hosts the p2m has eight l3 entries, not four (see + * shadow_set_p2m_entry()) so we can't use l3_table_offset. + * Instead, just count the number of l3es from zero. It's safe + * to do this because we already checked that the gfn is within + * the bounds of the p2m. */ + l3e += (addr >> L3_PAGETABLE_SHIFT); +#else + l3e += l3_table_offset(addr); +#endif +pod_retry_l3: + if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 ) + { + if ( p2m_flags_to_type(l3e_get_flags(*l3e)) == p2m_populate_on_demand ) + { + if ( q != p2m_query ) + { + if ( !p2m_pod_demand_populate(p2m, gfn, 18, q) ) + goto pod_retry_l3; + } + else + *t = p2m_populate_on_demand; + } + unmap_domain_page(l3e); + return _mfn(INVALID_MFN); + } + else if ( (l3e_get_flags(*l3e) & _PAGE_PSE) ) + { + mfn = _mfn(l3e_get_pfn(*l3e) + + l2_table_offset(addr) * L1_PAGETABLE_ENTRIES + + l1_table_offset(addr)); + *t = p2m_flags_to_type(l3e_get_flags(*l3e)); + unmap_domain_page(l3e); + + ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t)); + return (p2m_is_valid(*t)) ? mfn : _mfn(INVALID_MFN); + } + + mfn = _mfn(l3e_get_pfn(*l3e)); + unmap_domain_page(l3e); + } + + l2e = map_domain_page(mfn_x(mfn)); + l2e += l2_table_offset(addr); + +pod_retry_l2: + if ( (l2e_get_flags(*l2e) & _PAGE_PRESENT) == 0 ) + { + /* PoD: Try to populate a 2-meg chunk */ + if ( p2m_flags_to_type(l2e_get_flags(*l2e)) == p2m_populate_on_demand ) + { + if ( q != p2m_query ) { + if ( !p2m_pod_check_and_populate(p2m, gfn, + (l1_pgentry_t *)l2e, 9, q) ) + goto pod_retry_l2; + } else + *t = p2m_populate_on_demand; + } + + unmap_domain_page(l2e); + return _mfn(INVALID_MFN); + } + else if ( (l2e_get_flags(*l2e) & _PAGE_PSE) ) + { + mfn = _mfn(l2e_get_pfn(*l2e) + l1_table_offset(addr)); + *t = p2m_flags_to_type(l2e_get_flags(*l2e)); + unmap_domain_page(l2e); + + ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t)); + return (p2m_is_valid(*t)) ? mfn : _mfn(INVALID_MFN); + } + + mfn = _mfn(l2e_get_pfn(*l2e)); + unmap_domain_page(l2e); + + l1e = map_domain_page(mfn_x(mfn)); + l1e += l1_table_offset(addr); +pod_retry_l1: + if ( (l1e_get_flags(*l1e) & _PAGE_PRESENT) == 0 ) + { + /* PoD: Try to populate */ + if ( p2m_flags_to_type(l1e_get_flags(*l1e)) == p2m_populate_on_demand ) + { + if ( q != p2m_query ) { + if ( !p2m_pod_check_and_populate(p2m, gfn, + (l1_pgentry_t *)l1e, 0, q) ) + goto pod_retry_l1; + } else + *t = p2m_populate_on_demand; + } + + unmap_domain_page(l1e); + return _mfn(INVALID_MFN); + } + mfn = _mfn(l1e_get_pfn(*l1e)); + *t = p2m_flags_to_type(l1e_get_flags(*l1e)); + unmap_domain_page(l1e); + + ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t)); + return (p2m_is_valid(*t) || p2m_is_grant(*t)) ? mfn : _mfn(INVALID_MFN); +} + +/* Read the current domain's p2m table (through the linear mapping). */ +static mfn_t p2m_gfn_to_mfn_current(struct p2m_domain *p2m, + unsigned long gfn, p2m_type_t *t, p2m_access_t *a, + p2m_query_t q) +{ + mfn_t mfn = _mfn(INVALID_MFN); + p2m_type_t p2mt = p2m_mmio_dm; + paddr_t addr = ((paddr_t)gfn) << PAGE_SHIFT; + /* XXX This is for compatibility with the old model, where anything not + * XXX marked as RAM was considered to be emulated MMIO space. + * XXX Once we start explicitly registering MMIO regions in the p2m + * XXX we will return p2m_invalid for unmapped gfns */ + + /* Not currently implemented except for EPT */ + *a = p2m_access_rwx; + + if ( gfn <= p2m->max_mapped_pfn ) + { + l1_pgentry_t l1e = l1e_empty(), *p2m_entry; + l2_pgentry_t l2e = l2e_empty(); + int ret; +#if CONFIG_PAGING_LEVELS >= 4 + l3_pgentry_t l3e = l3e_empty(); +#endif + + ASSERT(gfn < (RO_MPT_VIRT_END - RO_MPT_VIRT_START) + / sizeof(l1_pgentry_t)); + +#if CONFIG_PAGING_LEVELS >= 4 + /* + * Read & process L3 + */ + p2m_entry = (l1_pgentry_t *) + &__linear_l2_table[l2_linear_offset(RO_MPT_VIRT_START) + + l3_linear_offset(addr)]; + pod_retry_l3: + ret = __copy_from_user(&l3e, p2m_entry, sizeof(l3e)); + + if ( ret != 0 || !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) + { + if ( (l3e_get_flags(l3e) & _PAGE_PSE) && + (p2m_flags_to_type(l3e_get_flags(l3e)) == p2m_populate_on_demand) ) + { + /* The read has succeeded, so we know that mapping exists */ + if ( q != p2m_query ) + { + if ( !p2m_pod_demand_populate(p2m, gfn, 18, q) ) + goto pod_retry_l3; + p2mt = p2m_invalid; + printk("%s: Allocate 1GB failed!\n", __func__); + goto out; + } + else + { + p2mt = p2m_populate_on_demand; + goto out; + } + } + goto pod_retry_l2; + } + + if ( l3e_get_flags(l3e) & _PAGE_PSE ) + { + p2mt = p2m_flags_to_type(l3e_get_flags(l3e)); + ASSERT(l3e_get_pfn(l3e) != INVALID_MFN || !p2m_is_ram(p2mt)); + if (p2m_is_valid(p2mt) ) + mfn = _mfn(l3e_get_pfn(l3e) + + l2_table_offset(addr) * L1_PAGETABLE_ENTRIES + + l1_table_offset(addr)); + else + p2mt = p2m_mmio_dm; + + goto out; + } +#endif + /* + * Read & process L2 + */ + p2m_entry = &__linear_l1_table[l1_linear_offset(RO_MPT_VIRT_START) + + l2_linear_offset(addr)]; + + pod_retry_l2: + ret = __copy_from_user(&l2e, + p2m_entry, + sizeof(l2e)); + if ( ret != 0 + || !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) + { + if( (l2e_get_flags(l2e) & _PAGE_PSE) + && ( p2m_flags_to_type(l2e_get_flags(l2e)) + == p2m_populate_on_demand ) ) + { + /* The read has succeeded, so we know that the mapping + * exits at this point. */ + if ( q != p2m_query ) + { + if ( !p2m_pod_check_and_populate(p2m, gfn, + p2m_entry, 9, q) ) + goto pod_retry_l2; + + /* Allocate failed. */ + p2mt = p2m_invalid; + printk("%s: Allocate failed!\n", __func__); + goto out; + } + else + { + p2mt = p2m_populate_on_demand; + goto out; + } + } + + goto pod_retry_l1; + } + + if (l2e_get_flags(l2e) & _PAGE_PSE) + { + p2mt = p2m_flags_to_type(l2e_get_flags(l2e)); + ASSERT(l2e_get_pfn(l2e) != INVALID_MFN || !p2m_is_ram(p2mt)); + + if ( p2m_is_valid(p2mt) ) + mfn = _mfn(l2e_get_pfn(l2e) + l1_table_offset(addr)); + else + p2mt = p2m_mmio_dm; + + goto out; + } + + /* + * Read and process L1 + */ + + /* Need to __copy_from_user because the p2m is sparse and this + * part might not exist */ + pod_retry_l1: + p2m_entry = &phys_to_machine_mapping[gfn]; + + ret = __copy_from_user(&l1e, + p2m_entry, + sizeof(l1e)); + + if ( ret == 0 ) { + p2mt = p2m_flags_to_type(l1e_get_flags(l1e)); + ASSERT(l1e_get_pfn(l1e) != INVALID_MFN || !p2m_is_ram(p2mt)); + + if ( p2m_flags_to_type(l1e_get_flags(l1e)) + == p2m_populate_on_demand ) + { + /* The read has succeeded, so we know that the mapping + * exits at this point. */ + if ( q != p2m_query ) + { + if ( !p2m_pod_check_and_populate(p2m, gfn, + (l1_pgentry_t *)p2m_entry, 0, q) ) + goto pod_retry_l1; + + /* Allocate failed. */ + p2mt = p2m_invalid; + goto out; + } + else + { + p2mt = p2m_populate_on_demand; + goto out; + } + } + + if ( p2m_is_valid(p2mt) || p2m_is_grant(p2mt) ) + mfn = _mfn(l1e_get_pfn(l1e)); + else + /* XXX see above */ + p2mt = p2m_mmio_dm; + } + } +out: + *t = p2mt; + return mfn; +} + +/* Walk the whole p2m table, changing any entries of the old type + * to the new type. This is used in hardware-assisted paging to + * quickly enable or diable log-dirty tracking */ +void p2m_change_type_global(struct p2m_domain *p2m, p2m_type_t ot, p2m_type_t nt) +{ + unsigned long mfn, gfn, flags; + l1_pgentry_t l1e_content; + l1_pgentry_t *l1e; + l2_pgentry_t *l2e; + mfn_t l1mfn, l2mfn, l3mfn; + unsigned long i1, i2, i3; + l3_pgentry_t *l3e; +#if CONFIG_PAGING_LEVELS == 4 + l4_pgentry_t *l4e; + unsigned long i4; +#endif /* CONFIG_PAGING_LEVELS == 4 */ + + BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); + BUG_ON(ot != nt && (ot == p2m_mmio_direct || nt == p2m_mmio_direct)); + + if ( !paging_mode_translate(p2m->domain) ) + return; + + if ( pagetable_get_pfn(p2m_get_pagetable(p2m)) == 0 ) + return; + + ASSERT(p2m_locked_by_me(p2m)); + +#if CONFIG_PAGING_LEVELS == 4 + l4e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); +#else /* CONFIG_PAGING_LEVELS == 3 */ + l3mfn = _mfn(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); + l3e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); +#endif + +#if CONFIG_PAGING_LEVELS >= 4 + for ( i4 = 0; i4 < L4_PAGETABLE_ENTRIES; i4++ ) + { + if ( !(l4e_get_flags(l4e[i4]) & _PAGE_PRESENT) ) + { + continue; + } + l3mfn = _mfn(l4e_get_pfn(l4e[i4])); + l3e = map_domain_page(l4e_get_pfn(l4e[i4])); +#endif + for ( i3 = 0; + i3 < ((CONFIG_PAGING_LEVELS==4) ? L3_PAGETABLE_ENTRIES : 8); + i3++ ) + { + if ( !(l3e_get_flags(l3e[i3]) & _PAGE_PRESENT) ) + { + continue; + } + if ( (l3e_get_flags(l3e[i3]) & _PAGE_PSE) ) + { + flags = l3e_get_flags(l3e[i3]); + if ( p2m_flags_to_type(flags) != ot ) + continue; + mfn = l3e_get_pfn(l3e[i3]); + gfn = get_gpfn_from_mfn(mfn); + flags = p2m_type_to_flags(nt, _mfn(mfn)); + l1e_content = l1e_from_pfn(mfn, flags | _PAGE_PSE); + p2m->write_p2m_entry(p2m, gfn, + (l1_pgentry_t *)&l3e[i3], + l3mfn, l1e_content, 3); + continue; + } + + l2mfn = _mfn(l3e_get_pfn(l3e[i3])); + l2e = map_domain_page(l3e_get_pfn(l3e[i3])); + for ( i2 = 0; i2 < L2_PAGETABLE_ENTRIES; i2++ ) + { + if ( !(l2e_get_flags(l2e[i2]) & _PAGE_PRESENT) ) + { + continue; + } + + if ( (l2e_get_flags(l2e[i2]) & _PAGE_PSE) ) + { + flags = l2e_get_flags(l2e[i2]); + if ( p2m_flags_to_type(flags) != ot ) + continue; + mfn = l2e_get_pfn(l2e[i2]); + /* Do not use get_gpfn_from_mfn because it may return + SHARED_M2P_ENTRY */ + gfn = (i2 + (i3 +#if CONFIG_PAGING_LEVELS >= 4 + + (i4 * L3_PAGETABLE_ENTRIES) +#endif + ) + * L2_PAGETABLE_ENTRIES) * L1_PAGETABLE_ENTRIES; + flags = p2m_type_to_flags(nt, _mfn(mfn)); + l1e_content = l1e_from_pfn(mfn, flags | _PAGE_PSE); + p2m->write_p2m_entry(p2m, gfn, + (l1_pgentry_t *)&l2e[i2], + l2mfn, l1e_content, 2); + continue; + } + + l1mfn = _mfn(l2e_get_pfn(l2e[i2])); + l1e = map_domain_page(mfn_x(l1mfn)); + + for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++, gfn++ ) + { + flags = l1e_get_flags(l1e[i1]); + if ( p2m_flags_to_type(flags) != ot ) + continue; + mfn = l1e_get_pfn(l1e[i1]); + gfn = i1 + (i2 + (i3 +#if CONFIG_PAGING_LEVELS >= 4 + + (i4 * L3_PAGETABLE_ENTRIES) +#endif + ) + * L2_PAGETABLE_ENTRIES) * L1_PAGETABLE_ENTRIES; + /* create a new 1le entry with the new type */ + flags = p2m_type_to_flags(nt, _mfn(mfn)); + l1e_content = l1e_from_pfn(mfn, flags); + p2m->write_p2m_entry(p2m, gfn, &l1e[i1], + l1mfn, l1e_content, 1); + } + unmap_domain_page(l1e); + } + unmap_domain_page(l2e); + } +#if CONFIG_PAGING_LEVELS >= 4 + unmap_domain_page(l3e); + } +#endif + +#if CONFIG_PAGING_LEVELS == 4 + unmap_domain_page(l4e); +#else /* CONFIG_PAGING_LEVELS == 3 */ + unmap_domain_page(l3e); +#endif + +} + +/* Set up the p2m function pointers for pagetable format */ +void p2m_pt_init(struct p2m_domain *p2m) +{ + p2m->set_entry = p2m_set_entry; + p2m->get_entry = p2m_gfn_to_mfn; + p2m->get_entry_current = p2m_gfn_to_mfn_current; + p2m->change_entry_type_global = p2m_change_type_global; + p2m->write_p2m_entry = paging_write_p2m_entry; +} + + +#if P2M_AUDIT +/* strict_m2p == 0 allows m2p mappings that don'#t match the p2m. + * It's intended for add_to_physmap, when the domain has just been allocated + * new mfns that might have stale m2p entries from previous owners */ +void audit_p2m(struct p2m_domain *p2m, int strict_m2p) +{ + struct page_info *page; + struct domain *od; + unsigned long mfn, gfn, m2pfn, lp2mfn = 0; + int entry_count = 0; + mfn_t p2mfn; + unsigned long orphans_d = 0, orphans_i = 0, mpbad = 0, pmbad = 0; + int test_linear; + p2m_type_t type; + struct domain *d = p2m->domain; + + if ( !paging_mode_translate(d) ) + return; + + //P2M_PRINTK("p2m audit starts\n"); + + test_linear = ( (d == current->domain) + && !pagetable_is_null(current->arch.monitor_table) ); + if ( test_linear ) + flush_tlb_local(); + + spin_lock(&d->page_alloc_lock); + + /* Audit part one: walk the domain's page allocation list, checking + * the m2p entries. */ + page_list_for_each ( page, &d->page_list ) + { + mfn = mfn_x(page_to_mfn(page)); + + // P2M_PRINTK("auditing guest page, mfn=%#lx\n", mfn); + + od = page_get_owner(page); + + if ( od != d ) + { + P2M_PRINTK("wrong owner %#lx -> %p(%u) != %p(%u)\n", + mfn, od, (od?od->domain_id:-1), d, d->domain_id); + continue; + } + + gfn = get_gpfn_from_mfn(mfn); + if ( gfn == INVALID_M2P_ENTRY ) + { + orphans_i++; + //P2M_PRINTK("orphaned guest page: mfn=%#lx has invalid gfn\n", + // mfn); + continue; + } + + if ( gfn == 0x55555555 || gfn == 0x5555555555555555 ) + { + orphans_d++; + //P2M_PRINTK("orphaned guest page: mfn=%#lx has debug gfn\n", + // mfn); + continue; + } + + if ( gfn == SHARED_M2P_ENTRY ) + { + P2M_PRINTK("shared mfn (%lx) on domain page list!\n", + mfn); + continue; + } + + p2mfn = gfn_to_mfn_type_p2m(p2m, gfn, &type, p2m_query); + if ( strict_m2p && mfn_x(p2mfn) != mfn ) + { + mpbad++; + P2M_PRINTK("map mismatch mfn %#lx -> gfn %#lx -> mfn %#lx" + " (-> gfn %#lx)\n", + mfn, gfn, mfn_x(p2mfn), + (mfn_valid(p2mfn) + ? get_gpfn_from_mfn(mfn_x(p2mfn)) + : -1u)); + /* This m2p entry is stale: the domain has another frame in + * this physical slot. No great disaster, but for neatness, + * blow away the m2p entry. */ + set_gpfn_from_mfn(mfn, INVALID_M2P_ENTRY); + } + + if ( test_linear && (gfn <= p2m->max_mapped_pfn) ) + { + lp2mfn = mfn_x(gfn_to_mfn_query(p2m, gfn, &type)); + if ( lp2mfn != mfn_x(p2mfn) ) + { + P2M_PRINTK("linear mismatch gfn %#lx -> mfn %#lx " + "(!= mfn %#lx)\n", gfn, lp2mfn, mfn_x(p2mfn)); + } + } + + // P2M_PRINTK("OK: mfn=%#lx, gfn=%#lx, p2mfn=%#lx, lp2mfn=%#lx\n", + // mfn, gfn, mfn_x(p2mfn), lp2mfn); + } + + spin_unlock(&d->page_alloc_lock); + + /* Audit part two: walk the domain's p2m table, checking the entries. */ + if ( pagetable_get_pfn(p2m_get_pagetable(p2m)) != 0 ) + { + l2_pgentry_t *l2e; + l1_pgentry_t *l1e; + int i1, i2; + +#if CONFIG_PAGING_LEVELS == 4 + l4_pgentry_t *l4e; + l3_pgentry_t *l3e; + int i4, i3; + l4e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); +#else /* CONFIG_PAGING_LEVELS == 3 */ + l3_pgentry_t *l3e; + int i3; + l3e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); +#endif + + gfn = 0; +#if CONFIG_PAGING_LEVELS >= 4 + for ( i4 = 0; i4 < L4_PAGETABLE_ENTRIES; i4++ ) + { + if ( !(l4e_get_flags(l4e[i4]) & _PAGE_PRESENT) ) + { + gfn += 1 << (L4_PAGETABLE_SHIFT - PAGE_SHIFT); + continue; + } + l3e = map_domain_page(mfn_x(_mfn(l4e_get_pfn(l4e[i4])))); +#endif + for ( i3 = 0; + i3 < ((CONFIG_PAGING_LEVELS==4) ? L3_PAGETABLE_ENTRIES : 8); + i3++ ) + { + if ( !(l3e_get_flags(l3e[i3]) & _PAGE_PRESENT) ) + { + gfn += 1 << (L3_PAGETABLE_SHIFT - PAGE_SHIFT); + continue; + } + + /* check for 1GB super page */ + if ( l3e_get_flags(l3e[i3]) & _PAGE_PSE ) + { + mfn = l3e_get_pfn(l3e[i3]); + ASSERT(mfn_valid(_mfn(mfn))); + /* we have to cover 512x512 4K pages */ + for ( i2 = 0; + i2 < (L2_PAGETABLE_ENTRIES * L1_PAGETABLE_ENTRIES); + i2++) + { + m2pfn = get_gpfn_from_mfn(mfn+i2); + if ( m2pfn != (gfn + i2) ) + { + pmbad++; + P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx" + " -> gfn %#lx\n", gfn+i2, mfn+i2, + m2pfn); + BUG(); + } + gfn += 1 << (L3_PAGETABLE_SHIFT - PAGE_SHIFT); + continue; + } + } + + l2e = map_domain_page(mfn_x(_mfn(l3e_get_pfn(l3e[i3])))); + for ( i2 = 0; i2 < L2_PAGETABLE_ENTRIES; i2++ ) + { + if ( !(l2e_get_flags(l2e[i2]) & _PAGE_PRESENT) ) + { + if ( (l2e_get_flags(l2e[i2]) & _PAGE_PSE) + && ( p2m_flags_to_type(l2e_get_flags(l2e[i2])) + == p2m_populate_on_demand ) ) + entry_count+=SUPERPAGE_PAGES; + gfn += 1 << (L2_PAGETABLE_SHIFT - PAGE_SHIFT); + continue; + } + + /* check for super page */ + if ( l2e_get_flags(l2e[i2]) & _PAGE_PSE ) + { + mfn = l2e_get_pfn(l2e[i2]); + ASSERT(mfn_valid(_mfn(mfn))); + for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++) + { + m2pfn = get_gpfn_from_mfn(mfn+i1); + /* Allow shared M2Ps */ + if ( (m2pfn != (gfn + i1)) && + (m2pfn != SHARED_M2P_ENTRY) ) + { + pmbad++; + P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx" + " -> gfn %#lx\n", gfn+i1, mfn+i1, + m2pfn); + BUG(); + } + } + gfn += 1 << (L2_PAGETABLE_SHIFT - PAGE_SHIFT); + continue; + } + + l1e = map_domain_page(mfn_x(_mfn(l2e_get_pfn(l2e[i2])))); + + for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++, gfn++ ) + { + p2m_type_t type; + + type = p2m_flags_to_type(l1e_get_flags(l1e[i1])); + if ( !(l1e_get_flags(l1e[i1]) & _PAGE_PRESENT) ) + { + if ( type == p2m_populate_on_demand ) + entry_count++; + continue; + } + mfn = l1e_get_pfn(l1e[i1]); + ASSERT(mfn_valid(_mfn(mfn))); + m2pfn = get_gpfn_from_mfn(mfn); + if ( m2pfn != gfn && + type != p2m_mmio_direct && + !p2m_is_grant(type) && + !p2m_is_shared(type) ) + { + pmbad++; + printk("mismatch: gfn %#lx -> mfn %#lx" + " -> gfn %#lx\n", gfn, mfn, m2pfn); + P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx" + " -> gfn %#lx\n", gfn, mfn, m2pfn); + BUG(); + } + } + unmap_domain_page(l1e); + } + unmap_domain_page(l2e); + } +#if CONFIG_PAGING_LEVELS >= 4 + unmap_domain_page(l3e); + } +#endif + +#if CONFIG_PAGING_LEVELS == 4 + unmap_domain_page(l4e); +#else /* CONFIG_PAGING_LEVELS == 3 */ + unmap_domain_page(l3e); +#endif + + } + + if ( entry_count != p2m->pod.entry_count ) + { + printk("%s: refcounted entry count %d, audit count %d!\n", + __func__, + p2m->pod.entry_count, + entry_count); + BUG(); + } + + //P2M_PRINTK("p2m audit complete\n"); + //if ( orphans_i | orphans_d | mpbad | pmbad ) + // P2M_PRINTK("p2m audit found %lu orphans (%lu inval %lu debug)\n", + // orphans_i + orphans_d, orphans_i, orphans_d); + if ( mpbad | pmbad ) + { + P2M_PRINTK("p2m audit found %lu odd p2m, %lu bad m2p entries\n", + pmbad, mpbad); + WARN(); + } +} +#endif /* P2M_AUDIT */ + diff -r d9982136d8fa -r 19452acd2304 xen/arch/x86/mm/p2m.c --- a/xen/arch/x86/mm/p2m.c Mon May 09 15:00:57 2011 +0100 +++ b/xen/arch/x86/mm/p2m.c Fri May 06 11:15:35 2011 +0100 @@ -37,10 +37,6 @@ #include <asm/hvm/nestedhvm.h> #include <asm/hvm/svm/amd-iommu-proto.h> -/* Debugging and auditing of the P2M code? */ -#define P2M_AUDIT 0 -#define P2M_DEBUGGING 0 - /* turn on/off 1GB host page table support for hap, default on */ static bool_t __read_mostly opt_hap_1gb = 1; boolean_param("hap_1gb", opt_hap_1gb); @@ -69,1853 +65,14 @@ #undef page_to_mfn #define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg)) - -/* PTE flags for the various types of p2m entry */ -#define P2M_BASE_FLAGS \ - (_PAGE_PRESENT | _PAGE_USER | _PAGE_DIRTY | _PAGE_ACCESSED) - -#define SUPERPAGE_PAGES (1UL << 9) -#define superpage_aligned(_x) (((_x)&(SUPERPAGE_PAGES-1))==0) - -unsigned long p2m_type_to_flags(p2m_type_t t, mfn_t mfn) -{ - unsigned long flags; -#ifdef __x86_64__ - /* - * AMD IOMMU: When we share p2m table with iommu, bit 9 - bit 11 will be - * used for iommu hardware to encode next io page level. Bit 59 - bit 62 - * are used for iommu flags, We could not use these bits to store p2m types. - */ - flags = (unsigned long)(t & 0x7f) << 12; -#else - flags = (t & 0x7UL) << 9; -#endif -#ifndef HAVE_GRANT_MAP_P2M - BUG_ON(p2m_is_grant(t)); -#endif - switch(t) - { - case p2m_invalid: - default: - return flags; - case p2m_ram_rw: - case p2m_grant_map_rw: - return flags | P2M_BASE_FLAGS | _PAGE_RW; - case p2m_ram_logdirty: - return flags | P2M_BASE_FLAGS; - case p2m_ram_ro: - case p2m_grant_map_ro: - return flags | P2M_BASE_FLAGS; - case p2m_ram_shared: - return flags | P2M_BASE_FLAGS; - case p2m_mmio_dm: - return flags; - case p2m_mmio_direct: - if ( !rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) - flags |= _PAGE_RW; - return flags | P2M_BASE_FLAGS | _PAGE_PCD; - case p2m_populate_on_demand: - return flags; - } -} - #if P2M_AUDIT -static void audit_p2m(struct p2m_domain *p2m, int strict_m2p); +extern void audit_p2m(struct p2m_domain *p2m, int strict_m2p); #else # define audit_p2m(_p2m, _m2p) do { (void)(_p2m),(_m2p); } while (0) #endif /* P2M_AUDIT */ -// Find the next level's P2M entry, checking for out-of-range gfn's... -// Returns NULL on error. -// -l1_pgentry_t * -p2m_find_entry(void *table, unsigned long *gfn_remainder, - unsigned long gfn, uint32_t shift, uint32_t max) -{ - u32 index; - - index = *gfn_remainder >> shift; - if ( index >= max ) - { - P2M_DEBUG("gfn=0x%lx out of range " - "(gfn_remainder=0x%lx shift=%d index=0x%x max=0x%x)\n", - gfn, *gfn_remainder, shift, index, max); - return NULL; - } - *gfn_remainder &= (1 << shift) - 1; - return (l1_pgentry_t *)table + index; -} - -struct page_info * -p2m_alloc_ptp(struct p2m_domain *p2m, unsigned long type) -{ - struct page_info *pg; - - ASSERT(p2m); - ASSERT(p2m->domain); - ASSERT(p2m->domain->arch.paging.alloc_page); - pg = p2m->domain->arch.paging.alloc_page(p2m->domain); - if (pg == NULL) - return NULL; - - page_list_add_tail(pg, &p2m->pages); - pg->u.inuse.type_info = type | 1 | PGT_validated; - - return pg; -} - -void -p2m_free_ptp(struct p2m_domain *p2m, struct page_info *pg) -{ - ASSERT(pg); - ASSERT(p2m); - ASSERT(p2m->domain); - ASSERT(p2m->domain->arch.paging.free_page); - - page_list_del(pg, &p2m->pages); - p2m->domain->arch.paging.free_page(p2m->domain, pg); - - return; -} - -/* Free intermediate tables from a p2m sub-tree */ -void -p2m_free_entry(struct p2m_domain *p2m, l1_pgentry_t *p2m_entry, int page_order) -{ - /* End if the entry is a leaf entry. */ - if ( page_order == 0 - || !(l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) - || (l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) - return; - - if ( page_order > 9 ) - { - l1_pgentry_t *l3_table = map_domain_page(l1e_get_pfn(*p2m_entry)); - for ( int i = 0; i < L3_PAGETABLE_ENTRIES; i++ ) - p2m_free_entry(p2m, l3_table + i, page_order - 9); - unmap_domain_page(l3_table); - } - - p2m_free_ptp(p2m, mfn_to_page(_mfn(l1e_get_pfn(*p2m_entry)))); -} - -// Walk one level of the P2M table, allocating a new table if required. -// Returns 0 on error. -// - -/* AMD IOMMU: Convert next level bits and r/w bits into 24 bits p2m flags */ -#define iommu_nlevel_to_flags(nl, f) ((((nl) & 0x7) << 9 )|(((f) & 0x3) << 21)) - -static void p2m_add_iommu_flags(l1_pgentry_t *p2m_entry, - unsigned int nlevel, unsigned int flags) -{ -#if CONFIG_PAGING_LEVELS == 4 - if ( iommu_hap_pt_share ) - l1e_add_flags(*p2m_entry, iommu_nlevel_to_flags(nlevel, flags)); -#endif -} - -static int -p2m_next_level(struct p2m_domain *p2m, mfn_t *table_mfn, void **table, - unsigned long *gfn_remainder, unsigned long gfn, u32 shift, - u32 max, unsigned long type) -{ - l1_pgentry_t *l1_entry; - l1_pgentry_t *p2m_entry; - l1_pgentry_t new_entry; - void *next; - int i; - - if ( !(p2m_entry = p2m_find_entry(*table, gfn_remainder, gfn, - shift, max)) ) - return 0; - - /* PoD: Not present doesn't imply empty. */ - if ( !l1e_get_flags(*p2m_entry) ) - { - struct page_info *pg; - - pg = p2m_alloc_ptp(p2m, type); - if ( pg == NULL ) - return 0; - - new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), - __PAGE_HYPERVISOR | _PAGE_USER); - - switch ( type ) { - case PGT_l3_page_table: - p2m_add_iommu_flags(&new_entry, 3, IOMMUF_readable|IOMMUF_writable); - p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 4); - break; - case PGT_l2_page_table: -#if CONFIG_PAGING_LEVELS == 3 - /* for PAE mode, PDPE only has PCD/PWT/P bits available */ - new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), _PAGE_PRESENT); -#endif - p2m_add_iommu_flags(&new_entry, 2, IOMMUF_readable|IOMMUF_writable); - p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 3); - break; - case PGT_l1_page_table: - p2m_add_iommu_flags(&new_entry, 1, IOMMUF_readable|IOMMUF_writable); - p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 2); - break; - default: - BUG(); - break; - } - } - - ASSERT(l1e_get_flags(*p2m_entry) & (_PAGE_PRESENT|_PAGE_PSE)); - - /* split 1GB pages into 2MB pages */ - if ( type == PGT_l2_page_table && (l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) - { - unsigned long flags, pfn; - struct page_info *pg; - - pg = p2m_alloc_ptp(p2m, PGT_l2_page_table); - if ( pg == NULL ) - return 0; - - flags = l1e_get_flags(*p2m_entry); - pfn = l1e_get_pfn(*p2m_entry); - - l1_entry = map_domain_page(mfn_x(page_to_mfn(pg))); - for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) - { - new_entry = l1e_from_pfn(pfn + (i * L1_PAGETABLE_ENTRIES), flags); - p2m_add_iommu_flags(&new_entry, 1, IOMMUF_readable|IOMMUF_writable); - p2m->write_p2m_entry(p2m, gfn, - l1_entry+i, *table_mfn, new_entry, 2); - } - unmap_domain_page(l1_entry); - new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), - __PAGE_HYPERVISOR|_PAGE_USER); //disable PSE - p2m_add_iommu_flags(&new_entry, 2, IOMMUF_readable|IOMMUF_writable); - p2m->write_p2m_entry(p2m, gfn, p2m_entry, *table_mfn, new_entry, 3); - } - - - /* split single 2MB large page into 4KB page in P2M table */ - if ( type == PGT_l1_page_table && (l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) - { - unsigned long flags, pfn; - struct page_info *pg; - - pg = p2m_alloc_ptp(p2m, PGT_l1_page_table); - if ( pg == NULL ) - return 0; - - /* New splintered mappings inherit the flags of the old superpage, - * with a little reorganisation for the _PAGE_PSE_PAT bit. */ - flags = l1e_get_flags(*p2m_entry); - pfn = l1e_get_pfn(*p2m_entry); - if ( pfn & 1 ) /* ==> _PAGE_PSE_PAT was set */ - pfn -= 1; /* Clear it; _PAGE_PSE becomes _PAGE_PAT */ - else - flags &= ~_PAGE_PSE; /* Clear _PAGE_PSE (== _PAGE_PAT) */ - - l1_entry = __map_domain_page(pg); - for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) - { - new_entry = l1e_from_pfn(pfn + i, flags); - p2m_add_iommu_flags(&new_entry, 0, 0); - p2m->write_p2m_entry(p2m, gfn, - l1_entry+i, *table_mfn, new_entry, 1); - } - unmap_domain_page(l1_entry); - - new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)), - __PAGE_HYPERVISOR|_PAGE_USER); - p2m_add_iommu_flags(&new_entry, 1, IOMMUF_readable|IOMMUF_writable); - p2m->write_p2m_entry(p2m, gfn, - p2m_entry, *table_mfn, new_entry, 2); - } - - *table_mfn = _mfn(l1e_get_pfn(*p2m_entry)); - next = map_domain_page(mfn_x(*table_mfn)); - unmap_domain_page(*table); - *table = next; - - return 1; -} - -/* - * Populate-on-demand functionality - */ -static -int set_p2m_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma); - -static int -p2m_pod_cache_add(struct p2m_domain *p2m, - struct page_info *page, - unsigned long order) -{ - int i; - struct page_info *p; - struct domain *d = p2m->domain; - -#ifndef NDEBUG - mfn_t mfn; - - mfn = page_to_mfn(page); - - /* Check to make sure this is a contiguous region */ - if( mfn_x(mfn) & ((1 << order) - 1) ) - { - printk("%s: mfn %lx not aligned order %lu! (mask %lx)\n", - __func__, mfn_x(mfn), order, ((1UL << order) - 1)); - return -1; - } - - for(i=0; i < 1 << order ; i++) { - struct domain * od; - - p = mfn_to_page(_mfn(mfn_x(mfn) + i)); - od = page_get_owner(p); - if(od != d) - { - printk("%s: mfn %lx expected owner d%d, got owner d%d!\n", - __func__, mfn_x(mfn), d->domain_id, - od?od->domain_id:-1); - return -1; - } - } -#endif - - ASSERT(p2m_locked_by_me(p2m)); - - /* - * Pages from domain_alloc and returned by the balloon driver aren't - * guaranteed to be zero; but by reclaiming zero pages, we implicitly - * promise to provide zero pages. So we scrub pages before using. - */ - for ( i = 0; i < (1 << order); i++ ) - { - char *b = map_domain_page(mfn_x(page_to_mfn(page)) + i); - clear_page(b); - unmap_domain_page(b); - } - - spin_lock(&d->page_alloc_lock); - - /* First, take all pages off the domain list */ - for(i=0; i < 1 << order ; i++) - { - p = page + i; - page_list_del(p, &d->page_list); - } - - /* Then add the first one to the appropriate populate-on-demand list */ - switch(order) - { - case 9: - page_list_add_tail(page, &p2m->pod.super); /* lock: page_alloc */ - p2m->pod.count += 1 << order; - break; - case 0: - page_list_add_tail(page, &p2m->pod.single); /* lock: page_alloc */ - p2m->pod.count += 1; - break; - default: - BUG(); - } - - /* Ensure that the PoD cache has never been emptied. - * This may cause "zombie domains" since the page will never be freed. */ - BUG_ON( d->arch.relmem != RELMEM_not_started ); - - spin_unlock(&d->page_alloc_lock); - - return 0; -} - -/* Get a page of size order from the populate-on-demand cache. Will break - * down 2-meg pages into singleton pages automatically. Returns null if - * a superpage is requested and no superpages are available. Must be called - * with the d->page_lock held. */ -static struct page_info * p2m_pod_cache_get(struct p2m_domain *p2m, - unsigned long order) -{ - struct page_info *p = NULL; - int i; - - if ( order == 9 && page_list_empty(&p2m->pod.super) ) - { - return NULL; - } - else if ( order == 0 && page_list_empty(&p2m->pod.single) ) - { - unsigned long mfn; - struct page_info *q; - - BUG_ON( page_list_empty(&p2m->pod.super) ); - - /* Break up a superpage to make single pages. NB count doesn't - * need to be adjusted. */ - p = page_list_remove_head(&p2m->pod.super); - mfn = mfn_x(page_to_mfn(p)); - - for ( i=0; i<SUPERPAGE_PAGES; i++ ) - { - q = mfn_to_page(_mfn(mfn+i)); - page_list_add_tail(q, &p2m->pod.single); - } - } - - switch ( order ) - { - case 9: - BUG_ON( page_list_empty(&p2m->pod.super) ); - p = page_list_remove_head(&p2m->pod.super); - p2m->pod.count -= 1 << order; /* Lock: page_alloc */ - break; - case 0: - BUG_ON( page_list_empty(&p2m->pod.single) ); - p = page_list_remove_head(&p2m->pod.single); - p2m->pod.count -= 1; - break; - default: - BUG(); - } - - /* Put the pages back on the domain page_list */ - for ( i = 0 ; i < (1 << order); i++ ) - { - BUG_ON(page_get_owner(p + i) != p2m->domain); - page_list_add_tail(p + i, &p2m->domain->page_list); - } - - return p; -} - -/* Set the size of the cache, allocating or freeing as necessary. */ -static int -p2m_pod_set_cache_target(struct p2m_domain *p2m, unsigned long pod_target, int preemptible) -{ - struct domain *d = p2m->domain; - int ret = 0; - - /* Increasing the target */ - while ( pod_target > p2m->pod.count ) - { - struct page_info * page; - int order; - - if ( (pod_target - p2m->pod.count) >= SUPERPAGE_PAGES ) - order = 9; - else - order = 0; - retry: - page = alloc_domheap_pages(d, order, 0); - if ( unlikely(page == NULL) ) - { - if ( order == 9 ) - { - /* If we can't allocate a superpage, try singleton pages */ - order = 0; - goto retry; - } - - printk("%s: Unable to allocate domheap page for pod cache. target %lu cachesize %d\n", - __func__, pod_target, p2m->pod.count); - ret = -ENOMEM; - goto out; - } - - p2m_pod_cache_add(p2m, page, order); - - if ( hypercall_preempt_check() && preemptible ) - { - ret = -EAGAIN; - goto out; - } - } - - /* Decreasing the target */ - /* We hold the p2m lock here, so we don't need to worry about - * cache disappearing under our feet. */ - while ( pod_target < p2m->pod.count ) - { - struct page_info * page; - int order, i; - - /* Grab the lock before checking that pod.super is empty, or the last - * entries may disappear before we grab the lock. */ - spin_lock(&d->page_alloc_lock); - - if ( (p2m->pod.count - pod_target) > SUPERPAGE_PAGES - && !page_list_empty(&p2m->pod.super) ) - order = 9; - else - order = 0; - - page = p2m_pod_cache_get(p2m, order); - - ASSERT(page != NULL); - - spin_unlock(&d->page_alloc_lock); - - /* Then free them */ - for ( i = 0 ; i < (1 << order) ; i++ ) - { - /* Copied from common/memory.c:guest_remove_page() */ - if ( unlikely(!get_page(page+i, d)) ) - { - gdprintk(XENLOG_INFO, "Bad page free for domain %u\n", d->domain_id); - ret = -EINVAL; - goto out; - } - - if ( test_and_clear_bit(_PGT_pinned, &(page+i)->u.inuse.type_info) ) - put_page_and_type(page+i); - - if ( test_and_clear_bit(_PGC_allocated, &(page+i)->count_info) ) - put_page(page+i); - - put_page(page+i); - - if ( hypercall_preempt_check() && preemptible ) - { - ret = -EAGAIN; - goto out; - } - } - } - -out: - return ret; -} - -/* - * The "right behavior" here requires some careful thought. First, some - * definitions: - * + M: static_max - * + B: number of pages the balloon driver has ballooned down to. - * + P: Number of populated pages. - * + T: Old target - * + T': New target - * - * The following equations should hold: - * 0 <= P <= T <= B <= M - * d->arch.p2m->pod.entry_count == B - P - * d->tot_pages == P + d->arch.p2m->pod.count - * - * Now we have the following potential cases to cover: - * B <T': Set the PoD cache size equal to the number of outstanding PoD - * entries. The balloon driver will deflate the balloon to give back - * the remainder of the ram to the guest OS. - * T <T'<B : Increase PoD cache size. - * T'<T<=B : Here we have a choice. We can decrease the size of the cache, - * get the memory right away. However, that means every time we - * reduce the memory target we risk the guest attempting to populate the - * memory before the balloon driver has reached its new target. Safer to - * never reduce the cache size here, but only when the balloon driver frees - * PoD ranges. - * - * If there are many zero pages, we could reach the target also by doing - * zero sweeps and marking the ranges PoD; but the balloon driver will have - * to free this memory eventually anyway, so we don't actually gain that much - * by doing so. - * - * NB that the equation (B<T') may require adjustment to the cache - * size as PoD pages are freed as well; i.e., freeing a PoD-backed - * entry when pod.entry_count == pod.count requires us to reduce both - * pod.entry_count and pod.count. - */ -int -p2m_pod_set_mem_target(struct domain *d, unsigned long target) -{ - unsigned pod_target; - struct p2m_domain *p2m = p2m_get_hostp2m(d); - int ret = 0; - unsigned long populated; - - p2m_lock(p2m); - - /* P == B: Nothing to do. */ - if ( p2m->pod.entry_count == 0 ) - goto out; - - /* Don't do anything if the domain is being torn down */ - if ( d->is_dying ) - goto out; - - /* T' < B: Don't reduce the cache size; let the balloon driver - * take care of it. */ - if ( target < d->tot_pages ) - goto out; - - populated = d->tot_pages - p2m->pod.count; - - pod_target = target - populated; - - /* B < T': Set the cache size equal to # of outstanding entries, - * let the balloon driver fill in the rest. */ - if ( pod_target > p2m->pod.entry_count ) - pod_target = p2m->pod.entry_count; - - ASSERT( pod_target >= p2m->pod.count ); - - ret = p2m_pod_set_cache_target(p2m, pod_target, 1/*preemptible*/); - -out: - p2m_unlock(p2m); - - return ret; -} - -void -p2m_pod_empty_cache(struct domain *d) -{ - struct p2m_domain *p2m = p2m_get_hostp2m(d); - struct page_info *page; - - /* After this barrier no new PoD activities can happen. */ - BUG_ON(!d->is_dying); - spin_barrier(&p2m->lock); - - spin_lock(&d->page_alloc_lock); - - while ( (page = page_list_remove_head(&p2m->pod.super)) ) - { - int i; - - for ( i = 0 ; i < SUPERPAGE_PAGES ; i++ ) - { - BUG_ON(page_get_owner(page + i) != d); - page_list_add_tail(page + i, &d->page_list); - } - - p2m->pod.count -= SUPERPAGE_PAGES; - } - - while ( (page = page_list_remove_head(&p2m->pod.single)) ) - { - BUG_ON(page_get_owner(page) != d); - page_list_add_tail(page, &d->page_list); - - p2m->pod.count -= 1; - } - - BUG_ON(p2m->pod.count != 0); - - spin_unlock(&d->page_alloc_lock); -} - -int -p2m_pod_offline_or_broken_hit(struct page_info *p) -{ - struct domain *d; - struct p2m_domain *p2m; - struct page_info *q, *tmp; - unsigned long mfn, bmfn; - - if ( !(d = page_get_owner(p)) || !(p2m = p2m_get_hostp2m(d)) ) - return 0; - - spin_lock(&d->page_alloc_lock); - bmfn = mfn_x(page_to_mfn(p)); - page_list_for_each_safe(q, tmp, &p2m->pod.super) - { - mfn = mfn_x(page_to_mfn(q)); - if ( (bmfn >= mfn) && ((bmfn - mfn) < SUPERPAGE_PAGES) ) - { - unsigned long i; - page_list_del(q, &p2m->pod.super); - for ( i = 0; i < SUPERPAGE_PAGES; i++) - { - q = mfn_to_page(_mfn(mfn + i)); - page_list_add_tail(q, &p2m->pod.single); - } - page_list_del(p, &p2m->pod.single); - p2m->pod.count--; - goto pod_hit; - } - } - - page_list_for_each_safe(q, tmp, &p2m->pod.single) - { - mfn = mfn_x(page_to_mfn(q)); - if ( mfn == bmfn ) - { - page_list_del(p, &p2m->pod.single); - p2m->pod.count--; - goto pod_hit; - } - } - - spin_unlock(&d->page_alloc_lock); - return 0; - -pod_hit: - page_list_add_tail(p, &d->arch.relmem_list); - spin_unlock(&d->page_alloc_lock); - return 1; -} - -void -p2m_pod_offline_or_broken_replace(struct page_info *p) -{ - struct domain *d; - struct p2m_domain *p2m; - - if ( !(d = page_get_owner(p)) || !(p2m = p2m_get_hostp2m(d)) ) - return; - - free_domheap_page(p); - - p = alloc_domheap_page(d, 0); - if ( unlikely(!p) ) - return; - - p2m_lock(p2m); - p2m_pod_cache_add(p2m, p, 0); - p2m_unlock(p2m); - return; -} - -/* This function is needed for two reasons: - * + To properly handle clearing of PoD entries - * + To "steal back" memory being freed for the PoD cache, rather than - * releasing it. - * - * Once both of these functions have been completed, we can return and - * allow decrease_reservation() to handle everything else. - */ -int -p2m_pod_decrease_reservation(struct domain *d, - xen_pfn_t gpfn, - unsigned int order) -{ - int ret=0; - int i; - struct p2m_domain *p2m = p2m_get_hostp2m(d); - - int steal_for_cache = 0; - int pod = 0, nonpod = 0, ram = 0; - - - /* If we don't have any outstanding PoD entries, let things take their - * course */ - if ( p2m->pod.entry_count == 0 ) - goto out; - - /* Figure out if we need to steal some freed memory for our cache */ - steal_for_cache = ( p2m->pod.entry_count > p2m->pod.count ); - - p2m_lock(p2m); - audit_p2m(p2m, 1); - - if ( unlikely(d->is_dying) ) - goto out_unlock; - - /* See what's in here. */ - /* FIXME: Add contiguous; query for PSE entries? */ - for ( i=0; i<(1<<order); i++) - { - p2m_type_t t; - - gfn_to_mfn_query(p2m, gpfn + i, &t); - - if ( t == p2m_populate_on_demand ) - pod++; - else - { - nonpod++; - if ( p2m_is_ram(t) ) - ram++; - } - } - - /* No populate-on-demand? Don't need to steal anything? Then we're done!*/ - if(!pod && !steal_for_cache) - goto out_unlock; - - if ( !nonpod ) - { - /* All PoD: Mark the whole region invalid and tell caller - * we're done. */ - set_p2m_entry(p2m, gpfn, _mfn(INVALID_MFN), order, p2m_invalid, p2m->default_access); - p2m->pod.entry_count-=(1<<order); /* Lock: p2m */ - BUG_ON(p2m->pod.entry_count < 0); - ret = 1; - goto out_entry_check; - } - - /* FIXME: Steal contig 2-meg regions for cache */ - - /* Process as long as: - * + There are PoD entries to handle, or - * + There is ram left, and we want to steal it - */ - for ( i=0; - i<(1<<order) && (pod>0 || (steal_for_cache && ram > 0)); - i++) - { - mfn_t mfn; - p2m_type_t t; - - mfn = gfn_to_mfn_query(p2m, gpfn + i, &t); - if ( t == p2m_populate_on_demand ) - { - set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid, p2m->default_access); - p2m->pod.entry_count--; /* Lock: p2m */ - BUG_ON(p2m->pod.entry_count < 0); - pod--; - } - else if ( steal_for_cache && p2m_is_ram(t) ) - { - struct page_info *page; - - ASSERT(mfn_valid(mfn)); - - page = mfn_to_page(mfn); - - set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid, p2m->default_access); - set_gpfn_from_mfn(mfn_x(mfn), INVALID_M2P_ENTRY); - - p2m_pod_cache_add(p2m, page, 0); - - steal_for_cache = ( p2m->pod.entry_count > p2m->pod.count ); - - nonpod--; - ram--; - } - } - - /* If there are no more non-PoD entries, tell decrease_reservation() that - * there's nothing left to do. */ - if ( nonpod == 0 ) - ret = 1; - -out_entry_check: - /* If we've reduced our "liabilities" beyond our "assets", free some */ - if ( p2m->pod.entry_count < p2m->pod.count ) - { - p2m_pod_set_cache_target(p2m, p2m->pod.entry_count, 0/*can't preempt*/); - } - -out_unlock: - audit_p2m(p2m, 1); - p2m_unlock(p2m); - -out: - return ret; -} - -void -p2m_pod_dump_data(struct p2m_domain *p2m) -{ - printk(" PoD entries=%d cachesize=%d\n", - p2m->pod.entry_count, p2m->pod.count); -} - - -/* Search for all-zero superpages to be reclaimed as superpages for the - * PoD cache. Must be called w/ p2m lock held, page_alloc lock not held. */ -static int -p2m_pod_zero_check_superpage(struct p2m_domain *p2m, unsigned long gfn) -{ - mfn_t mfn, mfn0 = _mfn(INVALID_MFN); - p2m_type_t type, type0 = 0; - unsigned long * map = NULL; - int ret=0, reset = 0; - int i, j; - int max_ref = 1; - struct domain *d = p2m->domain; - - if ( !superpage_aligned(gfn) ) - goto out; - - /* Allow an extra refcount for one shadow pt mapping in shadowed domains */ - if ( paging_mode_shadow(d) ) - max_ref++; - - /* Look up the mfns, checking to make sure they're the same mfn - * and aligned, and mapping them. */ - for ( i=0; i<SUPERPAGE_PAGES; i++ ) - { - - mfn = gfn_to_mfn_query(p2m, gfn + i, &type); - - if ( i == 0 ) - { - mfn0 = mfn; - type0 = type; - } - - /* Conditions that must be met for superpage-superpage: - * + All gfns are ram types - * + All gfns have the same type - * + All of the mfns are allocated to a domain - * + None of the mfns are used as pagetables, or allocated via xenheap - * + The first mfn is 2-meg aligned - * + All the other mfns are in sequence - * Adding for good measure: - * + None of the mfns are likely to be mapped elsewhere (refcount - * 2 or less for shadow, 1 for hap) - */ - if ( !p2m_is_ram(type) - || type != type0 - || ( (mfn_to_page(mfn)->count_info & PGC_allocated) == 0 ) - || ( (mfn_to_page(mfn)->count_info & (PGC_page_table|PGC_xen_heap)) != 0 ) - || ( (mfn_to_page(mfn)->count_info & PGC_xen_heap ) != 0 ) - || ( (mfn_to_page(mfn)->count_info & PGC_count_mask) > max_ref ) - || !( ( i == 0 && superpage_aligned(mfn_x(mfn0)) ) - || ( i != 0 && mfn_x(mfn) == (mfn_x(mfn0) + i) ) ) ) - goto out; - } - - /* Now, do a quick check to see if it may be zero before unmapping. */ - for ( i=0; i<SUPERPAGE_PAGES; i++ ) - { - /* Quick zero-check */ - map = map_domain_page(mfn_x(mfn0) + i); - - for ( j=0; j<16; j++ ) - if( *(map+j) != 0 ) - break; - - unmap_domain_page(map); - - if ( j < 16 ) - goto out; - - } - - /* Try to remove the page, restoring old mapping if it fails. */ - set_p2m_entry(p2m, gfn, - _mfn(POPULATE_ON_DEMAND_MFN), 9, - p2m_populate_on_demand, p2m->default_access); - - /* Make none of the MFNs are used elsewhere... for example, mapped - * via the grant table interface, or by qemu. Allow one refcount for - * being allocated to the domain. */ - for ( i=0; i < SUPERPAGE_PAGES; i++ ) - { - mfn = _mfn(mfn_x(mfn0) + i); - if ( (mfn_to_page(mfn)->count_info & PGC_count_mask) > 1 ) - { - reset = 1; - goto out_reset; - } - } - - /* Finally, do a full zero-check */ - for ( i=0; i < SUPERPAGE_PAGES; i++ ) - { - map = map_domain_page(mfn_x(mfn0) + i); - - for ( j=0; j<PAGE_SIZE/sizeof(*map); j++ ) - if( *(map+j) != 0 ) - { - reset = 1; - break; - } - - unmap_domain_page(map); - - if ( reset ) - goto out_reset; - } - - if ( tb_init_done ) - { - struct { - u64 gfn, mfn; - int d:16,order:16; - } t; - - t.gfn = gfn; - t.mfn = mfn_x(mfn); - t.d = d->domain_id; - t.order = 9; - - __trace_var(TRC_MEM_POD_ZERO_RECLAIM, 0, sizeof(t), &t); - } - - /* Finally! We've passed all the checks, and can add the mfn superpage - * back on the PoD cache, and account for the new p2m PoD entries */ - p2m_pod_cache_add(p2m, mfn_to_page(mfn0), 9); - p2m->pod.entry_count += SUPERPAGE_PAGES; - -out_reset: - if ( reset ) - set_p2m_entry(p2m, gfn, mfn0, 9, type0, p2m->default_access); - -out: - return ret; -} - -static void -p2m_pod_zero_check(struct p2m_domain *p2m, unsigned long *gfns, int count) -{ - mfn_t mfns[count]; - p2m_type_t types[count]; - unsigned long * map[count]; - struct domain *d = p2m->domain; - - int i, j; - int max_ref = 1; - - /* Allow an extra refcount for one shadow pt mapping in shadowed domains */ - if ( paging_mode_shadow(d) ) - max_ref++; - - /* First, get the gfn list, translate to mfns, and map the pages. */ - for ( i=0; i<count; i++ ) - { - mfns[i] = gfn_to_mfn_query(p2m, gfns[i], types + i); - /* If this is ram, and not a pagetable or from the xen heap, and probably not mapped - elsewhere, map it; otherwise, skip. */ - if ( p2m_is_ram(types[i]) - && ( (mfn_to_page(mfns[i])->count_info & PGC_allocated) != 0 ) - && ( (mfn_to_page(mfns[i])->count_info & (PGC_page_table|PGC_xen_heap)) == 0 ) - && ( (mfn_to_page(mfns[i])->count_info & PGC_count_mask) <= max_ref ) ) - map[i] = map_domain_page(mfn_x(mfns[i])); - else - map[i] = NULL; - } - - /* Then, go through and check for zeroed pages, removing write permission - * for those with zeroes. */ - for ( i=0; i<count; i++ ) - { - if(!map[i]) - continue; - - /* Quick zero-check */ - for ( j=0; j<16; j++ ) - if( *(map[i]+j) != 0 ) - break; - - if ( j < 16 ) - { - unmap_domain_page(map[i]); - map[i] = NULL; - continue; - } - - /* Try to remove the page, restoring old mapping if it fails. */ - set_p2m_entry(p2m, gfns[i], - _mfn(POPULATE_ON_DEMAND_MFN), 0, - p2m_populate_on_demand, p2m->default_access); - - /* See if the page was successfully unmapped. (Allow one refcount - * for being allocated to a domain.) */ - if ( (mfn_to_page(mfns[i])->count_info & PGC_count_mask) > 1 ) - { - unmap_domain_page(map[i]); - map[i] = NULL; - - set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i], p2m->default_access); - - continue; - } - } - - /* Now check each page for real */ - for ( i=0; i < count; i++ ) - { - if(!map[i]) - continue; - - for ( j=0; j<PAGE_SIZE/sizeof(*map[i]); j++ ) - if( *(map[i]+j) != 0 ) - break; - - unmap_domain_page(map[i]); - - /* See comment in p2m_pod_zero_check_superpage() re gnttab - * check timing. */ - if ( j < PAGE_SIZE/sizeof(*map[i]) ) - { - set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i], p2m->default_access); - } - else - { - if ( tb_init_done ) - { - struct { - u64 gfn, mfn; - int d:16,order:16; - } t; - - t.gfn = gfns[i]; - t.mfn = mfn_x(mfns[i]); - t.d = d->domain_id; - t.order = 0; - - __trace_var(TRC_MEM_POD_ZERO_RECLAIM, 0, sizeof(t), &t); - } - - /* Add to cache, and account for the new p2m PoD entry */ - p2m_pod_cache_add(p2m, mfn_to_page(mfns[i]), 0); - p2m->pod.entry_count++; - } - } - -} - -#define POD_SWEEP_LIMIT 1024 -static void -p2m_pod_emergency_sweep_super(struct p2m_domain *p2m) -{ - unsigned long i, start, limit; - - if ( p2m->pod.reclaim_super == 0 ) - { - p2m->pod.reclaim_super = (p2m->pod.max_guest>>9)<<9; - p2m->pod.reclaim_super -= SUPERPAGE_PAGES; - } - - start = p2m->pod.reclaim_super; - limit = (start > POD_SWEEP_LIMIT) ? (start - POD_SWEEP_LIMIT) : 0; - - for ( i=p2m->pod.reclaim_super ; i > 0 ; i -= SUPERPAGE_PAGES ) - { - p2m_pod_zero_check_superpage(p2m, i); - /* Stop if we're past our limit and we have found *something*. - * - * NB that this is a zero-sum game; we're increasing our cache size - * by increasing our 'debt'. Since we hold the p2m lock, - * (entry_count - count) must remain the same. */ - if ( !page_list_empty(&p2m->pod.super) && i < limit ) - break; - } - - p2m->pod.reclaim_super = i ? i - SUPERPAGE_PAGES : 0; -} - -#define POD_SWEEP_STRIDE 16 -static void -p2m_pod_emergency_sweep(struct p2m_domain *p2m) -{ - unsigned long gfns[POD_SWEEP_STRIDE]; - unsigned long i, j=0, start, limit; - p2m_type_t t; - - - if ( p2m->pod.reclaim_single == 0 ) - p2m->pod.reclaim_single = p2m->pod.max_guest; - - start = p2m->pod.reclaim_single; - limit = (start > POD_SWEEP_LIMIT) ? (start - POD_SWEEP_LIMIT) : 0; - - /* FIXME: Figure out how to avoid superpages */ - for ( i=p2m->pod.reclaim_single; i > 0 ; i-- ) - { - gfn_to_mfn_query(p2m, i, &t ); - if ( p2m_is_ram(t) ) - { - gfns[j] = i; - j++; - BUG_ON(j > POD_SWEEP_STRIDE); - if ( j == POD_SWEEP_STRIDE ) - { - p2m_pod_zero_check(p2m, gfns, j); - j = 0; - } - } - /* Stop if we're past our limit and we have found *something*. - * - * NB that this is a zero-sum game; we're increasing our cache size - * by re-increasing our 'debt'. Since we hold the p2m lock, - * (entry_count - count) must remain the same. */ - if ( p2m->pod.count > 0 && i < limit ) - break; - } - - if ( j ) - p2m_pod_zero_check(p2m, gfns, j); - - p2m->pod.reclaim_single = i ? i - 1 : i; - -} - -int -p2m_pod_demand_populate(struct p2m_domain *p2m, unsigned long gfn, - unsigned int order, - p2m_query_t q) -{ - struct domain *d = p2m->domain; - struct page_info *p = NULL; /* Compiler warnings */ - unsigned long gfn_aligned; - mfn_t mfn; - int i; - - ASSERT(p2m_locked_by_me(p2m)); - - /* This check is done with the p2m lock held. This will make sure that - * even if d->is_dying changes under our feet, p2m_pod_empty_cache() - * won't start until we're done. */ - if ( unlikely(d->is_dying) ) - goto out_fail; - - /* Because PoD does not have cache list for 1GB pages, it has to remap - * 1GB region to 2MB chunks for a retry. */ - if ( order == 18 ) - { - gfn_aligned = (gfn >> order) << order; - /* Note that we are supposed to call set_p2m_entry() 512 times to - * split 1GB into 512 2MB pages here. But We only do once here because - * set_p2m_entry() should automatically shatter the 1GB page into - * 512 2MB pages. The rest of 511 calls are unnecessary. - */ - set_p2m_entry(p2m, gfn_aligned, _mfn(POPULATE_ON_DEMAND_MFN), 9, - p2m_populate_on_demand, p2m->default_access); - audit_p2m(p2m, 1); - p2m_unlock(p2m); - return 0; - } - - /* Once we've ballooned down enough that we can fill the remaining - * PoD entries from the cache, don't sweep even if the particular - * list we want to use is empty: that can lead to thrashing zero pages - * through the cache for no good reason. */ - if ( p2m->pod.entry_count > p2m->pod.count ) - { - - /* If we're low, start a sweep */ - if ( order == 9 && page_list_empty(&p2m->pod.super) ) - p2m_pod_emergency_sweep_super(p2m); - - if ( page_list_empty(&p2m->pod.single) && - ( ( order == 0 ) - || (order == 9 && page_list_empty(&p2m->pod.super) ) ) ) - p2m_pod_emergency_sweep(p2m); - } - - /* Keep track of the highest gfn demand-populated by a guest fault */ - if ( q == p2m_guest && gfn > p2m->pod.max_guest ) - p2m->pod.max_guest = gfn; - - spin_lock(&d->page_alloc_lock); - - if ( p2m->pod.count == 0 ) - goto out_of_memory; - - /* Get a page f/ the cache. A NULL return value indicates that the - * 2-meg range should be marked singleton PoD, and retried */ - if ( (p = p2m_pod_cache_get(p2m, order)) == NULL ) - goto remap_and_retry; - - mfn = page_to_mfn(p); - - BUG_ON((mfn_x(mfn) & ((1 << order)-1)) != 0); - - spin_unlock(&d->page_alloc_lock); - - gfn_aligned = (gfn >> order) << order; - - set_p2m_entry(p2m, gfn_aligned, mfn, order, p2m_ram_rw, p2m->default_access); - - for( i = 0; i < (1UL << order); i++ ) - { - set_gpfn_from_mfn(mfn_x(mfn) + i, gfn_aligned + i); - paging_mark_dirty(d, mfn_x(mfn) + i); - } - - p2m->pod.entry_count -= (1 << order); /* Lock: p2m */ - BUG_ON(p2m->pod.entry_count < 0); - - if ( tb_init_done ) - { - struct { - u64 gfn, mfn; - int d:16,order:16; - } t; - - t.gfn = gfn; - t.mfn = mfn_x(mfn); - t.d = d->domain_id; - t.order = order; - - __trace_var(TRC_MEM_POD_POPULATE, 0, sizeof(t), &t); - } - - return 0; -out_of_memory: - spin_unlock(&d->page_alloc_lock); - - printk("%s: Out of populate-on-demand memory! tot_pages %" PRIu32 " pod_entries %" PRIi32 "\n", - __func__, d->tot_pages, p2m->pod.entry_count); - domain_crash(d); -out_fail: - return -1; -remap_and_retry: - BUG_ON(order != 9); - spin_unlock(&d->page_alloc_lock); - - /* Remap this 2-meg region in singleton chunks */ - gfn_aligned = (gfn>>order)<<order; - for(i=0; i<(1<<order); i++) - set_p2m_entry(p2m, gfn_aligned+i, _mfn(POPULATE_ON_DEMAND_MFN), 0, - p2m_populate_on_demand, p2m->default_access); - if ( tb_init_done ) - { - struct { - u64 gfn; - int d:16; - } t; - - t.gfn = gfn; - t.d = d->domain_id; - - __trace_var(TRC_MEM_POD_SUPERPAGE_SPLINTER, 0, sizeof(t), &t); - } - - return 0; -} - -/* Non-ept "lock-and-check" wrapper */ -static int p2m_pod_check_and_populate(struct p2m_domain *p2m, unsigned long gfn, - l1_pgentry_t *p2m_entry, int order, - p2m_query_t q) -{ - /* Only take the lock if we don't already have it. Otherwise it - * wouldn't be safe to do p2m lookups with the p2m lock held */ - int do_locking = !p2m_locked_by_me(p2m); - int r; - - if ( do_locking ) - p2m_lock(p2m); - - audit_p2m(p2m, 1); - - /* Check to make sure this is still PoD */ - if ( p2m_flags_to_type(l1e_get_flags(*p2m_entry)) != p2m_populate_on_demand ) - { - if ( do_locking ) - p2m_unlock(p2m); - return 0; - } - - r = p2m_pod_demand_populate(p2m, gfn, order, q); - - audit_p2m(p2m, 1); - if ( do_locking ) - p2m_unlock(p2m); - - return r; -} - -// Returns 0 on error (out of memory) -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) -{ - // XXX -- this might be able to be faster iff current->domain == d - mfn_t table_mfn = pagetable_get_mfn(p2m_get_pagetable(p2m)); - void *table =map_domain_page(mfn_x(table_mfn)); - unsigned long i, gfn_remainder = gfn; - l1_pgentry_t *p2m_entry; - l1_pgentry_t entry_content; - l2_pgentry_t l2e_content; - l3_pgentry_t l3e_content; - int rv=0; - unsigned int iommu_pte_flags = (p2mt == p2m_ram_rw) ? - IOMMUF_readable|IOMMUF_writable: - 0; - unsigned long old_mfn = 0; - - if ( tb_init_done ) - { - struct { - u64 gfn, mfn; - int p2mt; - int d:16,order:16; - } t; - - t.gfn = gfn; - t.mfn = mfn_x(mfn); - t.p2mt = p2mt; - t.d = p2m->domain->domain_id; - t.order = page_order; - - __trace_var(TRC_MEM_SET_P2M_ENTRY, 0, sizeof(t), &t); - } - -#if CONFIG_PAGING_LEVELS >= 4 - if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, - L4_PAGETABLE_SHIFT - PAGE_SHIFT, - L4_PAGETABLE_ENTRIES, PGT_l3_page_table) ) - goto out; -#endif - /* - * Try to allocate 1GB page table if this feature is supported. - */ - if ( page_order == 18 ) - { - l1_pgentry_t old_entry = l1e_empty(); - p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, - L3_PAGETABLE_SHIFT - PAGE_SHIFT, - L3_PAGETABLE_ENTRIES); - ASSERT(p2m_entry); - if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) && - !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) - { - /* We're replacing a non-SP page with a superpage. Make sure to - * handle freeing the table properly. */ - old_entry = *p2m_entry; - } - - ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct); - l3e_content = mfn_valid(mfn) - ? l3e_from_pfn(mfn_x(mfn), - p2m_type_to_flags(p2mt, mfn) | _PAGE_PSE) - : l3e_empty(); - entry_content.l1 = l3e_content.l3; - - if ( entry_content.l1 != 0 ) - { - p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags); - old_mfn = l1e_get_pfn(*p2m_entry); - } - - p2m->write_p2m_entry(p2m, gfn, p2m_entry, table_mfn, entry_content, 3); - /* NB: paging_write_p2m_entry() handles tlb flushes properly */ - - /* Free old intermediate tables if necessary */ - if ( l1e_get_flags(old_entry) & _PAGE_PRESENT ) - p2m_free_entry(p2m, &old_entry, page_order); - } - /* - * When using PAE Xen, we only allow 33 bits of pseudo-physical - * address in translated guests (i.e. 8 GBytes). This restriction - * comes from wanting to map the P2M table into the 16MB RO_MPT hole - * in Xen's address space for translated PV guests. - * When using AMD's NPT on PAE Xen, we are restricted to 4GB. - */ - else if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, - L3_PAGETABLE_SHIFT - PAGE_SHIFT, - ((CONFIG_PAGING_LEVELS == 3) - ? (hap_enabled(p2m->domain) ? 4 : 8) - : L3_PAGETABLE_ENTRIES), - PGT_l2_page_table) ) - goto out; - - if ( page_order == 0 ) - { - if ( !p2m_next_level(p2m, &table_mfn, &table, &gfn_remainder, gfn, - L2_PAGETABLE_SHIFT - PAGE_SHIFT, - L2_PAGETABLE_ENTRIES, PGT_l1_page_table) ) - goto out; - - p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, - 0, L1_PAGETABLE_ENTRIES); - ASSERT(p2m_entry); - - if ( mfn_valid(mfn) || (p2mt == p2m_mmio_direct) ) - entry_content = l1e_from_pfn(mfn_x(mfn), - p2m_type_to_flags(p2mt, mfn)); - else - entry_content = l1e_empty(); - - if ( entry_content.l1 != 0 ) - { - p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags); - old_mfn = l1e_get_pfn(*p2m_entry); - } - /* level 1 entry */ - p2m->write_p2m_entry(p2m, gfn, p2m_entry, table_mfn, entry_content, 1); - /* NB: paging_write_p2m_entry() handles tlb flushes properly */ - } - else if ( page_order == 9 ) - { - l1_pgentry_t old_entry = l1e_empty(); - p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, - L2_PAGETABLE_SHIFT - PAGE_SHIFT, - L2_PAGETABLE_ENTRIES); - ASSERT(p2m_entry); - - /* FIXME: Deal with 4k replaced by 2meg pages */ - if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) && - !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) ) - { - /* We're replacing a non-SP page with a superpage. Make sure to - * handle freeing the table properly. */ - old_entry = *p2m_entry; - } - - ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct); - if ( mfn_valid(mfn) || p2m_is_magic(p2mt) ) - l2e_content = l2e_from_pfn(mfn_x(mfn), - p2m_type_to_flags(p2mt, mfn) | - _PAGE_PSE); - else - l2e_content = l2e_empty(); - - entry_content.l1 = l2e_content.l2; - - if ( entry_content.l1 != 0 ) - { - p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags); - old_mfn = l1e_get_pfn(*p2m_entry); - } - - p2m->write_p2m_entry(p2m, gfn, p2m_entry, table_mfn, entry_content, 2); - /* NB: paging_write_p2m_entry() handles tlb flushes properly */ - - /* Free old intermediate tables if necessary */ - if ( l1e_get_flags(old_entry) & _PAGE_PRESENT ) - p2m_free_entry(p2m, &old_entry, page_order); - } - - /* Track the highest gfn for which we have ever had a valid mapping */ - if ( mfn_valid(mfn) - && (gfn + (1UL << page_order) - 1 > p2m->max_mapped_pfn) ) - p2m->max_mapped_pfn = gfn + (1UL << page_order) - 1; - - if ( iommu_enabled && need_iommu(p2m->domain) ) - { - if ( iommu_hap_pt_share ) - { - if ( old_mfn && (old_mfn != mfn_x(mfn)) ) - amd_iommu_flush_pages(p2m->domain, gfn, page_order); - } - else - { - if ( p2mt == p2m_ram_rw ) - for ( i = 0; i < (1UL << page_order); i++ ) - iommu_map_page(p2m->domain, gfn+i, mfn_x(mfn)+i, - IOMMUF_readable|IOMMUF_writable); - else - for ( int i = 0; i < (1UL << page_order); i++ ) - iommu_unmap_page(p2m->domain, gfn+i); - } - } - - /* Success */ - rv = 1; - -out: - unmap_domain_page(table); - return rv; -} - -static mfn_t -p2m_gfn_to_mfn(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *t, p2m_access_t *a, - p2m_query_t q) -{ - mfn_t mfn; - paddr_t addr = ((paddr_t)gfn) << PAGE_SHIFT; - l2_pgentry_t *l2e; - l1_pgentry_t *l1e; - - ASSERT(paging_mode_translate(p2m->domain)); - - /* XXX This is for compatibility with the old model, where anything not - * XXX marked as RAM was considered to be emulated MMIO space. - * XXX Once we start explicitly registering MMIO regions in the p2m - * XXX we will return p2m_invalid for unmapped gfns */ - *t = p2m_mmio_dm; - /* Not implemented except with EPT */ - *a = p2m_access_rwx; - - mfn = pagetable_get_mfn(p2m_get_pagetable(p2m)); - - if ( gfn > p2m->max_mapped_pfn ) - /* This pfn is higher than the highest the p2m map currently holds */ - return _mfn(INVALID_MFN); - -#if CONFIG_PAGING_LEVELS >= 4 - { - l4_pgentry_t *l4e = map_domain_page(mfn_x(mfn)); - l4e += l4_table_offset(addr); - if ( (l4e_get_flags(*l4e) & _PAGE_PRESENT) == 0 ) - { - unmap_domain_page(l4e); - return _mfn(INVALID_MFN); - } - mfn = _mfn(l4e_get_pfn(*l4e)); - unmap_domain_page(l4e); - } -#endif - { - l3_pgentry_t *l3e = map_domain_page(mfn_x(mfn)); -#if CONFIG_PAGING_LEVELS == 3 - /* On PAE hosts the p2m has eight l3 entries, not four (see - * shadow_set_p2m_entry()) so we can't use l3_table_offset. - * Instead, just count the number of l3es from zero. It's safe - * to do this because we already checked that the gfn is within - * the bounds of the p2m. */ - l3e += (addr >> L3_PAGETABLE_SHIFT); -#else - l3e += l3_table_offset(addr); -#endif -pod_retry_l3: - if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 ) - { - if ( p2m_flags_to_type(l3e_get_flags(*l3e)) == p2m_populate_on_demand ) - { - if ( q != p2m_query ) - { - if ( !p2m_pod_demand_populate(p2m, gfn, 18, q) ) - goto pod_retry_l3; - } - else - *t = p2m_populate_on_demand; - } - unmap_domain_page(l3e); - return _mfn(INVALID_MFN); - } - else if ( (l3e_get_flags(*l3e) & _PAGE_PSE) ) - { - mfn = _mfn(l3e_get_pfn(*l3e) + - l2_table_offset(addr) * L1_PAGETABLE_ENTRIES + - l1_table_offset(addr)); - *t = p2m_flags_to_type(l3e_get_flags(*l3e)); - unmap_domain_page(l3e); - - ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t)); - return (p2m_is_valid(*t)) ? mfn : _mfn(INVALID_MFN); - } - - mfn = _mfn(l3e_get_pfn(*l3e)); - unmap_domain_page(l3e); - } - - l2e = map_domain_page(mfn_x(mfn)); - l2e += l2_table_offset(addr); - -pod_retry_l2: - if ( (l2e_get_flags(*l2e) & _PAGE_PRESENT) == 0 ) - { - /* PoD: Try to populate a 2-meg chunk */ - if ( p2m_flags_to_type(l2e_get_flags(*l2e)) == p2m_populate_on_demand ) - { - if ( q != p2m_query ) { - if ( !p2m_pod_check_and_populate(p2m, gfn, - (l1_pgentry_t *)l2e, 9, q) ) - goto pod_retry_l2; - } else - *t = p2m_populate_on_demand; - } - - unmap_domain_page(l2e); - return _mfn(INVALID_MFN); - } - else if ( (l2e_get_flags(*l2e) & _PAGE_PSE) ) - { - mfn = _mfn(l2e_get_pfn(*l2e) + l1_table_offset(addr)); - *t = p2m_flags_to_type(l2e_get_flags(*l2e)); - unmap_domain_page(l2e); - - ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t)); - return (p2m_is_valid(*t)) ? mfn : _mfn(INVALID_MFN); - } - - mfn = _mfn(l2e_get_pfn(*l2e)); - unmap_domain_page(l2e); - - l1e = map_domain_page(mfn_x(mfn)); - l1e += l1_table_offset(addr); -pod_retry_l1: - if ( (l1e_get_flags(*l1e) & _PAGE_PRESENT) == 0 ) - { - /* PoD: Try to populate */ - if ( p2m_flags_to_type(l1e_get_flags(*l1e)) == p2m_populate_on_demand ) - { - if ( q != p2m_query ) { - if ( !p2m_pod_check_and_populate(p2m, gfn, - (l1_pgentry_t *)l1e, 0, q) ) - goto pod_retry_l1; - } else - *t = p2m_populate_on_demand; - } - - unmap_domain_page(l1e); - return _mfn(INVALID_MFN); - } - mfn = _mfn(l1e_get_pfn(*l1e)); - *t = p2m_flags_to_type(l1e_get_flags(*l1e)); - unmap_domain_page(l1e); - - ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t)); - return (p2m_is_valid(*t) || p2m_is_grant(*t)) ? mfn : _mfn(INVALID_MFN); -} - -/* Read the current domain's p2m table (through the linear mapping). */ -static mfn_t p2m_gfn_to_mfn_current(struct p2m_domain *p2m, - unsigned long gfn, p2m_type_t *t, p2m_access_t *a, - p2m_query_t q) -{ - mfn_t mfn = _mfn(INVALID_MFN); - p2m_type_t p2mt = p2m_mmio_dm; - paddr_t addr = ((paddr_t)gfn) << PAGE_SHIFT; - /* XXX This is for compatibility with the old model, where anything not - * XXX marked as RAM was considered to be emulated MMIO space. - * XXX Once we start explicitly registering MMIO regions in the p2m - * XXX we will return p2m_invalid for unmapped gfns */ - - /* Not currently implemented except for EPT */ - *a = p2m_access_rwx; - - if ( gfn <= p2m->max_mapped_pfn ) - { - l1_pgentry_t l1e = l1e_empty(), *p2m_entry; - l2_pgentry_t l2e = l2e_empty(); - int ret; -#if CONFIG_PAGING_LEVELS >= 4 - l3_pgentry_t l3e = l3e_empty(); -#endif - - ASSERT(gfn < (RO_MPT_VIRT_END - RO_MPT_VIRT_START) - / sizeof(l1_pgentry_t)); - -#if CONFIG_PAGING_LEVELS >= 4 - /* - * Read & process L3 - */ - p2m_entry = (l1_pgentry_t *) - &__linear_l2_table[l2_linear_offset(RO_MPT_VIRT_START) - + l3_linear_offset(addr)]; - pod_retry_l3: - ret = __copy_from_user(&l3e, p2m_entry, sizeof(l3e)); - - if ( ret != 0 || !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) - { - if ( (l3e_get_flags(l3e) & _PAGE_PSE) && - (p2m_flags_to_type(l3e_get_flags(l3e)) == p2m_populate_on_demand) ) - { - /* The read has succeeded, so we know that mapping exists */ - if ( q != p2m_query ) - { - if ( !p2m_pod_demand_populate(p2m, gfn, 18, q) ) - goto pod_retry_l3; - p2mt = p2m_invalid; - printk("%s: Allocate 1GB failed!\n", __func__); - goto out; - } - else - { - p2mt = p2m_populate_on_demand; - goto out; - } - } - goto pod_retry_l2; - } - - if ( l3e_get_flags(l3e) & _PAGE_PSE ) - { - p2mt = p2m_flags_to_type(l3e_get_flags(l3e)); - ASSERT(l3e_get_pfn(l3e) != INVALID_MFN || !p2m_is_ram(p2mt)); - if (p2m_is_valid(p2mt) ) - mfn = _mfn(l3e_get_pfn(l3e) + - l2_table_offset(addr) * L1_PAGETABLE_ENTRIES + - l1_table_offset(addr)); - else - p2mt = p2m_mmio_dm; - - goto out; - } -#endif - /* - * Read & process L2 - */ - p2m_entry = &__linear_l1_table[l1_linear_offset(RO_MPT_VIRT_START) - + l2_linear_offset(addr)]; - - pod_retry_l2: - ret = __copy_from_user(&l2e, - p2m_entry, - sizeof(l2e)); - if ( ret != 0 - || !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) - { - if( (l2e_get_flags(l2e) & _PAGE_PSE) - && ( p2m_flags_to_type(l2e_get_flags(l2e)) - == p2m_populate_on_demand ) ) - { - /* The read has succeeded, so we know that the mapping - * exits at this point. */ - if ( q != p2m_query ) - { - if ( !p2m_pod_check_and_populate(p2m, gfn, - p2m_entry, 9, q) ) - goto pod_retry_l2; - - /* Allocate failed. */ - p2mt = p2m_invalid; - printk("%s: Allocate failed!\n", __func__); - goto out; - } - else - { - p2mt = p2m_populate_on_demand; - goto out; - } - } - - goto pod_retry_l1; - } - - if (l2e_get_flags(l2e) & _PAGE_PSE) - { - p2mt = p2m_flags_to_type(l2e_get_flags(l2e)); - ASSERT(l2e_get_pfn(l2e) != INVALID_MFN || !p2m_is_ram(p2mt)); - - if ( p2m_is_valid(p2mt) ) - mfn = _mfn(l2e_get_pfn(l2e) + l1_table_offset(addr)); - else - p2mt = p2m_mmio_dm; - - goto out; - } - - /* - * Read and process L1 - */ - - /* Need to __copy_from_user because the p2m is sparse and this - * part might not exist */ - pod_retry_l1: - p2m_entry = &phys_to_machine_mapping[gfn]; - - ret = __copy_from_user(&l1e, - p2m_entry, - sizeof(l1e)); - - if ( ret == 0 ) { - p2mt = p2m_flags_to_type(l1e_get_flags(l1e)); - ASSERT(l1e_get_pfn(l1e) != INVALID_MFN || !p2m_is_ram(p2mt)); - - if ( p2m_flags_to_type(l1e_get_flags(l1e)) - == p2m_populate_on_demand ) - { - /* The read has succeeded, so we know that the mapping - * exits at this point. */ - if ( q != p2m_query ) - { - if ( !p2m_pod_check_and_populate(p2m, gfn, - (l1_pgentry_t *)p2m_entry, 0, q) ) - goto pod_retry_l1; - - /* Allocate failed. */ - p2mt = p2m_invalid; - goto out; - } - else - { - p2mt = p2m_populate_on_demand; - goto out; - } - } - - if ( p2m_is_valid(p2mt) || p2m_is_grant(p2mt) ) - mfn = _mfn(l1e_get_pfn(l1e)); - else - /* XXX see above */ - p2mt = p2m_mmio_dm; - } - } -out: - *t = p2mt; - return mfn; -} +/* XXX declare functions moved to p2m-pt.c */ +extern void p2m_pt_init(struct p2m_domain *p2m); /* Init the datastructures for later use by the p2m code */ static void p2m_initialise(struct domain *d, struct p2m_domain *p2m) @@ -1930,15 +87,12 @@ p2m->default_access = p2m_access_rwx; p2m->cr3 = CR3_EADDR; - p2m->set_entry = p2m_set_entry; - p2m->get_entry = p2m_gfn_to_mfn; - p2m->get_entry_current = p2m_gfn_to_mfn_current; - p2m->change_entry_type_global = p2m_change_type_global; - p2m->write_p2m_entry = paging_write_p2m_entry; cpus_clear(p2m->p2m_dirty_cpumask); if ( hap_enabled(d) && (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) ) - ept_p2m_init(d); + ept_p2m_init(p2m); + else + p2m_pt_init(p2m); return; } @@ -1986,7 +140,6 @@ p2m_unlock(p2m); } -static int set_p2m_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma) { @@ -2162,275 +315,6 @@ p2m_teardown_nestedp2m(d); } -#if P2M_AUDIT -/* strict_m2p == 0 allows m2p mappings that don'#t match the p2m. - * It's intended for add_to_physmap, when the domain has just been allocated - * new mfns that might have stale m2p entries from previous owners */ -static void audit_p2m(struct p2m_domain *p2m, int strict_m2p) -{ - struct page_info *page; - struct domain *od; - unsigned long mfn, gfn, m2pfn, lp2mfn = 0; - int entry_count = 0; - mfn_t p2mfn; - unsigned long orphans_d = 0, orphans_i = 0, mpbad = 0, pmbad = 0; - int test_linear; - p2m_type_t type; - struct domain *d = p2m->domain; - - if ( !paging_mode_translate(d) ) - return; - - //P2M_PRINTK("p2m audit starts\n"); - - test_linear = ( (d == current->domain) - && !pagetable_is_null(current->arch.monitor_table) ); - if ( test_linear ) - flush_tlb_local(); - - spin_lock(&d->page_alloc_lock); - - /* Audit part one: walk the domain's page allocation list, checking - * the m2p entries. */ - page_list_for_each ( page, &d->page_list ) - { - mfn = mfn_x(page_to_mfn(page)); - - // P2M_PRINTK("auditing guest page, mfn=%#lx\n", mfn); - - od = page_get_owner(page); - - if ( od != d ) - { - P2M_PRINTK("wrong owner %#lx -> %p(%u) != %p(%u)\n", - mfn, od, (od?od->domain_id:-1), d, d->domain_id); - continue; - } - - gfn = get_gpfn_from_mfn(mfn); - if ( gfn == INVALID_M2P_ENTRY ) - { - orphans_i++; - //P2M_PRINTK("orphaned guest page: mfn=%#lx has invalid gfn\n", - // mfn); - continue; - } - - if ( gfn == 0x55555555 || gfn == 0x5555555555555555 ) - { - orphans_d++; - //P2M_PRINTK("orphaned guest page: mfn=%#lx has debug gfn\n", - // mfn); - continue; - } - - if ( gfn == SHARED_M2P_ENTRY ) - { - P2M_PRINTK("shared mfn (%lx) on domain page list!\n", - mfn); - continue; - } - - p2mfn = gfn_to_mfn_type_p2m(p2m, gfn, &type, p2m_query); - if ( strict_m2p && mfn_x(p2mfn) != mfn ) - { - mpbad++; - P2M_PRINTK("map mismatch mfn %#lx -> gfn %#lx -> mfn %#lx" - " (-> gfn %#lx)\n", - mfn, gfn, mfn_x(p2mfn), - (mfn_valid(p2mfn) - ? get_gpfn_from_mfn(mfn_x(p2mfn)) - : -1u)); - /* This m2p entry is stale: the domain has another frame in - * this physical slot. No great disaster, but for neatness, - * blow away the m2p entry. */ - set_gpfn_from_mfn(mfn, INVALID_M2P_ENTRY); - } - - if ( test_linear && (gfn <= p2m->max_mapped_pfn) ) - { - lp2mfn = mfn_x(gfn_to_mfn_query(p2m, gfn, &type)); - if ( lp2mfn != mfn_x(p2mfn) ) - { - P2M_PRINTK("linear mismatch gfn %#lx -> mfn %#lx " - "(!= mfn %#lx)\n", gfn, lp2mfn, mfn_x(p2mfn)); - } - } - - // P2M_PRINTK("OK: mfn=%#lx, gfn=%#lx, p2mfn=%#lx, lp2mfn=%#lx\n", - // mfn, gfn, mfn_x(p2mfn), lp2mfn); - } - - spin_unlock(&d->page_alloc_lock); - - /* Audit part two: walk the domain's p2m table, checking the entries. */ - if ( pagetable_get_pfn(p2m_get_pagetable(p2m)) != 0 ) - { - l2_pgentry_t *l2e; - l1_pgentry_t *l1e; - int i1, i2; - -#if CONFIG_PAGING_LEVELS == 4 - l4_pgentry_t *l4e; - l3_pgentry_t *l3e; - int i4, i3; - l4e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); -#else /* CONFIG_PAGING_LEVELS == 3 */ - l3_pgentry_t *l3e; - int i3; - l3e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); -#endif - - gfn = 0; -#if CONFIG_PAGING_LEVELS >= 4 - for ( i4 = 0; i4 < L4_PAGETABLE_ENTRIES; i4++ ) - { - if ( !(l4e_get_flags(l4e[i4]) & _PAGE_PRESENT) ) - { - gfn += 1 << (L4_PAGETABLE_SHIFT - PAGE_SHIFT); - continue; - } - l3e = map_domain_page(mfn_x(_mfn(l4e_get_pfn(l4e[i4])))); -#endif - for ( i3 = 0; - i3 < ((CONFIG_PAGING_LEVELS==4) ? L3_PAGETABLE_ENTRIES : 8); - i3++ ) - { - if ( !(l3e_get_flags(l3e[i3]) & _PAGE_PRESENT) ) - { - gfn += 1 << (L3_PAGETABLE_SHIFT - PAGE_SHIFT); - continue; - } - - /* check for 1GB super page */ - if ( l3e_get_flags(l3e[i3]) & _PAGE_PSE ) - { - mfn = l3e_get_pfn(l3e[i3]); - ASSERT(mfn_valid(_mfn(mfn))); - /* we have to cover 512x512 4K pages */ - for ( i2 = 0; - i2 < (L2_PAGETABLE_ENTRIES * L1_PAGETABLE_ENTRIES); - i2++) - { - m2pfn = get_gpfn_from_mfn(mfn+i2); - if ( m2pfn != (gfn + i2) ) - { - pmbad++; - P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx" - " -> gfn %#lx\n", gfn+i2, mfn+i2, - m2pfn); - BUG(); - } - gfn += 1 << (L3_PAGETABLE_SHIFT - PAGE_SHIFT); - continue; - } - } - - l2e = map_domain_page(mfn_x(_mfn(l3e_get_pfn(l3e[i3])))); - for ( i2 = 0; i2 < L2_PAGETABLE_ENTRIES; i2++ ) - { - if ( !(l2e_get_flags(l2e[i2]) & _PAGE_PRESENT) ) - { - if ( (l2e_get_flags(l2e[i2]) & _PAGE_PSE) - && ( p2m_flags_to_type(l2e_get_flags(l2e[i2])) - == p2m_populate_on_demand ) ) - entry_count+=SUPERPAGE_PAGES; - gfn += 1 << (L2_PAGETABLE_SHIFT - PAGE_SHIFT); - continue; - } - - /* check for super page */ - if ( l2e_get_flags(l2e[i2]) & _PAGE_PSE ) - { - mfn = l2e_get_pfn(l2e[i2]); - ASSERT(mfn_valid(_mfn(mfn))); - for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++) - { - m2pfn = get_gpfn_from_mfn(mfn+i1); - /* Allow shared M2Ps */ - if ( (m2pfn != (gfn + i1)) && - (m2pfn != SHARED_M2P_ENTRY) ) - { - pmbad++; - P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx" - " -> gfn %#lx\n", gfn+i1, mfn+i1, - m2pfn); - BUG(); - } - } - gfn += 1 << (L2_PAGETABLE_SHIFT - PAGE_SHIFT); - continue; - } - - l1e = map_domain_page(mfn_x(_mfn(l2e_get_pfn(l2e[i2])))); - - for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++, gfn++ ) - { - p2m_type_t type; - - type = p2m_flags_to_type(l1e_get_flags(l1e[i1])); - if ( !(l1e_get_flags(l1e[i1]) & _PAGE_PRESENT) ) - { - if ( type == p2m_populate_on_demand ) - entry_count++; - continue; - } - mfn = l1e_get_pfn(l1e[i1]); - ASSERT(mfn_valid(_mfn(mfn))); - m2pfn = get_gpfn_from_mfn(mfn); - if ( m2pfn != gfn && - type != p2m_mmio_direct && - !p2m_is_grant(type) && - !p2m_is_shared(type) ) - { - pmbad++; - printk("mismatch: gfn %#lx -> mfn %#lx" - " -> gfn %#lx\n", gfn, mfn, m2pfn); - P2M_PRINTK("mismatch: gfn %#lx -> mfn %#lx" - " -> gfn %#lx\n", gfn, mfn, m2pfn); - BUG(); - } - } - unmap_domain_page(l1e); - } - unmap_domain_page(l2e); - } -#if CONFIG_PAGING_LEVELS >= 4 - unmap_domain_page(l3e); - } -#endif - -#if CONFIG_PAGING_LEVELS == 4 - unmap_domain_page(l4e); -#else /* CONFIG_PAGING_LEVELS == 3 */ - unmap_domain_page(l3e); -#endif - - } - - if ( entry_count != p2m->pod.entry_count ) - { - printk("%s: refcounted entry count %d, audit count %d!\n", - __func__, - p2m->pod.entry_count, - entry_count); - BUG(); - } - - //P2M_PRINTK("p2m audit complete\n"); - //if ( orphans_i | orphans_d | mpbad | pmbad ) - // P2M_PRINTK("p2m audit found %lu orphans (%lu inval %lu debug)\n", - // orphans_i + orphans_d, orphans_i, orphans_d); - if ( mpbad | pmbad ) - { - P2M_PRINTK("p2m audit found %lu odd p2m, %lu bad m2p entries\n", - pmbad, mpbad); - WARN(); - } -} -#endif /* P2M_AUDIT */ - - static void p2m_remove_page(struct p2m_domain *p2m, unsigned long gfn, unsigned long mfn, @@ -2475,88 +359,6 @@ p2m_unlock(p2m); } -#if CONFIG_PAGING_LEVELS == 3 -static int gfn_check_limit( - struct domain *d, unsigned long gfn, unsigned int order) -{ - /* - * 32bit AMD nested paging does not support over 4GB guest due to - * hardware translation limit. This limitation is checked by comparing - * gfn with 0xfffffUL. - */ - if ( !hap_enabled(d) || ((gfn + (1ul << order)) <= 0x100000UL) || - (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) ) - return 0; - - if ( !test_and_set_bool(d->arch.hvm_domain.svm.npt_4gb_warning) ) - dprintk(XENLOG_WARNING, "Dom%d failed to populate memory beyond" - " 4GB: specify 'hap=0' domain config option.\n", - d->domain_id); - - return -EINVAL; -} -#else -#define gfn_check_limit(d, g, o) 0 -#endif - -int -guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, - unsigned int order) -{ - struct p2m_domain *p2m = p2m_get_hostp2m(d); - unsigned long i; - p2m_type_t ot; - mfn_t omfn; - int pod_count = 0; - int rc = 0; - - BUG_ON(!paging_mode_translate(d)); - - rc = gfn_check_limit(d, gfn, order); - if ( rc != 0 ) - return rc; - - p2m_lock(p2m); - audit_p2m(p2m, 1); - - P2M_DEBUG("mark pod gfn=%#lx\n", gfn); - - /* Make sure all gpfns are unused */ - for ( i = 0; i < (1UL << order); i++ ) - { - omfn = gfn_to_mfn_query(p2m, gfn + i, &ot); - if ( p2m_is_ram(ot) ) - { - printk("%s: gfn_to_mfn returned type %d!\n", - __func__, ot); - rc = -EBUSY; - goto out; - } - else if ( ot == p2m_populate_on_demand ) - { - /* Count how man PoD entries we'll be replacing if successful */ - pod_count++; - } - } - - /* Now, actually do the two-way mapping */ - if ( !set_p2m_entry(p2m, gfn, _mfn(POPULATE_ON_DEMAND_MFN), order, - p2m_populate_on_demand, p2m->default_access) ) - rc = -EINVAL; - else - { - p2m->pod.entry_count += 1 << order; /* Lock: p2m */ - p2m->pod.entry_count -= pod_count; - BUG_ON(p2m->pod.entry_count < 0); - } - - audit_p2m(p2m, 1); - p2m_unlock(p2m); - -out: - return rc; -} - int guest_physmap_add_entry(struct p2m_domain *p2m, unsigned long gfn, unsigned long mfn, unsigned int page_order, @@ -2588,7 +390,7 @@ return 0; } - rc = gfn_check_limit(d, gfn, page_order); + rc = p2m_gfn_check_limit(d, gfn, page_order); if ( rc != 0 ) return rc; @@ -2682,142 +484,6 @@ return rc; } -/* Walk the whole p2m table, changing any entries of the old type - * to the new type. This is used in hardware-assisted paging to - * quickly enable or diable log-dirty tracking */ -void p2m_change_type_global(struct p2m_domain *p2m, p2m_type_t ot, p2m_type_t nt) -{ - unsigned long mfn, gfn, flags; - l1_pgentry_t l1e_content; - l1_pgentry_t *l1e; - l2_pgentry_t *l2e; - mfn_t l1mfn, l2mfn, l3mfn; - unsigned long i1, i2, i3; - l3_pgentry_t *l3e; -#if CONFIG_PAGING_LEVELS == 4 - l4_pgentry_t *l4e; - unsigned long i4; -#endif /* CONFIG_PAGING_LEVELS == 4 */ - - BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); - BUG_ON(ot != nt && (ot == p2m_mmio_direct || nt == p2m_mmio_direct)); - - if ( !paging_mode_translate(p2m->domain) ) - return; - - if ( pagetable_get_pfn(p2m_get_pagetable(p2m)) == 0 ) - return; - - ASSERT(p2m_locked_by_me(p2m)); - -#if CONFIG_PAGING_LEVELS == 4 - l4e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); -#else /* CONFIG_PAGING_LEVELS == 3 */ - l3mfn = _mfn(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); - l3e = map_domain_page(mfn_x(pagetable_get_mfn(p2m_get_pagetable(p2m)))); -#endif - -#if CONFIG_PAGING_LEVELS >= 4 - for ( i4 = 0; i4 < L4_PAGETABLE_ENTRIES; i4++ ) - { - if ( !(l4e_get_flags(l4e[i4]) & _PAGE_PRESENT) ) - { - continue; - } - l3mfn = _mfn(l4e_get_pfn(l4e[i4])); - l3e = map_domain_page(l4e_get_pfn(l4e[i4])); -#endif - for ( i3 = 0; - i3 < ((CONFIG_PAGING_LEVELS==4) ? L3_PAGETABLE_ENTRIES : 8); - i3++ ) - { - if ( !(l3e_get_flags(l3e[i3]) & _PAGE_PRESENT) ) - { - continue; - } - if ( (l3e_get_flags(l3e[i3]) & _PAGE_PSE) ) - { - flags = l3e_get_flags(l3e[i3]); - if ( p2m_flags_to_type(flags) != ot ) - continue; - mfn = l3e_get_pfn(l3e[i3]); - gfn = get_gpfn_from_mfn(mfn); - flags = p2m_type_to_flags(nt, _mfn(mfn)); - l1e_content = l1e_from_pfn(mfn, flags | _PAGE_PSE); - p2m->write_p2m_entry(p2m, gfn, - (l1_pgentry_t *)&l3e[i3], - l3mfn, l1e_content, 3); - continue; - } - - l2mfn = _mfn(l3e_get_pfn(l3e[i3])); - l2e = map_domain_page(l3e_get_pfn(l3e[i3])); - for ( i2 = 0; i2 < L2_PAGETABLE_ENTRIES; i2++ ) - { - if ( !(l2e_get_flags(l2e[i2]) & _PAGE_PRESENT) ) - { - continue; - } - - if ( (l2e_get_flags(l2e[i2]) & _PAGE_PSE) ) - { - flags = l2e_get_flags(l2e[i2]); - if ( p2m_flags_to_type(flags) != ot ) - continue; - mfn = l2e_get_pfn(l2e[i2]); - /* Do not use get_gpfn_from_mfn because it may return - SHARED_M2P_ENTRY */ - gfn = (i2 + (i3 -#if CONFIG_PAGING_LEVELS >= 4 - + (i4 * L3_PAGETABLE_ENTRIES) -#endif - ) - * L2_PAGETABLE_ENTRIES) * L1_PAGETABLE_ENTRIES; - flags = p2m_type_to_flags(nt, _mfn(mfn)); - l1e_content = l1e_from_pfn(mfn, flags | _PAGE_PSE); - p2m->write_p2m_entry(p2m, gfn, - (l1_pgentry_t *)&l2e[i2], - l2mfn, l1e_content, 2); - continue; - } - - l1mfn = _mfn(l2e_get_pfn(l2e[i2])); - l1e = map_domain_page(mfn_x(l1mfn)); - - for ( i1 = 0; i1 < L1_PAGETABLE_ENTRIES; i1++, gfn++ ) - { - flags = l1e_get_flags(l1e[i1]); - if ( p2m_flags_to_type(flags) != ot ) - continue; - mfn = l1e_get_pfn(l1e[i1]); - gfn = i1 + (i2 + (i3 -#if CONFIG_PAGING_LEVELS >= 4 - + (i4 * L3_PAGETABLE_ENTRIES) -#endif - ) - * L2_PAGETABLE_ENTRIES) * L1_PAGETABLE_ENTRIES; - /* create a new 1le entry with the new type */ - flags = p2m_type_to_flags(nt, _mfn(mfn)); - l1e_content = l1e_from_pfn(mfn, flags); - p2m->write_p2m_entry(p2m, gfn, &l1e[i1], - l1mfn, l1e_content, 1); - } - unmap_domain_page(l1e); - } - unmap_domain_page(l2e); - } -#if CONFIG_PAGING_LEVELS >= 4 - unmap_domain_page(l3e); - } -#endif - -#if CONFIG_PAGING_LEVELS == 4 - unmap_domain_page(l4e); -#else /* CONFIG_PAGING_LEVELS == 3 */ - unmap_domain_page(l3e); -#endif - -} /* Modify the p2m type of a single gfn from ot to nt, returning the * entry's previous type. Resets the access permissions. */ diff -r d9982136d8fa -r 19452acd2304 xen/include/asm-x86/hvm/vmx/vmx.h --- a/xen/include/asm-x86/hvm/vmx/vmx.h Mon May 09 15:00:57 2011 +0100 +++ b/xen/include/asm-x86/hvm/vmx/vmx.h Fri May 06 11:15:35 2011 +0100 @@ -399,7 +399,7 @@ void vmx_inject_extint(int trap); void vmx_inject_nmi(void); -void ept_p2m_init(struct domain *d); +void ept_p2m_init(struct p2m_domain *p2m); void ept_walk_table(struct domain *d, unsigned long gfn); void setup_ept_dump(void); diff -r d9982136d8fa -r 19452acd2304 xen/include/asm-x86/p2m.h --- a/xen/include/asm-x86/p2m.h Mon May 09 15:00:57 2011 +0100 +++ b/xen/include/asm-x86/p2m.h Fri May 06 11:15:35 2011 +0100 @@ -638,6 +638,34 @@ struct page_info *p2m_alloc_ptp(struct p2m_domain *p2m, unsigned long type); void p2m_free_ptp(struct p2m_domain *p2m, struct page_info *pg); +#if CONFIG_PAGING_LEVELS == 3 +static inline int p2m_gfn_check_limit( + struct domain *d, unsigned long gfn, unsigned int order) +{ + /* + * 32bit AMD nested paging does not support over 4GB guest due to + * hardware translation limit. This limitation is checked by comparing + * gfn with 0xfffffUL. + */ + if ( !hap_enabled(d) || ((gfn + (1ul << order)) <= 0x100000UL) || + (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) ) + return 0; + + if ( !test_and_set_bool(d->arch.hvm_domain.svm.npt_4gb_warning) ) + dprintk(XENLOG_WARNING, "Dom%d failed to populate memory beyond" + " 4GB: specify 'hap=0' domain config option.\n", + d->domain_id); + + return -EINVAL; +} +#else +#define p2m_gfn_check_limit(d, g, o) 0 +#endif + +/* Directly set a p2m entry: only for use by p2m code */ +int set_p2m_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, + unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma); + #endif /* _XEN_P2M_H */ /* _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |