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