Vt-d: fix dom0 graphics problem on Levnovo T410.
authorKeir Fraser <keir@xen.org>
Sat, 2 Oct 2010 14:04:21 +0000 (15:04 +0100)
committerKeir Fraser <keir@xen.org>
Sat, 2 Oct 2010 14:04:21 +0000 (15:04 +0100)
The patch is derived from a similar quirk in Linux kernel by David
Woodhouse and Adam Jackson.  It checks for VT enabling bit in IGD GGC
register.  If VT is not enabled correctly in the IGD, Xen does not
enable VT-d translation for IGD VT-d engine.  In case where iommu boot
parameter is set to force, Xen calls panic().

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
xen/drivers/passthrough/vtd/dmar.c
xen/drivers/passthrough/vtd/dmar.h
xen/drivers/passthrough/vtd/iommu.c

index f4078ff44b2647420562ea74d4d5f82162305922..d8edf03e5393a5db076b3575425e6e0798f3f1a5 100644 (file)
@@ -46,6 +46,7 @@ LIST_HEAD(acpi_rmrr_units);
 LIST_HEAD(acpi_atsr_units);
 LIST_HEAD(acpi_rhsa_units);
 
+static u64 igd_drhd_address;
 u8 dmar_host_address_width;
 
 void dmar_scope_add_buses(struct dmar_scope *scope, u16 sec_bus, u16 sub_bus)
@@ -239,6 +240,11 @@ struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd)
     return NULL;
 }
 
+int is_igd_drhd(struct acpi_drhd_unit *drhd)
+{
+    return ( drhd->address == igd_drhd_address ? 1 : 0);
+}
+
 /*
  * Count number of devices in device scope.  Do not include PCI sub
  * hierarchies.
@@ -272,7 +278,8 @@ static int scope_device_count(void *start, void *end)
 
 
 static int __init acpi_parse_dev_scope(void *start, void *end,
-                                       void *acpi_entry, int type)
+                                       void *acpi_entry, int type,
+                                       int *igd)
 {
     struct dmar_scope *scope = acpi_entry;
     struct acpi_ioapic_unit *acpi_ioapic_unit;
@@ -333,6 +340,8 @@ static int __init acpi_parse_dev_scope(void *start, void *end,
             if ( iommu_verbose )
                 dprintk(VTDPREFIX, "  endpoint: %x:%x.%x\n",
                         bus, path->dev, path->fn);
+            if ( (bus == 0) && (path->dev == 2) && (path->fn == 0) )
+                *igd = 1;
             break;
 
         case ACPI_DEV_IOAPIC:
@@ -379,7 +388,7 @@ acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
     void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
-    int ret;
+    int ret, igd = 0;
     static int include_all = 0;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*drhd))) != 0 )
@@ -404,7 +413,10 @@ acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
     dev_scope_start = (void *)(drhd + 1);
     dev_scope_end = ((void *)drhd) + header->length;
     ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
-                               dmaru, DMAR_TYPE);
+                               dmaru, DMAR_TYPE, &igd);
+
+    if ( igd )
+        igd_drhd_address = dmaru->address;
 
     if ( dmaru->include_all )
     {
@@ -492,7 +504,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
     struct acpi_rmrr_unit *rmrru;
     void *dev_scope_start, *dev_scope_end;
     u64 base_addr = rmrr->base_address, end_addr = rmrr->end_address;
-    int ret;
+    int ret, igd = 0;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*rmrr))) != 0 )
         return ret;
@@ -524,7 +536,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
     dev_scope_start = (void *)(rmrr + 1);
     dev_scope_end   = ((void *)rmrr) + header->length;
     ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
-                               rmrru, RMRR_TYPE);
+                               rmrru, RMRR_TYPE, &igd);
 
     if ( ret || (rmrru->scope.devices_cnt == 0) )
         xfree(rmrru);
@@ -589,7 +601,7 @@ acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
 {
     struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
     struct acpi_atsr_unit *atsru;
-    int ret;
+    int ret, igd = 0;
     static int all_ports;
     void *dev_scope_start, *dev_scope_end;
 
@@ -610,7 +622,7 @@ acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
         dev_scope_start = (void *)(atsr + 1);
         dev_scope_end   = ((void *)atsr) + header->length;
         ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
-                                   atsru, ATSR_TYPE);
+                                   atsru, ATSR_TYPE, &igd);
     }
     else
     {
index 5d864f67df189380be98da16fa0e0ef45a0fb446..e0c0fff85479c35137f48421f9443cd5ce89f6c6 100644 (file)
@@ -114,5 +114,6 @@ void *map_to_nocache_virt(int nr_iommus, u64 maddr);
 int vtd_hw_check(void);
 void disable_pmr(struct iommu *iommu);
 int is_usb_device(u8 bus, u8 devfn);
+int is_igd_drhd(struct acpi_drhd_unit *drhd);
 
 #endif /* _DMAR_H_ */
index 5e43a80e229c7370f8eb0d6d01c3401442d690be..aab25c423a4105f1dd51119f5e4501bde5674387 100644 (file)
@@ -689,10 +689,34 @@ static int iommu_set_root_entry(struct iommu *iommu)
     return 0;
 }
 
-static void iommu_enable_translation(struct iommu *iommu)
+#define GGC 0x52
+#define GGC_MEMORY_VT_ENABLED  (0x8 << 8)
+static int is_igd_vt_enabled(void)
+{
+    unsigned short ggc;
+
+    /* integrated graphics on Intel platforms is located at 0:2.0 */
+    ggc = pci_conf_read16(0, 2, 0, GGC);
+    return ( ggc & GGC_MEMORY_VT_ENABLED ? 1 : 0 );
+}
+
+static void iommu_enable_translation(struct acpi_drhd_unit *drhd)
 {
     u32 sts;
     unsigned long flags;
+    struct iommu *iommu = drhd->iommu;
+
+    if ( !is_igd_vt_enabled() && is_igd_drhd(drhd) ) 
+    {
+        if ( force_iommu )
+            panic("BIOS did not enable IGD for VT properly, crash Xen for security purpose!\n");
+        else
+        {
+            dprintk(XENLOG_WARNING VTDPREFIX,
+                    "BIOS did not enable IGD for VT properly.  Disabling IGD VT-d engine.\n");
+            return;
+        }
+    }
 
     if ( iommu_verbose )
         dprintk(VTDPREFIX,
@@ -1179,7 +1203,6 @@ static int intel_iommu_domain_init(struct domain *d)
 
 static void intel_iommu_dom0_init(struct domain *d)
 {
-    struct iommu *iommu;
     struct acpi_drhd_unit *drhd;
 
     if ( !iommu_passthrough && !need_iommu(d) )
@@ -1195,8 +1218,7 @@ static void intel_iommu_dom0_init(struct domain *d)
 
     for_each_drhd_unit ( drhd )
     {
-        iommu = drhd->iommu;
-        iommu_enable_translation(iommu);
+        iommu_enable_translation(drhd);
     }
 }
 
@@ -2165,7 +2187,7 @@ static void vtd_resume(void)
                     (u32) iommu_state[i][DMAR_FEUADDR_REG]);
         spin_unlock_irqrestore(&iommu->register_lock, flags);
 
-        iommu_enable_translation(iommu);
+        iommu_enable_translation(drhd);
     }
 }