Message ID | 20210528173935.29919-3-roger.pau@citrix.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | x86/ept: force WB to foreign and grant mappings | expand |
On 28.05.2021 19:39, Roger Pau Monne wrote: > This is an EPT specific function, so it shouldn't live in the generic > mtrr file. Such movement is also needed for future work that will > require passing a p2m_type_t parameter to epte_get_entry_emt, and > making that type visible to the mtrr users is cumbersome and > unneeded. > > Moving epte_get_entry_emt out of mtrr.c requires making the helper to > get the MTRR type of an address from the mtrr state public. While > there rename the function to start with the mtrr prefix, like other > mtrr related functions. > > While there fix some of the types of the function parameters. > > No functional change intended. > > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> > --- a/xen/arch/x86/hvm/vmx/vmx.c > +++ b/xen/arch/x86/hvm/vmx/vmx.c > @@ -417,12 +417,12 @@ static int vmx_domain_initialise(struct domain *d) > static void domain_creation_finished(struct domain *d) > { > gfn_t gfn = gaddr_to_gfn(APIC_DEFAULT_PHYS_BASE); > - uint8_t ipat; > + bool ipat; > > if ( !has_vlapic(d) || mfn_eq(apic_access_mfn, INVALID_MFN) ) > return; > > - ASSERT(epte_get_entry_emt(d, gfn_x(gfn), apic_access_mfn, 0, &ipat, > + ASSERT(epte_get_entry_emt(d, gfn, apic_access_mfn, 0, &ipat, > true) == MTRR_TYPE_WRBACK); > ASSERT(ipat); This being the only reason for the function to not be static in p2m-ept.c, I wonder whether it's really worthwhile to keep these assertions. But certainly not directly related to change at hand. Jan
> From: Roger Pau Monne <roger.pau@citrix.com> > Sent: Saturday, May 29, 2021 1:40 AM > > This is an EPT specific function, so it shouldn't live in the generic > mtrr file. Such movement is also needed for future work that will > require passing a p2m_type_t parameter to epte_get_entry_emt, and > making that type visible to the mtrr users is cumbersome and > unneeded. > > Moving epte_get_entry_emt out of mtrr.c requires making the helper to > get the MTRR type of an address from the mtrr state public. While > there rename the function to start with the mtrr prefix, like other > mtrr related functions. > > While there fix some of the types of the function parameters. > > No functional change intended. > > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> > --- > xen/arch/x86/hvm/mtrr.c | 107 +--------------------------- > xen/arch/x86/hvm/vmx/vmx.c | 4 +- > xen/arch/x86/mm/p2m-ept.c | 114 ++++++++++++++++++++++++++++-- > xen/include/asm-x86/hvm/vmx/vmx.h | 2 + > xen/include/asm-x86/mtrr.h | 5 +- > 5 files changed, 118 insertions(+), 114 deletions(-) > > diff --git a/xen/arch/x86/hvm/mtrr.c b/xen/arch/x86/hvm/mtrr.c > index 82ded1635c..4a9f3177ed 100644 > --- a/xen/arch/x86/hvm/mtrr.c > +++ b/xen/arch/x86/hvm/mtrr.c > @@ -194,8 +194,7 @@ void hvm_vcpu_cacheattr_destroy(struct vcpu *v) > * May return a negative value when order > 0, indicating to the caller > * that the respective mapping needs splitting. > */ > -static int get_mtrr_type(const struct mtrr_state *m, > - paddr_t pa, unsigned int order) > +int mtrr_get_type(const struct mtrr_state *m, paddr_t pa, unsigned int > order) > { > uint8_t overlap_mtrr = 0; > uint8_t overlap_mtrr_pos = 0; > @@ -323,7 +322,7 @@ static uint8_t effective_mm_type(struct mtrr_state > *m, > * just use it > */ > if ( gmtrr_mtype == NO_HARDCODE_MEM_TYPE ) > - mtrr_mtype = get_mtrr_type(m, gpa, 0); > + mtrr_mtype = mtrr_get_type(m, gpa, 0); > else > mtrr_mtype = gmtrr_mtype; > > @@ -350,7 +349,7 @@ uint32_t get_pat_flags(struct vcpu *v, > guest_eff_mm_type = effective_mm_type(g, pat, gpaddr, > gl1e_flags, gmtrr_mtype); > /* 2. Get the memory type of host physical address, with MTRR */ > - shadow_mtrr_type = get_mtrr_type(&mtrr_state, spaddr, 0); > + shadow_mtrr_type = mtrr_get_type(&mtrr_state, spaddr, 0); > > /* 3. Find the memory type in PAT, with host MTRR memory type > * and guest effective memory type. > @@ -789,106 +788,6 @@ void memory_type_changed(struct domain *d) > } > } > > -int epte_get_entry_emt(struct domain *d, unsigned long gfn, mfn_t mfn, > - unsigned int order, uint8_t *ipat, bool_t direct_mmio) > -{ > - int gmtrr_mtype, hmtrr_mtype; > - struct vcpu *v = current; > - unsigned long i; > - > - *ipat = 0; > - > - if ( v->domain != d ) > - v = d->vcpu ? d->vcpu[0] : NULL; > - > - /* Mask, not add, for order so it works with INVALID_MFN on unmapping > */ > - if ( rangeset_overlaps_range(mmio_ro_ranges, mfn_x(mfn), > - mfn_x(mfn) | ((1UL << order) - 1)) ) > - { > - if ( !order || rangeset_contains_range(mmio_ro_ranges, mfn_x(mfn), > - mfn_x(mfn) | ((1UL << order) - 1)) ) > - { > - *ipat = 1; > - return MTRR_TYPE_UNCACHABLE; > - } > - /* Force invalid memory type so resolve_misconfig() will split it */ > - return -1; > - } > - > - if ( !mfn_valid(mfn) ) > - { > - *ipat = 1; > - return MTRR_TYPE_UNCACHABLE; > - } > - > - if ( !direct_mmio && !is_iommu_enabled(d) > && !cache_flush_permitted(d) ) > - { > - *ipat = 1; > - return MTRR_TYPE_WRBACK; > - } > - > - for ( i = 0; i < (1ul << order); i++ ) > - { > - if ( is_special_page(mfn_to_page(mfn_add(mfn, i))) ) > - { > - if ( order ) > - return -1; > - *ipat = 1; > - return MTRR_TYPE_WRBACK; > - } > - } > - > - if ( direct_mmio ) > - return MTRR_TYPE_UNCACHABLE; > - > - gmtrr_mtype = hvm_get_mem_pinned_cacheattr(d, _gfn(gfn), order); > - if ( gmtrr_mtype >= 0 ) > - { > - *ipat = 1; > - return gmtrr_mtype != PAT_TYPE_UC_MINUS ? gmtrr_mtype > - : MTRR_TYPE_UNCACHABLE; > - } > - if ( gmtrr_mtype == -EADDRNOTAVAIL ) > - return -1; > - > - gmtrr_mtype = v ? get_mtrr_type(&v->arch.hvm.mtrr, gfn << PAGE_SHIFT, > order) > - : MTRR_TYPE_WRBACK; > - hmtrr_mtype = get_mtrr_type(&mtrr_state, mfn_x(mfn) << PAGE_SHIFT, > order); > - if ( gmtrr_mtype < 0 || hmtrr_mtype < 0 ) > - return -1; > - > - /* If both types match we're fine. */ > - if ( likely(gmtrr_mtype == hmtrr_mtype) ) > - return hmtrr_mtype; > - > - /* If either type is UC, we have to go with that one. */ > - if ( gmtrr_mtype == MTRR_TYPE_UNCACHABLE || > - hmtrr_mtype == MTRR_TYPE_UNCACHABLE ) > - return MTRR_TYPE_UNCACHABLE; > - > - /* If either type is WB, we have to go with the other one. */ > - if ( gmtrr_mtype == MTRR_TYPE_WRBACK ) > - return hmtrr_mtype; > - if ( hmtrr_mtype == MTRR_TYPE_WRBACK ) > - return gmtrr_mtype; > - > - /* > - * At this point we have disagreeing WC, WT, or WP types. The only > - * combination that can be cleanly resolved is WT:WP. The ones involving > - * WC need to be converted to UC, both due to the memory ordering > - * differences and because WC disallows reads to be cached (WT and WP > - * permit this), while WT and WP require writes to go straight to memory > - * (WC can buffer them). > - */ > - if ( (gmtrr_mtype == MTRR_TYPE_WRTHROUGH && > - hmtrr_mtype == MTRR_TYPE_WRPROT) || > - (gmtrr_mtype == MTRR_TYPE_WRPROT && > - hmtrr_mtype == MTRR_TYPE_WRTHROUGH) ) > - return MTRR_TYPE_WRPROT; > - > - return MTRR_TYPE_UNCACHABLE; > -} > - > /* > * Local variables: > * mode: C > diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c > index 7e3e67fdc3..0d4b47681b 100644 > --- a/xen/arch/x86/hvm/vmx/vmx.c > +++ b/xen/arch/x86/hvm/vmx/vmx.c > @@ -417,12 +417,12 @@ static int vmx_domain_initialise(struct domain *d) > static void domain_creation_finished(struct domain *d) > { > gfn_t gfn = gaddr_to_gfn(APIC_DEFAULT_PHYS_BASE); > - uint8_t ipat; > + bool ipat; > > if ( !has_vlapic(d) || mfn_eq(apic_access_mfn, INVALID_MFN) ) > return; > > - ASSERT(epte_get_entry_emt(d, gfn_x(gfn), apic_access_mfn, 0, &ipat, > + ASSERT(epte_get_entry_emt(d, gfn, apic_access_mfn, 0, &ipat, > true) == MTRR_TYPE_WRBACK); > ASSERT(ipat); > > diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c > index a3beaf91e2..f1d1d07e92 100644 > --- a/xen/arch/x86/mm/p2m-ept.c > +++ b/xen/arch/x86/mm/p2m-ept.c > @@ -20,6 +20,7 @@ > #include <public/hvm/dm_op.h> > #include <asm/altp2m.h> > #include <asm/current.h> > +#include <asm/iocap.h> > #include <asm/paging.h> > #include <asm/types.h> > #include <asm/domain.h> > @@ -485,6 +486,108 @@ static int ept_invalidate_emt_range(struct > p2m_domain *p2m, > return rc; > } > > +int epte_get_entry_emt(struct domain *d, gfn_t gfn, mfn_t mfn, > + unsigned int order, bool *ipat, bool direct_mmio) > +{ > + int gmtrr_mtype, hmtrr_mtype; > + struct vcpu *v = current; > + unsigned long i; > + > + *ipat = false; > + > + if ( v->domain != d ) > + v = d->vcpu ? d->vcpu[0] : NULL; > + > + /* Mask, not add, for order so it works with INVALID_MFN on unmapping > */ > + if ( rangeset_overlaps_range(mmio_ro_ranges, mfn_x(mfn), > + mfn_x(mfn) | ((1UL << order) - 1)) ) > + { > + if ( !order || rangeset_contains_range(mmio_ro_ranges, mfn_x(mfn), > + mfn_x(mfn) | ((1UL << order) - 1)) ) > + { > + *ipat = true; > + return MTRR_TYPE_UNCACHABLE; > + } > + /* Force invalid memory type so resolve_misconfig() will split it */ > + return -1; > + } > + > + if ( !mfn_valid(mfn) ) > + { > + *ipat = true; > + return MTRR_TYPE_UNCACHABLE; > + } > + > + if ( !direct_mmio && !is_iommu_enabled(d) > && !cache_flush_permitted(d) ) > + { > + *ipat = true; > + return MTRR_TYPE_WRBACK; > + } > + > + for ( i = 0; i < (1ul << order); i++ ) > + { > + if ( is_special_page(mfn_to_page(mfn_add(mfn, i))) ) > + { > + if ( order ) > + return -1; > + *ipat = true; > + return MTRR_TYPE_WRBACK; > + } > + } > + > + if ( direct_mmio ) > + return MTRR_TYPE_UNCACHABLE; > + > + gmtrr_mtype = hvm_get_mem_pinned_cacheattr(d, gfn, order); > + if ( gmtrr_mtype >= 0 ) > + { > + *ipat = true; > + return gmtrr_mtype != PAT_TYPE_UC_MINUS ? gmtrr_mtype > + : MTRR_TYPE_UNCACHABLE; > + } > + if ( gmtrr_mtype == -EADDRNOTAVAIL ) > + return -1; > + > + gmtrr_mtype = v ? mtrr_get_type(&v->arch.hvm.mtrr, > + gfn_x(gfn) << PAGE_SHIFT, order) > + : MTRR_TYPE_WRBACK; > + hmtrr_mtype = mtrr_get_type(&mtrr_state, mfn_x(mfn) << PAGE_SHIFT, > + order); > + if ( gmtrr_mtype < 0 || hmtrr_mtype < 0 ) > + return -1; > + > + /* If both types match we're fine. */ > + if ( likely(gmtrr_mtype == hmtrr_mtype) ) > + return hmtrr_mtype; > + > + /* If either type is UC, we have to go with that one. */ > + if ( gmtrr_mtype == MTRR_TYPE_UNCACHABLE || > + hmtrr_mtype == MTRR_TYPE_UNCACHABLE ) > + return MTRR_TYPE_UNCACHABLE; > + > + /* If either type is WB, we have to go with the other one. */ > + if ( gmtrr_mtype == MTRR_TYPE_WRBACK ) > + return hmtrr_mtype; > + if ( hmtrr_mtype == MTRR_TYPE_WRBACK ) > + return gmtrr_mtype; > + > + /* > + * At this point we have disagreeing WC, WT, or WP types. The only > + * combination that can be cleanly resolved is WT:WP. The ones involving > + * WC need to be converted to UC, both due to the memory ordering > + * differences and because WC disallows reads to be cached (WT and WP > + * permit this), while WT and WP require writes to go straight to memory > + * (WC can buffer them). > + */ > + if ( (gmtrr_mtype == MTRR_TYPE_WRTHROUGH && > + hmtrr_mtype == MTRR_TYPE_WRPROT) || > + (gmtrr_mtype == MTRR_TYPE_WRPROT && > + hmtrr_mtype == MTRR_TYPE_WRTHROUGH) ) > + return MTRR_TYPE_WRPROT; > + > + return MTRR_TYPE_UNCACHABLE; > +} > + > /* > * Resolve deliberately mis-configured (EMT field set to an invalid value) > * entries in the page table hierarchy for the given GFN: > @@ -519,7 +622,7 @@ static int resolve_misconfig(struct p2m_domain > *p2m, unsigned long gfn) > > if ( level == 0 || is_epte_superpage(&e) ) > { > - uint8_t ipat = 0; > + bool ipat; > > if ( e.emt != MTRR_NUM_TYPES ) > break; > @@ -535,7 +638,7 @@ static int resolve_misconfig(struct p2m_domain > *p2m, unsigned long gfn) > e.emt = 0; > if ( !is_epte_valid(&e) || !is_epte_present(&e) ) > continue; > - e.emt = epte_get_entry_emt(p2m->domain, gfn + i, > + e.emt = epte_get_entry_emt(p2m->domain, _gfn(gfn + i), > _mfn(e.mfn), 0, &ipat, > e.sa_p2mt == p2m_mmio_direct); > e.ipat = ipat; > @@ -553,7 +656,8 @@ static int resolve_misconfig(struct p2m_domain > *p2m, unsigned long gfn) > } > else > { > - int emt = epte_get_entry_emt(p2m->domain, gfn, _mfn(e.mfn), > + int emt = epte_get_entry_emt(p2m->domain, _gfn(gfn), > + _mfn(e.mfn), > level * EPT_TABLE_ORDER, &ipat, > e.sa_p2mt == p2m_mmio_direct); > bool_t recalc = e.recalc; > @@ -788,8 +892,8 @@ ept_set_entry(struct p2m_domain *p2m, gfn_t gfn_, > mfn_t mfn, > > if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) > { > - uint8_t ipat = 0; > - int emt = epte_get_entry_emt(p2m->domain, gfn, mfn, > + bool ipat; > + int emt = epte_get_entry_emt(p2m->domain, _gfn(gfn), mfn, > i * EPT_TABLE_ORDER, &ipat, > p2mt == p2m_mmio_direct); > > diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm- > x86/hvm/vmx/vmx.h > index 534e9fc221..f668ee1f09 100644 > --- a/xen/include/asm-x86/hvm/vmx/vmx.h > +++ b/xen/include/asm-x86/hvm/vmx/vmx.h > @@ -599,6 +599,8 @@ void ept_p2m_uninit(struct p2m_domain *p2m); > > void ept_walk_table(struct domain *d, unsigned long gfn); > bool_t ept_handle_misconfig(uint64_t gpa); > +int epte_get_entry_emt(struct domain *d, gfn_t gfn, mfn_t mfn, > + unsigned int order, bool *ipat, bool direct_mmio); > void setup_ept_dump(void); > void p2m_init_altp2m_ept(struct domain *d, unsigned int i); > /* Locate an alternate p2m by its EPTP */ > diff --git a/xen/include/asm-x86/mtrr.h b/xen/include/asm-x86/mtrr.h > index 24e5de5c22..e0fd1005ce 100644 > --- a/xen/include/asm-x86/mtrr.h > +++ b/xen/include/asm-x86/mtrr.h > @@ -72,12 +72,11 @@ extern int mtrr_add_page(unsigned long base, > unsigned long size, > unsigned int type, char increment); > extern int mtrr_del(int reg, unsigned long base, unsigned long size); > extern int mtrr_del_page(int reg, unsigned long base, unsigned long size); > +extern int mtrr_get_type(const struct mtrr_state *m, paddr_t pa, > + unsigned int order); > extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi); > extern u32 get_pat_flags(struct vcpu *v, u32 gl1e_flags, paddr_t gpaddr, > paddr_t spaddr, uint8_t gmtrr_mtype); > -extern int epte_get_entry_emt(struct domain *, unsigned long gfn, mfn_t > mfn, > - unsigned int order, uint8_t *ipat, > - bool_t direct_mmio); > extern unsigned char pat_type_2_pte_flags(unsigned char pat_type); > extern int hold_mtrr_updates_on_aps; > extern void mtrr_aps_sync_begin(void); > -- > 2.31.1
diff --git a/xen/arch/x86/hvm/mtrr.c b/xen/arch/x86/hvm/mtrr.c index 82ded1635c..4a9f3177ed 100644 --- a/xen/arch/x86/hvm/mtrr.c +++ b/xen/arch/x86/hvm/mtrr.c @@ -194,8 +194,7 @@ void hvm_vcpu_cacheattr_destroy(struct vcpu *v) * May return a negative value when order > 0, indicating to the caller * that the respective mapping needs splitting. */ -static int get_mtrr_type(const struct mtrr_state *m, - paddr_t pa, unsigned int order) +int mtrr_get_type(const struct mtrr_state *m, paddr_t pa, unsigned int order) { uint8_t overlap_mtrr = 0; uint8_t overlap_mtrr_pos = 0; @@ -323,7 +322,7 @@ static uint8_t effective_mm_type(struct mtrr_state *m, * just use it */ if ( gmtrr_mtype == NO_HARDCODE_MEM_TYPE ) - mtrr_mtype = get_mtrr_type(m, gpa, 0); + mtrr_mtype = mtrr_get_type(m, gpa, 0); else mtrr_mtype = gmtrr_mtype; @@ -350,7 +349,7 @@ uint32_t get_pat_flags(struct vcpu *v, guest_eff_mm_type = effective_mm_type(g, pat, gpaddr, gl1e_flags, gmtrr_mtype); /* 2. Get the memory type of host physical address, with MTRR */ - shadow_mtrr_type = get_mtrr_type(&mtrr_state, spaddr, 0); + shadow_mtrr_type = mtrr_get_type(&mtrr_state, spaddr, 0); /* 3. Find the memory type in PAT, with host MTRR memory type * and guest effective memory type. @@ -789,106 +788,6 @@ void memory_type_changed(struct domain *d) } } -int epte_get_entry_emt(struct domain *d, unsigned long gfn, mfn_t mfn, - unsigned int order, uint8_t *ipat, bool_t direct_mmio) -{ - int gmtrr_mtype, hmtrr_mtype; - struct vcpu *v = current; - unsigned long i; - - *ipat = 0; - - if ( v->domain != d ) - v = d->vcpu ? d->vcpu[0] : NULL; - - /* Mask, not add, for order so it works with INVALID_MFN on unmapping */ - if ( rangeset_overlaps_range(mmio_ro_ranges, mfn_x(mfn), - mfn_x(mfn) | ((1UL << order) - 1)) ) - { - if ( !order || rangeset_contains_range(mmio_ro_ranges, mfn_x(mfn), - mfn_x(mfn) | ((1UL << order) - 1)) ) - { - *ipat = 1; - return MTRR_TYPE_UNCACHABLE; - } - /* Force invalid memory type so resolve_misconfig() will split it */ - return -1; - } - - if ( !mfn_valid(mfn) ) - { - *ipat = 1; - return MTRR_TYPE_UNCACHABLE; - } - - if ( !direct_mmio && !is_iommu_enabled(d) && !cache_flush_permitted(d) ) - { - *ipat = 1; - return MTRR_TYPE_WRBACK; - } - - for ( i = 0; i < (1ul << order); i++ ) - { - if ( is_special_page(mfn_to_page(mfn_add(mfn, i))) ) - { - if ( order ) - return -1; - *ipat = 1; - return MTRR_TYPE_WRBACK; - } - } - - if ( direct_mmio ) - return MTRR_TYPE_UNCACHABLE; - - gmtrr_mtype = hvm_get_mem_pinned_cacheattr(d, _gfn(gfn), order); - if ( gmtrr_mtype >= 0 ) - { - *ipat = 1; - return gmtrr_mtype != PAT_TYPE_UC_MINUS ? gmtrr_mtype - : MTRR_TYPE_UNCACHABLE; - } - if ( gmtrr_mtype == -EADDRNOTAVAIL ) - return -1; - - gmtrr_mtype = v ? get_mtrr_type(&v->arch.hvm.mtrr, gfn << PAGE_SHIFT, order) - : MTRR_TYPE_WRBACK; - hmtrr_mtype = get_mtrr_type(&mtrr_state, mfn_x(mfn) << PAGE_SHIFT, order); - if ( gmtrr_mtype < 0 || hmtrr_mtype < 0 ) - return -1; - - /* If both types match we're fine. */ - if ( likely(gmtrr_mtype == hmtrr_mtype) ) - return hmtrr_mtype; - - /* If either type is UC, we have to go with that one. */ - if ( gmtrr_mtype == MTRR_TYPE_UNCACHABLE || - hmtrr_mtype == MTRR_TYPE_UNCACHABLE ) - return MTRR_TYPE_UNCACHABLE; - - /* If either type is WB, we have to go with the other one. */ - if ( gmtrr_mtype == MTRR_TYPE_WRBACK ) - return hmtrr_mtype; - if ( hmtrr_mtype == MTRR_TYPE_WRBACK ) - return gmtrr_mtype; - - /* - * At this point we have disagreeing WC, WT, or WP types. The only - * combination that can be cleanly resolved is WT:WP. The ones involving - * WC need to be converted to UC, both due to the memory ordering - * differences and because WC disallows reads to be cached (WT and WP - * permit this), while WT and WP require writes to go straight to memory - * (WC can buffer them). - */ - if ( (gmtrr_mtype == MTRR_TYPE_WRTHROUGH && - hmtrr_mtype == MTRR_TYPE_WRPROT) || - (gmtrr_mtype == MTRR_TYPE_WRPROT && - hmtrr_mtype == MTRR_TYPE_WRTHROUGH) ) - return MTRR_TYPE_WRPROT; - - return MTRR_TYPE_UNCACHABLE; -} - /* * Local variables: * mode: C diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 7e3e67fdc3..0d4b47681b 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -417,12 +417,12 @@ static int vmx_domain_initialise(struct domain *d) static void domain_creation_finished(struct domain *d) { gfn_t gfn = gaddr_to_gfn(APIC_DEFAULT_PHYS_BASE); - uint8_t ipat; + bool ipat; if ( !has_vlapic(d) || mfn_eq(apic_access_mfn, INVALID_MFN) ) return; - ASSERT(epte_get_entry_emt(d, gfn_x(gfn), apic_access_mfn, 0, &ipat, + ASSERT(epte_get_entry_emt(d, gfn, apic_access_mfn, 0, &ipat, true) == MTRR_TYPE_WRBACK); ASSERT(ipat); diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c index a3beaf91e2..f1d1d07e92 100644 --- a/xen/arch/x86/mm/p2m-ept.c +++ b/xen/arch/x86/mm/p2m-ept.c @@ -20,6 +20,7 @@ #include <public/hvm/dm_op.h> #include <asm/altp2m.h> #include <asm/current.h> +#include <asm/iocap.h> #include <asm/paging.h> #include <asm/types.h> #include <asm/domain.h> @@ -485,6 +486,108 @@ static int ept_invalidate_emt_range(struct p2m_domain *p2m, return rc; } +int epte_get_entry_emt(struct domain *d, gfn_t gfn, mfn_t mfn, + unsigned int order, bool *ipat, bool direct_mmio) +{ + int gmtrr_mtype, hmtrr_mtype; + struct vcpu *v = current; + unsigned long i; + + *ipat = false; + + if ( v->domain != d ) + v = d->vcpu ? d->vcpu[0] : NULL; + + /* Mask, not add, for order so it works with INVALID_MFN on unmapping */ + if ( rangeset_overlaps_range(mmio_ro_ranges, mfn_x(mfn), + mfn_x(mfn) | ((1UL << order) - 1)) ) + { + if ( !order || rangeset_contains_range(mmio_ro_ranges, mfn_x(mfn), + mfn_x(mfn) | ((1UL << order) - 1)) ) + { + *ipat = true; + return MTRR_TYPE_UNCACHABLE; + } + /* Force invalid memory type so resolve_misconfig() will split it */ + return -1; + } + + if ( !mfn_valid(mfn) ) + { + *ipat = true; + return MTRR_TYPE_UNCACHABLE; + } + + if ( !direct_mmio && !is_iommu_enabled(d) && !cache_flush_permitted(d) ) + { + *ipat = true; + return MTRR_TYPE_WRBACK; + } + + for ( i = 0; i < (1ul << order); i++ ) + { + if ( is_special_page(mfn_to_page(mfn_add(mfn, i))) ) + { + if ( order ) + return -1; + *ipat = true; + return MTRR_TYPE_WRBACK; + } + } + + if ( direct_mmio ) + return MTRR_TYPE_UNCACHABLE; + + gmtrr_mtype = hvm_get_mem_pinned_cacheattr(d, gfn, order); + if ( gmtrr_mtype >= 0 ) + { + *ipat = true; + return gmtrr_mtype != PAT_TYPE_UC_MINUS ? gmtrr_mtype + : MTRR_TYPE_UNCACHABLE; + } + if ( gmtrr_mtype == -EADDRNOTAVAIL ) + return -1; + + gmtrr_mtype = v ? mtrr_get_type(&v->arch.hvm.mtrr, + gfn_x(gfn) << PAGE_SHIFT, order) + : MTRR_TYPE_WRBACK; + hmtrr_mtype = mtrr_get_type(&mtrr_state, mfn_x(mfn) << PAGE_SHIFT, + order); + if ( gmtrr_mtype < 0 || hmtrr_mtype < 0 ) + return -1; + + /* If both types match we're fine. */ + if ( likely(gmtrr_mtype == hmtrr_mtype) ) + return hmtrr_mtype; + + /* If either type is UC, we have to go with that one. */ + if ( gmtrr_mtype == MTRR_TYPE_UNCACHABLE || + hmtrr_mtype == MTRR_TYPE_UNCACHABLE ) + return MTRR_TYPE_UNCACHABLE; + + /* If either type is WB, we have to go with the other one. */ + if ( gmtrr_mtype == MTRR_TYPE_WRBACK ) + return hmtrr_mtype; + if ( hmtrr_mtype == MTRR_TYPE_WRBACK ) + return gmtrr_mtype; + + /* + * At this point we have disagreeing WC, WT, or WP types. The only + * combination that can be cleanly resolved is WT:WP. The ones involving + * WC need to be converted to UC, both due to the memory ordering + * differences and because WC disallows reads to be cached (WT and WP + * permit this), while WT and WP require writes to go straight to memory + * (WC can buffer them). + */ + if ( (gmtrr_mtype == MTRR_TYPE_WRTHROUGH && + hmtrr_mtype == MTRR_TYPE_WRPROT) || + (gmtrr_mtype == MTRR_TYPE_WRPROT && + hmtrr_mtype == MTRR_TYPE_WRTHROUGH) ) + return MTRR_TYPE_WRPROT; + + return MTRR_TYPE_UNCACHABLE; +} + /* * Resolve deliberately mis-configured (EMT field set to an invalid value) * entries in the page table hierarchy for the given GFN: @@ -519,7 +622,7 @@ static int resolve_misconfig(struct p2m_domain *p2m, unsigned long gfn) if ( level == 0 || is_epte_superpage(&e) ) { - uint8_t ipat = 0; + bool ipat; if ( e.emt != MTRR_NUM_TYPES ) break; @@ -535,7 +638,7 @@ static int resolve_misconfig(struct p2m_domain *p2m, unsigned long gfn) e.emt = 0; if ( !is_epte_valid(&e) || !is_epte_present(&e) ) continue; - e.emt = epte_get_entry_emt(p2m->domain, gfn + i, + e.emt = epte_get_entry_emt(p2m->domain, _gfn(gfn + i), _mfn(e.mfn), 0, &ipat, e.sa_p2mt == p2m_mmio_direct); e.ipat = ipat; @@ -553,7 +656,8 @@ static int resolve_misconfig(struct p2m_domain *p2m, unsigned long gfn) } else { - int emt = epte_get_entry_emt(p2m->domain, gfn, _mfn(e.mfn), + int emt = epte_get_entry_emt(p2m->domain, _gfn(gfn), + _mfn(e.mfn), level * EPT_TABLE_ORDER, &ipat, e.sa_p2mt == p2m_mmio_direct); bool_t recalc = e.recalc; @@ -788,8 +892,8 @@ ept_set_entry(struct p2m_domain *p2m, gfn_t gfn_, mfn_t mfn, if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) { - uint8_t ipat = 0; - int emt = epte_get_entry_emt(p2m->domain, gfn, mfn, + bool ipat; + int emt = epte_get_entry_emt(p2m->domain, _gfn(gfn), mfn, i * EPT_TABLE_ORDER, &ipat, p2mt == p2m_mmio_direct); diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h index 534e9fc221..f668ee1f09 100644 --- a/xen/include/asm-x86/hvm/vmx/vmx.h +++ b/xen/include/asm-x86/hvm/vmx/vmx.h @@ -599,6 +599,8 @@ void ept_p2m_uninit(struct p2m_domain *p2m); void ept_walk_table(struct domain *d, unsigned long gfn); bool_t ept_handle_misconfig(uint64_t gpa); +int epte_get_entry_emt(struct domain *d, gfn_t gfn, mfn_t mfn, + unsigned int order, bool *ipat, bool direct_mmio); void setup_ept_dump(void); void p2m_init_altp2m_ept(struct domain *d, unsigned int i); /* Locate an alternate p2m by its EPTP */ diff --git a/xen/include/asm-x86/mtrr.h b/xen/include/asm-x86/mtrr.h index 24e5de5c22..e0fd1005ce 100644 --- a/xen/include/asm-x86/mtrr.h +++ b/xen/include/asm-x86/mtrr.h @@ -72,12 +72,11 @@ extern int mtrr_add_page(unsigned long base, unsigned long size, unsigned int type, char increment); extern int mtrr_del(int reg, unsigned long base, unsigned long size); extern int mtrr_del_page(int reg, unsigned long base, unsigned long size); +extern int mtrr_get_type(const struct mtrr_state *m, paddr_t pa, + unsigned int order); extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi); extern u32 get_pat_flags(struct vcpu *v, u32 gl1e_flags, paddr_t gpaddr, paddr_t spaddr, uint8_t gmtrr_mtype); -extern int epte_get_entry_emt(struct domain *, unsigned long gfn, mfn_t mfn, - unsigned int order, uint8_t *ipat, - bool_t direct_mmio); extern unsigned char pat_type_2_pte_flags(unsigned char pat_type); extern int hold_mtrr_updates_on_aps; extern void mtrr_aps_sync_begin(void);
This is an EPT specific function, so it shouldn't live in the generic mtrr file. Such movement is also needed for future work that will require passing a p2m_type_t parameter to epte_get_entry_emt, and making that type visible to the mtrr users is cumbersome and unneeded. Moving epte_get_entry_emt out of mtrr.c requires making the helper to get the MTRR type of an address from the mtrr state public. While there rename the function to start with the mtrr prefix, like other mtrr related functions. While there fix some of the types of the function parameters. No functional change intended. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> --- xen/arch/x86/hvm/mtrr.c | 107 +--------------------------- xen/arch/x86/hvm/vmx/vmx.c | 4 +- xen/arch/x86/mm/p2m-ept.c | 114 ++++++++++++++++++++++++++++-- xen/include/asm-x86/hvm/vmx/vmx.h | 2 + xen/include/asm-x86/mtrr.h | 5 +- 5 files changed, 118 insertions(+), 114 deletions(-)