mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
vtd: interrupt remapping for MSI/MSI-x.
Signed-off-by: Weidong Han <weidong.han@intel.com>
This commit is contained in:
1 parent
99c3cae637
commit
4151cc56f1
4 files changed
+198
No files matched your search
@@ -25,6 +25,7 @@
|
||||
#include <mach_apic.h>
|
||||
#include <io_ports.h>
|
||||
#include <public/physdev.h>
|
||||
#include <xen/iommu.h>
|
||||
|
||||
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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <xen/pci.h>
|
||||
#include <public/hvm/ioreq.h>
|
||||
#include <public/domctl.h>
|
||||
#include <asm/msi.h>
|
||||
|
||||
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);
|
||||
|
||||