diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index c26c73295f..1f1550eb77 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -339,7 +339,7 @@ static int setup_compat_l4(struct vcpu *v) l4tab = __map_domain_page(pg); clear_page(l4tab); - init_guest_l4_table(l4tab, v->domain); + init_guest_l4_table(l4tab, v->domain, 1); unmap_domain_page(l4tab); v->arch.guest_table = pagetable_from_page(pg); @@ -971,7 +971,11 @@ int arch_set_info_guest( case -EINTR: rc = -ERESTART; case -ERESTART: + break; case 0: + if ( !compat && !VM_ASSIST(d, m2p_strict) && + !paging_mode_refcounts(d) ) + fill_ro_mpt(cr3_gfn); break; default: if ( cr3_page == current->arch.old_guest_table ) @@ -1006,7 +1010,10 @@ int arch_set_info_guest( default: if ( cr3_page == current->arch.old_guest_table ) cr3_page = NULL; + break; case 0: + if ( VM_ASSIST(d, m2p_strict) ) + zap_ro_mpt(cr3_gfn); break; } } diff --git a/xen/arch/x86/domain_build.c b/xen/arch/x86/domain_build.c index 378e6503eb..03e4bfeb84 100644 --- a/xen/arch/x86/domain_build.c +++ b/xen/arch/x86/domain_build.c @@ -1205,7 +1205,7 @@ int __init construct_dom0( l3start = __va(mpt_alloc); mpt_alloc += PAGE_SIZE; } clear_page(l4tab); - init_guest_l4_table(l4tab, d); + init_guest_l4_table(l4tab, d, 0); v->arch.guest_table = pagetable_from_paddr(__pa(l4start)); if ( is_pv_32on64_domain(d) ) v->arch.guest_table_user = v->arch.guest_table; diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index b724aa59c6..034de22793 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -1380,7 +1380,8 @@ static int alloc_l3_table(struct page_info *page) return rc > 0 ? 0 : rc; } -void init_guest_l4_table(l4_pgentry_t l4tab[], const struct domain *d) +void init_guest_l4_table(l4_pgentry_t l4tab[], const struct domain *d, + bool_t zap_ro_mpt) { /* Xen private mappings. */ memcpy(&l4tab[ROOT_PAGETABLE_FIRST_XEN_SLOT], @@ -1395,6 +1396,25 @@ void init_guest_l4_table(l4_pgentry_t l4tab[], const struct domain *d) l4e_from_pfn(domain_page_map_to_mfn(l4tab), __PAGE_HYPERVISOR); l4tab[l4_table_offset(PERDOMAIN_VIRT_START)] = l4e_from_page(d->arch.perdomain_l3_pg, __PAGE_HYPERVISOR); + if ( zap_ro_mpt || is_pv_32on64_domain(d) || paging_mode_refcounts(d) ) + l4tab[l4_table_offset(RO_MPT_VIRT_START)] = l4e_empty(); +} + +void fill_ro_mpt(unsigned long mfn) +{ + l4_pgentry_t *l4tab = map_domain_page(mfn); + + l4tab[l4_table_offset(RO_MPT_VIRT_START)] = + idle_pg_table[l4_table_offset(RO_MPT_VIRT_START)]; + unmap_domain_page(l4tab); +} + +void zap_ro_mpt(unsigned long mfn) +{ + l4_pgentry_t *l4tab = map_domain_page(mfn); + + l4tab[l4_table_offset(RO_MPT_VIRT_START)] = l4e_empty(); + unmap_domain_page(l4tab); } static int alloc_l4_table(struct page_info *page) @@ -1444,7 +1464,7 @@ static int alloc_l4_table(struct page_info *page) adjust_guest_l4e(pl4e[i], d); } - init_guest_l4_table(pl4e, d); + init_guest_l4_table(pl4e, d, !VM_ASSIST(d, m2p_strict)); unmap_domain_page(pl4e); return rc > 0 ? 0 : rc; @@ -2754,6 +2774,8 @@ int new_guest_cr3(unsigned long mfn) invalidate_shadow_ldt(curr, 0); + if ( !VM_ASSIST(d, m2p_strict) && !paging_mode_refcounts(d) ) + fill_ro_mpt(mfn); curr->arch.guest_table = pagetable_from_pfn(mfn); update_cr3(curr); @@ -3111,6 +3133,8 @@ long do_mmuext_op( op.arg1.mfn); break; } + if ( VM_ASSIST(d, m2p_strict) && !paging_mode_refcounts(d) ) + zap_ro_mpt(op.arg1.mfn); } curr->arch.guest_table_user = pagetable_from_pfn(op.arg1.mfn); diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index c82aa96682..43e70d8900 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -1435,6 +1435,13 @@ void sh_install_xen_entries_in_l4(struct domain *d, mfn_t gl4mfn, mfn_t sl4mfn) shadow_l4e_from_mfn(page_to_mfn(d->arch.perdomain_l3_pg), __PAGE_HYPERVISOR); + if ( !shadow_mode_external(d) && !is_pv_32on64_domain(d) && + !VM_ASSIST(d, m2p_strict) ) + { + /* open coded zap_ro_mpt(mfn_x(sl4mfn)): */ + sl4e[shadow_l4_table_offset(RO_MPT_VIRT_START)] = shadow_l4e_empty(); + } + /* Shadow linear mapping for 4-level shadows. N.B. for 3-level * shadows on 64-bit xen, this linear mapping is later replaced by the * monitor pagetable structure, which is built in make_monitor_table @@ -4071,6 +4078,16 @@ sh_update_cr3(struct vcpu *v, int do_locking) if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) flush_tlb_mask(d->domain_dirty_cpumask); sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow); + if ( !shadow_mode_external(d) && !is_pv_32on64_domain(d) ) + { + mfn_t smfn = pagetable_get_mfn(v->arch.shadow_table[0]); + + if ( !(v->arch.flags & TF_kernel_mode) && VM_ASSIST(d, m2p_strict) ) + zap_ro_mpt(mfn_x(smfn)); + else if ( (v->arch.flags & TF_kernel_mode) && + !VM_ASSIST(d, m2p_strict) ) + fill_ro_mpt(mfn_x(smfn)); + } #else #error This should never happen #endif diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c index de95c38854..a771a014fd 100644 --- a/xen/arch/x86/x86_64/mm.c +++ b/xen/arch/x86/x86_64/mm.c @@ -480,7 +480,7 @@ static int setup_m2p_table(struct mem_hotadd_info *info) l2_ro_mpt += l2_table_offset(va); } - /* NB. Cannot be GLOBAL as shadow_mode_translate reuses this area. */ + /* NB. Cannot be GLOBAL: guest user mode should not see it. */ l2e_write(l2_ro_mpt, l2e_from_pfn(mfn, /*_PAGE_GLOBAL|*/_PAGE_PSE|_PAGE_USER|_PAGE_PRESENT)); } @@ -583,7 +583,7 @@ void __init paging_init(void) 0x77, 1UL << L3_PAGETABLE_SHIFT); ASSERT(!l2_table_offset(va)); - /* NB. Cannot be GLOBAL as shadow_mode_translate reuses this area. */ + /* NB. Cannot be GLOBAL: guest user mode should not see it. */ l3e_write(&l3_ro_mpt[l3_table_offset(va)], l3e_from_page(l1_pg, /*_PAGE_GLOBAL|*/_PAGE_PSE|_PAGE_USER|_PAGE_PRESENT)); @@ -621,7 +621,7 @@ void __init paging_init(void) l3e_from_page(l2_pg, __PAGE_HYPERVISOR | _PAGE_USER)); ASSERT(!l2_table_offset(va)); } - /* NB. Cannot be GLOBAL as shadow_mode_translate reuses this area. */ + /* NB. Cannot be GLOBAL: guest user mode should not see it. */ if ( l1_pg ) l2e_write(l2_ro_mpt, l2e_from_page( l1_pg, /*_PAGE_GLOBAL|*/_PAGE_PSE|_PAGE_USER|_PAGE_PRESENT)); diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h index 3569753da7..7700c779be 100644 --- a/xen/include/asm-x86/config.h +++ b/xen/include/asm-x86/config.h @@ -346,7 +346,8 @@ extern unsigned long xen_phys_start; #define NATIVE_VM_ASSIST_VALID ((1UL << VMASST_TYPE_4gb_segments) | \ (1UL << VMASST_TYPE_4gb_segments_notify) | \ (1UL << VMASST_TYPE_writable_pagetables) | \ - (1UL << VMASST_TYPE_pae_extended_cr3)) + (1UL << VMASST_TYPE_pae_extended_cr3) | \ + (1UL << VMASST_TYPE_m2p_strict)) #define VM_ASSIST_VALID NATIVE_VM_ASSIST_VALID #define COMPAT_VM_ASSIST_VALID (NATIVE_VM_ASSIST_VALID & \ ((1UL << COMPAT_BITS_PER_LONG) - 1)) diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h index 136f1c36b8..d1f95c85d9 100644 --- a/xen/include/asm-x86/mm.h +++ b/xen/include/asm-x86/mm.h @@ -318,7 +318,10 @@ static inline void *__page_to_virt(const struct page_info *pg) int free_page_type(struct page_info *page, unsigned long type, int preemptible); -void init_guest_l4_table(l4_pgentry_t[], const struct domain *); +void init_guest_l4_table(l4_pgentry_t[], const struct domain *, + bool_t zap_ro_mpt); +void fill_ro_mpt(unsigned long mfn); +void zap_ro_mpt(unsigned long mfn); int is_iomem_page(unsigned long mfn); diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h index dd52a50d7c..17ecb94d3d 100644 --- a/xen/include/public/xen.h +++ b/xen/include/public/xen.h @@ -486,6 +486,18 @@ DEFINE_XEN_GUEST_HANDLE(mmuext_op_t); /* x86/PAE guests: support PDPTs above 4GB. */ #define VMASST_TYPE_pae_extended_cr3 3 +/* + * x86/64 guests: strictly hide M2P from user mode. + * This allows the guest to control respective hypervisor behavior: + * - when not set, L4 tables get created with the respective slot blank, + * and whenever the L4 table gets used as a kernel one the missing + * mapping gets inserted, + * - when set, L4 tables get created with the respective slot initialized + * as before, and whenever the L4 table gets used as a user one the + * mapping gets zapped. + */ +#define VMASST_TYPE_m2p_strict 32 + #if __XEN_INTERFACE_VERSION__ < 0x00040600 #define MAX_VMASST_TYPE 3 #endif