mm.c

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

C
2,038
字号
            page_unlock(l3pg);            return rc;        }        if ( unlikely(!get_page_from_l3e(nl3e, pfn, d)) )            return page_unlock(l3pg), 0;        adjust_guest_l3e(nl3e, d);        if ( unlikely(!UPDATE_ENTRY(l3, pl3e, ol3e, nl3e, pfn, curr,                                    preserve_ad)) )        {            ol3e = nl3e;            rc = 0;        }    }    else if ( unlikely(!UPDATE_ENTRY(l3, pl3e, ol3e, nl3e, pfn, curr,                                     preserve_ad)) )    {        page_unlock(l3pg);        return 0;    }    if ( likely(rc) )    {        if ( !create_pae_xen_mappings(d, pl3e) )            BUG();        pae_flush_pgd(pfn, pgentry_ptr_to_slot(pl3e), nl3e);    }    page_unlock(l3pg);    put_page_from_l3e(ol3e, pfn);    return rc;}#endif#if CONFIG_PAGING_LEVELS >= 4/* Update the L4 entry at pl4e to new value nl4e. pl4e is within frame pfn. */static int mod_l4_entry(l4_pgentry_t *pl4e,                         l4_pgentry_t nl4e,                         unsigned long pfn,                        int preserve_ad){    struct vcpu *curr = current;    struct domain *d = curr->domain;    l4_pgentry_t ol4e;    struct page_info *l4pg = mfn_to_page(pfn);    int rc = 1;    if ( unlikely(!is_guest_l4_slot(d, pgentry_ptr_to_slot(pl4e))) )    {        MEM_LOG("Illegal L4 update attempt in Xen-private area %p", pl4e);        return 0;    }    page_lock(l4pg);    if ( unlikely(__copy_from_user(&ol4e, pl4e, sizeof(ol4e)) != 0) )        return page_unlock(l4pg), 0;    if ( l4e_get_flags(nl4e) & _PAGE_PRESENT )    {        if ( unlikely(l4e_get_flags(nl4e) & L4_DISALLOW_MASK) )        {            page_unlock(l4pg);            MEM_LOG("Bad L4 flags %x",                    l4e_get_flags(nl4e) & L4_DISALLOW_MASK);            return 0;        }        /* Fast path for identical mapping and presence. */        if ( !l4e_has_changed(ol4e, nl4e, _PAGE_PRESENT) )        {            adjust_guest_l4e(nl4e, d);            rc = UPDATE_ENTRY(l4, pl4e, ol4e, nl4e, pfn, curr, preserve_ad);            page_unlock(l4pg);            return rc;        }        if ( unlikely(!get_page_from_l4e(nl4e, pfn, d)) )            return page_unlock(l4pg), 0;        adjust_guest_l4e(nl4e, d);        if ( unlikely(!UPDATE_ENTRY(l4, pl4e, ol4e, nl4e, pfn, curr,                                    preserve_ad)) )        {            ol4e = nl4e;            rc = 0;        }    }    else if ( unlikely(!UPDATE_ENTRY(l4, pl4e, ol4e, nl4e, pfn, curr,                                     preserve_ad)) )    {        page_unlock(l4pg);        return 0;    }    page_unlock(l4pg);    put_page_from_l4e(ol4e, pfn);    return rc;}#endifvoid put_page(struct page_info *page){    u32 nx, x, y = page->count_info;    do {        x  = y;        nx = x - 1;    }    while ( unlikely((y = cmpxchg(&page->count_info, x, nx)) != x) );    if ( unlikely((nx & PGC_count_mask) == 0) )    {        cleanup_page_cacheattr(page);        free_domheap_page(page);    }}int get_page(struct page_info *page, struct domain *domain){    u32 x, nx, y = page->count_info;    u32 d, nd = page->u.inuse._domain;    u32 _domain = pickle_domptr(domain);    do {        x  = y;        nx = x + 1;        d  = nd;        if ( unlikely((x & PGC_count_mask) == 0) ||  /* Not allocated? */             unlikely((nx & PGC_count_mask) == 0) || /* Count overflow? */             unlikely(d != _domain) )                /* Wrong owner? */        {            if ( !_shadow_mode_refcounts(domain) && !domain->is_dying )                gdprintk(XENLOG_INFO,                         "Error pfn %lx: rd=%p, od=%p, caf=%08x, taf=%"                         PRtype_info "\n",                         page_to_mfn(page), domain, unpickle_domptr(d),                         x, page->u.inuse.type_info);            return 0;        }        asm volatile (            LOCK_PREFIX "cmpxchg8b %2"            : "=d" (nd), "=a" (y),            "=m" (*(volatile u64 *)(&page->count_info))            : "0" (d), "1" (x), "c" (d), "b" (nx) );    }    while ( unlikely(nd != d) || unlikely(y != x) );    return 1;}static int alloc_page_type(struct page_info *page, unsigned long type){    struct domain *owner = page_get_owner(page);    /* A page table is dirtied when its type count becomes non-zero. */    if ( likely(owner != NULL) )        paging_mark_dirty(owner, page_to_mfn(page));    switch ( type & PGT_type_mask )    {    case PGT_l1_page_table:        return alloc_l1_table(page);    case PGT_l2_page_table:        return alloc_l2_table(page, type);    case PGT_l3_page_table:        return alloc_l3_table(page);    case PGT_l4_page_table:        return alloc_l4_table(page);    case PGT_seg_desc_page:        return alloc_segdesc_page(page);    default:        printk("Bad type in alloc_page_type %lx t=%" PRtype_info " c=%x\n",                type, page->u.inuse.type_info,               page->count_info);        BUG();    }    return 0;}void free_page_type(struct page_info *page, unsigned long type){    struct domain *owner = page_get_owner(page);    unsigned long gmfn;    if ( likely(owner != NULL) )    {        /*         * We have to flush before the next use of the linear mapping         * (e.g., update_va_mapping()) or we could end up modifying a page         * that is no longer a page table (and hence screw up ref counts).         */        if ( current->domain == owner )            queue_deferred_ops(owner, DOP_FLUSH_ALL_TLBS);        else            flush_tlb_mask(owner->domain_dirty_cpumask);        if ( unlikely(paging_mode_enabled(owner)) )        {            /* A page table is dirtied when its type count becomes zero. */            paging_mark_dirty(owner, page_to_mfn(page));            if ( shadow_mode_refcounts(owner) )                return;            gmfn = mfn_to_gmfn(owner, page_to_mfn(page));            ASSERT(VALID_M2P(gmfn));            shadow_remove_all_shadows(owner->vcpu[0], _mfn(gmfn));        }    }    switch ( type & PGT_type_mask )    {    case PGT_l1_page_table:        free_l1_table(page);        break;    case PGT_l2_page_table:        free_l2_table(page);        break;#if CONFIG_PAGING_LEVELS >= 3    case PGT_l3_page_table:        free_l3_table(page);        break;#endif#if CONFIG_PAGING_LEVELS >= 4    case PGT_l4_page_table:        free_l4_table(page);        break;#endif    default:        printk("%s: type %lx pfn %lx\n",__FUNCTION__,               type, page_to_mfn(page));        BUG();    }}void put_page_type(struct page_info *page){    unsigned long nx, x, y = page->u.inuse.type_info; again:    do {        x  = y;        nx = x - 1;        ASSERT((x & PGT_count_mask) != 0);        if ( unlikely((nx & PGT_count_mask) == 0) )        {            if ( unlikely((nx & PGT_type_mask) <= PGT_l4_page_table) &&                 likely(nx & PGT_validated) )            {                /*                 * Page-table pages must be unvalidated when count is zero. The                 * 'free' is safe because the refcnt is non-zero and validated                 * bit is clear => other ops will spin or fail.                 */                if ( unlikely((y = cmpxchg(&page->u.inuse.type_info, x,                                            x & ~PGT_validated)) != x) )                    goto again;                /* We cleared the 'valid bit' so we do the clean up. */                free_page_type(page, x);                /* Carry on, but with the 'valid bit' now clear. */                x  &= ~PGT_validated;                nx &= ~PGT_validated;            }            /*             * Record TLB information for flush later. We do not stamp page             * tables when running in shadow mode:             *  1. Pointless, since it's the shadow pt's which must be tracked.             *  2. Shadow mode reuses this field for shadowed page tables to             *     store flags info -- we don't want to conflict with that.             */            if ( !(shadow_mode_enabled(page_get_owner(page)) &&                   (page->count_info & PGC_page_table)) )                page->tlbflush_timestamp = tlbflush_current_time();        }    }    while ( unlikely((y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x) );}int get_page_type(struct page_info *page, unsigned long type){    unsigned long nx, x, y = page->u.inuse.type_info;    ASSERT(!(type & ~(PGT_type_mask | PGT_pae_xen_l2))); again:    do {        x  = y;        nx = x + 1;        if ( unlikely((nx & PGT_count_mask) == 0) )        {            MEM_LOG("Type count overflow on pfn %lx", page_to_mfn(page));            return 0;        }        else if ( unlikely((x & PGT_count_mask) == 0) )        {            struct domain *d = page_get_owner(page);            /* Normally we should never let a page go from type count 0             * to type count 1 when it is shadowed. One exception:             * out-of-sync shadowed pages are allowed to become             * writeable. */            if ( d && shadow_mode_enabled(d)                 && (page->count_info & PGC_page_table)                 && !((page->shadow_flags & (1u<<29))                      && type == PGT_writable_page) )               shadow_remove_all_shadows(d->vcpu[0], _mfn(page_to_mfn(page)));            ASSERT(!(x & PGT_pae_xen_l2));            if ( (x & PGT_type_mask) != type )            {                /*                 * On type change we check to flush stale TLB entries. This                  * may be unnecessary (e.g., page was GDT/LDT) but those                  * circumstances should be very rare.                 */                cpumask_t mask = d->domain_dirty_cpumask;                /* Don't flush if the timestamp is old enough */                tlbflush_filter(mask, page->tlbflush_timestamp);                if ( unlikely(!cpus_empty(mask)) &&                     /* Shadow mode: track only writable pages. */                     (!shadow_mode_enabled(page_get_owner(page)) ||                      ((nx & PGT_type_mask) == PGT_writable_page)) )                {                    perfc_incr(need_flush_tlb_flush);                    flush_tlb_mask(mask);                }                /* We lose existing type and validity. */                nx &= ~(PGT_type_mask | PGT_validated);                nx |= type;                /* No special validation needed for writable pages. */                /* Page tables and GDT/LDT need to be scanned for validity. */                if ( type == PGT_writable_page )                    nx |= PGT_validated;            }        }        else if ( unlikely((x & (PGT_type_mask|PGT_pae_xen_l2)) != type) )        {            /* Don't log failure if it could be a recursive-mapping attempt. */            if ( ((x & PGT_type_mask) == PGT_l2_page_table) &&                 (type == PGT_l1_page_table) )                return 0;            if ( ((x & PGT_type_mask) == PGT_l3_page_table) &&                 (type == PGT_l2_page_table) )                return 0;            if ( ((x & PGT_type_mask) == PGT_l4_page_table) &&                 (type == PGT_l3_page_table) )                return 0;            MEM_LOG("Bad type (saw %" PRtype_info " != exp %" PRtype_info ") "                    "for mfn %lx (pfn %lx)",                    x, type, page_to_mfn(page),                    get_gpfn_from_mfn(page_to_mfn(page)));            return 0;        }        else if ( unlikely(!(x & PGT_validated)) )        {            /* Someone else is updating validation of this page. Wait... */            while ( (y = page->u.inuse.type_info) == x )                cpu_relax();            goto again;        }    }    while ( unlikely((y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x) );    if ( unlikely((x & PGT_type_mask) != type) )    {        /* Special pages should not be accessible from devices. */        struct domain *d = page_get_owner(page);        if ( d && unlikely(need_iommu(d)) )        {            if ( (x & PGT_type_mask) == PGT_writable_page )                iommu_unmap_page(d, mfn_to_gmfn(d, page_to_mfn(page)));            else if ( type == PGT_writable_page )                iommu_map_page(d, mfn_to_gmfn(d, page_to_mfn(page)),                               page_to_mfn(page));        }    }    if ( unlikely(!(nx & PGT_validated)) )    {        /* Try to validate page type; drop the new reference on failure. */        if ( unlikely(!alloc_page_type(page, type)) )        {     

⌨️ 快捷键说明

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