aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2010-03-11 17:15:54 +0000
committerKeir Fraser <keir.fraser@citrix.com>2010-03-11 17:15:54 +0000
commit8343b1c548720a423101c278f7a070a1e8ace94d (patch)
treea2af800d7bc08c96ade7c782faf429b4ee9dbc12
parent01046a5b39339d278e6d8b55ee32d462d63092da (diff)
downloadxen-8343b1c548720a423101c278f7a070a1e8ace94d.tar.gz
xen-8343b1c548720a423101c278f7a070a1e8ace94d.tar.bz2
xen-8343b1c548720a423101c278f7a070a1e8ace94d.zip
VT-d: various initialization fixes
Detect invalid/unsupported configurations in iommu_alloc() - offsets read from hardware must not lead to exceeding a single page (since only that much gets mapped). This covers the apparently not uncommon case of the address pointed to by a DMAR reading as all ones (Linux for example also checks for this). Further correct error handling of that function: Without storing the allocated "struct iommu" instance in the drhd, iommu_free() won't do anything, and hence all successfully set up pieces would be leaked. Also keep iommu_free() from calling destroy_irq() when no irq was ever set up. Additionally, clear_fault_bits() has no need to read the capabilities field from I/O memory - it's already cached in "struct iommu". Finally, simplify print_iommu_regs() and its output, and actually use this function. Signed-off-by: Jan Beulich <jbeulich@novell.com>
-rw-r--r--xen/drivers/passthrough/vtd/iommu.c37
-rw-r--r--xen/drivers/passthrough/vtd/utils.c57
2 files changed, 42 insertions, 52 deletions
diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c
index 460e7032cc..6b18216d82 100644
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -1068,9 +1068,21 @@ static int iommu_alloc(struct acpi_drhd_unit *drhd)
iommu->cap = dmar_readq(iommu->reg, DMAR_CAP_REG);
iommu->ecap = dmar_readq(iommu->reg, DMAR_ECAP_REG);
- dprintk(XENLOG_INFO VTDPREFIX,
- "drhd->address = %"PRIx64"\n", drhd->address);
- dprintk(XENLOG_INFO VTDPREFIX, "iommu->reg = %p\n", iommu->reg);
+ drhd->iommu = iommu;
+
+ dprintk(XENLOG_DEBUG VTDPREFIX,
+ "drhd->address = %"PRIx64" iommu->reg = %p\n",
+ drhd->address, iommu->reg);
+ dprintk(XENLOG_DEBUG VTDPREFIX,
+ "cap = %"PRIx64" ecap = %"PRIx64"\n", iommu->cap, iommu->ecap);
+ if ( cap_fault_reg_offset(iommu->cap) +
+ cap_num_fault_regs(iommu->cap) * PRIMARY_FAULT_REG_LEN >= PAGE_SIZE ||
+ ecap_iotlb_offset(iommu->ecap) >= PAGE_SIZE )
+ {
+ dprintk(XENLOG_ERR VTDPREFIX, "IOMMU: unsupported\n");
+ print_iommu_regs(drhd);
+ return -ENODEV;
+ }
/* Calculate number of pagetable levels: between 2 and 4. */
sagaw = cap_sagaw(iommu->cap);
@@ -1081,7 +1093,7 @@ static int iommu_alloc(struct acpi_drhd_unit *drhd)
{
dprintk(XENLOG_ERR VTDPREFIX,
"IOMMU: unsupported sagaw %lx\n", sagaw);
- xfree(iommu);
+ print_iommu_regs(drhd);
return -ENODEV;
}
iommu->nr_pt_levels = agaw_to_level(agaw);
@@ -1111,7 +1123,6 @@ static int iommu_alloc(struct acpi_drhd_unit *drhd)
spin_lock_init(&iommu->lock);
spin_lock_init(&iommu->register_lock);
- drhd->iommu = iommu;
return 0;
}
@@ -1122,6 +1133,8 @@ static void iommu_free(struct acpi_drhd_unit *drhd)
if ( iommu == NULL )
return;
+ drhd->iommu = NULL;
+
if ( iommu->root_maddr != 0 )
{
free_pgtable_maddr(iommu->root_maddr);
@@ -1135,10 +1148,9 @@ static void iommu_free(struct acpi_drhd_unit *drhd)
xfree(iommu->domid_map);
free_intel_iommu(iommu->intel);
- destroy_irq(iommu->irq);
+ if ( iommu->irq >= 0 )
+ destroy_irq(iommu->irq);
xfree(iommu);
-
- drhd->iommu = NULL;
}
#define guestwidth_to_adjustwidth(gaw) ({ \
@@ -1765,13 +1777,8 @@ void clear_fault_bits(struct iommu *iommu)
unsigned long flags;
spin_lock_irqsave(&iommu->register_lock, flags);
- 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);
+ val = dmar_readq(iommu->reg, cap_fault_reg_offset(iommu->cap) + 8);
+ dmar_writeq(iommu->reg, cap_fault_reg_offset(iommu->cap) + 8, val);
dmar_writel(iommu->reg, DMAR_FSTS_REG, DMA_FSTS_FAULTS);
spin_unlock_irqrestore(&iommu->register_lock, flags);
}
diff --git a/xen/drivers/passthrough/vtd/utils.c b/xen/drivers/passthrough/vtd/utils.c
index 4d5a5430f1..24bb106b39 100644
--- a/xen/drivers/passthrough/vtd/utils.c
+++ b/xen/drivers/passthrough/vtd/utils.c
@@ -59,45 +59,28 @@ void disable_pmr(struct iommu *iommu)
void print_iommu_regs(struct acpi_drhd_unit *drhd)
{
struct iommu *iommu = drhd->iommu;
+ u64 cap;
printk("---- print_iommu_regs ----\n");
- printk("print_iommu_regs: drhd->address = %"PRIx64"\n", drhd->address);
- printk("print_iommu_regs: DMAR_VER_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_VER_REG));
- printk("print_iommu_regs: DMAR_CAP_REG = %"PRIx64"\n",
- dmar_readq(iommu->reg,DMAR_CAP_REG));
- printk("print_iommu_regs: n_fault_reg = %"PRIx64"\n",
- cap_num_fault_regs(dmar_readq(iommu->reg, DMAR_CAP_REG)));
- printk("print_iommu_regs: fault_recording_offset_l = %"PRIx64"\n",
- cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG)));
- printk("print_iommu_regs: fault_recording_offset_h = %"PRIx64"\n",
- cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG)) + 8);
- printk("print_iommu_regs: fault_recording_reg_l = %"PRIx64"\n",
- dmar_readq(iommu->reg,
- cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG))));
- printk("print_iommu_regs: fault_recording_reg_h = %"PRIx64"\n",
- dmar_readq(iommu->reg,
- cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG)) + 8));
- printk("print_iommu_regs: DMAR_ECAP_REG = %"PRIx64"\n",
- dmar_readq(iommu->reg,DMAR_ECAP_REG));
- printk("print_iommu_regs: DMAR_GCMD_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_GCMD_REG));
- printk("print_iommu_regs: DMAR_GSTS_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_GSTS_REG));
- printk("print_iommu_regs: DMAR_RTADDR_REG = %"PRIx64"\n",
- dmar_readq(iommu->reg,DMAR_RTADDR_REG));
- printk("print_iommu_regs: DMAR_CCMD_REG = %"PRIx64"\n",
- dmar_readq(iommu->reg,DMAR_CCMD_REG));
- printk("print_iommu_regs: DMAR_FSTS_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_FSTS_REG));
- printk("print_iommu_regs: DMAR_FECTL_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_FECTL_REG));
- printk("print_iommu_regs: DMAR_FEDATA_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_FEDATA_REG));
- printk("print_iommu_regs: DMAR_FEADDR_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_FEADDR_REG));
- printk("print_iommu_regs: DMAR_FEUADDR_REG = %x\n",
- dmar_readl(iommu->reg,DMAR_FEUADDR_REG));
+ printk(" drhd->address = %"PRIx64"\n", drhd->address);
+ printk(" VER = %x\n", dmar_readl(iommu->reg, DMAR_VER_REG));
+ printk(" CAP = %"PRIx64"\n", cap = dmar_readq(iommu->reg, DMAR_CAP_REG));
+ printk(" n_fault_reg = %"PRIx64"\n", cap_num_fault_regs(cap));
+ printk(" fault_recording_offset = %"PRIx64"\n", cap_fault_reg_offset(cap));
+ printk(" fault_recording_reg_l = %"PRIx64"\n",
+ dmar_readq(iommu->reg, cap_fault_reg_offset(cap)));
+ printk(" fault_recording_reg_h = %"PRIx64"\n",
+ dmar_readq(iommu->reg, cap_fault_reg_offset(cap) + 8));
+ printk(" ECAP = %"PRIx64"\n", dmar_readq(iommu->reg, DMAR_ECAP_REG));
+ printk(" GCMD = %x\n", dmar_readl(iommu->reg, DMAR_GCMD_REG));
+ printk(" GSTS = %x\n", dmar_readl(iommu->reg, DMAR_GSTS_REG));
+ printk(" RTADDR = %"PRIx64"\n", dmar_readq(iommu->reg,DMAR_RTADDR_REG));
+ printk(" CCMD = %"PRIx64"\n", dmar_readq(iommu->reg, DMAR_CCMD_REG));
+ printk(" FSTS = %x\n", dmar_readl(iommu->reg, DMAR_FSTS_REG));
+ printk(" FECTL = %x\n", dmar_readl(iommu->reg, DMAR_FECTL_REG));
+ printk(" FEDATA = %x\n", dmar_readl(iommu->reg, DMAR_FEDATA_REG));
+ printk(" FEADDR = %x\n", dmar_readl(iommu->reg, DMAR_FEADDR_REG));
+ printk(" FEUADDR = %x\n", dmar_readl(iommu->reg, DMAR_FEUADDR_REG));
}
static u32 get_level_index(unsigned long gmfn, int level)