common.c

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

C
1,973
字号
    SHADOW_ERROR("gmfn %lx was OOS but not in hash table\n", mfn_x(gmfn));    BUG();}mfn_t oos_snapshot_lookup(struct vcpu *v, mfn_t gmfn){    int idx;    mfn_t *oos;    mfn_t *oos_snapshot;    struct domain *d = v->domain;        for_each_vcpu(d, v)     {        oos = v->arch.paging.shadow.oos;        oos_snapshot = v->arch.paging.shadow.oos_snapshot;        idx = mfn_x(gmfn) % SHADOW_OOS_PAGES;        if ( mfn_x(oos[idx]) != mfn_x(gmfn) )            idx = (idx + 1) % SHADOW_OOS_PAGES;        if ( mfn_x(oos[idx]) == mfn_x(gmfn) )        {            return oos_snapshot[idx];        }    }    SHADOW_ERROR("gmfn %lx was OOS but not in hash table\n", mfn_x(gmfn));    BUG();    return _mfn(INVALID_MFN);}/* Pull a single guest page back into sync */void sh_resync(struct vcpu *v, mfn_t gmfn){    int idx;    mfn_t *oos;    mfn_t *oos_snapshot;    struct oos_fixup *oos_fixup;    struct domain *d = v->domain;    for_each_vcpu(d, v)     {        oos = v->arch.paging.shadow.oos;        oos_fixup = v->arch.paging.shadow.oos_fixup;        oos_snapshot = v->arch.paging.shadow.oos_snapshot;        idx = mfn_x(gmfn) % SHADOW_OOS_PAGES;        if ( mfn_x(oos[idx]) != mfn_x(gmfn) )            idx = (idx + 1) % SHADOW_OOS_PAGES;                if ( mfn_x(oos[idx]) == mfn_x(gmfn) )        {            _sh_resync(v, gmfn, &oos_fixup[idx], oos_snapshot[idx]);            oos[idx] = _mfn(INVALID_MFN);            return;        }    }    SHADOW_ERROR("gmfn %lx was OOS but not in hash table\n", mfn_x(gmfn));    BUG();}/* Figure out whether it's definitely safe not to sync this l1 table, * by making a call out to the mode in which that shadow was made. */static int sh_skip_sync(struct vcpu *v, mfn_t gl1mfn){    struct page_info *pg = mfn_to_page(gl1mfn);    if ( pg->shadow_flags & SHF_L1_32 )        return SHADOW_INTERNAL_NAME(sh_safe_not_to_sync, 2)(v, gl1mfn);    else if ( pg->shadow_flags & SHF_L1_PAE )        return SHADOW_INTERNAL_NAME(sh_safe_not_to_sync, 3)(v, gl1mfn);#if CONFIG_PAGING_LEVELS >= 4    else if ( pg->shadow_flags & SHF_L1_64 )        return SHADOW_INTERNAL_NAME(sh_safe_not_to_sync, 4)(v, gl1mfn);#endif    SHADOW_ERROR("gmfn 0x%lx was OOS but not shadowed as an l1.\n",                  mfn_x(gl1mfn));    BUG();    return 0; /* BUG() is no longer __attribute__((noreturn)). */}/* Pull all out-of-sync pages back into sync.  Pages brought out of sync * on other vcpus are allowed to remain out of sync, but their contents * will be made safe (TLB flush semantics); pages unsynced by this vcpu * are brought back into sync and write-protected.  If skip != 0, we try * to avoid resyncing at all if we think we can get away with it. */void sh_resync_all(struct vcpu *v, int skip, int this, int others, int do_locking){    int idx;    struct vcpu *other;    mfn_t *oos = v->arch.paging.shadow.oos;    mfn_t *oos_snapshot = v->arch.paging.shadow.oos_snapshot;    struct oos_fixup *oos_fixup = v->arch.paging.shadow.oos_fixup;    SHADOW_PRINTK("d=%d, v=%d\n", v->domain->domain_id, v->vcpu_id);    ASSERT(do_locking || shadow_locked_by_me(v->domain));    if ( !this )        goto resync_others;    if ( do_locking )        shadow_lock(v->domain);    /* First: resync all of this vcpu's oos pages */    for ( idx = 0; idx < SHADOW_OOS_PAGES; idx++ )         if ( mfn_valid(oos[idx]) )        {            /* Write-protect and sync contents */            _sh_resync(v, oos[idx], &oos_fixup[idx], oos_snapshot[idx]);            oos[idx] = _mfn(INVALID_MFN);        }    if ( do_locking )        shadow_unlock(v->domain); resync_others:    if ( !others )        return;    /* Second: make all *other* vcpus' oos pages safe. */    for_each_vcpu(v->domain, other)    {        if ( v == other )             continue;        if ( do_locking )            shadow_lock(v->domain);        oos = other->arch.paging.shadow.oos;        oos_fixup = other->arch.paging.shadow.oos_fixup;        oos_snapshot = other->arch.paging.shadow.oos_snapshot;        for ( idx = 0; idx < SHADOW_OOS_PAGES; idx++ )         {            if ( !mfn_valid(oos[idx]) )                continue;            if ( skip )            {                /* Update the shadows and leave the page OOS. */                if ( sh_skip_sync(v, oos[idx]) )                    continue;                _sh_resync_l1(other, oos[idx], oos_snapshot[idx]);            }            else            {                /* Write-protect and sync contents */                _sh_resync(other, oos[idx], &oos_fixup[idx], oos_snapshot[idx]);                oos[idx] = _mfn(INVALID_MFN);            }        }                if ( do_locking )            shadow_unlock(v->domain);    }}/* Allow a shadowed page to go out of sync */int sh_unsync(struct vcpu *v, mfn_t gmfn){    struct page_info *pg;        ASSERT(shadow_locked_by_me(v->domain));    SHADOW_PRINTK("d=%d, v=%d, gmfn=%05lx va %lx\n",                  v->domain->domain_id, v->vcpu_id, mfn_x(gmfn), va);    pg = mfn_to_page(gmfn);     /* Guest page must be shadowed *only* as L1 and *only* once when out     * of sync.  Also, get out now if it's already out of sync.      * Also, can't safely unsync if some vcpus have paging disabled.*/    if ( pg->shadow_flags &          ((SHF_page_type_mask & ~SHF_L1_ANY) | SHF_out_of_sync)          || sh_page_has_multiple_shadows(pg)         || !is_hvm_domain(v->domain)         || !v->domain->arch.paging.shadow.oos_active )        return 0;    pg->shadow_flags |= SHF_out_of_sync|SHF_oos_may_write;    oos_hash_add(v, gmfn);    perfc_incr(shadow_unsync);    return 1;}#endif /* (SHADOW_OPTIMIZATIONS & SHOPT_OUT_OF_SYNC) *//**************************************************************************//* Code for "promoting" a guest page to the point where the shadow code is * willing to let it be treated as a guest page table.  This generally * involves making sure there are no writable mappings available to the guest * for this page. */void shadow_promote(struct vcpu *v, mfn_t gmfn, unsigned int type){    struct page_info *page = mfn_to_page(gmfn);    ASSERT(mfn_valid(gmfn));#if (SHADOW_OPTIMIZATIONS & SHOPT_OUT_OF_SYNC)     /* Is the page already shadowed and out of sync? */    if ( page_is_out_of_sync(page) )         sh_resync(v, gmfn);#endif    /* We should never try to promote a gmfn that has writeable mappings */    ASSERT((page->u.inuse.type_info & PGT_type_mask) != PGT_writable_page           || (page->u.inuse.type_info & PGT_count_mask) == 0           || v->domain->is_shutting_down);    /* Is the page already shadowed? */    if ( !test_and_set_bit(_PGC_page_table, &page->count_info) )        page->shadow_flags = 0;    ASSERT(!test_bit(type, &page->shadow_flags));    set_bit(type, &page->shadow_flags);}void shadow_demote(struct vcpu *v, mfn_t gmfn, u32 type){    struct page_info *page = mfn_to_page(gmfn);    ASSERT(test_bit(_PGC_page_table, &page->count_info));    ASSERT(test_bit(type, &page->shadow_flags));    clear_bit(type, &page->shadow_flags);    if ( (page->shadow_flags & SHF_page_type_mask) == 0 )    {#if (SHADOW_OPTIMIZATIONS & SHOPT_OUT_OF_SYNC)         /* Was the page out of sync? */        if ( page_is_out_of_sync(page) )         {            oos_hash_remove(v, gmfn);        }#endif         clear_bit(_PGC_page_table, &page->count_info);    }}/**************************************************************************//* Validate a pagetable change from the guest and update the shadows. * Returns a bitmask of SHADOW_SET_* flags. */intsh_validate_guest_entry(struct vcpu *v, mfn_t gmfn, void *entry, u32 size){    int result = 0;    struct page_info *page = mfn_to_page(gmfn);    paging_mark_dirty(v->domain, mfn_x(gmfn));        // Determine which types of shadows are affected, and update each.    //    // Always validate L1s before L2s to prevent another cpu with a linear    // mapping of this gmfn from seeing a walk that results from     // using the new L2 value and the old L1 value.  (It is OK for such a    // guest to see a walk that uses the old L2 value with the new L1 value,    // as hardware could behave this way if one level of the pagewalk occurs    // before the store, and the next level of the pagewalk occurs after the    // store.    //    // Ditto for L2s before L3s, etc.    //    if ( !(page->count_info & PGC_page_table) )        return 0;  /* Not shadowed at all */    if ( page->shadow_flags & SHF_L1_32 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl1e, 2)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L2_32 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl2e, 2)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L1_PAE )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl1e, 3)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L2_PAE )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl2e, 3)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L2H_PAE )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl2he, 3)            (v, gmfn, entry, size);#if CONFIG_PAGING_LEVELS >= 4     if ( page->shadow_flags & SHF_L1_64 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl1e, 4)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L2_64 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl2e, 4)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L2H_64 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl2he, 4)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L3_64 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl3e, 4)            (v, gmfn, entry, size);    if ( page->shadow_flags & SHF_L4_64 )         result |= SHADOW_INTERNAL_NAME(sh_map_and_validate_gl4e, 4)            (v, gmfn, entry, size);#else /* 32-bit hypervisor does not support 64-bit guests */    ASSERT((page->shadow_flags             & (SHF_L4_64|SHF_L3_64|SHF_L2H_64|SHF_L2_64|SHF_L1_64)) == 0);#endif    return result;}voidsh_validate_guest_pt_write(struct vcpu *v, mfn_t gmfn,                           void *entry, u32 size)/* This is the entry point for emulated writes to pagetables in HVM guests and * PV translated guests. */{    struct domain *d = v->domain;    int rc;    ASSERT(shadow_locked_by_me(v->domain));    rc = sh_validate_guest_entry(v, gmfn, entry, size);    if ( rc & SHADOW_SET_FLUSH )        /* Need to flush TLBs to pick up shadow PT changes */        flush_tlb_mask(d->domain_dirty_cpumask);    if ( rc & SHADOW_SET_ERROR )     {        /* This page is probably not a pagetable any more: tear it out of the          * shadows, along with any tables that reference it.           * Since the validate call above will have made a "safe" (i.e. zero)          * shadow entry, we can let the domain live even if we can't fully          * unshadow the page. */        sh_remove_shadows(v, gmfn, 0, 0);    }}int shadow_write_guest_entry(struct vcpu *v, intpte_t *p,                             intpte_t new, mfn_t gmfn)/* Write a new value into the guest pagetable, and update the shadows  * appropriately.  Returns 0 if we page-faulted, 1 for success. */{    int failed;    shadow_lock(v->domain);    failed = __copy_to_user(p, &new, sizeof(new));    if ( failed != sizeof(new) )        sh_validate_guest_entry(v, gmfn, p, sizeof(new));    shadow_unlock(v->domain);    return (failed == 0);}int shadow_cmpxchg_guest_entry(struct vcpu *v, intpte_t *p,                               intpte_t *old, intpte_t new, mfn_t gmfn)/* Cmpxchg a new value into the guest pagetable, and update the shadows  * appropriately. Returns 0 if we page-faulted, 1 if not. * N.B. caller should check the value of "old" to see if the * cmpxchg itself was successful. */{    int failed;    intpte_t t = *old;    shadow_lock(v->domain);    failed = cmpxchg_user(p, t, new);    if ( t == *old )        sh_validate_guest_entry(v, gmfn, p, sizeof(new));    *old = t;    shadow_unlock(v->domain);    return (failed == 0);}/**************************************************************************//* Memory management for shadow pages. */ /* Allocating shadow pages * ----------------------- * * Most shadow pages are allocated singly, but there is one case where * we need to allocate multiple pages together: shadowing 32-bit guest * tables on PAE or 64-bit shadows.  A 32-bit guest l1 table covers 4MB * of virtual address space, and needs to be shadowed by two PAE/64-bit * l1 tables (covering 2MB of virtual address space each).  Similarly, a * 32-bit guest l2 table (4GB va) needs to be shadowed by four * PAE/64-bit l2 tables (1GB va each).  These multi-page shadows are * contiguous and aligned; functions for handling offsets into them are * defined in shadow.c (shadow_l1_index() etc.) *     * This table shows the allocation behaviour of the different modes: * * Xen paging      pae  pae  64b  64b  64b * Guest paging    32b  pae  32b  pae  64b * PV or HVM       HVM   *   HVM  HVM   *  * Shadow paging   pae  pae  pae  pae  64b * * sl1 size         8k   4k   8k   4k   4k * sl2 size        16k   4k  16k   4k   4k

⌨️ 快捷键说明

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