/* * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include #include #include #include #include #include #include #include #include #include const struct iommu_init_ops *__initdata iommu_init_ops; struct iommu_ops __read_mostly iommu_ops; enum iommu_intremap __read_mostly iommu_intremap = iommu_intremap_full; #ifndef iommu_intpost /* * In the current implementation of VT-d posted interrupts, in some extreme * cases, the per cpu list which saves the blocked vCPU will be very long, * and this will affect the interrupt latency, so let this feature off by * default until we find a good solution to resolve it. */ bool __read_mostly iommu_intpost; #endif int __init iommu_hardware_setup(void) { struct IO_APIC_route_entry **ioapic_entries = NULL; int rc; if ( !iommu_init_ops ) return -ENODEV; rc = scan_pci_devices(); if ( rc ) return rc; if ( !iommu_ops.init ) iommu_ops = *iommu_init_ops->ops; else /* x2apic setup may have previously initialised the struct. */ ASSERT(iommu_ops.init == iommu_init_ops->ops->init); if ( !x2apic_enabled && iommu_intremap ) { /* * If x2APIC is enabled interrupt remapping is already enabled, so * there's no need to mess with the IO-APIC because the remapping * entries are already correctly setup by x2apic_bsp_setup. */ ioapic_entries = alloc_ioapic_entries(); if ( !ioapic_entries ) return -ENOMEM; rc = save_IO_APIC_setup(ioapic_entries); if ( rc ) { free_ioapic_entries(ioapic_entries); return rc; } mask_8259A(); mask_IO_APIC_setup(ioapic_entries); } rc = iommu_init_ops->setup(); if ( ioapic_entries ) { restore_IO_APIC_setup(ioapic_entries, rc); unmask_8259A(); free_ioapic_entries(ioapic_entries); } return rc; } int iommu_enable_x2apic(void) { if ( system_state < SYS_STATE_active ) { if ( !iommu_supports_x2apic() ) return -EOPNOTSUPP; iommu_ops = *iommu_init_ops->ops; } else if ( !x2apic_enabled ) return -EOPNOTSUPP; if ( !iommu_ops.enable_x2apic ) return -EOPNOTSUPP; return iommu_ops.enable_x2apic(); } void iommu_update_ire_from_apic( unsigned int apic, unsigned int reg, unsigned int value) { iommu_vcall(&iommu_ops, update_ire_from_apic, apic, reg, value); } unsigned int iommu_read_apic_from_ire(unsigned int apic, unsigned int reg) { return iommu_call(&iommu_ops, read_apic_from_ire, apic, reg); } int __init iommu_setup_hpet_msi(struct msi_desc *msi) { const struct iommu_ops *ops = iommu_get_ops(); return ops->setup_hpet_msi ? ops->setup_hpet_msi(msi) : -ENODEV; } void __hwdom_init arch_iommu_check_autotranslated_hwdom(struct domain *d) { if ( !is_iommu_enabled(d) ) panic("Presently, iommu must be enabled for PVH hardware domain\n"); if ( !iommu_hwdom_strict ) panic("PVH hardware domain iommu must be set in 'strict' mode\n"); } int arch_iommu_domain_init(struct domain *d) { struct domain_iommu *hd = dom_iommu(d); spin_lock_init(&hd->arch.mapping_lock); INIT_PAGE_LIST_HEAD(&hd->arch.pgtables.list); spin_lock_init(&hd->arch.pgtables.lock); return 0; } void arch_iommu_domain_destroy(struct domain *d) { } static bool __hwdom_init hwdom_iommu_map(const struct domain *d, unsigned long pfn, unsigned long max_pfn) { mfn_t mfn = _mfn(pfn); unsigned int i, type; /* * Set up 1:1 mapping for dom0. Default to include only conventional RAM * areas and let RMRRs include needed reserved regions. When set, the * inclusive mapping additionally maps in every pfn up to 4GB except those * that fall in unusable ranges for PV Dom0. */ if ( (pfn > max_pfn && !mfn_valid(mfn)) || xen_in_range(pfn) ) return false; switch ( type = page_get_ram_type(mfn) ) { case RAM_TYPE_UNUSABLE: return false; case RAM_TYPE_CONVENTIONAL: if ( iommu_hwdom_strict ) return false; break; default: if ( type & RAM_TYPE_RESERVED ) { if ( !iommu_hwdom_inclusive && !iommu_hwdom_reserved ) return false; } else if ( is_hvm_domain(d) || !iommu_hwdom_inclusive || pfn > max_pfn ) return false; } /* Check that it doesn't overlap with the Interrupt Address Range. */ if ( pfn >= 0xfee00 && pfn <= 0xfeeff ) return false; /* ... or the IO-APIC */ for ( i = 0; has_vioapic(d) && i < d->arch.hvm.nr_vioapics; i++ ) if ( pfn == PFN_DOWN(domain_vioapic(d, i)->base_address) ) return false; /* * ... or the PCIe MCFG regions. * TODO: runtime added MMCFG regions are not checked to make sure they * don't overlap with already mapped regions, thus preventing trapping. */ if ( has_vpci(d) && vpci_is_mmcfg_address(d, pfn_to_paddr(pfn)) ) return false; return true; } void __hwdom_init arch_iommu_hwdom_init(struct domain *d) { unsigned long i, top, max_pfn; unsigned int flush_flags = 0; BUG_ON(!is_hardware_domain(d)); /* Reserved IOMMU mappings are enabled by default. */ if ( iommu_hwdom_reserved == -1 ) iommu_hwdom_reserved = 1; if ( iommu_hwdom_inclusive ) { printk(XENLOG_WARNING "IOMMU inclusive mappings are deprecated and will be removed in future versions\n"); if ( !is_pv_domain(d) ) { printk(XENLOG_WARNING "IOMMU inclusive mappings are only supported on PV Dom0\n"); iommu_hwdom_inclusive = false; } } if ( iommu_hwdom_passthrough ) return; max_pfn = (GB(4) >> PAGE_SHIFT) - 1; top = max(max_pdx, pfn_to_pdx(max_pfn) + 1); for ( i = 0; i < top; i++ ) { unsigned long pfn = pdx_to_pfn(i); int rc; if ( !hwdom_iommu_map(d, pfn, max_pfn) ) rc = 0; else if ( paging_mode_translate(d) ) rc = set_identity_p2m_entry(d, pfn, p2m_access_rw, 0); else rc = iommu_map(d, _dfn(pfn), _mfn(pfn), 1ul << PAGE_ORDER_4K, IOMMUF_readable | IOMMUF_writable, &flush_flags); if ( rc ) printk(XENLOG_WARNING "%pd: identity %smapping of %lx failed: %d\n", d, !paging_mode_translate(d) ? "IOMMU " : "", pfn, rc); if (!(i & 0xfffff)) process_pending_softirqs(); } /* Use if to avoid compiler warning */ if ( iommu_iotlb_flush_all(d, flush_flags) ) return; } int iommu_free_pgtables(struct domain *d) { struct domain_iommu *hd = dom_iommu(d); struct page_info *pg; unsigned int done = 0; while ( (pg = page_list_remove_head(&hd->arch.pgtables.list)) ) { free_domheap_page(pg); if ( !(++done & 0xff) && general_preempt_check() ) return -ERESTART; } return 0; } struct page_info *iommu_alloc_pgtable(struct domain *d) { struct domain_iommu *hd = dom_iommu(d); unsigned int memflags = 0; struct page_info *pg; void *p; #ifdef CONFIG_NUMA if ( hd->node != NUMA_NO_NODE ) memflags = MEMF_node(hd->node); #endif pg = alloc_domheap_page(NULL, memflags); if ( !pg ) return NULL; p = __map_domain_page(pg); clear_page(p); if ( hd->platform_ops->sync_cache ) iommu_vcall(hd->platform_ops, sync_cache, p, PAGE_SIZE); unmap_domain_page(p); spin_lock(&hd->arch.pgtables.lock); page_list_add(pg, &hd->arch.pgtables.list); spin_unlock(&hd->arch.pgtables.lock); return pg; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */