From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:07 +0000 (+0000) Subject: amd_iommu: Invalidate address translations on INVALIDATE_IOMMU_ALL X-Git-Tag: v10.2.0-rc1~76^2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b6a59a2ebf13df35370440b63ea1b8cf0f06435b;p=thirdparty%2Fqemu.git amd_iommu: Invalidate address translations on INVALIDATE_IOMMU_ALL When the kernel IOMMU driver issues an INVALIDATE_IOMMU_ALL, the address translation and interrupt remapping information must be cleared for all Device IDs and all domains. Introduce a helper to sync the shadow page table for all the address spaces with registered notifiers, which replays both MAP and UNMAP events. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-15-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 9027f7c054..d74d42b3dd 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -941,6 +941,47 @@ static void amdvi_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) amdvi_sync_shadow_page_table_range(as, &dte[0], 0, UINT64_MAX, false); } +static void amdvi_address_space_sync(AMDVIAddressSpace *as) +{ + IOMMUNotifier *n; + uint64_t dte[4] = { 0 }; + + /* If only UNMAP notifiers are registered, drop all existing mappings */ + if (!(as->notifier_flags & IOMMU_NOTIFIER_MAP)) { + IOMMU_NOTIFIER_FOREACH(n, &as->iommu) { + /* + * Directly calling memory_region_unmap_iommu_notifier_range() does + * not guarantee that the addr_mask eventually passed as parameter + * to the notifier is valid. Use amdvi_address_space_unmap() which + * ensures the notifier range is divided into properly aligned + * regions, and issues notifications for each one. + */ + amdvi_address_space_unmap(as, n); + } + return; + } + + if (amdvi_as_to_dte(as, dte)) { + return; + } + + amdvi_sync_shadow_page_table_range(as, &dte[0], 0, UINT64_MAX, true); +} + +/* + * This differs from the replay() method in that it issues both MAP and UNMAP + * notifications since it is called after global invalidation events in order to + * re-sync all address spaces. + */ +static void amdvi_iommu_address_space_sync_all(AMDVIState *s) +{ + AMDVIAddressSpace *as; + + QLIST_FOREACH(as, &s->amdvi_as_with_notifiers, next) { + amdvi_address_space_sync(as); + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -983,6 +1024,13 @@ static void amdvi_inval_all(AMDVIState *s, uint64_t *cmd) amdvi_intremap_inval_notify_all(s, true, 0, 0); amdvi_iotlb_reset(s); + + /* + * Fully replay the address space i.e. send both UNMAP and MAP events in + * order to synchronize guest and host IO page tables tables. + */ + amdvi_iommu_address_space_sync_all(s); + trace_amdvi_all_inval(); }