* The `qinval` boolean controls the Queued Invalidation sub-feature, and is
active by default on compatible hardware. Queued Invalidation is a
feature in second-generation IOMMUs and is a functional prerequisite for
- Interrupt Remapping.
+ Interrupt Remapping. Note that Xen disregards this setting for Intel VT-d
+ version 6 and greater as Registered-Based Invalidation isn't supported
+ by them.
* The `igfx` boolean is active by default, and controls whether the IOMMU in
front of an Intel Graphics Device is enabled or not.
iommu->cap = dmar_readq(iommu->reg, DMAR_CAP_REG);
iommu->ecap = dmar_readq(iommu->reg, DMAR_ECAP_REG);
+ iommu->version = dmar_readl(iommu->reg, DMAR_VER_REG);
+
+ if ( !iommu_qinval && !has_register_based_invalidation(iommu) )
+ {
+ printk(XENLOG_WARNING VTDPREFIX "IOMMU %d: cannot disable Queued Invalidation\n",
+ iommu->index);
+ iommu_qinval = true;
+ }
if ( iommu_verbose )
{
*/
if ( enable_qinval(iommu) != 0 )
{
+ /* Ensure register-based invalidation is available */
+ if ( !has_register_based_invalidation(iommu) )
+ return -EIO;
+
iommu->flush.context = vtd_flush_context_reg;
iommu->flush.iotlb = vtd_flush_iotlb_reg;
}
struct acpi_drhd_unit *drhd;
struct vtd_iommu *iommu;
int ret;
+ bool reg_inval_supported = true;
if ( list_empty(&acpi_drhd_units) )
{
}
/* We enable the following features only if they are supported by all VT-d
- * engines: Snoop Control, DMA passthrough, Queued Invalidation, Interrupt
- * Remapping, and Posted Interrupt
+ * engines: Snoop Control, DMA passthrough, Register-based Invalidation,
+ * Queued Invalidation, Interrupt Remapping, and Posted Interrupt.
*/
for_each_drhd_unit ( drhd )
{
if ( iommu_qinval && !ecap_queued_inval(iommu->ecap) )
iommu_qinval = 0;
+ if ( !has_register_based_invalidation(iommu) )
+ reg_inval_supported = false;
+
if ( iommu_intremap && !ecap_intr_remap(iommu->ecap) )
iommu_intremap = iommu_intremap_off;
softirq_tasklet_init(&vtd_fault_tasklet, do_iommu_page_fault, NULL);
+ if ( !iommu_qinval && !reg_inval_supported )
+ {
+ dprintk(XENLOG_ERR VTDPREFIX, "No available invalidation interface\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
if ( !iommu_qinval && iommu_intremap )
{
iommu_intremap = iommu_intremap_off;
struct list_head ats_devices;
unsigned long *domid_bitmap; /* domain id bitmap */
u16 *domid_map; /* domain id mapping array */
+ uint32_t version;
};
#define INTEL_IOMMU_DEBUG(fmt, args...) \
dprintk(XENLOG_WARNING VTDPREFIX, fmt, ## args); \
} while(0)
+/* Register-based invalidation isn't supported by VT-d version 6 and beyond. */
+static inline bool has_register_based_invalidation(const struct vtd_iommu *vtd)
+{
+ return VER_MAJOR(vtd->version) < 6;
+}
+
#endif
return 0;
}
+static int vtd_flush_context_noop(struct vtd_iommu *iommu, uint16_t did,
+ uint16_t source_id, uint8_t function_mask,
+ uint64_t type, bool flush_non_present_entry)
+{
+ WARN();
+ return -EIO;
+}
+
+static int vtd_flush_iotlb_noop(struct vtd_iommu *iommu, uint16_t did,
+ uint64_t addr, unsigned int size_order,
+ uint64_t type, bool flush_non_present_entry,
+ bool flush_dev_iotlb)
+{
+ WARN();
+ return -EIO;
+}
+
void disable_qinval(struct vtd_iommu *iommu)
{
u32 sts;
out:
spin_unlock_irqrestore(&iommu->register_lock, flags);
- iommu->flush.context = vtd_flush_context_reg;
- iommu->flush.iotlb = vtd_flush_iotlb_reg;
+ /*
+ * Assign callbacks to noop to catch errors if register-based invalidation
+ * isn't supported.
+ */
+ if ( has_register_based_invalidation(iommu) )
+ {
+ iommu->flush.context = vtd_flush_context_reg;
+ iommu->flush.iotlb = vtd_flush_iotlb_reg;
+ }
+ else
+ {
+ iommu->flush.context = vtd_flush_context_noop;
+ iommu->flush.iotlb = vtd_flush_iotlb_noop;
+ }
}