common.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,973 行 · 第 1/5 页
C
1,973 行
sreg = hvm_get_seg_reg(x86_seg_ss, sh_ctxt); sh_ctxt->ctxt.addr_size = creg->attr.fields.db ? 32 : 16; sh_ctxt->ctxt.sp_size = sreg->attr.fields.db ? 32 : 16; } /* Attempt to prefetch whole instruction. */ sh_ctxt->insn_buf_eip = regs->eip; sh_ctxt->insn_buf_bytes = (!hvm_translate_linear_addr( x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf), hvm_access_insn_fetch, sh_ctxt, &addr) && !hvm_fetch_from_guest_virt_nofault( sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf), 0)) ? sizeof(sh_ctxt->insn_buf) : 0; return &hvm_shadow_emulator_ops;}/* Update an initialized emulation context to prepare for the next * instruction */void shadow_continue_emulation(struct sh_emulate_ctxt *sh_ctxt, struct cpu_user_regs *regs){ struct vcpu *v = current; unsigned long addr, diff; /* We don't refetch the segment bases, because we don't emulate * writes to segment registers */ if ( is_hvm_vcpu(v) ) { diff = regs->eip - sh_ctxt->insn_buf_eip; if ( diff > sh_ctxt->insn_buf_bytes ) { /* Prefetch more bytes. */ sh_ctxt->insn_buf_bytes = (!hvm_translate_linear_addr( x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf), hvm_access_insn_fetch, sh_ctxt, &addr) && !hvm_fetch_from_guest_virt_nofault( sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf), 0)) ? sizeof(sh_ctxt->insn_buf) : 0; sh_ctxt->insn_buf_eip = regs->eip; } }} #if (SHADOW_OPTIMIZATIONS & SHOPT_OUT_OF_SYNC)/**************************************************************************//* Out-of-sync shadows. */ /* From time to time, we let a shadowed pagetable page go out of sync * with its shadow: the guest is allowed to write directly to the page, * and those writes are not synchronously reflected in the shadow. * This lets us avoid many emulations if the guest is writing a lot to a * pagetable, but it relaxes a pretty important invariant in the shadow * pagetable design. Therefore, some rules: * * 1. Only L1 pagetables may go out of sync: any page that is shadowed * at at higher level must be synchronously updated. This makes * using linear shadow pagetables much less dangerous. * That means that: (a) unsyncing code needs to check for higher-level * shadows, and (b) promotion code needs to resync. * * 2. All shadow operations on a guest page require the page to be brought * back into sync before proceeding. This must be done under the * shadow lock so that the page is guaranteed to remain synced until * the operation completes. * * Exceptions to this rule: the pagefault and invlpg handlers may * update only one entry on an out-of-sync page without resyncing it. * * 3. Operations on shadows that do not start from a guest page need to * be aware that they may be handling an out-of-sync shadow. * * 4. Operations that do not normally take the shadow lock (fast-path * #PF handler, INVLPG) must fall back to a locking, syncing version * if they see an out-of-sync table. * * 5. Operations corresponding to guest TLB flushes (MOV CR3, INVLPG) * must explicitly resync all relevant pages or update their * shadows. * * Currently out-of-sync pages are listed in a simple open-addressed * hash table with a second chance (must resist temptation to radically * over-engineer hash tables...) The virtual address of the access * which caused us to unsync the page is also kept in the hash table, as * a hint for finding the writable mappings later. * * We keep a hash per vcpu, because we want as much as possible to do * the re-sync on the save vcpu we did the unsync on, so the VA hint * will be valid. */#if SHADOW_AUDIT & SHADOW_AUDIT_ENTRIES_FULLstatic void sh_oos_audit(struct domain *d) { int idx, expected_idx, expected_idx_alt; struct page_info *pg; struct vcpu *v; for_each_vcpu(d, v) { for ( idx = 0; idx < SHADOW_OOS_PAGES; idx++ ) { mfn_t *oos = v->arch.paging.shadow.oos; if ( !mfn_valid(oos[idx]) ) continue; expected_idx = mfn_x(oos[idx]) % SHADOW_OOS_PAGES; expected_idx_alt = ((expected_idx + 1) % SHADOW_OOS_PAGES); if ( idx != expected_idx && idx != expected_idx_alt ) { printk("%s: idx %d contains gmfn %lx, expected at %d or %d.\n", __func__, idx, mfn_x(oos[idx]), expected_idx, expected_idx_alt); BUG(); } pg = mfn_to_page(oos[idx]); if ( !(pg->count_info & PGC_page_table) ) { printk("%s: idx %x gmfn %lx not a pt (count %"PRIx32")\n", __func__, idx, mfn_x(oos[idx]), pg->count_info); BUG(); } if ( !(pg->shadow_flags & SHF_out_of_sync) ) { printk("%s: idx %x gmfn %lx not marked oos (flags %lx)\n", __func__, idx, mfn_x(oos[idx]), pg->shadow_flags); BUG(); } if ( (pg->shadow_flags & SHF_page_type_mask & ~SHF_L1_ANY) ) { printk("%s: idx %x gmfn %lx shadowed as non-l1 (flags %lx)\n", __func__, idx, mfn_x(oos[idx]), pg->shadow_flags); BUG(); } } }}#endif#if SHADOW_AUDIT & SHADOW_AUDIT_ENTRIESvoid oos_audit_hash_is_present(struct domain *d, mfn_t gmfn) { int idx; struct vcpu *v; mfn_t *oos; ASSERT(mfn_is_out_of_sync(gmfn)); for_each_vcpu(d, v) { oos = v->arch.paging.shadow.oos; 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; } SHADOW_ERROR("gmfn %lx marked OOS but not in hash table\n", mfn_x(gmfn)); BUG();}#endif/* Update the shadow, but keep the page out of sync. */static inline void _sh_resync_l1(struct vcpu *v, mfn_t gmfn, mfn_t snpmfn){ struct page_info *pg = mfn_to_page(gmfn); ASSERT(mfn_valid(gmfn)); ASSERT(page_is_out_of_sync(pg)); /* Call out to the appropriate per-mode resyncing function */ if ( pg->shadow_flags & SHF_L1_32 ) SHADOW_INTERNAL_NAME(sh_resync_l1, 2)(v, gmfn, snpmfn); else if ( pg->shadow_flags & SHF_L1_PAE ) SHADOW_INTERNAL_NAME(sh_resync_l1, 3)(v, gmfn, snpmfn);#if CONFIG_PAGING_LEVELS >= 4 else if ( pg->shadow_flags & SHF_L1_64 ) SHADOW_INTERNAL_NAME(sh_resync_l1, 4)(v, gmfn, snpmfn);#endif}/* * Fixup arrays: We limit the maximum number of writable mappings to * SHADOW_OOS_FIXUPS and store enough information to remove them * quickly on resync. */static inline int oos_fixup_flush_gmfn(struct vcpu *v, mfn_t gmfn, struct oos_fixup *fixup){ int i; for ( i = 0; i < SHADOW_OOS_FIXUPS; i++ ) { if ( mfn_x(fixup->smfn[i]) != INVALID_MFN ) { sh_remove_write_access_from_sl1p(v, gmfn, fixup->smfn[i], fixup->off[i]); fixup->smfn[i] = _mfn(INVALID_MFN); } } /* Always flush the TLBs. See comment on oos_fixup_add(). */ return 1;}void oos_fixup_add(struct vcpu *v, mfn_t gmfn, mfn_t smfn, unsigned long off){ int idx, next; mfn_t *oos; struct oos_fixup *oos_fixup; struct domain *d = v->domain; perfc_incr(shadow_oos_fixup_add); for_each_vcpu(d, v) { oos = v->arch.paging.shadow.oos; oos_fixup = v->arch.paging.shadow.oos_fixup; 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) ) { next = oos_fixup[idx].next; if ( mfn_x(oos_fixup[idx].smfn[next]) != INVALID_MFN ) { /* Reuse this slot and remove current writable mapping. */ sh_remove_write_access_from_sl1p(v, gmfn, oos_fixup[idx].smfn[next], oos_fixup[idx].off[next]); perfc_incr(shadow_oos_fixup_evict); /* We should flush the TLBs now, because we removed a writable mapping, but since the shadow is already OOS we have no problem if another vcpu write to this page table. We just have to be very careful to *always* flush the tlbs on resync. */ } oos_fixup[idx].smfn[next] = smfn; oos_fixup[idx].off[next] = off; oos_fixup[idx].next = (next + 1) % SHADOW_OOS_FIXUPS; return; } } SHADOW_ERROR("gmfn %lx was OOS but not in hash table\n", mfn_x(gmfn)); BUG();}static int oos_remove_write_access(struct vcpu *v, mfn_t gmfn, struct oos_fixup *fixup){ int ftlb = 0; ftlb |= oos_fixup_flush_gmfn(v, gmfn, fixup); switch ( sh_remove_write_access(v, gmfn, 0, 0) ) { default: case 0: break; case 1: ftlb |= 1; break; case -1: /* An unfindable writeable typecount has appeared, probably via a * grant table entry: can't shoot the mapping, so try to unshadow * the page. If that doesn't work either, the guest is granting * his pagetables and must be killed after all. * This will flush the tlb, so we can return with no worries. */ sh_remove_shadows(v, gmfn, 0 /* Be thorough */, 1 /* Must succeed */); return 1; } if ( ftlb ) flush_tlb_mask(v->domain->domain_dirty_cpumask); return 0;}/* Pull all the entries on an out-of-sync page back into sync. */static void _sh_resync(struct vcpu *v, mfn_t gmfn, struct oos_fixup *fixup, mfn_t snp){ struct page_info *pg = mfn_to_page(gmfn); ASSERT(shadow_locked_by_me(v->domain)); ASSERT(mfn_is_out_of_sync(gmfn)); /* Guest page must be shadowed *only* as L1 when out of sync. */ ASSERT(!(mfn_to_page(gmfn)->shadow_flags & SHF_page_type_mask & ~SHF_L1_ANY)); ASSERT(!sh_page_has_multiple_shadows(mfn_to_page(gmfn))); SHADOW_PRINTK("d=%d, v=%d, gmfn=%05lx, va=%lx\n", v->domain->domain_id, v->vcpu_id, mfn_x(gmfn), va); /* Need to pull write access so the page *stays* in sync. */ if ( oos_remove_write_access(v, gmfn, fixup) ) { /* Page has been unshadowed. */ return; } /* No more writable mappings of this page, please */ pg->shadow_flags &= ~SHF_oos_may_write; /* Update the shadows with current guest entries. */ _sh_resync_l1(v, gmfn, snp); /* Now we know all the entries are synced, and will stay that way */ pg->shadow_flags &= ~SHF_out_of_sync; perfc_incr(shadow_resync);}/* Add an MFN to the list of out-of-sync guest pagetables */static void oos_hash_add(struct vcpu *v, mfn_t gmfn){ int i, idx, oidx, swap = 0; void *gptr, *gsnpptr; 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; struct oos_fixup fixup = { .next = 0 }; for (i = 0; i < SHADOW_OOS_FIXUPS; i++ ) fixup.smfn[i] = _mfn(INVALID_MFN); idx = mfn_x(gmfn) % SHADOW_OOS_PAGES; oidx = idx; if ( mfn_valid(oos[idx]) && (mfn_x(oos[idx]) % SHADOW_OOS_PAGES) == idx ) { /* Punt the current occupant into the next slot */ SWAP(oos[idx], gmfn); SWAP(oos_fixup[idx], fixup); swap = 1; idx = (idx + 1) % SHADOW_OOS_PAGES; } if ( mfn_valid(oos[idx]) ) { /* Crush the current occupant. */ _sh_resync(v, oos[idx], &oos_fixup[idx], oos_snapshot[idx]); perfc_incr(shadow_unsync_evict); } oos[idx] = gmfn; oos_fixup[idx] = fixup; if ( swap ) SWAP(oos_snapshot[idx], oos_snapshot[oidx]); gptr = sh_map_domain_page(oos[oidx]); gsnpptr = sh_map_domain_page(oos_snapshot[oidx]); memcpy(gsnpptr, gptr, PAGE_SIZE); sh_unmap_domain_page(gptr); sh_unmap_domain_page(gsnpptr);}/* Remove an MFN from the list of out-of-sync guest pagetables */static void oos_hash_remove(struct vcpu *v, mfn_t gmfn){ int idx; mfn_t *oos; struct domain *d = v->domain; SHADOW_PRINTK("D%dV%d gmfn %lx\n", v->domain->domain_id, v->vcpu_id, mfn_x(gmfn)); for_each_vcpu(d, v) { oos = v->arch.paging.shadow.oos; 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) ) { oos[idx] = _mfn(INVALID_MFN); return; } }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?