p2m.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,188 行 · 第 1/3 页

C
1,188
字号
/****************************************************************************** * arch/x86/mm/p2m.c * * physical-to-machine mappings for automatically-translated domains. * * 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 <asm/hvm/vmx/vmx.h> /* ept_p2m_init() */#include <xen/iommu.h>/* Debugging and auditing of the P2M code? */#define P2M_AUDIT     0#define P2M_DEBUGGING 0/* * The P2M lock.  This protects all updates to the p2m table. * Updates are expected to be safe against concurrent reads, * which do *not* require the lock. * * Locking discipline: always acquire this lock before the shadow or HAP one */#define p2m_lock_init(_p2m)                     \    do {                                        \        spin_lock_init(&(_p2m)->lock);          \        (_p2m)->locker = -1;                    \        (_p2m)->locker_function = "nobody";     \    } while (0)#define p2m_lock(_p2m)                                          \    do {                                                        \        if ( unlikely((_p2m)->locker == current->processor) )   \        {                                                       \            printk("Error: p2m lock held by %s\n",              \                   (_p2m)->locker_function);                    \            BUG();                                              \        }                                                       \        spin_lock(&(_p2m)->lock);                               \        ASSERT((_p2m)->locker == -1);                           \        (_p2m)->locker = current->processor;                    \        (_p2m)->locker_function = __func__;                     \    } while (0)#define p2m_unlock(_p2m)                                \    do {                                                \        ASSERT((_p2m)->locker == current->processor);   \        (_p2m)->locker = -1;                            \        (_p2m)->locker_function = "nobody";             \        spin_unlock(&(_p2m)->lock);                     \    } while (0)#define p2m_locked_by_me(_p2m)                            \    (current->processor == (_p2m)->locker)/* 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) (frame_table + mfn_x(_m))#undef mfn_valid#define mfn_valid(_mfn) (mfn_x(_mfn) < max_page)#undef page_to_mfn#define page_to_mfn(_pg) (_mfn((_pg) - frame_table))/* PTE flags for the various types of p2m entry */#define P2M_BASE_FLAGS \        (_PAGE_PRESENT | _PAGE_USER | _PAGE_DIRTY | _PAGE_ACCESSED)static unsigned long p2m_type_to_flags(p2m_type_t t) {    unsigned long flags = (t & 0x7UL) << 9;    switch(t)    {    case p2m_invalid:    default:        return flags;    case p2m_ram_rw:        return flags | P2M_BASE_FLAGS | _PAGE_RW;    case p2m_ram_logdirty:        return flags | P2M_BASE_FLAGS;    case p2m_ram_ro:        return flags | P2M_BASE_FLAGS;    case p2m_mmio_dm:        return flags;    case p2m_mmio_direct:        return flags | P2M_BASE_FLAGS | _PAGE_RW | _PAGE_PCD;    }}// Find the next level's P2M entry, checking for out-of-range gfn's...// Returns NULL on error.//static l1_pgentry_t *p2m_find_entry(void *table, unsigned long *gfn_remainder,                   unsigned long gfn, u32 shift, u32 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;}// Walk one level of the P2M table, allocating a new table if required.// Returns 0 on error.//static intp2m_next_level(struct domain *d, 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;    ASSERT(d->arch.p2m->alloc_page);    if ( !(p2m_entry = p2m_find_entry(*table, gfn_remainder, gfn,                                      shift, max)) )        return 0;    if ( !(l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) )    {        struct page_info *pg = d->arch.p2m->alloc_page(d);        if ( pg == NULL )            return 0;        list_add_tail(&pg->list, &d->arch.p2m->pages);        pg->u.inuse.type_info = type | 1 | PGT_validated;        pg->count_info = 1;        new_entry = l1e_from_pfn(mfn_x(page_to_mfn(pg)),                                 __PAGE_HYPERVISOR|_PAGE_USER);        switch ( type ) {        case PGT_l3_page_table:            paging_write_p2m_entry(d, 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            paging_write_p2m_entry(d, gfn,                                   p2m_entry, *table_mfn, new_entry, 3);            break;        case PGT_l1_page_table:            paging_write_p2m_entry(d, gfn,                                   p2m_entry, *table_mfn, new_entry, 2);            break;        default:            BUG();            break;        }    }    ASSERT(l1e_get_flags(*p2m_entry) & _PAGE_PRESENT);    /* split single 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 = d->arch.p2m->alloc_page(d);        if ( pg == NULL )            return 0;        list_add_tail(&pg->list, &d->arch.p2m->pages);        pg->u.inuse.type_info = PGT_l1_page_table | 1 | PGT_validated;        pg->count_info = 1;                /* 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(mfn_x(page_to_mfn(pg)));        for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ )        {            new_entry = l1e_from_pfn(pfn + i, flags);            paging_write_p2m_entry(d, 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);        paging_write_p2m_entry(d, 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 intp2m_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,               unsigned int page_order, p2m_type_t p2mt){    // XXX -- this might be able to be faster iff current->domain == d    mfn_t table_mfn = pagetable_get_mfn(d->arch.phys_table);    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;    int rv=0;#if CONFIG_PAGING_LEVELS >= 4    if ( !p2m_next_level(d, &table_mfn, &table, &gfn_remainder, gfn,                         L4_PAGETABLE_SHIFT - PAGE_SHIFT,                         L4_PAGETABLE_ENTRIES, PGT_l3_page_table) )        goto out;#endif    /*     * 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.     */    if ( !p2m_next_level(d, &table_mfn, &table, &gfn_remainder, gfn,                         L3_PAGETABLE_SHIFT - PAGE_SHIFT,                         ((CONFIG_PAGING_LEVELS == 3)                          ? (d->arch.hvm_domain.hap_enabled ? 4 : 8)                          : L3_PAGETABLE_ENTRIES),                         PGT_l2_page_table) )        goto out;    if ( page_order == 0 )    {        if ( !p2m_next_level(d, &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));        else            entry_content = l1e_empty();                /* level 1 entry */        paging_write_p2m_entry(d, gfn, p2m_entry, table_mfn, entry_content, 1);    }    else     {        p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn,                                   L2_PAGETABLE_SHIFT - PAGE_SHIFT,                                   L2_PAGETABLE_ENTRIES);        ASSERT(p2m_entry);                if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) &&             !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) )        {            P2M_ERROR("configure P2M table 4KB L2 entry with large page\n");            domain_crash(d);            goto out;        }                if ( mfn_valid(mfn) )            l2e_content = l2e_from_pfn(mfn_x(mfn),                                       p2m_type_to_flags(p2mt) | _PAGE_PSE);        else            l2e_content = l2e_empty();                entry_content.l1 = l2e_content.l2;        paging_write_p2m_entry(d, gfn, p2m_entry, table_mfn, entry_content, 2);    }    /* Track the highest gfn for which we have ever had a valid mapping */    if ( mfn_valid(mfn) && (gfn > d->arch.p2m->max_mapped_pfn) )        d->arch.p2m->max_mapped_pfn = gfn + (1UL << page_order) - 1;    if ( iommu_enabled && (is_hvm_domain(d) || need_iommu(d)) )    {        if ( p2mt == p2m_ram_rw )            for ( i = 0; i < (1UL << page_order); i++ )                iommu_map_page(d, gfn+i, mfn_x(mfn)+i );        else            for ( int i = 0; i < (1UL << page_order); i++ )                iommu_unmap_page(d, gfn+i);    }    /* Success */    rv = 1; out:    unmap_domain_page(table);    return rv;}static mfn_tp2m_gfn_to_mfn(struct domain *d, unsigned long gfn, p2m_type_t *t){    mfn_t mfn;    paddr_t addr = ((paddr_t)gfn) << PAGE_SHIFT;    l2_pgentry_t *l2e;    l1_pgentry_t *l1e;    ASSERT(paging_mode_translate(d));    /* 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;    mfn = pagetable_get_mfn(d->arch.phys_table);    if ( gfn > d->arch.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        if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 )        {            unmap_domain_page(l3e);            return _mfn(INVALID_MFN);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?