[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen stable-4.14] xen/arm: Do not invalidate the P2M when the PT is shared with the IOMMU



commit cbadf67bcab4e29c883410db393f4f5ef34df04a
Author:     Stefano Stabellini <sstabellini@xxxxxxxxxx>
AuthorDate: Wed Aug 4 13:57:07 2021 -0700
Commit:     Stefano Stabellini <stefano.stabellini@xxxxxxxxxx>
CommitDate: Tue Dec 14 12:39:20 2021 -0800

    xen/arm: Do not invalidate the P2M when the PT is shared with the IOMMU
    
    Set/Way flushes never work correctly in a virtualized environment.
    
    Our current implementation is based on clearing the valid bit in the p2m
    pagetable to track guest memory accesses. This technique doesn't work
    when the IOMMU is enabled for the domain and the pagetable is shared
    between IOMMU and MMU because it triggers IOMMU faults.
    
    Specifically, p2m_invalidate_root causes IOMMU faults if
    iommu_use_hap_pt returns true for the domain.
    
    Add a check in p2m_set_way_flush: if a set/way instruction is used
    and iommu_use_hap_pt returns true, rather than failing with obscure
    IOMMU faults, inject an undef exception straight away into the guest,
    and print a verbose error message to explain the problem.
    
    Also add an ASSERT in p2m_invalidate_root to make sure we don't
    inadvertently stumble across this problem again in the future.
    
    Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxx>
    Reviewed-by: Julien Grall <jgrall@xxxxxxxxxx>
    (cherry picked from commit 2b45ff60301a988badec526846e77b538383ae63)
---
 xen/arch/arm/arm64/vsysreg.c |  2 +-
 xen/arch/arm/p2m.c           | 17 ++++++++++++++++-
 xen/arch/arm/vcpreg.c        |  2 +-
 xen/include/asm-arm/p2m.h    |  4 +++-
 4 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/xen/arch/arm/arm64/vsysreg.c b/xen/arch/arm/arm64/vsysreg.c
index 8a85507d9d..69a91aaa84 100644
--- a/xen/arch/arm/arm64/vsysreg.c
+++ b/xen/arch/arm/arm64/vsysreg.c
@@ -98,7 +98,7 @@ void do_sysreg(struct cpu_user_regs *regs,
     case HSR_SYSREG_DCCSW:
     case HSR_SYSREG_DCCISW:
         if ( !hsr.sysreg.read )
-            p2m_set_way_flush(current);
+            p2m_set_way_flush(current, regs, hsr);
         break;
 
     /*
diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 4eeb867ca1..ac821a9094 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -10,6 +10,7 @@
 #include <asm/flushtlb.h>
 #include <asm/guest_walk.h>
 #include <asm/page.h>
+#include <asm/traps.h>
 
 #define MAX_VMID_8_BIT  (1UL << 8)
 #define MAX_VMID_16_BIT (1UL << 16)
@@ -1158,11 +1159,16 @@ static void p2m_invalidate_table(struct p2m_domain 
*p2m, mfn_t mfn)
 /*
  * Invalidate all entries in the root page-tables. This is
  * useful to get fault on entry and do an action.
+ *
+ * p2m_invalid_root() should not be called when the P2M is shared with
+ * the IOMMU because it will cause IOMMU fault.
  */
 void p2m_invalidate_root(struct p2m_domain *p2m)
 {
     unsigned int i;
 
+    ASSERT(!iommu_use_hap_pt(p2m->domain));
+
     p2m_write_lock(p2m);
 
     for ( i = 0; i < P2M_ROOT_LEVEL; i++ )
@@ -1781,11 +1787,20 @@ void p2m_flush_vm(struct vcpu *v)
  *
  *  - Once the caches are enabled, we stop trapping VM ops.
  */
-void p2m_set_way_flush(struct vcpu *v)
+void p2m_set_way_flush(struct vcpu *v, struct cpu_user_regs *regs,
+                       const union hsr hsr)
 {
     /* This function can only work with the current vCPU. */
     ASSERT(v == current);
 
+    if ( iommu_use_hap_pt(current->domain) )
+    {
+        gprintk(XENLOG_ERR,
+                "The cache should be flushed by VA rather than by set/way.\n");
+        inject_undef_exception(regs, hsr);
+        return;
+    }
+
     if ( !(v->arch.hcr_el2 & HCR_TVM) )
     {
         v->arch.need_flush_to_ram = true;
diff --git a/xen/arch/arm/vcpreg.c b/xen/arch/arm/vcpreg.c
index cdc91cdf5b..f4557b80a7 100644
--- a/xen/arch/arm/vcpreg.c
+++ b/xen/arch/arm/vcpreg.c
@@ -204,7 +204,7 @@ void do_cp15_32(struct cpu_user_regs *regs, const union hsr 
hsr)
     case HSR_CPREG32(DCCSW):
     case HSR_CPREG32(DCCISW):
         if ( !cp32.read )
-            p2m_set_way_flush(current);
+            p2m_set_way_flush(current, regs, hsr);
         break;
 
     /*
diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
index 28ca9a838e..ea8a03449d 100644
--- a/xen/include/asm-arm/p2m.h
+++ b/xen/include/asm-arm/p2m.h
@@ -7,6 +7,7 @@
 #include <xen/mem_access.h>
 
 #include <asm/current.h>
+#include <asm/hsr.h>
 
 #define paddr_bits PADDR_BITS
 
@@ -263,7 +264,8 @@ void p2m_invalidate_root(struct p2m_domain *p2m);
  */
 int p2m_cache_flush_range(struct domain *d, gfn_t *pstart, gfn_t end);
 
-void p2m_set_way_flush(struct vcpu *v);
+void p2m_set_way_flush(struct vcpu *v, struct cpu_user_regs *regs,
+                       const union hsr hsr);
 
 void p2m_toggle_cache(struct vcpu *v, bool was_enabled);
 
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.14



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.