📄 mm.c
字号:
#define adjust_guest_l2e(_p, _d) ((void)(_d))#define adjust_guest_l3e(_p, _d) ((void)(_d))#endif#ifdef CONFIG_COMPAT#define unadjust_guest_l3e(pl3e, d) \ do { \ if ( unlikely(is_pv_32on64_domain(d)) && \ likely(l3e_get_flags((pl3e)) & _PAGE_PRESENT) ) \ l3e_remove_flags((pl3e), _PAGE_USER|_PAGE_RW|_PAGE_ACCESSED); \ } while ( 0 )#else#define unadjust_guest_l3e(_p, _d) ((void)(_d))#endifvoid put_page_from_l1e(l1_pgentry_t l1e, struct domain *d){ unsigned long pfn = l1e_get_pfn(l1e); struct page_info *page; struct domain *e; struct vcpu *v; if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || is_iomem_page(pfn) ) return; page = mfn_to_page(pfn); e = page_get_owner(page); /* * Check if this is a mapping that was established via a grant reference. * If it was then we should not be here: we require that such mappings are * explicitly destroyed via the grant-table interface. * * The upshot of this is that the guest can end up with active grants that * it cannot destroy (because it no longer has a PTE to present to the * grant-table interface). This can lead to subtle hard-to-catch bugs, * hence a special grant PTE flag can be enabled to catch the bug early. * * (Note that the undestroyable active grants are not a security hole in * Xen. All active grants can safely be cleaned up when the domain dies.) */ if ( (l1e_get_flags(l1e) & _PAGE_GNTTAB) && !d->is_shutting_down && !d->is_dying ) { MEM_LOG("Attempt to implicitly unmap a granted PTE %" PRIpte, l1e_get_intpte(l1e)); domain_crash(d); } /* Remember we didn't take a type-count of foreign writable mappings * to paging-external domains */ if ( (l1e_get_flags(l1e) & _PAGE_RW) && !(unlikely((e != d) && paging_mode_external(e))) ) { put_page_and_type(page); } else { /* We expect this is rare so we blow the entire shadow LDT. */ if ( unlikely(((page->u.inuse.type_info & PGT_type_mask) == PGT_ldt_page)) && unlikely(((page->u.inuse.type_info & PGT_count_mask) != 0)) && (d == e) ) { for_each_vcpu ( d, v ) invalidate_shadow_ldt(v); } put_page(page); }}/* * NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'. * Note also that this automatically deals correctly with linear p.t.'s. */static void put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn){ if ( (l2e_get_flags(l2e) & _PAGE_PRESENT) && (l2e_get_pfn(l2e) != pfn) ) put_page_and_type(l2e_get_page(l2e));}#if CONFIG_PAGING_LEVELS >= 3static void put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn){ if ( (l3e_get_flags(l3e) & _PAGE_PRESENT) && (l3e_get_pfn(l3e) != pfn) ) put_page_and_type(l3e_get_page(l3e));}#endif#if CONFIG_PAGING_LEVELS >= 4static void put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn){ if ( (l4e_get_flags(l4e) & _PAGE_PRESENT) && (l4e_get_pfn(l4e) != pfn) ) put_page_and_type(l4e_get_page(l4e));}#endifstatic int alloc_l1_table(struct page_info *page){ struct domain *d = page_get_owner(page); unsigned long pfn = page_to_mfn(page); l1_pgentry_t *pl1e; int i; pl1e = map_domain_page(pfn); for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) { if ( is_guest_l1_slot(i) && unlikely(!get_page_from_l1e(pl1e[i], d)) ) goto fail; adjust_guest_l1e(pl1e[i], d); } unmap_domain_page(pl1e); return 1; fail: MEM_LOG("Failure in alloc_l1_table: entry %d", i); while ( i-- > 0 ) if ( is_guest_l1_slot(i) ) put_page_from_l1e(pl1e[i], d); unmap_domain_page(pl1e); return 0;}#if defined(CONFIG_X86_PAE) || defined(CONFIG_COMPAT)static int create_pae_xen_mappings(struct domain *d, l3_pgentry_t *pl3e){ struct page_info *page; l2_pgentry_t *pl2e; l3_pgentry_t l3e3;#ifndef CONFIG_COMPAT l2_pgentry_t l2e; int i;#endif if ( !is_pv_32bit_domain(d) ) return 1; pl3e = (l3_pgentry_t *)((unsigned long)pl3e & PAGE_MASK); /* 3rd L3 slot contains L2 with Xen-private mappings. It *must* exist. */ l3e3 = pl3e[3]; if ( !(l3e_get_flags(l3e3) & _PAGE_PRESENT) ) { MEM_LOG("PAE L3 3rd slot is empty"); return 0; } /* * The Xen-private mappings include linear mappings. The L2 thus cannot * be shared by multiple L3 tables. The test here is adequate because: * 1. Cannot appear in slots != 3 because get_page_type() checks the * PGT_pae_xen_l2 flag, which is asserted iff the L2 appears in slot 3 * 2. Cannot appear in another page table's L3: * a. alloc_l3_table() calls this function and this check will fail * b. mod_l3_entry() disallows updates to slot 3 in an existing table */ page = l3e_get_page(l3e3); BUG_ON(page->u.inuse.type_info & PGT_pinned); BUG_ON((page->u.inuse.type_info & PGT_count_mask) == 0); BUG_ON(!(page->u.inuse.type_info & PGT_pae_xen_l2)); if ( (page->u.inuse.type_info & PGT_count_mask) != 1 ) { MEM_LOG("PAE L3 3rd slot is shared"); return 0; } /* Xen private mappings. */ pl2e = map_domain_page(l3e_get_pfn(l3e3));#ifndef CONFIG_COMPAT memcpy(&pl2e[L2_PAGETABLE_FIRST_XEN_SLOT & (L2_PAGETABLE_ENTRIES-1)], &idle_pg_table_l2[L2_PAGETABLE_FIRST_XEN_SLOT], L2_PAGETABLE_XEN_SLOTS * sizeof(l2_pgentry_t)); for ( i = 0; i < PDPT_L2_ENTRIES; i++ ) { l2e = l2e_from_page( virt_to_page(page_get_owner(page)->arch.mm_perdomain_pt) + i, __PAGE_HYPERVISOR); l2e_write(&pl2e[l2_table_offset(PERDOMAIN_VIRT_START) + i], l2e); } for ( i = 0; i < (LINEARPT_MBYTES >> (L2_PAGETABLE_SHIFT - 20)); i++ ) { l2e = l2e_empty(); if ( l3e_get_flags(pl3e[i]) & _PAGE_PRESENT ) l2e = l2e_from_pfn(l3e_get_pfn(pl3e[i]), __PAGE_HYPERVISOR); l2e_write(&pl2e[l2_table_offset(LINEAR_PT_VIRT_START) + i], l2e); }#else memcpy(&pl2e[COMPAT_L2_PAGETABLE_FIRST_XEN_SLOT(d)], &compat_idle_pg_table_l2[ l2_table_offset(HIRO_COMPAT_MPT_VIRT_START)], COMPAT_L2_PAGETABLE_XEN_SLOTS(d) * sizeof(*pl2e));#endif unmap_domain_page(pl2e); return 1;}#else# define create_pae_xen_mappings(d, pl3e) (1)#endif#ifdef CONFIG_X86_PAE/* Flush a pgdir update into low-memory caches. */static void pae_flush_pgd( unsigned long mfn, unsigned int idx, l3_pgentry_t nl3e){ struct domain *d = page_get_owner(mfn_to_page(mfn)); struct vcpu *v; intpte_t _ol3e, _nl3e, _pl3e; l3_pgentry_t *l3tab_ptr; struct pae_l3_cache *cache; if ( unlikely(shadow_mode_enabled(d)) ) { cpumask_t m = CPU_MASK_NONE; /* Re-shadow this l3 table on any vcpus that are using it */ for_each_vcpu ( d, v ) if ( pagetable_get_pfn(v->arch.guest_table) == mfn ) { paging_update_cr3(v); cpus_or(m, m, v->vcpu_dirty_cpumask); } flush_tlb_mask(m); } /* If below 4GB then the pgdir is not shadowed in low memory. */ if ( !l3tab_needs_shadow(mfn) ) return; for_each_vcpu ( d, v ) { cache = &v->arch.pae_l3_cache; spin_lock(&cache->lock); if ( cache->high_mfn == mfn ) { l3tab_ptr = &cache->table[cache->inuse_idx][idx]; _ol3e = l3e_get_intpte(*l3tab_ptr); _nl3e = l3e_get_intpte(nl3e); _pl3e = cmpxchg(&l3e_get_intpte(*l3tab_ptr), _ol3e, _nl3e); BUG_ON(_pl3e != _ol3e); } spin_unlock(&cache->lock); } flush_tlb_mask(d->domain_dirty_cpumask);}#else# define pae_flush_pgd(mfn, idx, nl3e) ((void)0)#endifstatic int alloc_l2_table(struct page_info *page, unsigned long type){ struct domain *d = page_get_owner(page); unsigned long pfn = page_to_mfn(page); l2_pgentry_t *pl2e; int i; pl2e = map_domain_page(pfn); for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) { if ( is_guest_l2_slot(d, type, i) && unlikely(!get_page_from_l2e(pl2e[i], pfn, d)) ) goto fail; adjust_guest_l2e(pl2e[i], d); }#if CONFIG_PAGING_LEVELS == 2 /* Xen private mappings. */ memcpy(&pl2e[L2_PAGETABLE_FIRST_XEN_SLOT], &idle_pg_table[L2_PAGETABLE_FIRST_XEN_SLOT], L2_PAGETABLE_XEN_SLOTS * sizeof(l2_pgentry_t)); pl2e[l2_table_offset(LINEAR_PT_VIRT_START)] = l2e_from_pfn(pfn, __PAGE_HYPERVISOR); for ( i = 0; i < PDPT_L2_ENTRIES; i++ ) pl2e[l2_table_offset(PERDOMAIN_VIRT_START) + i] = l2e_from_page( virt_to_page(page_get_owner(page)->arch.mm_perdomain_pt) + i, __PAGE_HYPERVISOR);#endif unmap_domain_page(pl2e); return 1; fail: MEM_LOG("Failure in alloc_l2_table: entry %d", i); while ( i-- > 0 ) if ( is_guest_l2_slot(d, type, i) ) put_page_from_l2e(pl2e[i], pfn); unmap_domain_page(pl2e); return 0;}#if CONFIG_PAGING_LEVELS >= 3static int alloc_l3_table(struct page_info *page){ struct domain *d = page_get_owner(page); unsigned long pfn = page_to_mfn(page); l3_pgentry_t *pl3e; int i;#ifdef CONFIG_X86_PAE /* * PAE pgdirs above 4GB are unacceptable if the guest does not understand * the weird 'extended cr3' format for dealing with high-order address * bits. We cut some slack for control tools (before vcpu0 is initialised). */ if ( (pfn >= 0x100000) && unlikely(!VM_ASSIST(d, VMASST_TYPE_pae_extended_cr3)) && d->vcpu[0] && d->vcpu[0]->is_initialised ) { MEM_LOG("PAE pgd must be below 4GB (0x%lx >= 0x100000)", pfn); return 0; }#endif pl3e = map_domain_page(pfn); /* * PAE guests allocate full pages, but aren't required to initialize * more than the first four entries; when running in compatibility * mode, however, the full page is visible to the MMU, and hence all * 512 entries must be valid/verified, which is most easily achieved * by clearing them out. */ if ( is_pv_32on64_domain(d) ) memset(pl3e + 4, 0, (L3_PAGETABLE_ENTRIES - 4) * sizeof(*pl3e)); for ( i = 0; i < L3_PAGETABLE_ENTRIES; i++ ) {#if defined(CONFIG_X86_PAE) || defined(CONFIG_COMPAT) if ( is_pv_32bit_domain(d) && (i == 3) ) { if ( !(l3e_get_flags(pl3e[i]) & _PAGE_PRESENT) || (l3e_get_flags(pl3e[i]) & l3_disallow_mask(d)) || !get_page_and_type_from_pagenr(l3e_get_pfn(pl3e[i]), PGT_l2_page_table | PGT_pae_xen_l2, d) ) goto fail; } else#endif if ( is_guest_l3_slot(i) && unlikely(!get_page_from_l3e(pl3e[i], pfn, d)) ) goto fail; adjust_guest_l3e(pl3e[i], d); } if ( !create_pae_xen_mappings(d, pl3e) ) goto fail; unmap_domain_page(pl3e); return 1; fail: MEM_LOG("Failure in alloc_l3_table: entry %d", i); while ( i-- > 0 ) if ( is_guest_l3_slot(i) ) put_page_from_l3e(pl3e[i], pfn); unmap_domain_page(pl3e); return 0;}#else#define alloc_l3_table(page) (0)#endif#if CONFIG_PAGING_LEVELS >= 4static int alloc_l4_table(struct page_info *page){ struct domain *d = page_get_owner(page); unsigned long pfn = page_to_mfn(page); l4_pgentry_t *pl4e = page_to_virt(page); int i; for ( i = 0; i < L4_PAGETABLE_ENTRIES; i++ ) { if ( is_guest_l4_slot(d, i) && unlikely(!get_page_from_l4e(pl4e[i], pfn, d)) ) goto fail; adjust_guest_l4e(pl4e[i], d); } /* Xen private mappings. */ memcpy(&pl4e[ROOT_PAGETABLE_FIRST_XEN_SLOT], &idle_pg_table[ROOT_PAGETABLE_FIRST_XEN_SLOT], ROOT_PAGETABLE_XEN_SLOTS * sizeof(l4_pgentry_t)); pl4e[l4_table_offset(LINEAR_PT_VIRT_START)] = l4e_from_pfn(pfn, __PAGE_HYPERVISOR); pl4e[l4_table_offset(PERDOMAIN_VIRT_START)] = l4e_from_page(virt_to_page(d->arch.mm_perdomain_l3), __PAGE_HYPERVISOR); if ( is_pv_32on64_domain(d) ) pl4e[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -