📄 iommu.c
字号:
if ( pg_maddr == 0 ) return -ENOMEM; page = (struct dma_pte *)map_vtd_domain_page(pg_maddr); pte = page + (gfn & LEVEL_MASK); pte_present = dma_pte_present(*pte); dma_set_pte_addr(*pte, (paddr_t)mfn << PAGE_SHIFT_4K); dma_set_pte_prot(*pte, DMA_PTE_READ | DMA_PTE_WRITE); iommu_flush_cache_entry(pte); unmap_vtd_domain_page(page); for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; if ( !test_bit(iommu->index, &hd->iommu_bitmap) ) continue; if ( iommu_flush_iotlb_psi(iommu, domain_iommu_domid(d), (paddr_t)gfn << PAGE_SHIFT_4K, 1, !pte_present) ) iommu_flush_write_buffer(iommu); } return 0;}int intel_iommu_unmap_page(struct domain *d, unsigned long gfn){ struct acpi_drhd_unit *drhd; struct iommu *iommu; drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); iommu = drhd->iommu;#ifdef CONTEXT_PASSTHRU /* do nothing if dom0 and iommu supports pass thru */ if ( ecap_pass_thru(iommu->ecap) && (d->domain_id == 0) ) return 0;#endif dma_pte_clear_one(d, (paddr_t)gfn << PAGE_SHIFT_4K); return 0;}int iommu_page_mapping(struct domain *domain, paddr_t iova, paddr_t hpa, size_t size, int prot){ struct hvm_iommu *hd = domain_hvm_iommu(domain); struct acpi_drhd_unit *drhd; struct iommu *iommu; u64 start_pfn, end_pfn; struct dma_pte *page = NULL, *pte = NULL; int index; u64 pg_maddr; if ( (prot & (DMA_PTE_READ|DMA_PTE_WRITE)) == 0 ) return -EINVAL; iova = (iova >> PAGE_SHIFT_4K) << PAGE_SHIFT_4K; start_pfn = hpa >> PAGE_SHIFT_4K; end_pfn = (PAGE_ALIGN_4K(hpa + size)) >> PAGE_SHIFT_4K; index = 0; while ( start_pfn < end_pfn ) { pg_maddr = addr_to_dma_page_maddr(domain, iova + PAGE_SIZE_4K*index, 1); if ( pg_maddr == 0 ) return -ENOMEM; page = (struct dma_pte *)map_vtd_domain_page(pg_maddr); pte = page + (start_pfn & LEVEL_MASK); dma_set_pte_addr(*pte, (paddr_t)start_pfn << PAGE_SHIFT_4K); dma_set_pte_prot(*pte, prot); iommu_flush_cache_entry(pte); unmap_vtd_domain_page(page); start_pfn++; index++; } if ( index > 0 ) { for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; if ( test_bit(iommu->index, &hd->iommu_bitmap) ) if ( iommu_flush_iotlb_psi(iommu, domain_iommu_domid(domain), iova, index, 1)) iommu_flush_write_buffer(iommu); } } return 0;}int iommu_page_unmapping(struct domain *domain, paddr_t addr, size_t size){ dma_pte_clear_range(domain, addr, addr + size); return 0;}static int iommu_prepare_rmrr_dev(struct domain *d, struct acpi_rmrr_unit *rmrr, u8 bus, u8 devfn){ u64 size; int ret; /* page table init */ size = rmrr->end_address - rmrr->base_address + 1; ret = iommu_page_mapping(d, rmrr->base_address, rmrr->base_address, size, DMA_PTE_READ|DMA_PTE_WRITE); if ( ret ) return ret; if ( domain_context_mapped(bus, devfn) == 0 ) ret = domain_context_mapping(d, bus, devfn); return ret;}static int intel_iommu_add_device(struct pci_dev *pdev){ struct acpi_rmrr_unit *rmrr; u16 bdf; int ret, i; if ( !pdev->domain ) return -EINVAL; ret = domain_context_mapping(pdev->domain, pdev->bus, pdev->devfn); if ( ret ) { gdprintk(XENLOG_ERR VTDPREFIX, "intel_iommu_add_device: context mapping failed\n"); return ret; } for_each_rmrr_device ( rmrr, bdf, i ) { if ( PCI_BUS(bdf) == pdev->bus && PCI_DEVFN2(bdf) == pdev->devfn ) { ret = iommu_prepare_rmrr_dev(pdev->domain, rmrr, pdev->bus, pdev->devfn); if ( ret ) gdprintk(XENLOG_ERR VTDPREFIX, "intel_iommu_add_device: RMRR mapping failed\n"); break; } } return ret;}static int intel_iommu_remove_device(struct pci_dev *pdev){ struct acpi_rmrr_unit *rmrr; u16 bdf; int i; if ( !pdev->domain ) return -EINVAL; /* If the device belongs to dom0, and it has RMRR, don't remove it * from dom0, because BIOS may use RMRR at booting time. */ if ( pdev->domain->domain_id == 0 ) { for_each_rmrr_device ( rmrr, bdf, i ) { if ( PCI_BUS(bdf) == pdev->bus && PCI_DEVFN2(bdf) == pdev->devfn ) return 0; } } return domain_context_unmap(pdev->domain, pdev->bus, pdev->devfn);}static void setup_dom0_devices(struct domain *d){ struct hvm_iommu *hd; struct pci_dev *pdev; int bus, dev, func; u32 l; hd = domain_hvm_iommu(d); write_lock(&pcidevs_lock); for ( bus = 0; bus < 256; bus++ ) { for ( dev = 0; dev < 32; dev++ ) { for ( func = 0; func < 8; func++ ) { l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID); /* some broken boards return 0 or ~0 if a slot is empty: */ if ( (l == 0xffffffff) || (l == 0x00000000) || (l == 0x0000ffff) || (l == 0xffff0000) ) continue; pdev = alloc_pdev(bus, PCI_DEVFN(dev, func)); pdev->domain = d; list_add(&pdev->domain_list, &d->arch.pdev_list); domain_context_mapping(d, pdev->bus, pdev->devfn); } } } write_unlock(&pcidevs_lock);}void clear_fault_bits(struct iommu *iommu){ u64 val; val = dmar_readq( iommu->reg, cap_fault_reg_offset(dmar_readq(iommu->reg,DMAR_CAP_REG))+0x8); dmar_writeq( iommu->reg, cap_fault_reg_offset(dmar_readq(iommu->reg,DMAR_CAP_REG))+8, val); dmar_writel(iommu->reg, DMAR_FSTS_REG, DMA_FSTS_FAULTS);}static int init_vtd_hw(void){ struct acpi_drhd_unit *drhd; struct iommu *iommu; struct iommu_flush *flush = NULL; int vector; int ret; for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; ret = iommu_set_root_entry(iommu); if ( ret ) { gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: set root entry failed\n"); return -EIO; } vector = iommu_set_interrupt(iommu); dma_msi_data_init(iommu, vector); dma_msi_addr_init(iommu, cpu_physical_id(first_cpu(cpu_online_map))); iommu->vector = vector; clear_fault_bits(iommu); dmar_writel(iommu->reg, DMAR_FECTL_REG, 0); /* initialize flush functions */ flush = iommu_get_flush(iommu); flush->context = flush_context_reg; flush->iotlb = flush_iotlb_reg; } for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; if ( qinval_setup(iommu) != 0 ) dprintk(XENLOG_INFO VTDPREFIX, "Queued Invalidation hardware not found\n"); } for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; if ( intremap_setup(iommu) != 0 ) dprintk(XENLOG_INFO VTDPREFIX, "Interrupt Remapping hardware not found\n"); } return 0;}static void setup_dom0_rmrr(struct domain *d){ struct acpi_rmrr_unit *rmrr; u16 bdf; int ret, i; for_each_rmrr_device ( rmrr, bdf, i ) { ret = iommu_prepare_rmrr_dev(d, rmrr, PCI_BUS(bdf), PCI_DEVFN2(bdf)); if ( ret ) gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: mapping reserved region failed\n"); }}int intel_vtd_setup(void){ struct acpi_drhd_unit *drhd; struct iommu *iommu; if ( !vtd_enabled ) return -ENODEV; spin_lock_init(&domid_bitmap_lock); clflush_size = get_clflush_size(); for_each_drhd_unit ( drhd ) if ( iommu_alloc(drhd) != 0 ) goto error; /* Allocate IO page directory page for the domain. */ drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); iommu = drhd->iommu; /* Allocate domain id bitmap, and set bit 0 as reserved */ domid_bitmap_size = cap_ndoms(iommu->cap); domid_bitmap = xmalloc_array(unsigned long, BITS_TO_LONGS(domid_bitmap_size)); if ( domid_bitmap == NULL ) goto error; memset(domid_bitmap, 0, domid_bitmap_size / 8); set_bit(0, domid_bitmap); if ( init_vtd_hw() ) goto error; register_keyhandler('V', dump_iommu_info, "dump iommu info"); return 0; error: for_each_drhd_unit ( drhd ) iommu_free(drhd); vtd_enabled = 0; return -ENOMEM;}/* * If the device isn't owned by dom0, it means it already * has been assigned to other domain, or it's not exist. */int device_assigned(u8 bus, u8 devfn){ struct pci_dev *pdev; if ( (pdev = pci_lock_domain_pdev(dom0, bus, devfn)) ) { spin_unlock(&pdev->lock); return 0; } return 1;}int intel_iommu_assign_device(struct domain *d, u8 bus, u8 devfn){ struct acpi_rmrr_unit *rmrr; int ret = 0, i; u16 bdf; if ( list_empty(&acpi_drhd_units) ) return -ENODEV; ret = reassign_device_ownership(dom0, d, bus, devfn); if ( ret ) return ret; /* Setup rmrr identity mapping */ for_each_rmrr_device( rmrr, bdf, i ) { if ( PCI_BUS(bdf) == bus && PCI_DEVFN2(bdf) == devfn ) { /* FIXME: Because USB RMRR conflicts with guest bios region, * ignore USB RMRR temporarily. */ if ( is_usb_device(bus, devfn) ) return 0; ret = iommu_prepare_rmrr_dev(d, rmrr, bus, devfn); if ( ret ) gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: mapping reserved region failed\n"); return ret; } } return ret;}static int intel_iommu_group_id(u8 bus, u8 devfn){ u8 secbus; if ( !bus2bridge[bus].map || find_pcie_endpoint(&bus, &devfn, &secbus) ) return PCI_BDF2(bus, devfn); else return -1;}u8 iommu_state[MAX_IOMMU_REGS * MAX_IOMMUS];int iommu_suspend(void){ struct acpi_drhd_unit *drhd; struct iommu *iommu; int i = 0; iommu_flush_all(); for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; iommu_state[DMAR_RTADDR_REG * i] = (u64) dmar_readq(iommu->reg, DMAR_RTADDR_REG); iommu_state[DMAR_FECTL_REG * i] = (u32) dmar_readl(iommu->reg, DMAR_FECTL_REG); iommu_state[DMAR_FEDATA_REG * i] = (u32) dmar_readl(iommu->reg, DMAR_FEDATA_REG); iommu_state[DMAR_FEADDR_REG * i] = (u32) dmar_readl(iommu->reg, DMAR_FEADDR_REG); iommu_state[DMAR_FEUADDR_REG * i] = (u32) dmar_readl(iommu->reg, DMAR_FEUADDR_REG); iommu_state[DMAR_PLMBASE_REG * i] = (u32) dmar_readl(iommu->reg, DMAR_PLMBASE_REG); iommu_state[DMAR_PLMLIMIT_REG * i] = (u32) dmar_readl(iommu->reg, DMAR_PLMLIMIT_REG); iommu_state[DMAR_PHMBASE_REG * i] = (u64) dmar_readq(iommu->reg, DMAR_PHMBASE_REG); iommu_state[DMAR_PHMLIMIT_REG * i] = (u64) dmar_readq(iommu->reg, DMAR_PHMLIMIT_REG); i++; } return 0;}int iommu_resume(void){ struct acpi_drhd_unit *drhd; struct iommu *iommu; int i = 0; iommu_flush_all(); init_vtd_hw(); for_each_drhd_unit ( drhd ) { iommu = drhd->iommu; dmar_writeq( iommu->reg, DMAR_RTADDR_REG, (u64) iommu_state[DMAR_RTADDR_REG * i]); dmar_writel(iommu->reg, DMAR_FECTL_REG, (u32) iommu_state[DMAR_FECTL_REG * i]); dmar_writel(iommu->reg, DMAR_FEDATA_REG, (u32) iommu_state[DMAR_FEDATA_REG * i]); dmar_writel(iommu->reg, DMAR_FEADDR_REG, (u32) iommu_state[DMAR_FEADDR_REG * i]); dmar_writel(iommu->reg, DMAR_FEUADDR_REG, (u32) iommu_state[DMAR_FEUADDR_REG * i]); dmar_writel(iommu->reg, DMAR_PLMBASE_REG, (u32) iommu_state[DMAR_PLMBASE_REG * i]); dmar_writel(iommu->reg, DMAR_PLMLIMIT_REG, (u32) iommu_state[DMAR_PLMLIMIT_REG * i]); dmar_writeq(iommu->reg, DMAR_PHMBASE_REG, (u64) iommu_state[DMAR_PHMBASE_REG * i]); dmar_writeq(iommu->reg, DMAR_PHMLIMIT_REG, (u64) iommu_state[DMAR_PHMLIMIT_REG * i]); if ( iommu_enable_translation(iommu) ) return -EIO; i++; } return 0;}struct iommu_ops intel_iommu_ops = { .init = intel_iommu_domain_init, .add_device = intel_iommu_add_device, .remove_device = intel_iommu_remove_device, .assign_device = intel_iommu_assign_device, .teardown = iommu_domain_teardown, .map_page = intel_iommu_map_page, .unmap_page = intel_iommu_unmap_page, .reassign_device = reassign_device_ownership, .get_device_group_id = intel_iommu_group_id, .update_ire_from_apic = io_apic_write_remap_rte, .update_ire_from_msi = msi_msg_write_remap_rte,};/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -