[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen stable-4.15] AMD/IOMMU: re-arrange/complete re-assignment handling
commit d39756f0539cfe85742687e8134fdf1181139390 Author: Jan Beulich <jbeulich@xxxxxxxx> AuthorDate: Wed Aug 25 14:42:54 2021 +0200 Commit: Jan Beulich <jbeulich@xxxxxxxx> CommitDate: Wed Aug 25 14:42:54 2021 +0200 AMD/IOMMU: re-arrange/complete re-assignment handling Prior to the assignment step having completed successfully, devices should not get associated with their new owner. Hand the device to DomIO (perhaps temporarily), until after the de-assignment step has completed. De-assignment of a device (from other than Dom0) as well as failure of reassign_device() during assignment should result in unity mappings getting torn down. This in turn requires switching to a refcounted mapping approach, as was already used by VT-d for its RMRRs, to prevent unmapping a region used by multiple devices. This is CVE-2021-28696 / part of XSA-378. Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> Reviewed-by: Paul Durrant <paul@xxxxxxx> master commit: 899272539cbe1acda736a850015416fff653a1b6 master date: 2021-08-25 14:16:26 +0200 --- xen/drivers/passthrough/amd/iommu.h | 6 ++- xen/drivers/passthrough/amd/iommu_map.c | 63 +++++++++++++++++------------ xen/drivers/passthrough/amd/pci_amd_iommu.c | 54 +++++++++++++++++++------ 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/xen/drivers/passthrough/amd/iommu.h b/xen/drivers/passthrough/amd/iommu.h index f0e3e5b1a4..b2852594ec 100644 --- a/xen/drivers/passthrough/amd/iommu.h +++ b/xen/drivers/passthrough/amd/iommu.h @@ -232,8 +232,10 @@ int __must_check amd_iommu_unmap_page(struct domain *d, dfn_t dfn, unsigned int *flush_flags); int __must_check amd_iommu_alloc_root(struct domain *d); int amd_iommu_reserve_domain_unity_map(struct domain *domain, - paddr_t phys_addr, unsigned long size, - int iw, int ir); + const struct ivrs_unity_map *map, + unsigned int flag); +int amd_iommu_reserve_domain_unity_unmap(struct domain *d, + const struct ivrs_unity_map *map); int __must_check amd_iommu_flush_iotlb_pages(struct domain *d, dfn_t dfn, unsigned long page_count, unsigned int flush_flags); diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c index 560af54b76..2e7916b1e6 100644 --- a/xen/drivers/passthrough/amd/iommu_map.c +++ b/xen/drivers/passthrough/amd/iommu_map.c @@ -419,38 +419,49 @@ int amd_iommu_flush_iotlb_all(struct domain *d) return 0; } -int amd_iommu_reserve_domain_unity_map(struct domain *domain, - paddr_t phys_addr, - unsigned long size, int iw, int ir) +int amd_iommu_reserve_domain_unity_map(struct domain *d, + const struct ivrs_unity_map *map, + unsigned int flag) { - unsigned long npages, i; - unsigned long gfn; - unsigned int flags = !!ir; - unsigned int flush_flags = 0; - int rt = 0; - - if ( iw ) - flags |= IOMMUF_writable; - - npages = region_to_pages(phys_addr, size); - gfn = phys_addr >> PAGE_SHIFT; - for ( i = 0; i < npages; i++ ) + int rc; + + if ( d == dom_io ) + return 0; + + for ( rc = 0; !rc && map; map = map->next ) { - unsigned long frame = gfn + i; + p2m_access_t p2ma = p2m_access_n; - rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags, - &flush_flags); - if ( rt != 0 ) - break; + if ( map->read ) + p2ma |= p2m_access_r; + if ( map->write ) + p2ma |= p2m_access_w; + + rc = iommu_identity_mapping(d, p2ma, map->addr, + map->addr + map->length - 1, flag); } - /* Use while-break to avoid compiler warning */ - while ( flush_flags && - amd_iommu_flush_iotlb_pages(domain, _dfn(gfn), - npages, flush_flags) ) - break; + return rc; +} + +int amd_iommu_reserve_domain_unity_unmap(struct domain *d, + const struct ivrs_unity_map *map) +{ + int rc; + + if ( d == dom_io ) + return 0; + + for ( rc = 0; map; map = map->next ) + { + int ret = iommu_identity_mapping(d, p2m_access_x, map->addr, + map->addr + map->length - 1, 0); + + if ( ret && ret != -ENOENT && !rc ) + rc = ret; + } - return rt; + return rc; } int __init amd_iommu_quarantine_init(struct domain *d) diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c index adcc23fc43..7c3966ef52 100644 --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c @@ -329,6 +329,7 @@ static int reassign_device(struct domain *source, struct domain *target, { struct amd_iommu *iommu; int bdf, rc; + const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); bdf = PCI_BDF2(pdev->bus, pdev->devfn); iommu = find_iommu_for_device(pdev->seg, bdf); @@ -343,10 +344,24 @@ static int reassign_device(struct domain *source, struct domain *target, amd_iommu_disable_domain_device(source, iommu, devfn, pdev); - if ( devfn == pdev->devfn ) + /* + * If the device belongs to the hardware domain, and it has a unity mapping, + * don't remove it from the hardware domain, because BIOS may reference that + * mapping. + */ + if ( !is_hardware_domain(source) ) { - list_move(&pdev->domain_list, &target->pdev_list); - pdev->domain = target; + rc = amd_iommu_reserve_domain_unity_unmap( + source, + ivrs_mappings[get_dma_requestor_id(pdev->seg, bdf)].unity_map); + if ( rc ) + return rc; + } + + if ( devfn == pdev->devfn && pdev->domain != dom_io ) + { + list_move(&pdev->domain_list, &dom_io->pdev_list); + pdev->domain = dom_io; } rc = allocate_domain_resources(target); @@ -357,6 +372,12 @@ static int reassign_device(struct domain *source, struct domain *target, AMD_IOMMU_DEBUG("Re-assign %pp from dom%d to dom%d\n", &pdev->sbdf, source->domain_id, target->domain_id); + if ( devfn == pdev->devfn && pdev->domain != target ) + { + list_move(&pdev->domain_list, &target->pdev_list); + pdev->domain = target; + } + return 0; } @@ -367,20 +388,28 @@ static int amd_iommu_assign_device(struct domain *d, u8 devfn, struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); int bdf = PCI_BDF2(pdev->bus, devfn); int req_id = get_dma_requestor_id(pdev->seg, bdf); - const struct ivrs_unity_map *unity_map; + int rc = amd_iommu_reserve_domain_unity_map( + d, ivrs_mappings[req_id].unity_map, flag); + + if ( !rc ) + rc = reassign_device(pdev->domain, d, devfn, pdev); - for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; - unity_map = unity_map->next ) + if ( rc && !is_hardware_domain(d) ) { - int rc = amd_iommu_reserve_domain_unity_map( - d, unity_map->addr, unity_map->length, - unity_map->write, unity_map->read); + int ret = amd_iommu_reserve_domain_unity_unmap( + d, ivrs_mappings[req_id].unity_map); - if ( rc ) - return rc; + if ( ret ) + { + printk(XENLOG_ERR "AMD-Vi: " + "unity-unmap for %pd/%04x:%02x:%02x.%u failed (%d)\n", + d, pdev->seg, pdev->bus, + PCI_SLOT(devfn), PCI_FUNC(devfn), ret); + domain_crash(d); + } } - return reassign_device(pdev->domain, d, devfn, pdev); + return rc; } static void amd_iommu_clear_root_pgtable(struct domain *d) @@ -394,6 +423,7 @@ static void amd_iommu_clear_root_pgtable(struct domain *d) static void amd_iommu_domain_destroy(struct domain *d) { + iommu_identity_map_teardown(d); ASSERT(!dom_iommu(d)->arch.amd.root_table); } -- generated by git-patchbot for /home/xen/git/xen.git#stable-4.15
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |