90 lines
2.8 KiB
Diff
90 lines
2.8 KiB
Diff
|
References: bnc#712051, CVE-2011-3131
|
||
|
|
||
|
# HG changeset patch
|
||
|
# User Dario Faggioli <dario.faggioli@citrix.com>
|
||
|
# Date 1326798686 0
|
||
|
# Node ID a7ae457c327cd790704643e0ed4def3e717b47b3
|
||
|
# Parent 2913ccc6d70f15ffcc15c7e066c9269b15a30a09
|
||
|
Move IOMMU faults handling into softirq for VT-d.
|
||
|
|
||
|
Dealing with interrupts from VT-d IOMMU(s) is deferred to a
|
||
|
softirq-tasklet, raised by the actual IRQ handler. Since a new
|
||
|
interrupt is not generated, even if further faults occur, until we
|
||
|
cleared all the pending ones, there's no need of disabling IRQs, as
|
||
|
the hardware does it by its own. Notice that this may cause the log
|
||
|
to overflow, but none of the existing entry will be overwritten.
|
||
|
|
||
|
Signed-off-by: Dario Faggioli <dario.faggioli@citrix.com>
|
||
|
Committed-by: Keir Fraser <keir@xen.org>
|
||
|
|
||
|
--- a/xen/drivers/passthrough/vtd/iommu.c
|
||
|
+++ b/xen/drivers/passthrough/vtd/iommu.c
|
||
|
@@ -53,6 +53,8 @@ bool_t __read_mostly untrusted_msi;
|
||
|
|
||
|
int nr_iommus;
|
||
|
|
||
|
+static struct tasklet vtd_fault_tasklet;
|
||
|
+
|
||
|
static void setup_dom0_devices(struct domain *d);
|
||
|
static void setup_dom0_rmrr(struct domain *d);
|
||
|
|
||
|
@@ -866,10 +868,8 @@ static void iommu_fault_status(u32 fault
|
||
|
}
|
||
|
|
||
|
#define PRIMARY_FAULT_REG_LEN (16)
|
||
|
-static void iommu_page_fault(int irq, void *dev_id,
|
||
|
- struct cpu_user_regs *regs)
|
||
|
+static void __do_iommu_page_fault(struct iommu *iommu)
|
||
|
{
|
||
|
- struct iommu *iommu = dev_id;
|
||
|
int reg, fault_index;
|
||
|
u32 fault_status;
|
||
|
unsigned long flags;
|
||
|
@@ -943,6 +943,37 @@ clear_overflow:
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+static void do_iommu_page_fault(unsigned long data)
|
||
|
+{
|
||
|
+ struct acpi_drhd_unit *drhd;
|
||
|
+
|
||
|
+ if ( list_empty(&acpi_drhd_units) )
|
||
|
+ {
|
||
|
+ INTEL_IOMMU_DEBUG("no device found, something must be very wrong!\n");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * No matter from whom the interrupt came from, check all the
|
||
|
+ * IOMMUs present in the system. This allows for having just one
|
||
|
+ * tasklet (instead of one per each IOMMUs) and should be more than
|
||
|
+ * fine, considering how rare the event of a fault should be.
|
||
|
+ */
|
||
|
+ for_each_drhd_unit ( drhd )
|
||
|
+ __do_iommu_page_fault(drhd->iommu);
|
||
|
+}
|
||
|
+
|
||
|
+static void iommu_page_fault(int irq, void *dev_id,
|
||
|
+ struct cpu_user_regs *regs)
|
||
|
+{
|
||
|
+ /*
|
||
|
+ * Just flag the tasklet as runnable. This is fine, according to VT-d
|
||
|
+ * specs since a new interrupt won't be generated until we clear all
|
||
|
+ * the faults that caused this one to happen.
|
||
|
+ */
|
||
|
+ tasklet_schedule(&vtd_fault_tasklet);
|
||
|
+}
|
||
|
+
|
||
|
static void dma_msi_unmask(unsigned int irq)
|
||
|
{
|
||
|
struct iommu *iommu = irq_to_iommu[irq];
|
||
|
@@ -2083,6 +2114,8 @@ int __init intel_vtd_setup(void)
|
||
|
iommu_hap_pt_share = FALSE;
|
||
|
}
|
||
|
|
||
|
+ softirq_tasklet_init(&vtd_fault_tasklet, do_iommu_page_fault, 0);
|
||
|
+
|
||
|
if ( !iommu_qinval && iommu_intremap )
|
||
|
{
|
||
|
iommu_intremap = 0;
|