u32 sts;
unsigned long flags;
struct vtd_iommu *iommu = drhd->iommu;
+ static const char crash_fmt[] = "%s; crash Xen for security purpose\n";
if ( drhd->gfx_only )
{
+ static const char disable_fmt[] = XENLOG_WARNING VTDPREFIX
+ " %s; disabling IGD VT-d engine\n";
+
if ( !iommu_igfx )
{
- printk(XENLOG_INFO VTDPREFIX
- "Passed iommu=no-igfx option. Disabling IGD VT-d engine.\n");
+ printk(disable_fmt, "passed iommu=no-igfx option");
return;
}
if ( !is_igd_vt_enabled_quirk() )
{
+ static const char msg[] = "firmware did not enable IGD for VT properly";
+
if ( force_iommu )
- panic("BIOS did not enable IGD for VT properly, crash Xen for security purpose\n");
+ panic(crash_fmt, msg);
- printk(XENLOG_WARNING VTDPREFIX
- "BIOS did not enable IGD for VT properly. Disabling IGD VT-d engine.\n");
+ printk(disable_fmt, msg);
return;
}
}
+ if ( !is_azalia_tlb_enabled(drhd) )
+ {
+ static const char msg[] = "firmware did not enable TLB for sound device";
+
+ if ( force_iommu )
+ panic(crash_fmt, msg);
+
+ printk(XENLOG_WARNING VTDPREFIX " %s; disabling ISOCH VT-d engine\n",
+ msg);
+ return;
+ }
+
/* apply platform specific errata workarounds */
vtd_ops_preamble_quirk(iommu);
is_cantiga_b3 = 1;
}
+/*
+ * QUIRK to work around certain BIOSes enabling the ISOCH DMAR unit for the
+ * Azalia sound device, but not giving it any TLB entries, causing it to
+ * deadlock.
+ */
+bool is_azalia_tlb_enabled(const struct acpi_drhd_unit *drhd)
+{
+ pci_sbdf_t sbdf;
+ unsigned int vtisochctrl;
+
+ /* Only dedicated units are of interest. */
+ if ( drhd->include_all || drhd->scope.devices_cnt != 1 )
+ return true;
+
+ /* Check for the specific device. */
+ sbdf = PCI_SBDF2(drhd->segment, drhd->scope.devices[0]);
+ if ( pci_conf_read16(sbdf, PCI_VENDOR_ID) != PCI_VENDOR_ID_INTEL ||
+ pci_conf_read16(sbdf, PCI_DEVICE_ID) != 0x3a3e )
+ return true;
+
+ /* Check for the corresponding System Management Registers device. */
+ sbdf = PCI_SBDF(drhd->segment, 0, 0x14, 0);
+ if ( pci_conf_read16(sbdf, PCI_VENDOR_ID) != PCI_VENDOR_ID_INTEL ||
+ pci_conf_read16(sbdf, PCI_DEVICE_ID) != 0x342e )
+ return true;
+
+ vtisochctrl = pci_conf_read32(sbdf, 0x188);
+ if ( vtisochctrl == 0xffffffff )
+ {
+ printk(XENLOG_WARNING VTDPREFIX
+ " Cannot access VTISOCHCTRL at this time\n");
+ return true;
+ }
+
+ /*
+ * If Azalia DMA is routed to the non-isoch DMAR unit, that's fine in
+ * principle, but not consistent with the ACPI tables.
+ */
+ if ( vtisochctrl & 1 )
+ {
+ printk(XENLOG_WARNING VTDPREFIX
+ " Inconsistency between chipset registers and ACPI tables\n");
+ return true;
+ }
+
+ /* Drop all bits other than the number of TLB entries. */
+ vtisochctrl &= 0x1c;
+
+ /* If we have at least the recommended number of TLB entries, fine. */
+ if ( vtisochctrl >= 16 )
+ return true;
+
+ /* Zero TLB entries? */
+ if ( !vtisochctrl )
+ return false;
+
+ printk(XENLOG_WARNING VTDPREFIX
+ " Recommended TLB entries for ISOCH unit is 16; firmware set %u\n",
+ vtisochctrl);
+
+ return true;
+}
+
/* check for Sandybridge IGD device ID's */
static void __init snb_errata_init(void)
{