Mailing List Archive

[xen master] AMD/IOMMU: update live PTEs atomically
commit 3b055121c5410e2c3105d6d06aa24ca0d58868cd
Author: Jan Beulich <jbeulich@suse.com>
AuthorDate: Tue Oct 20 14:22:52 2020 +0200
Commit: Jan Beulich <jbeulich@suse.com>
CommitDate: Tue Oct 20 14:22:52 2020 +0200

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>
---
xen/drivers/passthrough/amd/iommu_map.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c
index 050eca838b..480cd41c58 100644
--- a/xen/drivers/passthrough/amd/iommu_map.c
+++ b/xen/drivers/passthrough/amd/iommu_map.c
@@ -41,7 +41,7 @@ static unsigned int clear_iommu_pte_present(unsigned long l1_mfn,
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);

@@ -53,26 +53,30 @@ static unsigned int set_iommu_pde_present(union amd_iommu_pte *pte,
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;
}
--
generated by git-patchbot for /home/xen/git/xen.git#master