diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2010-03-11 17:15:54 +0000 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2010-03-11 17:15:54 +0000 |
commit | 8343b1c548720a423101c278f7a070a1e8ace94d (patch) | |
tree | a2af800d7bc08c96ade7c782faf429b4ee9dbc12 | |
parent | 01046a5b39339d278e6d8b55ee32d462d63092da (diff) | |
download | xen-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.c | 37 | ||||
-rw-r--r-- | xen/drivers/passthrough/vtd/utils.c | 57 |
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) |