# HG changeset patch
# User Wei Wang <wei.wang2@xxxxxxx>
# Date 1303734497 -3600
# Node ID 2dde81ed2e3def60b5ca02f8faff023a6bd487a7
# Parent 54c51e366c0a2742888f21607af200c8f8d4966d
AMD IOMMU: Fix an interrupt remapping issue
Some device could generate bogus interrupts if an IO-APIC RTE and an
iommu interrupt remapping entry are not consistent during 2 adjacent
64bits IO-APIC RTE updates. For example, if the 2nd operation updates
destination bits in RTE for SATA device and unmask it, in some case,
SATA device will assert ioapic pin to generate interrupt immediately
using new destination but iommu could still translate it into the old
destination, then dom0 would be confused. To fix that, we sync up
interrupt remapping entry with IO-APIC IRE on every 32 bits operation
and forward IOAPIC RTE updates after interrupt.
Signed-off-by: Wei Wang <wei.wang2@xxxxxxx>
Acked-by: Jan Beulich <jbeulich@xxxxxxxxxx>
xen-unstable changeset: 23200:995a0c01a076
xen-unstable date: Tue Apr 12 13:26:19 2011 +0100
---
diff -r 54c51e366c0a -r 2dde81ed2e3d xen/drivers/passthrough/amd/iommu_intr.c
--- a/xen/drivers/passthrough/amd/iommu_intr.c Mon Apr 25 13:27:12 2011 +0100
+++ b/xen/drivers/passthrough/amd/iommu_intr.c Mon Apr 25 13:28:17 2011 +0100
@@ -117,8 +117,7 @@
static void update_intremap_entry_from_ioapic(
int bdf,
struct amd_iommu *iommu,
- struct IO_APIC_route_entry *ioapic_rte,
- unsigned int rte_upper, unsigned int value)
+ struct IO_APIC_route_entry *ioapic_rte)
{
unsigned long flags;
u32* entry;
@@ -130,28 +129,26 @@
req_id = get_intremap_requestor_id(bdf);
lock = get_intremap_lock(req_id);
- /* only remap interrupt vector when lower 32 bits in ioapic ire changed */
- if ( likely(!rte_upper) )
+
+ delivery_mode = rte->delivery_mode;
+ vector = rte->vector;
+ dest_mode = rte->dest_mode;
+ dest = rte->dest.logical.logical_dest;
+
+ spin_lock_irqsave(lock, flags);
+
+ offset = get_intremap_offset(vector, delivery_mode);
+ entry = (u32*)get_intremap_entry(req_id, offset);
+ update_intremap_entry(entry, vector, delivery_mode, dest_mode, dest);
+
+ spin_unlock_irqrestore(lock, flags);
+
+ if ( iommu->enabled )
{
- delivery_mode = rte->delivery_mode;
- vector = rte->vector;
- dest_mode = rte->dest_mode;
- dest = rte->dest.logical.logical_dest;
-
- spin_lock_irqsave(lock, flags);
- offset = get_intremap_offset(vector, delivery_mode);
- entry = (u32*)get_intremap_entry(req_id, offset);
-
- update_intremap_entry(entry, vector, delivery_mode, dest_mode, dest);
- spin_unlock_irqrestore(lock, flags);
-
- if ( iommu->enabled )
- {
- spin_lock_irqsave(&iommu->lock, flags);
- invalidate_interrupt_table(iommu, req_id);
- flush_command_buffer(iommu);
- spin_unlock_irqrestore(&iommu->lock, flags);
- }
+ spin_lock_irqsave(&iommu->lock, flags);
+ invalidate_interrupt_table(iommu, req_id);
+ flush_command_buffer(iommu);
+ spin_unlock_irqrestore(&iommu->lock, flags);
}
}
@@ -199,7 +196,8 @@
spin_lock_irqsave(lock, flags);
offset = get_intremap_offset(vector, delivery_mode);
entry = (u32*)get_intremap_entry(req_id, offset);
- update_intremap_entry(entry, vector, delivery_mode, dest_mode,
dest);
+ update_intremap_entry(entry, vector,
+ delivery_mode, dest_mode, dest);
spin_unlock_irqrestore(lock, flags);
if ( iommu->enabled )
@@ -217,16 +215,17 @@
void amd_iommu_ioapic_update_ire(
unsigned int apic, unsigned int reg, unsigned int value)
{
- struct IO_APIC_route_entry ioapic_rte = { 0 };
- unsigned int rte_upper = (reg & 1) ? 1 : 0;
+ struct IO_APIC_route_entry old_rte = { 0 };
+ struct IO_APIC_route_entry new_rte = { 0 };
+ unsigned int rte_lo = (reg & 1) ? reg - 1 : reg;
int saved_mask, bdf;
struct amd_iommu *iommu;
- *IO_APIC_BASE(apic) = reg;
- *(IO_APIC_BASE(apic)+4) = value;
-
if ( !iommu_intremap )
+ {
+ __io_apic_write(apic, reg, value);
return;
+ }
/* get device id of ioapic devices */
bdf = ioapic_bdf[IO_APIC_ID(apic)];
@@ -235,30 +234,49 @@
{
AMD_IOMMU_DEBUG("Fail to find iommu for ioapic device id = 0x%x\n",
bdf);
+ __io_apic_write(apic, reg, value);
return;
}
- if ( rte_upper )
+
+ /* save io-apic rte lower 32 bits */
+ *((u32 *)&old_rte) = __io_apic_read(apic, rte_lo);
+ saved_mask = old_rte.mask;
+
+ if ( reg == rte_lo )
+ {
+ *((u32 *)&new_rte) = value;
+ /* read upper 32 bits from io-apic rte */
+ *(((u32 *)&new_rte) + 1) = __io_apic_read(apic, reg + 1);
+ }
+ else
+ {
+ *((u32 *)&new_rte) = *((u32 *)&old_rte);
+ *(((u32 *)&new_rte) + 1) = value;
+ }
+
+ /* mask the interrupt while we change the intremap table */
+ if ( !saved_mask )
+ {
+ old_rte.mask = 1;
+ __io_apic_write(apic, rte_lo, *((u32 *)&old_rte));
+ }
+
+ /* Update interrupt remapping entry */
+ update_intremap_entry_from_ioapic(bdf, iommu, &new_rte);
+
+ /* Forward write access to IO-APIC RTE */
+ __io_apic_write(apic, reg, value);
+
+ /* For lower bits access, return directly to avoid double writes */
+ if ( reg == rte_lo )
return;
- /* read both lower and upper 32-bits of rte entry */
- *IO_APIC_BASE(apic) = reg;
- *(((u32 *)&ioapic_rte) + 0) = *(IO_APIC_BASE(apic)+4);
- *IO_APIC_BASE(apic) = reg + 1;
- *(((u32 *)&ioapic_rte) + 1) = *(IO_APIC_BASE(apic)+4);
-
- /* mask the interrupt while we change the intremap table */
- saved_mask = ioapic_rte.mask;
- ioapic_rte.mask = 1;
- *IO_APIC_BASE(apic) = reg;
- *(IO_APIC_BASE(apic)+4) = *(((int *)&ioapic_rte)+0);
- ioapic_rte.mask = saved_mask;
-
- update_intremap_entry_from_ioapic(
- bdf, iommu, &ioapic_rte, rte_upper, value);
-
/* unmask the interrupt after we have updated the intremap table */
- *IO_APIC_BASE(apic) = reg;
- *(IO_APIC_BASE(apic)+4) = *(((u32 *)&ioapic_rte)+0);
+ if ( !saved_mask )
+ {
+ old_rte.mask = saved_mask;
+ __io_apic_write(apic, rte_lo, *((u32 *)&old_rte));
+ }
}
static void update_intremap_entry_from_msi_msg(
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|