From 4151cc56f19b3fe8aabde12ddf532cd9502c637d Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Wed, 28 May 2008 09:29:05 +0100 Subject: vtd: interrupt remapping for MSI/MSI-x. Signed-off-by: Weidong Han --- xen/arch/x86/msi.c | 7 ++ xen/drivers/passthrough/vtd/intremap.c | 170 +++++++++++++++++++++++++++++++++ xen/drivers/passthrough/vtd/vtd.h | 18 ++++ xen/include/xen/iommu.h | 3 + 4 files changed, 198 insertions(+) diff --git a/xen/arch/x86/msi.c b/xen/arch/x86/msi.c index 37d7914841..9c321aac17 100644 --- a/xen/arch/x86/msi.c +++ b/xen/arch/x86/msi.c @@ -25,6 +25,7 @@ #include #include #include +#include extern int msi_irq_enable; @@ -156,6 +157,9 @@ void read_msi_msg(unsigned int irq, struct msi_msg *msg) default: BUG(); } + + if ( vtd_enabled ) + msi_msg_read_remap_rte(entry, msg); } static int set_vector_msi(struct msi_desc *entry) @@ -202,6 +206,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) { struct msi_desc *entry = irq_desc[irq].msi_desc; + if ( vtd_enabled ) + msi_msg_write_remap_rte(entry, msg); + switch ( entry->msi_attrib.type ) { case PCI_CAP_ID_MSI: diff --git a/xen/drivers/passthrough/vtd/intremap.c b/xen/drivers/passthrough/vtd/intremap.c index 6326dba0d3..0eb16ebf37 100644 --- a/xen/drivers/passthrough/vtd/intremap.c +++ b/xen/drivers/passthrough/vtd/intremap.c @@ -251,6 +251,176 @@ void io_apic_write_remap_rte( *(IO_APIC_BASE(apic)+4) = *(((u32 *)&old_rte)+1); } +static void remap_entry_to_msi_msg( + struct iommu *iommu, struct msi_msg *msg) +{ + struct iremap_entry *iremap_entry = NULL, *iremap_entries; + struct msi_msg_remap_entry *remap_rte; + int index; + unsigned long flags; + struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); + + if ( ir_ctrl == NULL ) + { + dprintk(XENLOG_ERR VTDPREFIX, + "remap_entry_to_msi_msg: ir_ctl == NULL"); + return; + } + + remap_rte = (struct msi_msg_remap_entry *) msg; + index = (remap_rte->address_lo.index_15 << 15) | + remap_rte->address_lo.index_0_14; + + if ( index > ir_ctrl->iremap_index ) + panic("%s: index (%d) is larger than remap table entry size (%d)\n", + __func__, index, ir_ctrl->iremap_index); + + spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); + + iremap_entries = + (struct iremap_entry *)map_vtd_domain_page(ir_ctrl->iremap_maddr); + iremap_entry = &iremap_entries[index]; + + msg->address_hi = MSI_ADDR_BASE_HI; + msg->address_lo = + MSI_ADDR_BASE_LO | + ((iremap_entry->lo.dm == 0) ? + MSI_ADDR_DESTMODE_PHYS: + MSI_ADDR_DESTMODE_LOGIC) | + ((iremap_entry->lo.dlm != dest_LowestPrio) ? + MSI_ADDR_REDIRECTION_CPU: + MSI_ADDR_REDIRECTION_LOWPRI) | + iremap_entry->lo.dst >> 8; + + msg->data = + MSI_DATA_TRIGGER_EDGE | + MSI_DATA_LEVEL_ASSERT | + ((iremap_entry->lo.dlm != dest_LowestPrio) ? + MSI_DATA_DELIVERY_FIXED: + MSI_DATA_DELIVERY_LOWPRI) | + iremap_entry->lo.vector; + + unmap_vtd_domain_page(iremap_entries); + spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); +} + +static void msi_msg_to_remap_entry( + struct iommu *iommu, struct pci_dev *pdev, struct msi_msg *msg) +{ + struct iremap_entry *iremap_entry = NULL, *iremap_entries; + struct iremap_entry new_ire; + struct msi_msg_remap_entry *remap_rte; + unsigned int index; + unsigned long flags; + struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); + int i = 0; + + remap_rte = (struct msi_msg_remap_entry *) msg; + spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); + + iremap_entries = + (struct iremap_entry *)map_vtd_domain_page(ir_ctrl->iremap_maddr); + + /* If the entry for a PCI device has been there, use the old entry, + * Or, assign a new entry for it. + */ + for ( i = 0; i <= ir_ctrl->iremap_index; i++ ) + { + iremap_entry = &iremap_entries[i]; + if ( iremap_entry->hi.sid == + ((pdev->bus << 8) | pdev->devfn) ) + break; + } + + if ( i > ir_ctrl->iremap_index ) + { + ir_ctrl->iremap_index++; + index = ir_ctrl->iremap_index; + } + else + index = i; + + if ( index > IREMAP_ENTRY_NR - 1 ) + panic("msi_msg_to_remap_entry: intremap index is more than 256!\n"); + + iremap_entry = &iremap_entries[index]; + memcpy(&new_ire, iremap_entry, sizeof(struct iremap_entry)); + + /* Set interrupt remapping table entry */ + new_ire.lo.fpd = 0; + new_ire.lo.dm = (msg->address_lo >> MSI_ADDR_DESTMODE_SHIFT) & 0x1; + new_ire.lo.rh = 0; + new_ire.lo.tm = (msg->data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + new_ire.lo.dlm = (msg->data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x1; + new_ire.lo.avail = 0; + new_ire.lo.res_1 = 0; + new_ire.lo.vector = (msg->data >> MSI_DATA_VECTOR_SHIFT) & + MSI_DATA_VECTOR_MASK; + new_ire.lo.res_2 = 0; + new_ire.lo.dst = ((msg->address_lo >> MSI_ADDR_DEST_ID_SHIFT) + & 0xff) << 8; + + new_ire.hi.sid = (pdev->bus << 8) | pdev->devfn; + new_ire.hi.sq = 0; + new_ire.hi.svt = 1; + new_ire.hi.res_1 = 0; + new_ire.lo.p = 1; /* finally, set present bit */ + + /* now construct new MSI/MSI-X rte entry */ + remap_rte->address_lo.dontcare = 0; + remap_rte->address_lo.index_15 = index & 0x8000; + remap_rte->address_lo.index_0_14 = index & 0x7fff; + remap_rte->address_lo.SHV = 1; + remap_rte->address_lo.format = 1; + + remap_rte->address_hi = 0; + remap_rte->data = 0; + + memcpy(iremap_entry, &new_ire, sizeof(struct iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + invalidate_sync(iommu); + + unmap_vtd_domain_page(iremap_entries); + spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); + return; +} + +void msi_msg_read_remap_rte( + struct msi_desc *msi_desc, struct msi_msg *msg) +{ + struct pci_dev *pdev = msi_desc->dev; + struct acpi_drhd_unit *drhd = NULL; + struct iommu *iommu = NULL; + struct ir_ctrl *ir_ctrl; + + drhd = acpi_find_matched_drhd_unit(pdev); + iommu = drhd->iommu; + + ir_ctrl = iommu_ir_ctrl(iommu); + if ( !iommu || !ir_ctrl || ir_ctrl->iremap_maddr == 0 ) + return; + + remap_entry_to_msi_msg(iommu, msg); +} + +void msi_msg_write_remap_rte( + struct msi_desc *msi_desc, struct msi_msg *msg) +{ + struct pci_dev *pdev = msi_desc->dev; + struct acpi_drhd_unit *drhd = NULL; + struct iommu *iommu = NULL; + struct ir_ctrl *ir_ctrl; + + drhd = acpi_find_matched_drhd_unit(msi_desc->dev); + iommu = drhd->iommu; + + ir_ctrl = iommu_ir_ctrl(iommu); + if ( !iommu || !ir_ctrl || ir_ctrl->iremap_maddr == 0 ) + return; + + msi_msg_to_remap_entry(iommu, pdev, msg); +} + int intremap_setup(struct iommu *iommu) { struct ir_ctrl *ir_ctrl; diff --git a/xen/drivers/passthrough/vtd/vtd.h b/xen/drivers/passthrough/vtd/vtd.h index 8e3fbb3bef..534a1c3f58 100644 --- a/xen/drivers/passthrough/vtd/vtd.h +++ b/xen/drivers/passthrough/vtd/vtd.h @@ -42,6 +42,24 @@ struct IO_APIC_route_remap_entry { }; }; +struct msi_msg_remap_entry { + union { + u32 val; + struct { + u32 dontcare:2, + index_15:1, + SHV:1, + format:1, + index_0_14:15, + addr_id_val:12; /* Interrupt address identifier value, + must be 0FEEh */ + }; + } address_lo; /* low 32 bits of msi message address */ + + u32 address_hi; /* high 32 bits of msi message address */ + u32 data; /* msi message data */ +}; + unsigned int get_clflush_size(void); u64 alloc_pgtable_maddr(void); void free_pgtable_maddr(u64 maddr); diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 594aa5aefd..e0fecba4db 100644 --- a/xen/include/xen/iommu.h +++ b/xen/include/xen/iommu.h @@ -26,6 +26,7 @@ #include #include #include +#include extern int vtd_enabled; extern int iommu_enabled; @@ -78,6 +79,8 @@ int pt_irq_destroy_bind_vtd(struct domain *d, unsigned int io_apic_read_remap_rte(unsigned int apic, unsigned int reg); void io_apic_write_remap_rte(unsigned int apic, unsigned int reg, unsigned int value); +void msi_msg_read_remap_rte(struct msi_desc *msi_desc, struct msi_msg *msg); +void msi_msg_write_remap_rte(struct msi_desc *msi_desc, struct msi_msg *msg); struct qi_ctrl *iommu_qi_ctrl(struct iommu *iommu); struct ir_ctrl *iommu_ir_ctrl(struct iommu *iommu); struct iommu_flush *iommu_get_flush(struct iommu *iommu); -- cgit v1.2.3