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 + -
显示快捷键?