References: bnc#633573

# HG changeset patch
# User Allen Kay <allen.m.kay@intel.com>
# Date 1294992706 0
# Node ID 93e7bf0e1845f1a82441fb740522a9b9cb32beda
# Parent  47713825a3f910fc7cf7571947e8b3b4eab23d5f
vt-d: quirks for Sandybridge errata workaround, WLAN, VT-d fault escalation

Adding errata workaround for newly released Sandybridge processor
graphics, additional WLAN device ID's for WLAN quirk, a quirk for
masking VT-d fault escalation to IOH HW that can cause system hangs on
some OEM hardware where the BIOS erroneously escalates VT-d faults to
the platform.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>

# HG changeset patch
# User Keir Fraser <keir@xen.org>
# Date 1295625672 0
# Node ID 1637fdbfc21e2c732eca29136943a568f8f341cd
# Parent  43592043cefc8357e6e6a0ab9ba85ca480968cb1
[VTD][QUIRK] turn off Sandybridge IGD quirk by default

Turn off Sandybridge IGD quirk by default until potential issues such
as MMIO register conflict with OS device driver and proper locking in
preamble and postamble functions are addressed.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>

# HG changeset patch
# User Allen Kay <allen.m.kay@intel.com>
# Date 1296587456 0
# Node ID 3edd21ffe407ac0e853d51aa8302d9bdb4068749
# Parent  0e2c8b75f7d233f15f8bb49d9db0579e7a350964
passthrough/vtd: disable 64-bit MMCFG quirk on 32-bit Xen

Attached patch disables pci_vtd_quirk for 32-bit Xen since 32-bit xen
does not support MMCFG access.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>

# HG changeset patch
# User Keir Fraser <keir@xen.org>
# Date 1297240805 0
# Node ID c23b711f92646a7e441ee80dbb15b9e1e87c83f8
# Parent  aeda4adecaf85618918dc674855721e3fc9eb33d
[VTD][QUIRK] add spin lock across snb pre/postamble functions

Added a spinlock across snb_vtd_ops_preamble() and
snb_vtd_ops_postamble() to make modifications to IGD registers atomic.
Continue keeping snb_igd_quirk default off.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>

Index: xen-4.0.3-testing/xen/drivers/passthrough/vtd/extern.h
===================================================================
--- xen-4.0.3-testing.orig/xen/drivers/passthrough/vtd/extern.h
+++ xen-4.0.3-testing/xen/drivers/passthrough/vtd/extern.h
@@ -87,5 +87,6 @@ void __init platform_quirks_init(void);
 void vtd_ops_preamble_quirk(struct iommu* iommu);
 void vtd_ops_postamble_quirk(struct iommu* iommu);
 void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map);
+void pci_vtd_quirk(struct pci_dev *pdev);
 
 #endif // _VTD_EXTERN_H_
Index: xen-4.0.3-testing/xen/drivers/passthrough/vtd/iommu.c
===================================================================
--- xen-4.0.3-testing.orig/xen/drivers/passthrough/vtd/iommu.c
+++ xen-4.0.3-testing/xen/drivers/passthrough/vtd/iommu.c
@@ -1864,6 +1864,7 @@ static void setup_dom0_devices(struct do
             list_add(&pdev->domain_list, &d->arch.pdev_list);
             domain_context_mapping(d, pdev->bus, pdev->devfn);
             pci_enable_acs(pdev);
+            pci_vtd_quirk(pdev);
         }
     }
     spin_unlock(&pcidevs_lock);
Index: xen-4.0.3-testing/xen/drivers/passthrough/vtd/quirks.c
===================================================================
--- xen-4.0.3-testing.orig/xen/drivers/passthrough/vtd/quirks.c
+++ xen-4.0.3-testing/xen/drivers/passthrough/vtd/quirks.c
@@ -47,12 +47,15 @@
 #define IS_CTG(id)    (id == 0x2a408086)
 #define IS_ILK(id)    (id == 0x00408086 || id == 0x00448086 || id== 0x00628086 || id == 0x006A8086)
 #define IS_CPT(id)    (id == 0x01008086 || id == 0x01048086)
+#define IS_SNB_GFX(id) (id == 0x01068086 || id == 0x01168086 || id == 0x01268086 || id == 0x01028086 || id == 0x01128086 || id == 0x01228086 || id == 0x010A8086)
 
 u32 ioh_id;
 u32 igd_id;
 bool_t rwbf_quirk;
 static int is_cantiga_b3;
+static int is_snb_gfx;
 static u8 *igd_reg_va;
+static spinlock_t igd_lock;
 
 /*
  * QUIRK to workaround Xen boot issue on Calpella/Ironlake OEM BIOS
@@ -92,6 +95,13 @@ static void cantiga_b3_errata_init(void)
         is_cantiga_b3 = 1;
 }
 
+/* check for Sandybridge IGD device ID's */
+static void snb_errata_init(void)
+{
+    is_snb_gfx = IS_SNB_GFX(igd_id);
+    spin_lock_init(&igd_lock);
+}
+
 /*
  * QUIRK to workaround Cantiga IGD VT-d low power errata.
  * This errata impacts IGD assignment on Cantiga systems
@@ -104,12 +114,15 @@ static void cantiga_b3_errata_init(void)
 /*
  * map IGD MMIO+0x2000 page to allow Xen access to IGD 3D register.
  */
-static void map_igd_reg(void)
+static void *map_igd_reg(void)
 {
     u64 igd_mmio, igd_reg;
 
-    if ( !is_cantiga_b3 || igd_reg_va != NULL )
-        return;
+    if ( !is_cantiga_b3 && !is_snb_gfx )
+        return NULL;
+
+    if ( igd_reg_va )
+        return igd_reg_va;
 
     /* get IGD mmio address in PCI BAR */
     igd_mmio = ((u64)pci_conf_read32(0, IGD_DEV, 0, 0x14) << 32) +
@@ -121,6 +134,7 @@ static void map_igd_reg(void)
     /* ioremap this physical page */
     set_fixmap_nocache(FIX_IGD_MMIO, igd_reg);
     igd_reg_va = (u8 *)fix_to_virt(FIX_IGD_MMIO);
+    return igd_reg_va;
 }
 
 /*
@@ -134,6 +148,9 @@ static int cantiga_vtd_ops_preamble(stru
     if ( !is_igd_drhd(drhd) || !is_cantiga_b3 )
         return 0;
 
+    if ( !map_igd_reg() )
+        return 0;
+
     /*
      * read IGD register at IGD MMIO + 0x20A4 to force IGD
      * to exit low power state.  Since map_igd_reg()
@@ -144,11 +161,74 @@ static int cantiga_vtd_ops_preamble(stru
 }
 
 /*
+ * Sandybridge RC6 power management inhibit state erratum.
+ * This can cause power high power consumption.
+ * Workaround is to prevent graphics get into RC6
+ * state when doing VT-d IOTLB operations, do the VT-d
+ * IOTLB operation, and then re-enable RC6 state.
+ */
+static void snb_vtd_ops_preamble(struct iommu* iommu)
+{
+    struct intel_iommu *intel = iommu->intel;
+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
+    s_time_t start_time;
+
+    if ( !is_igd_drhd(drhd) || !is_snb_gfx )
+        return;
+
+    if ( !map_igd_reg() )
+        return;
+
+    *((volatile u32 *)(igd_reg_va + 0x54)) = 0x000FFFFF;
+    *((volatile u32 *)(igd_reg_va + 0x700)) = 0;
+
+    start_time = NOW();
+    while ( (*((volatile u32 *)(igd_reg_va + 0x2AC)) & 0xF) != 0 )
+    {
+        if ( NOW() > start_time + DMAR_OPERATION_TIMEOUT )
+        {
+            dprintk(XENLOG_INFO VTDPREFIX,
+                    "snb_vtd_ops_preamble: failed to disable idle handshake\n");
+            break;
+        }
+        cpu_relax();
+    }
+
+    *((volatile u32*)(igd_reg_va + 0x50)) = 0x10001;
+}
+
+static void snb_vtd_ops_postamble(struct iommu* iommu)
+{
+    struct intel_iommu *intel = iommu->intel;
+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
+
+    if ( !is_igd_drhd(drhd) || !is_snb_gfx )
+        return;
+
+    if ( !map_igd_reg() )
+        return;
+
+    *((volatile u32 *)(igd_reg_va + 0x54)) = 0xA;
+    *((volatile u32 *)(igd_reg_va + 0x50)) = 0x10000;
+}
+
+/*
  * call before VT-d translation enable and IOTLB flush operations.
  */
+
+static int snb_igd_quirk;
+boolean_param("snb_igd_quirk", snb_igd_quirk);
+
 void vtd_ops_preamble_quirk(struct iommu* iommu)
 {
     cantiga_vtd_ops_preamble(iommu);
+    if ( snb_igd_quirk )
+    {
+        spin_lock(&igd_lock);
+
+        /* match unlock in postamble */
+        snb_vtd_ops_preamble(iommu);
+    }
 }
 
 /*
@@ -156,7 +236,13 @@ void vtd_ops_preamble_quirk(struct iommu
  */
 void vtd_ops_postamble_quirk(struct iommu* iommu)
 {
-    return;
+    if ( snb_igd_quirk )
+    {
+        snb_vtd_ops_postamble(iommu);
+
+        /* match the lock in preamble */
+        spin_unlock(&igd_lock);
+    }
 }
 
 /* initialize platform identification flags */
@@ -175,6 +261,8 @@ void __init platform_quirks_init(void)
     /* initialize cantiga B3 identification */
     cantiga_b3_errata_init();
 
+    snb_errata_init();
+
     /* ioremap IGD MMIO+0x2000 page */
     map_igd_reg();
 }
@@ -246,11 +334,14 @@ void me_wifi_quirk(struct domain *domain
         id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
         switch (id)
         {
-            case 0x00878086:
+            case 0x00878086:        /* Kilmer Peak */
             case 0x00898086:
-            case 0x00828086:
+            case 0x00828086:        /* Taylor Peak */
             case 0x00858086:
-            case 0x42388086:
+            case 0x008F8086:        /* Rainbow Peak */
+            case 0x00908086:
+            case 0x00918086:
+            case 0x42388086:        /* Puma Peak */
             case 0x422b8086:
             case 0x422c8086:
                 map_me_phantom_function(domain, 22, map);
@@ -258,6 +349,28 @@ void me_wifi_quirk(struct domain *domain
             default:
                 break;
         }
+    }
+}
+
+/*
+ * Mask reporting Intel VT-d faults to IOH core logic:
+ *   - Some platform escalates VT-d faults to platform errors
+ *   - This can cause system failure upon non-fatal VT-d faults
+ *   - Potential security issue if malicious guest trigger VT-d faults
+ */
+void pci_vtd_quirk(struct pci_dev *pdev)
+{
+#ifdef CONFIG_X86_64
+    int bus = pdev->bus;
+    int dev = PCI_SLOT(pdev->devfn);
+    int func = PCI_FUNC(pdev->devfn);
+    int id, val;
 
+    id = pci_conf_read32(bus, dev, func, 0);
+    if ( id == 0x342e8086 || id == 0x3c288086 )
+    {
+        val = pci_conf_read32(bus, dev, func, 0x1AC);
+        pci_conf_write32(bus, dev, func, 0x1AC, val | (1 << 31));
     }
+#endif
 }
