summaryrefslogtreecommitdiffstats
path: root/system/xen/xsa/xsa347-4.13-2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'system/xen/xsa/xsa347-4.13-2.patch')
-rw-r--r--system/xen/xsa/xsa347-4.13-2.patch72
1 files changed, 72 insertions, 0 deletions
diff --git a/system/xen/xsa/xsa347-4.13-2.patch b/system/xen/xsa/xsa347-4.13-2.patch
new file mode 100644
index 0000000000..fbe7461636
--- /dev/null
+++ b/system/xen/xsa/xsa347-4.13-2.patch
@@ -0,0 +1,72 @@
+From: Jan Beulich <jbeulich@suse.com>
+Subject: AMD/IOMMU: update live PTEs atomically
+
+Updating a live PTE bitfield by bitfield risks the compiler re-ordering
+the individual updates as well as splitting individual updates into
+multiple memory writes. Construct the new entry fully in a local
+variable, do the check to determine the flushing needs on the thus
+established new entry, and then write the new entry by a single insn.
+
+Similarly using memset() to clear a PTE is unsafe, as the order of
+writes the function does is, at least in principle, undefined.
+
+This is part of XSA-347.
+
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+Reviewed-by: Paul Durrant <paul@xen.org>
+
+--- a/xen/drivers/passthrough/amd/iommu_map.c
++++ b/xen/drivers/passthrough/amd/iommu_map.c
+@@ -45,7 +45,7 @@ static unsigned int clear_iommu_pte_pres
+ pte = &table[pfn_to_pde_idx(dfn, 1)];
+
+ flush_flags = pte->pr ? IOMMU_FLUSHF_modified : 0;
+- memset(pte, 0, sizeof(*pte));
++ write_atomic(&pte->raw, 0);
+
+ unmap_domain_page(table);
+
+@@ -57,26 +57,30 @@ static unsigned int set_iommu_pde_presen
+ unsigned int next_level, bool iw,
+ bool ir)
+ {
++ union amd_iommu_pte new = {}, old;
+ unsigned int flush_flags = IOMMU_FLUSHF_added;
+
+- if ( pte->pr &&
+- (pte->mfn != next_mfn ||
+- pte->iw != iw ||
+- pte->ir != ir ||
+- pte->next_level != next_level) )
+- flush_flags |= IOMMU_FLUSHF_modified;
+-
+ /*
+ * FC bit should be enabled in PTE, this helps to solve potential
+ * issues with ATS devices
+ */
+- pte->fc = !next_level;
++ new.fc = !next_level;
++
++ new.mfn = next_mfn;
++ new.iw = iw;
++ new.ir = ir;
++ new.next_level = next_level;
++ new.pr = true;
++
++ old.raw = read_atomic(&pte->raw);
++ old.ign0 = 0;
++ old.ign1 = 0;
++ old.ign2 = 0;
++
++ if ( old.pr && old.raw != new.raw )
++ flush_flags |= IOMMU_FLUSHF_modified;
+
+- pte->mfn = next_mfn;
+- pte->iw = iw;
+- pte->ir = ir;
+- pte->next_level = next_level;
+- pte->pr = 1;
++ write_atomic(&pte->raw, new.raw);
+
+ return flush_flags;
+ }