/* * arch/x86/mm/hap/guest_walk.c * * Guest page table walker * Copyright (c) 2007, AMD Corporation (Wei Huang) * Copyright (c) 2007, XenSource Inc. * * 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, see . */ /* Allow uniquely identifying static symbols in the 3 generated objects. */ asm(".file \"" __OBJECT_FILE__ "\""); #include #include #include #include "private.h" /* for hap_gva_to_gfn_* */ #define _hap_gva_to_gfn(levels) hap_gva_to_gfn_##levels##_levels #define hap_gva_to_gfn(levels) _hap_gva_to_gfn(levels) #define _hap_p2m_ga_to_gfn(levels) hap_p2m_ga_to_gfn_##levels##_levels #define hap_p2m_ga_to_gfn(levels) _hap_p2m_ga_to_gfn(levels) #if GUEST_PAGING_LEVELS > CONFIG_PAGING_LEVELS #error GUEST_PAGING_LEVELS must not exceed CONFIG_PAGING_LEVELS #endif #include #include unsigned long hap_gva_to_gfn(GUEST_PAGING_LEVELS)( struct vcpu *v, struct p2m_domain *p2m, unsigned long gva, uint32_t *pfec) { unsigned long cr3 = v->arch.hvm_vcpu.guest_cr[3]; return hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)(v, p2m, cr3, gva, pfec, NULL); } unsigned long hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)( struct vcpu *v, struct p2m_domain *p2m, unsigned long cr3, paddr_t ga, uint32_t *pfec, unsigned int *page_order) { bool walk_ok; mfn_t top_mfn; void *top_map; p2m_type_t p2mt; walk_t gw; gfn_t top_gfn; struct page_info *top_page; /* Get the top-level table's MFN */ top_gfn = _gfn(cr3 >> PAGE_SHIFT); top_page = p2m_get_page_from_gfn(p2m, top_gfn, &p2mt, NULL, P2M_ALLOC | P2M_UNSHARE); if ( p2m_is_paging(p2mt) ) { ASSERT(p2m_is_hostp2m(p2m)); *pfec = PFEC_page_paged; if ( top_page ) put_page(top_page); p2m_mem_paging_populate(p2m->domain, cr3 >> PAGE_SHIFT); return gfn_x(INVALID_GFN); } if ( p2m_is_shared(p2mt) ) { *pfec = PFEC_page_shared; if ( top_page ) put_page(top_page); return gfn_x(INVALID_GFN); } if ( !top_page ) { *pfec &= ~PFEC_page_present; goto out_tweak_pfec; } top_mfn = _mfn(page_to_mfn(top_page)); /* Map the top-level table and call the tree-walker */ ASSERT(mfn_valid(top_mfn)); top_map = map_domain_page(top_mfn); #if GUEST_PAGING_LEVELS == 3 top_map += (cr3 & ~(PAGE_MASK | 31)); #endif walk_ok = guest_walk_tables(v, p2m, ga, &gw, *pfec, top_mfn, top_map); unmap_domain_page(top_map); put_page(top_page); /* Interpret the answer */ if ( walk_ok ) { gfn_t gfn = guest_walk_to_gfn(&gw); struct page_info *page; page = p2m_get_page_from_gfn(p2m, gfn, &p2mt, NULL, P2M_ALLOC | P2M_UNSHARE); if ( page ) put_page(page); if ( p2m_is_paging(p2mt) ) { ASSERT(p2m_is_hostp2m(p2m)); *pfec = PFEC_page_paged; p2m_mem_paging_populate(p2m->domain, gfn_x(gfn)); return gfn_x(INVALID_GFN); } if ( p2m_is_shared(p2mt) ) { *pfec = PFEC_page_shared; return gfn_x(INVALID_GFN); } if ( page_order ) *page_order = guest_walk_to_page_order(&gw); return gfn_x(gfn); } *pfec = gw.pfec; out_tweak_pfec: /* * SDM Intel 64 Volume 3, Chapter Paging, PAGE-FAULT EXCEPTIONS: * The PFEC_insn_fetch flag is set only when NX or SMEP are enabled. */ if ( !hvm_nx_enabled(v) && !hvm_smep_enabled(v) ) *pfec &= ~PFEC_insn_fetch; return gfn_x(INVALID_GFN); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */