p2m-ept.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 507 行
C
507 行
/* * 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 <xen/iommu.h>#include <asm/mtrr.h>#include <asm/hvm/cacheattr.h>static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type){ switch(type) { case p2m_invalid: case p2m_mmio_dm: default: return; case p2m_ram_rw: case p2m_mmio_direct: entry->r = entry->w = entry->x = 1; return; case p2m_ram_logdirty: case p2m_ram_ro: entry->r = entry->x = 1; entry->w = 0; return; }}#define GUEST_TABLE_NORMAL_PAGE 1#define GUEST_TABLE_SUPER_PAGE 2#define GUEST_TABLE_SPLIT_PAGE 3static int ept_set_middle_entry(struct domain *d, ept_entry_t *ept_entry){ struct page_info *pg; pg = d->arch.p2m->alloc_page(d); if ( pg == NULL ) return 0; pg->count_info = 1; pg->u.inuse.type_info = 1 | PGT_validated; list_add_tail(&pg->list, &d->arch.p2m->pages); ept_entry->emt = 0; ept_entry->sp_avail = 0; ept_entry->avail1 = 0; ept_entry->mfn = page_to_mfn(pg); ept_entry->rsvd = 0; ept_entry->avail2 = 0; /* last step */ ept_entry->r = ept_entry->w = ept_entry->x = 1; return 1;}static int ept_next_level(struct domain *d, bool_t read_only, ept_entry_t **table, unsigned long *gfn_remainder, u32 shift, int order){ ept_entry_t *ept_entry, *next; u32 index; index = *gfn_remainder >> shift; ept_entry = (*table) + index; if ( !(ept_entry->epte & 0x7) ) { if ( read_only ) return 0; if ( !ept_set_middle_entry(d, ept_entry) ) return 0; } if ( !ept_entry->sp_avail ) { *gfn_remainder &= (1UL << shift) - 1; next = map_domain_page(ept_entry->mfn); unmap_domain_page(*table); *table = next; return GUEST_TABLE_NORMAL_PAGE; } else { if ( order == shift || read_only ) return GUEST_TABLE_SUPER_PAGE; else return GUEST_TABLE_SPLIT_PAGE; }}static intept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn, unsigned int order, p2m_type_t p2mt){ ept_entry_t *table = NULL; unsigned long gfn_remainder = gfn, offset = 0; ept_entry_t *ept_entry = NULL; u32 index; int i, rv = 0, ret = 0; int walk_level = order / EPT_TABLE_ORDER; /* we only support 4k and 2m pages now */ BUG_ON(order && order != EPT_TABLE_ORDER); if ( order != 0 ) if ( (gfn & ((1UL << order) - 1)) ) return 1; table = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table))); ASSERT(table != NULL); for ( i = EPT_DEFAULT_GAW; i > walk_level; i-- ) { ret = ept_next_level(d, 0, &table, &gfn_remainder, i * EPT_TABLE_ORDER, order); if ( !ret ) goto out; else if ( ret != GUEST_TABLE_NORMAL_PAGE ) break; } index = gfn_remainder >> ( i ? (i * EPT_TABLE_ORDER): order); walk_level = ( i ? ( i * EPT_TABLE_ORDER) : order) / EPT_TABLE_ORDER; offset = (gfn_remainder & ( ((1 << (i*EPT_TABLE_ORDER)) - 1))); ept_entry = table + index; if ( ret != GUEST_TABLE_SPLIT_PAGE ) { if ( mfn_valid(mfn_x(mfn)) || (p2mt == p2m_mmio_direct) ) { /* Track the highest gfn for which we have ever had a valid mapping */ if ( gfn > d->arch.p2m->max_mapped_pfn ) d->arch.p2m->max_mapped_pfn = gfn; ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn)); ept_entry->sp_avail = walk_level ? 1 : 0; if ( ret == GUEST_TABLE_SUPER_PAGE ) { ept_entry->mfn = mfn_x(mfn) - offset; if ( ept_entry->avail1 == p2m_ram_logdirty && p2mt == p2m_ram_rw ) for ( i = 0; i < 512; i++ ) paging_mark_dirty(d, mfn_x(mfn)-offset+i); } else ept_entry->mfn = mfn_x(mfn); ept_entry->avail1 = p2mt; ept_entry->rsvd = 0; ept_entry->avail2 = 0; /* last step */ ept_entry->r = ept_entry->w = ept_entry->x = 1; ept_p2m_type_to_flags(ept_entry, p2mt); } else ept_entry->epte = 0; } else { /* It's super page before, now set one of the 4k pages, so * we should split the 2m page to 4k pages now. */ ept_entry_t *split_table = NULL; ept_entry_t *split_ept_entry = NULL; unsigned long split_mfn = ept_entry->mfn; p2m_type_t split_p2mt = ept_entry->avail1; /* alloc new page for new ept middle level entry which is * before a leaf super entry */ if ( !ept_set_middle_entry(d, ept_entry) ) goto out; /* split the super page before to 4k pages */ split_table = map_domain_page(ept_entry->mfn); offset = gfn & ((1 << EPT_TABLE_ORDER) - 1); for ( i = 0; i < 512; i++ ) { split_ept_entry = split_table + i; split_ept_entry->emt = epte_get_entry_emt(d, gfn-offset+i, split_mfn+i); split_ept_entry->sp_avail = 0; split_ept_entry->mfn = split_mfn+i; split_ept_entry->avail1 = split_p2mt; split_ept_entry->rsvd = 0; split_ept_entry->avail2 = 0; /* last step */ split_ept_entry->r = split_ept_entry->w = split_ept_entry->x = 1; ept_p2m_type_to_flags(split_ept_entry, split_p2mt); } /* Set the destinated 4k page as normal */ split_ept_entry = split_table + offset; split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn)); split_ept_entry->mfn = mfn_x(mfn); split_ept_entry->avail1 = p2mt; ept_p2m_type_to_flags(split_ept_entry, p2mt); unmap_domain_page(split_table); } /* Success */ rv = 1;out: unmap_domain_page(table); ept_sync_domain(d); /* Now the p2m table is not shared with vt-d page table */ if ( iommu_enabled && is_hvm_domain(d) ) { if ( p2mt == p2m_ram_rw ) { if ( order == EPT_TABLE_ORDER ) { for ( i = 0; i < ( 1 << order ); i++ ) iommu_map_page(d, gfn-offset+i, mfn_x(mfn)-offset+i); } else if ( !order ) iommu_map_page(d, gfn, mfn_x(mfn)); } else { if ( order == EPT_TABLE_ORDER ) { for ( i = 0; i < ( 1 << order ); i++ ) iommu_unmap_page(d, gfn-offset+i); } else if ( !order ) iommu_unmap_page(d, gfn); } } return rv;}/* Read ept p2m entries */static mfn_t ept_get_entry(struct domain *d, unsigned long gfn, p2m_type_t *t){ ept_entry_t *table = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table))); unsigned long gfn_remainder = gfn; ept_entry_t *ept_entry; u32 index; int i, ret=0; mfn_t mfn = _mfn(INVALID_MFN); *t = p2m_mmio_dm; /* This pfn is higher than the highest the p2m map currently holds */ if ( gfn > d->arch.p2m->max_mapped_pfn ) goto out; /* Should check if gfn obeys GAW here. */ for ( i = EPT_DEFAULT_GAW; i > 0; i-- ) { ret = ept_next_level(d, 1, &table, &gfn_remainder, i * EPT_TABLE_ORDER, 0); if ( !ret ) goto out; else if ( ret == GUEST_TABLE_SUPER_PAGE ) break; } index = gfn_remainder >> ( i * EPT_TABLE_ORDER); ept_entry = table + index; if ( ept_entry->avail1 != p2m_invalid ) { *t = ept_entry->avail1; 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;}static uint64_t ept_get_entry_content(struct domain *d, unsigned long gfn){ ept_entry_t *table = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table))); unsigned long gfn_remainder = gfn; ept_entry_t *ept_entry; uint64_t content = 0; u32 index; int i, ret=0; /* This pfn is higher than the highest the p2m map currently holds */ if ( gfn > d->arch.p2m->max_mapped_pfn ) goto out; for ( i = EPT_DEFAULT_GAW; i > 0; i-- ) { ret = ept_next_level(d, 1, &table, &gfn_remainder, i * EPT_TABLE_ORDER, 0); if ( !ret ) goto out; else if ( ret == GUEST_TABLE_SUPER_PAGE ) break; } index = gfn_remainder >> ( i * EPT_TABLE_ORDER); ept_entry = table + index; content = ept_entry->epte; out: unmap_domain_page(table); return content;}static mfn_t ept_get_entry_current(unsigned long gfn, p2m_type_t *t){ return ept_get_entry(current->domain, gfn, t);}void ept_change_entry_emt_with_range(struct domain *d, unsigned long start_gfn, unsigned long end_gfn){ unsigned long gfn; p2m_type_t p2mt; uint64_t epte; int order = 0; unsigned long mfn; for ( gfn = start_gfn; gfn <= end_gfn; gfn++ ) { epte = ept_get_entry_content(d, gfn); if ( epte == 0 ) continue; mfn = (epte & EPTE_MFN_MASK) >> PAGE_SHIFT; if ( !mfn_valid(mfn) ) continue; p2mt = (epte & EPTE_AVAIL1_MASK) >> 8; order = 0; if ( epte & EPTE_SUPER_PAGE_MASK ) { if ( !(gfn & ( (1 << EPT_TABLE_ORDER) - 1)) && ((gfn + 0x1FF) <= end_gfn) ) { /* gfn assigned with 2M, and the end covers more than 2m areas. * Set emt for super page. */ order = EPT_TABLE_ORDER; ept_set_entry(d, gfn, _mfn(mfn), order, p2mt); gfn += 0x1FF; } else { /* change emt for partial entries of the 2m area */ ept_set_entry(d, gfn, _mfn(mfn), order, p2mt); gfn = ((gfn >> EPT_TABLE_ORDER) << EPT_TABLE_ORDER) + 0x1FF; } } else /* gfn assigned with 4k */ ept_set_entry(d, gfn, _mfn(mfn), order, p2mt); }}/* 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_global(struct domain *d, p2m_type_t ot, p2m_type_t nt){ ept_entry_t *l4e, *l3e, *l2e, *l1e; int i4, i3, i2, i1; if ( pagetable_get_pfn(d->arch.phys_table) == 0 ) return; BUG_ON(EPT_DEFAULT_GAW != 3); l4e = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table))); for (i4 = 0; i4 < EPT_PAGETABLE_ENTRIES; i4++ ) { if ( !l4e[i4].epte ) continue; if ( !l4e[i4].sp_avail ) { l3e = map_domain_page(l4e[i4].mfn); for ( i3 = 0; i3 < EPT_PAGETABLE_ENTRIES; i3++ ) { if ( !l3e[i3].epte ) continue; if ( !l3e[i3].sp_avail ) { l2e = map_domain_page(l3e[i3].mfn); for ( i2 = 0; i2 < EPT_PAGETABLE_ENTRIES; i2++ ) { if ( !l2e[i2].epte ) continue; if ( !l2e[i2].sp_avail ) { l1e = map_domain_page(l2e[i2].mfn); for ( i1 = 0; i1 < EPT_PAGETABLE_ENTRIES; i1++ ) { if ( !l1e[i1].epte ) continue; if ( l1e[i1].avail1 != ot ) continue; l1e[i1].avail1 = nt; ept_p2m_type_to_flags(l1e+i1, nt); } unmap_domain_page(l1e); } else { if ( l2e[i2].avail1 != ot ) continue; l2e[i2].avail1 = nt; ept_p2m_type_to_flags(l2e+i2, nt); } } unmap_domain_page(l2e); } else { if ( l3e[i3].avail1 != ot ) continue; l3e[i3].avail1 = nt; ept_p2m_type_to_flags(l3e+i3, nt); } } unmap_domain_page(l3e); } else { if ( l4e[i4].avail1 != ot ) continue; l4e[i4].avail1 = nt; ept_p2m_type_to_flags(l4e+i4, nt); } } unmap_domain_page(l4e); ept_sync_domain(d);}void ept_p2m_init(struct domain *d){ d->arch.p2m->set_entry = ept_set_entry; d->arch.p2m->get_entry = ept_get_entry; d->arch.p2m->get_entry_current = ept_get_entry_current; d->arch.p2m->change_entry_type_global = ept_change_entry_type_global;}/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?