From: Jan Beulich Subject: VT-d: prepare for per-device quarantine page tables (part I) Arrange for domain ID and page table root to be passed around, the latter in particular to domain_pgd_maddr() such that taking it from the per-domain fields can be overridden. No functional change intended. Signed-off-by: Jan Beulich Reviewed-by: Paul Durrant Reviewed-by: Roger Pau Monné Reviewed-by: Kevin Tian --- a/xen/drivers/passthrough/vtd/extern.h +++ b/xen/drivers/passthrough/vtd/extern.h @@ -85,9 +85,10 @@ void *map_vtd_domain_page(u64 maddr); void unmap_vtd_domain_page(const void *va); int domain_context_mapping_one(struct domain *domain, struct vtd_iommu *iommu, uint8_t bus, uint8_t devfn, - const struct pci_dev *pdev, unsigned int mode); + const struct pci_dev *pdev, domid_t domid, + paddr_t pgd_maddr, unsigned int mode); int domain_context_unmap_one(struct domain *domain, struct vtd_iommu *iommu, - u8 bus, u8 devfn); + uint8_t bus, uint8_t devfn, domid_t domid); int intel_iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt); unsigned int io_apic_read_remap_rte(unsigned int apic, unsigned int reg); @@ -106,7 +107,8 @@ void platform_quirks_init(void); void vtd_ops_preamble_quirk(struct vtd_iommu *iommu); void vtd_ops_postamble_quirk(struct vtd_iommu *iommu); int __must_check me_wifi_quirk(struct domain *domain, uint8_t bus, - uint8_t devfn, unsigned int mode); + uint8_t devfn, domid_t domid, paddr_t pgd_maddr, + unsigned int mode); void pci_vtd_quirk(const struct pci_dev *); void quirk_iommu_caps(struct vtd_iommu *iommu); --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -43,7 +43,7 @@ #include "../ats.h" /* dom_io is used as a sentinel for quarantined devices */ -#define QUARANTINE_SKIP(d) ((d) == dom_io && !dom_iommu(d)->arch.vtd.pgd_maddr) +#define QUARANTINE_SKIP(d, pgd_maddr) ((d) == dom_io && !(pgd_maddr)) /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ bool __read_mostly untrusted_msi; @@ -358,15 +358,17 @@ static u64 addr_to_dma_page_maddr(struct return pte_maddr; } -static uint64_t domain_pgd_maddr(struct domain *d, unsigned int nr_pt_levels) +static paddr_t domain_pgd_maddr(struct domain *d, paddr_t pgd_maddr, + unsigned int nr_pt_levels) { struct domain_iommu *hd = dom_iommu(d); - uint64_t pgd_maddr; unsigned int agaw; ASSERT(spin_is_locked(&hd->arch.mapping_lock)); - if ( iommu_use_hap_pt(d) ) + if ( pgd_maddr ) + /* nothing */; + else if ( iommu_use_hap_pt(d) ) { pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); @@ -1385,18 +1387,18 @@ int domain_context_mapping_one( struct domain *domain, struct vtd_iommu *iommu, uint8_t bus, uint8_t devfn, const struct pci_dev *pdev, - unsigned int mode) + domid_t domid, paddr_t pgd_maddr, unsigned int mode) { struct domain_iommu *hd = dom_iommu(domain); struct context_entry *context, *context_entries, lctxt; __uint128_t old; - u64 maddr, pgd_maddr; + uint64_t maddr; uint16_t seg = iommu->drhd->segment, prev_did = 0; struct domain *prev_dom = NULL; int rc, ret; bool_t flush_dev_iotlb; - if ( QUARANTINE_SKIP(domain) ) + if ( QUARANTINE_SKIP(domain, pgd_maddr) ) return 0; ASSERT(pcidevs_locked()); @@ -1433,10 +1435,12 @@ int domain_context_mapping_one( } else { + paddr_t root; + spin_lock(&hd->arch.mapping_lock); - pgd_maddr = domain_pgd_maddr(domain, iommu->nr_pt_levels); - if ( !pgd_maddr ) + root = domain_pgd_maddr(domain, pgd_maddr, iommu->nr_pt_levels); + if ( !root ) { spin_unlock(&hd->arch.mapping_lock); spin_unlock(&iommu->lock); @@ -1446,7 +1450,7 @@ int domain_context_mapping_one( return -ENOMEM; } - context_set_address_root(lctxt, pgd_maddr); + context_set_address_root(lctxt, root); if ( ats_enabled && ecap_dev_iotlb(iommu->ecap) ) context_set_translation_type(lctxt, CONTEXT_TT_DEV_IOTLB); else @@ -1562,15 +1566,21 @@ int domain_context_mapping_one( unmap_vtd_domain_page(context_entries); if ( !seg && !rc ) - rc = me_wifi_quirk(domain, bus, devfn, mode); + rc = me_wifi_quirk(domain, bus, devfn, domid, pgd_maddr, mode); if ( rc ) { if ( !prev_dom ) - ret = domain_context_unmap_one(domain, iommu, bus, devfn); + ret = domain_context_unmap_one(domain, iommu, bus, devfn, + domain->domain_id); else if ( prev_dom != domain ) /* Avoid infinite recursion. */ + { + hd = dom_iommu(prev_dom); ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, + domain->domain_id, + hd->arch.vtd.pgd_maddr, mode & MAP_WITH_RMRR) < 0; + } else ret = 1; @@ -1592,6 +1602,7 @@ static int domain_context_mapping(struct { const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); const struct acpi_rmrr_unit *rmrr; + paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; int ret = 0; unsigned int i, mode = 0; uint16_t seg = pdev->seg, bdf; @@ -1654,7 +1665,8 @@ static int domain_context_mapping(struct printk(VTDPREFIX "%pd:PCIe: map %pp\n", domain, &PCI_SBDF3(seg, bus, devfn)); ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, - pdev, mode); + pdev, domain->domain_id, pgd_maddr, + mode); if ( ret > 0 ) ret = 0; if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) @@ -1671,7 +1683,8 @@ static int domain_context_mapping(struct domain, &PCI_SBDF3(seg, bus, devfn)); ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, - pdev, mode); + pdev, domain->domain_id, pgd_maddr, + mode); if ( ret < 0 ) break; prev_present = ret; @@ -1699,7 +1712,8 @@ static int domain_context_mapping(struct */ if ( ret >= 0 ) ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, - NULL, mode); + NULL, domain->domain_id, pgd_maddr, + mode); /* * Devices behind PCIe-to-PCI/PCIx bridge may generate different @@ -1714,7 +1728,8 @@ static int domain_context_mapping(struct if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && (secbus != pdev->bus || pdev->devfn != 0) ) ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, - NULL, mode); + NULL, domain->domain_id, pgd_maddr, + mode); if ( ret ) { @@ -1742,14 +1757,14 @@ static int domain_context_mapping(struct int domain_context_unmap_one( struct domain *domain, struct vtd_iommu *iommu, - u8 bus, u8 devfn) + uint8_t bus, uint8_t devfn, domid_t domid) { struct context_entry *context, *context_entries; u64 maddr; int iommu_domid, rc, ret; bool_t flush_dev_iotlb; - if ( QUARANTINE_SKIP(domain) ) + if ( QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) return 0; ASSERT(pcidevs_locked()); @@ -1803,7 +1818,7 @@ int domain_context_unmap_one( unmap_vtd_domain_page(context_entries); if ( !iommu->drhd->segment && !rc ) - rc = me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC); + rc = me_wifi_quirk(domain, bus, devfn, domid, 0, UNMAP_ME_PHANTOM_FUNC); if ( rc && !is_hardware_domain(domain) && domain != dom_io ) { @@ -1850,7 +1865,8 @@ static int domain_context_unmap(struct d if ( iommu_debug ) printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", domain, &PCI_SBDF3(seg, bus, devfn)); - ret = domain_context_unmap_one(domain, iommu, bus, devfn); + ret = domain_context_unmap_one(domain, iommu, bus, devfn, + domain->domain_id); if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) disable_ats_device(pdev); @@ -1863,7 +1879,8 @@ static int domain_context_unmap(struct d if ( iommu_debug ) printk(VTDPREFIX "%pd:PCI: unmap %pp\n", domain, &PCI_SBDF3(seg, bus, devfn)); - ret = domain_context_unmap_one(domain, iommu, bus, devfn); + ret = domain_context_unmap_one(domain, iommu, bus, devfn, + domain->domain_id); if ( ret ) break; @@ -1889,12 +1906,15 @@ static int domain_context_unmap(struct d /* PCIe to PCI/PCIx bridge */ if ( pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) { - ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn); + ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, + domain->domain_id); if ( !ret ) - ret = domain_context_unmap_one(domain, iommu, secbus, 0); + ret = domain_context_unmap_one(domain, iommu, secbus, 0, + domain->domain_id); } else /* Legacy PCI bridge */ - ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn); + ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, + domain->domain_id); break; @@ -1904,7 +1924,8 @@ static int domain_context_unmap(struct d return -EINVAL; } - if ( !ret && !QUARANTINE_SKIP(domain) && pdev->devfn == devfn ) + if ( !ret && pdev->devfn == devfn && + !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) check_cleanup_domid_map(domain, pdev, iommu); return ret; @@ -2511,7 +2532,7 @@ static int reassign_device_ownership( { int ret; - if ( !QUARANTINE_SKIP(target) ) + if ( !QUARANTINE_SKIP(target, dom_iommu(target)->arch.vtd.pgd_maddr) ) { if ( !has_arch_pdevs(target) ) vmx_pi_hooks_assign(target); @@ -2526,7 +2547,8 @@ static int reassign_device_ownership( ret = domain_context_mapping(target, devfn, pdev); - if ( !ret && !QUARANTINE_SKIP(source) && pdev->devfn == devfn ) + if ( !ret && pdev->devfn == devfn && + !QUARANTINE_SKIP(source, dom_iommu(source)->arch.vtd.pgd_maddr) ) { const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); --- a/xen/drivers/passthrough/vtd/quirks.c +++ b/xen/drivers/passthrough/vtd/quirks.c @@ -408,6 +408,8 @@ void __init platform_quirks_init(void) static int __must_check map_me_phantom_function(struct domain *domain, unsigned int dev, + domid_t domid, + paddr_t pgd_maddr, unsigned int mode) { struct acpi_drhd_unit *drhd; @@ -421,16 +423,17 @@ static int __must_check map_me_phantom_f /* map or unmap ME phantom function */ if ( !(mode & UNMAP_ME_PHANTOM_FUNC) ) rc = domain_context_mapping_one(domain, drhd->iommu, 0, - PCI_DEVFN(dev, 7), NULL, mode); + PCI_DEVFN(dev, 7), NULL, + domid, pgd_maddr, mode); else rc = domain_context_unmap_one(domain, drhd->iommu, 0, - PCI_DEVFN(dev, 7)); + PCI_DEVFN(dev, 7), domid); return rc; } int me_wifi_quirk(struct domain *domain, uint8_t bus, uint8_t devfn, - unsigned int mode) + domid_t domid, paddr_t pgd_maddr, unsigned int mode) { u32 id; int rc = 0; @@ -454,7 +457,7 @@ int me_wifi_quirk(struct domain *domain, case 0x423b8086: case 0x423c8086: case 0x423d8086: - rc = map_me_phantom_function(domain, 3, mode); + rc = map_me_phantom_function(domain, 3, domid, pgd_maddr, mode); break; default: break; @@ -480,7 +483,7 @@ int me_wifi_quirk(struct domain *domain, case 0x42388086: /* Puma Peak */ case 0x422b8086: case 0x422c8086: - rc = map_me_phantom_function(domain, 22, mode); + rc = map_me_phantom_function(domain, 22, domid, pgd_maddr, mode); break; default: break;