All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH][VTD] consolidate VT-d quirks into a single file quirks.c
@ 2010-10-14  0:05 Kay, Allen M
  2010-10-14  9:04 ` Jan Beulich
  0 siblings, 1 reply; 8+ messages in thread
From: Kay, Allen M @ 2010-10-14  0:05 UTC (permalink / raw)
  To: Xen-devel; +Cc: Jan Beulich

[-- Attachment #1: Type: text/plain, Size: 328 bytes --]

Consolidate VT-d quirks into a single file - quirks.c.  This includes quirks to workaround OEM BIOS issue with VT-d enabling in IGD, Cantiga VT-d buffer flush issue, Cantiga IGD Vt-d low power related errata, and a quirk to workaround issues related to wifi direct assignment.

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

[-- Attachment #2: quirks10135.patch --]
[-- Type: application/octet-stream, Size: 21883 bytes --]

diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/Makefile
--- a/xen/drivers/passthrough/vtd/Makefile	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/Makefile	Wed Oct 13 10:01:35 2010 -0700
@@ -6,3 +6,4 @@
 obj-y += utils.o
 obj-y += qinval.o
 obj-y += intremap.o
+obj-y += quirks.o
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/dmar.c
--- a/xen/drivers/passthrough/vtd/dmar.c	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/dmar.c	Wed Oct 13 10:01:35 2010 -0700
@@ -242,6 +242,12 @@
 
 int is_igd_drhd(struct acpi_drhd_unit *drhd)
 {
+    if ( drhd == NULL )
+    {
+        dprintk(XENLOG_WARNING VTDPREFIX, "unexpected drhd == NULL\n");
+        return 0;
+    }
+
     return ( drhd->address == igd_drhd_address ? 1 : 0);
 }
 
@@ -279,7 +285,7 @@
 
 static int __init acpi_parse_dev_scope(void *start, void *end,
                                        void *acpi_entry, int type,
-                                       int *igd)
+                                       int *match_igd)
 {
     struct dmar_scope *scope = acpi_entry;
     struct acpi_ioapic_unit *acpi_ioapic_unit;
@@ -340,8 +346,8 @@
             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;
+
+            *match_igd = match_igd_quirk(bus, path->dev, path->fn);
             break;
 
         case ACPI_DEV_IOAPIC:
@@ -388,7 +394,7 @@
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
     void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
-    int ret, igd = 0;
+    int ret, match_igd = 0;
     static int include_all = 0;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*drhd))) != 0 )
@@ -413,9 +419,10 @@
     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, &igd);
+                               dmaru, DMAR_TYPE, &match_igd);
 
-    if ( igd )
+    /* remember the IGD VT-d engine */
+    if ( match_igd )
         igd_drhd_address = dmaru->address;
 
     if ( dmaru->include_all )
@@ -504,7 +511,7 @@
     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, igd = 0;
+    int ret;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*rmrr))) != 0 )
         return ret;
@@ -536,7 +543,7 @@
     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, &igd);
+                               rmrru, RMRR_TYPE, NULL);
 
     if ( ret || (rmrru->scope.devices_cnt == 0) )
         xfree(rmrru);
@@ -601,7 +608,7 @@
 {
     struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
     struct acpi_atsr_unit *atsru;
-    int ret, igd = 0;
+    int ret;
     static int all_ports;
     void *dev_scope_start, *dev_scope_end;
 
@@ -622,7 +629,7 @@
         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, &igd);
+                                   atsru, ATSR_TYPE, NULL);
     }
     else
     {
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/extern.h
--- a/xen/drivers/passthrough/vtd/extern.h	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/extern.h	Wed Oct 13 10:01:35 2010 -0700
@@ -26,6 +26,7 @@
 
 extern int qinval_enabled;
 extern int ats_enabled;
+extern bool_t rwbf_quirk;
 
 void print_iommu_regs(struct acpi_drhd_unit *drhd);
 void print_vtd_entries(struct iommu *iommu, int bus, int devfn, u64 gmfn);
@@ -35,6 +36,12 @@
 void disable_qinval(struct iommu *iommu);
 int enable_intremap(struct iommu *iommu, int eim);
 void disable_intremap(struct iommu *iommu);
+
+void iommu_flush_cache_entry(void *addr, unsigned int size);
+void iommu_flush_cache_page(void *addr, unsigned long npages);
+int iommu_alloc(struct acpi_drhd_unit *drhd);
+void iommu_free(struct acpi_drhd_unit *drhd);
+
 int queue_invalidate_context(struct iommu *iommu,
     u16 did, u16 source_id, u8 function_mask, u8 granu);
 int queue_invalidate_iotlb(struct iommu *iommu,
@@ -45,18 +52,41 @@
 int iommu_flush_iec_global(struct iommu *iommu);
 int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx);
 void clear_fault_bits(struct iommu *iommu);
+
 struct iommu * ioapic_to_iommu(unsigned int apic_id);
 struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id);
 struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu);
 struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd);
+struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
 int ats_device(int seg, int bus, int devfn);
 int enable_ats_device(int seg, int bus, int devfn);
 int disable_ats_device(int seg, int bus, int devfn);
 int invalidate_ats_tcs(struct iommu *iommu);
+
 int qinval_device_iotlb(struct iommu *iommu,
                         u32 max_invs_pend, u16 sid, u16 size, u64 addr);
 int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
                          u64 addr, unsigned int size_order, u64 type);
-struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
+unsigned int get_cache_line_size(void);
+void cacheline_flush(char *);
+void flush_all_cache(void);
+
+u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
+void free_pgtable_maddr(u64 maddr);
+void *map_vtd_domain_page(u64 maddr);
+void unmap_vtd_domain_page(void *va);
+int domain_context_mapping_one(struct domain *domain, struct iommu *iommu,
+                               u8 bus, u8 devfn);
+int domain_context_unmap_one(struct domain *domain, struct iommu *iommu,
+                             u8 bus, u8 devfn);
+
+int match_igd_quirk(int bus, int dev, int func);
+int is_igd_vt_enabled_quirk(void);
+void __init platform_quirks(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, u8 map);
 
 #endif // _VTD_EXTERN_H_
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c	Wed Oct 13 10:01:35 2010 -0700
@@ -44,7 +44,6 @@
 #endif
 
 int nr_iommus;
-static bool_t rwbf_quirk;
 
 static void setup_dom0_devices(struct domain *d);
 static void setup_dom0_rmrr(struct domain *d);
@@ -482,16 +481,36 @@
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline iommu_flush_iotlb_dsi(struct iommu *iommu, u16 did,
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status =  flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline get_alignment(u64 base, unsigned int size)
@@ -515,6 +534,7 @@
 {
     unsigned int align;
     struct iommu_flush *flush = iommu_get_flush(iommu);
+    int status;
 
     ASSERT(!(addr & (~PAGE_MASK_4K)));
     ASSERT(pages > 0);
@@ -535,8 +555,16 @@
     addr >>= PAGE_SHIFT_4K + align;
     addr <<= PAGE_SHIFT_4K + align;
 
-    return flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+   return status;
 }
 
 static void iommu_flush_all(void)
@@ -689,24 +717,13 @@
     return 0;
 }
 
-#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 ( is_igd_drhd(drhd) && !is_igd_vt_enabled_quirk() ) 
     {
         if ( force_iommu )
             panic("BIOS did not enable IGD for VT properly, crash Xen for security purpose!\n");
@@ -718,6 +735,9 @@
         }
     }
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     if ( iommu_verbose )
         dprintk(VTDPREFIX,
                 "iommu_enable_translation: iommu->reg = %p\n", iommu->reg);
@@ -730,6 +750,9 @@
                   (sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
 
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
     /* Disable PMRs when VT-d engine takes effect per spec definition */
     disable_pmr(iommu);
 }
@@ -739,6 +762,9 @@
     u32 sts;
     unsigned long flags;
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     spin_lock_irqsave(&iommu->register_lock, flags);
     sts = dmar_readl(iommu->reg, DMAR_GSTS_REG);
     dmar_writel(iommu->reg, DMAR_GCMD_REG, sts & (~DMA_GCMD_TE));
@@ -747,6 +773,9 @@
     IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl,
                   !(sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
 }
 
 enum faulttype {
@@ -1090,6 +1119,7 @@
         xfree(iommu);
         return -ENOMEM;
     }
+    iommu->intel->drhd = drhd;
 
     iommu->reg = map_to_nocache_virt(nr_iommus, drhd->address);
     iommu->index = nr_iommus++;
@@ -1222,7 +1252,7 @@
     }
 }
 
-static int domain_context_mapping_one(
+int domain_context_mapping_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1324,6 +1354,8 @@
 
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1405,7 +1437,7 @@
     return ret;
 }
 
-static int domain_context_unmap_one(
+int domain_context_unmap_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1453,6 +1485,8 @@
     spin_unlock(&iommu->lock);
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1951,19 +1985,6 @@
     spin_unlock(&pcidevs_lock);
 }
 
-static void __init platform_quirks(void)
-{
-    u32 id;
-
-    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
-    id = pci_conf_read32(0, 0, 0, 0);
-    if ( id == 0x2a408086 )
-    {
-        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
-        rwbf_quirk = 1;
-    }
-}
-
 int __init intel_vtd_setup(void)
 {
     struct acpi_drhd_unit *drhd;
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/iommu.h
--- a/xen/drivers/passthrough/vtd/iommu.h	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.h	Wed Oct 13 10:01:35 2010 -0700
@@ -501,6 +501,7 @@
     struct qi_ctrl qi_ctrl;
     struct ir_ctrl ir_ctrl;
     struct iommu_flush flush;
+    struct acpi_drhd_unit *drhd;
 };
 
 #endif
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/quirks.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/passthrough/vtd/quirks.c	Wed Oct 13 10:01:35 2010 -0700
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Allen Kay <allen.m.kay@intel.com>
+ */
+
+#include <xen/irq.h>
+#include <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <xen/domain_page.h>
+#include <xen/iommu.h>
+#include <asm/hvm/iommu.h>
+#include <xen/numa.h>
+#include <xen/softirq.h>
+#include <xen/time.h>
+#include <xen/pci.h>
+#include <xen/pci_regs.h>
+#include <xen/keyhandler.h>
+#include <asm/msi.h>
+#include <asm/irq.h>
+#include <mach_apic.h>
+#include "iommu.h"
+#include "dmar.h"
+#include "extern.h"
+#include "vtd.h"
+
+#define IGD_BAR_MASK 0xFFFF0000
+#define GGC 0x52
+#define GGC_MEMORY_VT_ENABLED  (0x8 << 8)
+
+#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)
+
+bool_t rwbf_quirk;
+static int is_cantiga_b3;
+static u64 *igd_reg_va;
+
+/*
+ * QUIRK to workaround Xen boot issue on Calpella/Ironlake OEM BIOS
+ * not enabling VT-d properly in IGD.  The workaround is to not enabling
+ * IGD VT-d translation if VT is not enabled in IGD.
+ */
+int match_igd_quirk(int bus, int dev, int func)
+{
+    u32 id;
+    int ret = 0;
+
+    if ( (bus != 0) || (dev != INTEL_IGD_DEV) || (func != 0) )
+        goto exit;
+
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( IS_ILK(id) )
+        ret = 1;
+
+exit:
+    return ret;
+}
+
+int is_igd_vt_enabled_quirk(void)
+{
+    u16 ggc;
+
+    /* integrated graphics on Intel platforms is located at 0:2.0 */
+    ggc = pci_conf_read16(0, INTEL_IGD_DEV, 0, GGC);
+    return ( ggc & GGC_MEMORY_VT_ENABLED ? 1 : 0 );
+}
+
+/*
+ * QUIRK to workaround cantiga VT-d buffer flush issue.
+ * The workaround is to force write buffer flush even if
+ * VT-d capability indicates it is not required.
+ */
+static void cantiga_b3_errata_setup(void)
+{
+    u16 vid;
+    u8 did_hi, rid;
+
+    vid = pci_conf_read16(0, INTEL_IGD_DEV, 0, 0);
+    if ( vid != 0x8086 )
+        return;
+
+    did_hi = pci_conf_read8(0, INTEL_IGD_DEV, 0, 3);
+    rid = pci_conf_read8(0, INTEL_IGD_DEV, 0, 8);
+
+    if ( (did_hi == 0x2A) && (rid == 0x7) )
+        is_cantiga_b3 = 1;
+}
+
+/* setup platform identification flags */
+void __init platform_quirks(void)
+{
+    u32 id;
+
+    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( id == 0x2a408086 )
+    {
+        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
+        rwbf_quirk = 1;
+    }
+    cantiga_b3_errata_setup();
+}
+
+/*
+ * QUIRK to workaround Cantiga IGD VT-d low power errata.
+ * This errata impacts IGD assignment on Cantiga systems
+ * and can potentially cause VT-d operations to hang.
+ * The workaround is to access an IGD PCI config register
+ * to get IGD out of low power state before VT-d translation
+ * enable/disable and IOTLB flushes.
+ */
+
+/* map IGD MMIO+0x2000 page */
+static int map_igd_reg(void)
+{
+    u64 igd_mmio, igd_reg;
+    int status;
+
+    if ( igd_reg_va != NULL )
+        return 0;
+
+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
+    status = map_pages_to_xen(igd_reg, igd_reg >> PAGE_SHIFT, 1,
+                           __PAGE_HYPERVISOR);
+    if ( status == 0 )
+        igd_reg_va = (u64 *) igd_reg;
+    else
+        dprintk(XENLOG_INFO VTDPREFIX, "map_igd_reg() failed\n");
+
+    return status;
+}
+
+static void cantiga_vtd_ops_preamble(struct iommu* iommu)
+{
+    struct intel_iommu *intel = iommu->intel;
+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
+    int data;
+
+    if ( !is_igd_drhd(drhd) || !is_cantiga_b3 )
+        return;
+
+    if ( map_igd_reg() )
+        return;
+
+    /* just want to read from the regster, don't need the data */
+    data = *((int*)(igd_reg_va + 0x20A4));
+}
+
+/*
+ * call before VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_preamble_quirk(struct iommu* iommu)
+{
+    cantiga_vtd_ops_preamble(iommu);
+}
+
+/*
+ * call after VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_postamble_quirk(struct iommu* iommu)
+{
+    return;
+}
+
+/*
+ * QUIRK to workaround wifi direct assignment issue.  This issue
+ * impacts only cases where Intel integrated wifi device is directly
+ * is directly assigned to a guest.
+ *
+ * The workaround is to map ME phantom device 0:3.7 or 0:22.7
+ * to the ME vt-d engine if detect the user is trying to directly
+ * assigning Intel integrated wifi device to a guest.
+ */
+void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, u8 map)
+{
+    struct acpi_drhd_unit *drhd;
+    struct pci_dev *pdev;
+    u32 id;
+
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( IS_CTG(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 3, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:3.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x42328086:
+            case 0x42358086:
+            case 0x42368086:
+            case 0x42378086:
+            case 0x423a8086:
+            case 0x423b8086:
+            case 0x423c8086:
+            case 0x423d8086:
+                /* find ME VT-d engine base on a real ME device 0:3.0 */
+                pdev = pci_get_pdev(0, PCI_DEVFN(3, 0));
+                drhd = acpi_find_matched_drhd_unit(pdev);
+
+                /* map or unmap ME phantom function 0:3.7 */
+                if ( map )
+                    domain_context_mapping_one(domain, drhd->iommu, 0, PCI_DEVFN(3, 7));
+                else
+                    domain_context_unmap_one(domain, drhd->iommu, 0, PCI_DEVFN(3, 7));
+
+                break;
+            default:
+                break;
+        }
+    }
+    else if ( IS_ILK(id) || IS_CPT(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 22, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:22.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x00878086:
+            case 0x00898086:
+            case 0x00828086:
+            case 0x00858086:
+            case 0x42388086:
+            case 0x422b8086:
+                /* find ME VT-d engine base on a real ME device 0:22.0 */
+                pdev = pci_get_pdev(0, PCI_DEVFN(22, 0));
+                drhd = acpi_find_matched_drhd_unit(pdev);
+
+                /* map or unmap ME phantom function 0:22.7 */
+                if ( map )
+                    domain_context_mapping_one(domain, drhd->iommu, 0, PCI_DEVFN(22, 7));
+                else
+                    domain_context_unmap_one(domain, drhd->iommu, 0, PCI_DEVFN(22, 7));
+
+                break;
+            default:
+                break;
+        }
+
+    }
+}
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/vtd.h
--- a/xen/drivers/passthrough/vtd/vtd.h	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/vtd.h	Wed Oct 13 10:01:35 2010 -0700
@@ -23,6 +23,10 @@
 
 #include <xen/iommu.h>
 
+#define INTEL_IGD_DEV            0
+#define MAP_ME_PHANTOM_FUNC      1
+#define UNMAP_ME_PHANTOM_FUNC    0
+
 /* Accomodate both IOAPIC and IOSAPIC. */
 struct IO_xAPIC_route_entry {
     __u32   vector      :  8,
@@ -97,18 +101,4 @@
     u32	data;		/* msi message data */
 };
 
-unsigned int get_cache_line_size(void);
-void cacheline_flush(char *);
-void flush_all_cache(void);
-u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
-void free_pgtable_maddr(u64 maddr);
-void *map_vtd_domain_page(u64 maddr);
-void unmap_vtd_domain_page(void *va);
-
-void iommu_flush_cache_entry(void *addr, unsigned int size);
-void iommu_flush_cache_page(void *addr, unsigned long npages);
-
-int iommu_alloc(struct acpi_drhd_unit *drhd);
-void iommu_free(struct acpi_drhd_unit *drhd);
-
 #endif // _VTD_H_
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/x86/vtd.c
--- a/xen/drivers/passthrough/vtd/x86/vtd.c	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/x86/vtd.c	Wed Oct 13 10:01:35 2010 -0700
@@ -28,6 +28,7 @@
 #include "../iommu.h"
 #include "../dmar.h"
 #include "../vtd.h"
+#include "../extern.h"
 
 /*
  * iommu_inclusive_mapping: when set, all memory below 4GB is included in dom0

[-- Attachment #3: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC][PATCH][VTD] consolidate VT-d quirks into a single file quirks.c
  2010-10-14  0:05 [RFC][PATCH][VTD] consolidate VT-d quirks into a single file quirks.c Kay, Allen M
@ 2010-10-14  9:04 ` Jan Beulich
  2010-10-15  0:25   ` [RFC][PATCH][VTD][v2] " Kay, Allen M
  0 siblings, 1 reply; 8+ messages in thread
From: Jan Beulich @ 2010-10-14  9:04 UTC (permalink / raw)
  To: Allen M Kay; +Cc: Xen-devel

>>> On 14.10.10 at 02:05, "Kay, Allen M" <allen.m.kay@intel.com> wrote:
> Consolidate VT-d quirks into a single file - quirks.c.  This includes quirks to 
> workaround OEM BIOS issue with VT-d enabling in IGD, Cantiga VT-d buffer flush 
> issue, Cantiga IGD Vt-d low power related errata, and a quirk to workaround 
> issues related to wifi direct assignment.
> 
> Signed-off-by: Allen Kay allen.m.kay@intel.com 

Hmm, I was actually e.g. expecting the extra parameter c/s  had
added to acpi_parse_dev_scope() to go away altogether again.
Below is the relevant portion of the reduced equivalent of your
original patch that we currently apply to 4.0.1 - note that
acpi_parse_dev_scope() even before this already did similar
type dependent work using acpi_entry.

>+static int map_igd_reg(void)
>+{
>+    u64 igd_mmio, igd_reg;
>+    int status;
>+
>+    if ( igd_reg_va != NULL )
>+        return 0;
>+
>+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
>+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
>+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
>+    status = map_pages_to_xen(igd_reg, igd_reg >> PAGE_SHIFT, 1,
>+                           __PAGE_HYPERVISOR);

You're mapping to *virtual* address igd_reg here? Even if this was
done only at boot time it would seem wrong, but as I understand it
this is done so even at run time, which is an absolute no-go.

>+static void cantiga_vtd_ops_preamble(struct iommu* iommu)
>+{
>+    struct intel_iommu *intel = iommu->intel;
>+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
>+    int data;
>+
>+    if ( !is_igd_drhd(drhd) || !is_cantiga_b3 )
>+        return;
>+
>+    if ( map_igd_reg() )
>+        return;
>+
>+    /* just want to read from the regster, don't need the data */
>+    data = *((int*)(igd_reg_va + 0x20A4));

This needs a volatile qualifier or consumer of "data", or else the
compiler will very likely eliminate the memory read.

Also the comment above map_igd_reg() talks about reading a
PCI config space register, but here you do a thus unexplained
memory read (on top of the explained config space accesses in
map_igd_reg()).

>+void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, u8 map)
>+{
>+    struct acpi_drhd_unit *drhd;
>+    struct pci_dev *pdev;
>+    u32 id;
>+
>+    id = pci_conf_read32(0, 0, 0, 0);
>+    if ( IS_CTG(id) )
>+    {
>+        /* quit if ME does not exist */
>+        if ( pci_conf_read32(0, 3, 0, 0) == 0xffffffff )
>+            return;
>+
>+        /* if device is WLAN device, map ME phantom device 0:3.7 */
>+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
>+        switch (id)
>+        {
>+            case 0x42328086:
>+            case 0x42358086:
>+            case 0x42368086:
>+            case 0x42378086:
>+            case 0x423a8086:
>+            case 0x423b8086:
>+            case 0x423c8086:
>+            case 0x423d8086:
>+                /* find ME VT-d engine base on a real ME device 0:3.0 */
>+                pdev = pci_get_pdev(0, PCI_DEVFN(3, 0));
>+                drhd = acpi_find_matched_drhd_unit(pdev);
>+
>+                /* map or unmap ME phantom function 0:3.7 */
>+                if ( map )
>+                    domain_context_mapping_one(domain, drhd->iommu, 0, PCI_DEVFN(3, 7));
>+                else
>+                    domain_context_unmap_one(domain, drhd->iommu, 0, PCI_DEVFN(3, 7));
>+
>+                break;
>+            default:
>+                break;
>+        }
>+    }

This and the subsequent else if's body look identical except for the
former using device 3 and the latter using device 22. Can't these
be folded, thus making the code smaller and easier to read (I
assume that the device IDs checked for in the switch statement are
actually unique, i.e. are known to only occur as either device 3 or
device 22)?

One general comment: Reviewing would be easier if you generated
your patches with -p.

Jan

--- a/xen/drivers/passthrough/vtd/dmar.c
+++ b/xen/drivers/passthrough/vtd/dmar.c
@@ -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(str
     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.
@@ -333,6 +339,15 @@ static int __init acpi_parse_dev_scope(v
             if ( iommu_verbose )
                 dprintk(VTDPREFIX, "  endpoint: %x:%x.%x\n",
                         bus, path->dev, path->fn);
+
+            if ( type == DMAR_TYPE )
+            {
+                struct acpi_drhd_unit *drhd = acpi_entry;
+
+                if ( (bus == 0) && (path->dev == 2) && (path->fn == 0) )
+                    igd_drhd_address = drhd->address;
+            }
+
             break;
 
         case ACPI_DEV_IOAPIC:

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC][PATCH][VTD][v2] consolidate VT-d quirks into a single file quirks.c
  2010-10-14  9:04 ` Jan Beulich
@ 2010-10-15  0:25   ` Kay, Allen M
  2010-10-15  8:47     ` Jan Beulich
  0 siblings, 1 reply; 8+ messages in thread
From: Kay, Allen M @ 2010-10-15  0:25 UTC (permalink / raw)
  To: Jan Beulich; +Cc: Xen-devel

[-- Attachment #1: Type: text/plain, Size: 1411 bytes --]

>>+static int map_igd_reg(void)
>>+{
>>+    u64 igd_mmio, igd_reg;
>>+    int status;
>>+
>>+    if ( igd_reg_va != NULL )
>>+        return 0;
>>+
>>+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
>>+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
>>+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
>>+    status = map_pages_to_xen(igd_reg, igd_reg >> PAGE_SHIFT, 1,
>>+                           __PAGE_HYPERVISOR);
>
> You're mapping to *virtual* address igd_reg here? Even if this was
> done only at boot time it would seem wrong, but as I understand it
> this is done so even at run time, which is an absolute no-go.

I don't quite understand this comment.  This mapping is only done once, subsequent calls will just return since igd_reg_va will no longer be NULL.  Can you elaborate on the reason mapping cannot be done here?  Any suggestion for alternatives?

Other than that, I have incorporated you other feedbacks.  See attached patch for the changes:

1) backed out new parameter in acpi_parse_dev_scope().  Changed code as you suggested.
2) added "volatile" qualifier to mmio read operation in cantiga_vtd_ops_preamble()
3) consolidated code for ME phantom function map/unmap.
4) moved initial call to map_igd_reg() to platform_quirks_setup() - not sure if this alleviates your concern above...
5) added more comments

Allen

[-- Attachment #2: quirks10142.patch --]
[-- Type: application/octet-stream, Size: 23169 bytes --]

diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/Makefile
--- a/xen/drivers/passthrough/vtd/Makefile	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/Makefile	Thu Oct 14 10:21:32 2010 -0700
@@ -6,3 +6,4 @@ obj-y += dmar.o
 obj-y += utils.o
 obj-y += qinval.o
 obj-y += intremap.o
+obj-y += quirks.o
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/dmar.c
--- a/xen/drivers/passthrough/vtd/dmar.c	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/dmar.c	Thu Oct 14 10:21:32 2010 -0700
@@ -242,6 +242,12 @@ struct acpi_rhsa_unit * drhd_to_rhsa(str
 
 int is_igd_drhd(struct acpi_drhd_unit *drhd)
 {
+    if ( drhd == NULL )
+    {
+        dprintk(XENLOG_WARNING VTDPREFIX, "unexpected drhd == NULL\n");
+        return 0;
+    }
+
     return ( drhd->address == igd_drhd_address ? 1 : 0);
 }
 
@@ -278,8 +284,7 @@ static int scope_device_count(void *star
 
 
 static int __init acpi_parse_dev_scope(void *start, void *end,
-                                       void *acpi_entry, int type,
-                                       int *igd)
+                                       void *acpi_entry, int type)
 {
     struct dmar_scope *scope = acpi_entry;
     struct acpi_ioapic_unit *acpi_ioapic_unit;
@@ -340,8 +345,15 @@ static int __init acpi_parse_dev_scope(v
             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;
+
+            if ( type == DMAR_TYPE )
+            {
+                struct acpi_drhd_unit *drhd = acpi_entry;
+
+                if ( (bus == 0) && (path->dev == 2) && (path->fn == 0) )
+                    igd_drhd_address = drhd->address;
+            }
+
             break;
 
         case ACPI_DEV_IOAPIC:
@@ -388,7 +400,7 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
     void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
-    int ret, igd = 0;
+    int ret;
     static int include_all = 0;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*drhd))) != 0 )
@@ -413,10 +425,7 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
     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, &igd);
-
-    if ( igd )
-        igd_drhd_address = dmaru->address;
+                               dmaru, DMAR_TYPE);
 
     if ( dmaru->include_all )
     {
@@ -504,7 +513,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     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, igd = 0;
+    int ret;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*rmrr))) != 0 )
         return ret;
@@ -536,7 +545,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     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, &igd);
+                               rmrru, RMRR_TYPE);
 
     if ( ret || (rmrru->scope.devices_cnt == 0) )
         xfree(rmrru);
@@ -601,7 +610,7 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
 {
     struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
     struct acpi_atsr_unit *atsru;
-    int ret, igd = 0;
+    int ret;
     static int all_ports;
     void *dev_scope_start, *dev_scope_end;
 
@@ -622,7 +631,7 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
         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, &igd);
+                                   atsru, ATSR_TYPE);
     }
     else
     {
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/extern.h
--- a/xen/drivers/passthrough/vtd/extern.h	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/extern.h	Thu Oct 14 10:21:32 2010 -0700
@@ -26,6 +26,7 @@
 
 extern int qinval_enabled;
 extern int ats_enabled;
+extern bool_t rwbf_quirk;
 
 void print_iommu_regs(struct acpi_drhd_unit *drhd);
 void print_vtd_entries(struct iommu *iommu, int bus, int devfn, u64 gmfn);
@@ -35,6 +36,12 @@ int enable_qinval(struct iommu *iommu);
 void disable_qinval(struct iommu *iommu);
 int enable_intremap(struct iommu *iommu, int eim);
 void disable_intremap(struct iommu *iommu);
+
+void iommu_flush_cache_entry(void *addr, unsigned int size);
+void iommu_flush_cache_page(void *addr, unsigned long npages);
+int iommu_alloc(struct acpi_drhd_unit *drhd);
+void iommu_free(struct acpi_drhd_unit *drhd);
+
 int queue_invalidate_context(struct iommu *iommu,
     u16 did, u16 source_id, u8 function_mask, u8 granu);
 int queue_invalidate_iotlb(struct iommu *iommu,
@@ -45,18 +52,40 @@ int invalidate_sync(struct iommu *iommu)
 int iommu_flush_iec_global(struct iommu *iommu);
 int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx);
 void clear_fault_bits(struct iommu *iommu);
+
 struct iommu * ioapic_to_iommu(unsigned int apic_id);
 struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id);
 struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu);
 struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd);
+struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
 int ats_device(int seg, int bus, int devfn);
 int enable_ats_device(int seg, int bus, int devfn);
 int disable_ats_device(int seg, int bus, int devfn);
 int invalidate_ats_tcs(struct iommu *iommu);
+
 int qinval_device_iotlb(struct iommu *iommu,
                         u32 max_invs_pend, u16 sid, u16 size, u64 addr);
 int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
                          u64 addr, unsigned int size_order, u64 type);
-struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
+unsigned int get_cache_line_size(void);
+void cacheline_flush(char *);
+void flush_all_cache(void);
+
+u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
+void free_pgtable_maddr(u64 maddr);
+void *map_vtd_domain_page(u64 maddr);
+void unmap_vtd_domain_page(void *va);
+int domain_context_mapping_one(struct domain *domain, struct iommu *iommu,
+                               u8 bus, u8 devfn);
+int domain_context_unmap_one(struct domain *domain, struct iommu *iommu,
+                             u8 bus, u8 devfn);
+
+int is_igd_vt_enabled_quirk(void);
+void __init platform_quirks_setup(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);
 
 #endif // _VTD_EXTERN_H_
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c	Thu Oct 14 10:21:32 2010 -0700
@@ -44,7 +44,6 @@
 #endif
 
 int nr_iommus;
-static bool_t rwbf_quirk;
 
 static void setup_dom0_devices(struct domain *d);
 static void setup_dom0_rmrr(struct domain *d);
@@ -482,16 +481,36 @@ static int inline iommu_flush_iotlb_glob
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline iommu_flush_iotlb_dsi(struct iommu *iommu, u16 did,
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status =  flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline get_alignment(u64 base, unsigned int size)
@@ -515,6 +534,7 @@ static int inline iommu_flush_iotlb_psi(
 {
     unsigned int align;
     struct iommu_flush *flush = iommu_get_flush(iommu);
+    int status;
 
     ASSERT(!(addr & (~PAGE_MASK_4K)));
     ASSERT(pages > 0);
@@ -535,8 +555,16 @@ static int inline iommu_flush_iotlb_psi(
     addr >>= PAGE_SHIFT_4K + align;
     addr <<= PAGE_SHIFT_4K + align;
 
-    return flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static void iommu_flush_all(void)
@@ -689,24 +717,13 @@ static int iommu_set_root_entry(struct i
     return 0;
 }
 
-#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 ( is_igd_drhd(drhd) && !is_igd_vt_enabled_quirk() ) 
     {
         if ( force_iommu )
             panic("BIOS did not enable IGD for VT properly, crash Xen for security purpose!\n");
@@ -718,6 +735,9 @@ static void iommu_enable_translation(str
         }
     }
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     if ( iommu_verbose )
         dprintk(VTDPREFIX,
                 "iommu_enable_translation: iommu->reg = %p\n", iommu->reg);
@@ -730,6 +750,9 @@ static void iommu_enable_translation(str
                   (sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
 
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
     /* Disable PMRs when VT-d engine takes effect per spec definition */
     disable_pmr(iommu);
 }
@@ -739,6 +762,9 @@ static void iommu_disable_translation(st
     u32 sts;
     unsigned long flags;
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     spin_lock_irqsave(&iommu->register_lock, flags);
     sts = dmar_readl(iommu->reg, DMAR_GSTS_REG);
     dmar_writel(iommu->reg, DMAR_GCMD_REG, sts & (~DMA_GCMD_TE));
@@ -747,6 +773,9 @@ static void iommu_disable_translation(st
     IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl,
                   !(sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
 }
 
 enum faulttype {
@@ -1090,6 +1119,7 @@ int __init iommu_alloc(struct acpi_drhd_
         xfree(iommu);
         return -ENOMEM;
     }
+    iommu->intel->drhd = drhd;
 
     iommu->reg = map_to_nocache_virt(nr_iommus, drhd->address);
     iommu->index = nr_iommus++;
@@ -1222,7 +1252,7 @@ static void intel_iommu_dom0_init(struct
     }
 }
 
-static int domain_context_mapping_one(
+int domain_context_mapping_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1324,6 +1354,8 @@ static int domain_context_mapping_one(
 
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1405,7 +1437,7 @@ static int domain_context_mapping(struct
     return ret;
 }
 
-static int domain_context_unmap_one(
+int domain_context_unmap_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1453,6 +1485,8 @@ static int domain_context_unmap_one(
     spin_unlock(&iommu->lock);
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1951,19 +1985,6 @@ static void setup_dom0_rmrr(struct domai
     spin_unlock(&pcidevs_lock);
 }
 
-static void __init platform_quirks(void)
-{
-    u32 id;
-
-    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
-    id = pci_conf_read32(0, 0, 0, 0);
-    if ( id == 0x2a408086 )
-    {
-        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
-        rwbf_quirk = 1;
-    }
-}
-
 int __init intel_vtd_setup(void)
 {
     struct acpi_drhd_unit *drhd;
@@ -1972,7 +1993,7 @@ int __init intel_vtd_setup(void)
     if ( list_empty(&acpi_drhd_units) )
         return -ENODEV;
 
-    platform_quirks();
+    platform_quirks_setup();
 
     irq_to_iommu = xmalloc_array(struct iommu*, nr_irqs);
     BUG_ON(!irq_to_iommu);
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/iommu.h
--- a/xen/drivers/passthrough/vtd/iommu.h	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.h	Thu Oct 14 10:21:32 2010 -0700
@@ -501,6 +501,7 @@ struct intel_iommu {
     struct qi_ctrl qi_ctrl;
     struct ir_ctrl ir_ctrl;
     struct iommu_flush flush;
+    struct acpi_drhd_unit *drhd;
 };
 
 #endif
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/quirks.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/passthrough/vtd/quirks.c	Thu Oct 14 10:21:32 2010 -0700
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Allen Kay <allen.m.kay@intel.com>
+ */
+
+#include <xen/irq.h>
+#include <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <xen/domain_page.h>
+#include <xen/iommu.h>
+#include <asm/hvm/iommu.h>
+#include <xen/numa.h>
+#include <xen/softirq.h>
+#include <xen/time.h>
+#include <xen/pci.h>
+#include <xen/pci_regs.h>
+#include <xen/keyhandler.h>
+#include <asm/msi.h>
+#include <asm/irq.h>
+#include <mach_apic.h>
+#include "iommu.h"
+#include "dmar.h"
+#include "extern.h"
+#include "vtd.h"
+
+#define IGD_BAR_MASK 0xFFFF0000
+#define GGC 0x52
+#define GGC_MEMORY_VT_ENABLED  (0x8 << 8)
+
+#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)
+
+bool_t rwbf_quirk;
+static int is_cantiga_b3;
+static u64 *igd_reg_va;
+
+/*
+ * QUIRK to workaround Xen boot issue on Calpella/Ironlake OEM BIOS
+ * not enabling VT-d properly in IGD.  The workaround is to not enabling
+ * IGD VT-d translation if VT is not enabled in IGD.
+ */
+int is_igd_vt_enabled_quirk(void)
+{
+    u16 ggc;
+
+    /* integrated graphics on Intel platforms is located at 0:2.0 */
+    ggc = pci_conf_read16(0, INTEL_IGD_DEV, 0, GGC);
+    return ( ggc & GGC_MEMORY_VT_ENABLED ? 1 : 0 );
+}
+
+/*
+ * QUIRK to workaround cantiga VT-d buffer flush issue.
+ * The workaround is to force write buffer flush even if
+ * VT-d capability indicates it is not required.
+ */
+static void cantiga_b3_errata_setup(void)
+{
+    u16 vid;
+    u8 did_hi, rid;
+
+    vid = pci_conf_read16(0, INTEL_IGD_DEV, 0, 0);
+    if ( vid != 0x8086 )
+        return;
+
+    did_hi = pci_conf_read8(0, INTEL_IGD_DEV, 0, 3);
+    rid = pci_conf_read8(0, INTEL_IGD_DEV, 0, 8);
+
+    if ( (did_hi == 0x2A) && (rid == 0x7) )
+        is_cantiga_b3 = 1;
+}
+
+/*
+ * QUIRK to workaround Cantiga IGD VT-d low power errata.
+ * This errata impacts IGD assignment on Cantiga systems
+ * and can potentially cause VT-d operations to hang.
+ * The workaround is to access an IGD PCI config register
+ * to get IGD out of low power state before VT-d translation
+ * enable/disable and IOTLB flushes.
+ */
+
+/*
+ * map IGD MMIO+0x2000 page to allow Xen access to IGD 3D register.
+ */
+static int map_igd_reg(void)
+{
+    u64 igd_mmio, igd_reg;
+    int status;
+
+    if ( !is_cantiga_b3 || igd_reg_va != NULL )
+        return 0;
+
+    /* get IGD mmio address in PCI BAR */
+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
+
+    /* offset of IGD regster we want to access is in 0x2000 range */
+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
+    status = map_pages_to_xen(igd_reg, igd_reg >> PAGE_SHIFT, 1,
+                           __PAGE_HYPERVISOR);
+    if ( status == 0 )
+        igd_reg_va = (u64 *) igd_reg;
+    else
+        dprintk(XENLOG_INFO VTDPREFIX, "map_igd_reg() failed\n");
+
+    return status;
+}
+
+/*
+ * force IGD to exit low power mode by accessing a IGD 3D regsiter.
+ */
+static int cantiga_vtd_ops_preamble(struct iommu* iommu)
+{
+    struct intel_iommu *intel = iommu->intel;
+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
+
+    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()
+     * already mapped page starting 0x2000, we just need to
+     * add page offset 0x0A4 to virtual address base.
+     */
+    return ( *((volatile int *)(igd_reg_va + 0x0A4)) );
+}
+
+/*
+ * call before VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_preamble_quirk(struct iommu* iommu)
+{
+    cantiga_vtd_ops_preamble(iommu);
+}
+
+/*
+ * call after VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_postamble_quirk(struct iommu* iommu)
+{
+    return;
+}
+
+/* setup platform identification flags */
+void __init platform_quirks_setup(void)
+{
+    u32 id;
+
+    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( id == 0x2a408086 )
+    {
+        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
+        rwbf_quirk = 1;
+    }
+    cantiga_b3_errata_setup();
+    map_igd_reg();
+}
+
+/*
+ * QUIRK to workaround wifi direct assignment issue.  This issue
+ * impacts only cases where Intel integrated wifi device is directly
+ * is directly assigned to a guest.
+ *
+ * The workaround is to map ME phantom device 0:3.7 or 0:22.7
+ * to the ME vt-d engine if detect the user is trying to directly
+ * assigning Intel integrated wifi device to a guest.
+ */
+
+static void map_me_phantom_function(struct domain *domain, u32 dev, int map)
+{
+    struct acpi_drhd_unit *drhd;
+    struct pci_dev *pdev;
+
+    /* find ME VT-d engine base on a real ME device */
+    pdev = pci_get_pdev(0, PCI_DEVFN(dev, 0));
+    drhd = acpi_find_matched_drhd_unit(pdev);
+
+    /* map or unmap ME phantom function */
+    if ( map )
+        domain_context_mapping_one(domain, drhd->iommu, 0,
+                                   PCI_DEVFN(dev, 7));
+    else
+        domain_context_unmap_one(domain, drhd->iommu, 0,
+                                 PCI_DEVFN(dev, 7));
+}
+
+void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map)
+{
+    u32 id;
+
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( IS_CTG(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 3, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:3.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x42328086:
+            case 0x42358086:
+            case 0x42368086:
+            case 0x42378086:
+            case 0x423a8086:
+            case 0x423b8086:
+            case 0x423c8086:
+            case 0x423d8086:
+                map_me_phantom_function(domain, 3, map);
+                break;
+            default:
+                break;
+        }
+    }
+    else if ( IS_ILK(id) || IS_CPT(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 22, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:22.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x00878086:
+            case 0x00898086:
+            case 0x00828086:
+            case 0x00858086:
+            case 0x42388086:
+            case 0x422b8086:
+                map_me_phantom_function(domain, 22, map);
+                break;
+            default:
+                break;
+        }
+
+    }
+}
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/vtd.h
--- a/xen/drivers/passthrough/vtd/vtd.h	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/vtd.h	Thu Oct 14 10:21:32 2010 -0700
@@ -23,6 +23,10 @@
 
 #include <xen/iommu.h>
 
+#define INTEL_IGD_DEV            0
+#define MAP_ME_PHANTOM_FUNC      1
+#define UNMAP_ME_PHANTOM_FUNC    0
+
 /* Accomodate both IOAPIC and IOSAPIC. */
 struct IO_xAPIC_route_entry {
     __u32   vector      :  8,
@@ -97,18 +101,4 @@ struct msi_msg_remap_entry {
     u32	data;		/* msi message data */
 };
 
-unsigned int get_cache_line_size(void);
-void cacheline_flush(char *);
-void flush_all_cache(void);
-u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
-void free_pgtable_maddr(u64 maddr);
-void *map_vtd_domain_page(u64 maddr);
-void unmap_vtd_domain_page(void *va);
-
-void iommu_flush_cache_entry(void *addr, unsigned int size);
-void iommu_flush_cache_page(void *addr, unsigned long npages);
-
-int iommu_alloc(struct acpi_drhd_unit *drhd);
-void iommu_free(struct acpi_drhd_unit *drhd);
-
 #endif // _VTD_H_
diff -r 6e8dd34122e7 xen/drivers/passthrough/vtd/x86/vtd.c
--- a/xen/drivers/passthrough/vtd/x86/vtd.c	Tue Oct 12 10:27:56 2010 +0100
+++ b/xen/drivers/passthrough/vtd/x86/vtd.c	Thu Oct 14 10:21:32 2010 -0700
@@ -28,6 +28,7 @@
 #include "../iommu.h"
 #include "../dmar.h"
 #include "../vtd.h"
+#include "../extern.h"
 
 /*
  * iommu_inclusive_mapping: when set, all memory below 4GB is included in dom0

[-- Attachment #3: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC][PATCH][VTD][v2] consolidate VT-d quirks into a single file quirks.c
  2010-10-15  0:25   ` [RFC][PATCH][VTD][v2] " Kay, Allen M
@ 2010-10-15  8:47     ` Jan Beulich
  2010-10-27 22:29       ` [RFC][PATCH][VTD][v3] " Kay, Allen M
  0 siblings, 1 reply; 8+ messages in thread
From: Jan Beulich @ 2010-10-15  8:47 UTC (permalink / raw)
  To: Allen M Kay; +Cc: Xen-devel

>>> On 15.10.10 at 02:25, "Kay, Allen M" <allen.m.kay@intel.com> wrote:
>> >+static int map_igd_reg(void)
>>>+{
>>>+    u64 igd_mmio, igd_reg;
>>>+    int status;
>>>+
>>>+    if ( igd_reg_va != NULL )
>>>+        return 0;
>>>+
>>>+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
>>>+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
>>>+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
>>>+    status = map_pages_to_xen(igd_reg, igd_reg >> PAGE_SHIFT, 1,
>>>+                           __PAGE_HYPERVISOR);
>>
>> You're mapping to *virtual* address igd_reg here? Even if this was
>> done only at boot time it would seem wrong, but as I understand it
>> this is done so even at run time, which is an absolute no-go.
> 
> I don't quite understand this comment.  This mapping is only done once, 
> subsequent calls will just return since igd_reg_va will no longer be NULL.  
> Can you elaborate on the reason mapping cannot be done here?  Any suggestion 
> for alternatives?

The issue is not with doing a mapping, but with where (in virtual
address space) you map to: You're passing a *physical* address
for what is to be a *virtual* one (first argument to
map_pages_to_xen()), i.e. as soon as there is any domain the
mapping will conflict with the domain's use of virtual addresses.
You need to reserve a page somewhere - possibly a fixmap entry
(perhaps the only solution for 32-bit, unless you want to rely on
map_domain_page{,_global}() currently not really depending on
the mapped page being a domain page) -, given that ioremap()
doesn't really exist in Xen.

> Other than that, I have incorporated you other feedbacks.  See attached 
> patch for the changes:

Thanks. There's one more thing I just noticed: Isn't warning you're
adding to is_igd_drhd() going to trigger needlessly (and perhaps
frequently) on Intel IOMMU systems without any device at 0:2.0
I think if at all, the warning should only be issued once.

Jan

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [RFC][PATCH][VTD][v3] consolidate VT-d quirks into a  single file quirks.c
  2010-10-15  8:47     ` Jan Beulich
@ 2010-10-27 22:29       ` Kay, Allen M
  2010-10-28  6:57         ` Jan Beulich
  0 siblings, 1 reply; 8+ messages in thread
From: Kay, Allen M @ 2010-10-27 22:29 UTC (permalink / raw)
  To: Jan Beulich; +Cc: Xen-devel

[-- Attachment #1: Type: text/plain, Size: 802 bytes --]

Jan, sorry disappearing for the last week.  I had to attend to some urgent matters.

> The issue is not with doing a mapping, but with where (in virtual address space)
> you map to: You're passing a *physical* address for what is to be a *virtual* one
> (first argument to map_pages_to_xen()), i.e. as soon as there is any domain the
> mapping will conflict with the domain's use of virtual addresses.

Agree.  I fixed it in the attached v3 patch by using a fixmap entry for doing ioremap.

> There's one more thing I just noticed: Isn't warning you're
> adding to is_igd_drhd() going to trigger needlessly (and perhaps
> frequently) on Intel IOMMU systems without any device at 0:2.0
> I think if at all, the warning should only be issued once.

I removed the warning message.

Allen

[-- Attachment #2: quirks10272.patch --]
[-- Type: application/octet-stream, Size: 23521 bytes --]

diff -r 36a317494429 xen/drivers/passthrough/vtd/Makefile
--- a/xen/drivers/passthrough/vtd/Makefile	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/Makefile	Wed Oct 27 08:25:39 2010 -0700
@@ -6,3 +6,4 @@ obj-y += dmar.o
 obj-y += utils.o
 obj-y += qinval.o
 obj-y += intremap.o
+obj-y += quirks.o
diff -r 36a317494429 xen/drivers/passthrough/vtd/dmar.c
--- a/xen/drivers/passthrough/vtd/dmar.c	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/dmar.c	Wed Oct 27 08:25:39 2010 -0700
@@ -242,6 +242,9 @@ struct acpi_rhsa_unit * drhd_to_rhsa(str
 
 int is_igd_drhd(struct acpi_drhd_unit *drhd)
 {
+    if ( drhd == NULL )
+        return 0;
+
     return ( drhd->address == igd_drhd_address ? 1 : 0);
 }
 
@@ -278,8 +281,7 @@ static int scope_device_count(void *star
 
 
 static int __init acpi_parse_dev_scope(void *start, void *end,
-                                       void *acpi_entry, int type,
-                                       int *igd)
+                                       void *acpi_entry, int type)
 {
     struct dmar_scope *scope = acpi_entry;
     struct acpi_ioapic_unit *acpi_ioapic_unit;
@@ -340,8 +342,15 @@ static int __init acpi_parse_dev_scope(v
             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;
+
+            if ( type == DMAR_TYPE )
+            {
+                struct acpi_drhd_unit *drhd = acpi_entry;
+
+                if ( (bus == 0) && (path->dev == 2) && (path->fn == 0) )
+                    igd_drhd_address = drhd->address;
+            }
+
             break;
 
         case ACPI_DEV_IOAPIC:
@@ -388,7 +397,7 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
     void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
-    int ret, igd = 0;
+    int ret;
     static int include_all = 0;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*drhd))) != 0 )
@@ -413,10 +422,7 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
     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, &igd);
-
-    if ( igd )
-        igd_drhd_address = dmaru->address;
+                               dmaru, DMAR_TYPE);
 
     if ( dmaru->include_all )
     {
@@ -504,7 +510,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     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, igd = 0;
+    int ret;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*rmrr))) != 0 )
         return ret;
@@ -536,7 +542,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     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, &igd);
+                               rmrru, RMRR_TYPE);
 
     if ( ret || (rmrru->scope.devices_cnt == 0) )
         xfree(rmrru);
@@ -601,7 +607,7 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
 {
     struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
     struct acpi_atsr_unit *atsru;
-    int ret, igd = 0;
+    int ret;
     static int all_ports;
     void *dev_scope_start, *dev_scope_end;
 
@@ -622,7 +628,7 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
         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, &igd);
+                                   atsru, ATSR_TYPE);
     }
     else
     {
diff -r 36a317494429 xen/drivers/passthrough/vtd/extern.h
--- a/xen/drivers/passthrough/vtd/extern.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/extern.h	Wed Oct 27 08:25:39 2010 -0700
@@ -26,6 +26,7 @@
 
 extern int qinval_enabled;
 extern int ats_enabled;
+extern bool_t rwbf_quirk;
 
 void print_iommu_regs(struct acpi_drhd_unit *drhd);
 void print_vtd_entries(struct iommu *iommu, int bus, int devfn, u64 gmfn);
@@ -35,6 +36,12 @@ int enable_qinval(struct iommu *iommu);
 void disable_qinval(struct iommu *iommu);
 int enable_intremap(struct iommu *iommu, int eim);
 void disable_intremap(struct iommu *iommu);
+
+void iommu_flush_cache_entry(void *addr, unsigned int size);
+void iommu_flush_cache_page(void *addr, unsigned long npages);
+int iommu_alloc(struct acpi_drhd_unit *drhd);
+void iommu_free(struct acpi_drhd_unit *drhd);
+
 int queue_invalidate_context(struct iommu *iommu,
     u16 did, u16 source_id, u8 function_mask, u8 granu);
 int queue_invalidate_iotlb(struct iommu *iommu,
@@ -45,18 +52,40 @@ int invalidate_sync(struct iommu *iommu)
 int iommu_flush_iec_global(struct iommu *iommu);
 int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx);
 void clear_fault_bits(struct iommu *iommu);
+
 struct iommu * ioapic_to_iommu(unsigned int apic_id);
 struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id);
 struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu);
 struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd);
+struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
 int ats_device(int seg, int bus, int devfn);
 int enable_ats_device(int seg, int bus, int devfn);
 int disable_ats_device(int seg, int bus, int devfn);
 int invalidate_ats_tcs(struct iommu *iommu);
+
 int qinval_device_iotlb(struct iommu *iommu,
                         u32 max_invs_pend, u16 sid, u16 size, u64 addr);
 int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
                          u64 addr, unsigned int size_order, u64 type);
-struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
+unsigned int get_cache_line_size(void);
+void cacheline_flush(char *);
+void flush_all_cache(void);
+
+u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
+void free_pgtable_maddr(u64 maddr);
+void *map_vtd_domain_page(u64 maddr);
+void unmap_vtd_domain_page(void *va);
+int domain_context_mapping_one(struct domain *domain, struct iommu *iommu,
+                               u8 bus, u8 devfn);
+int domain_context_unmap_one(struct domain *domain, struct iommu *iommu,
+                             u8 bus, u8 devfn);
+
+int is_igd_vt_enabled_quirk(void);
+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);
 
 #endif // _VTD_EXTERN_H_
diff -r 36a317494429 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c	Wed Oct 27 08:25:39 2010 -0700
@@ -44,7 +44,6 @@
 #endif
 
 int nr_iommus;
-static bool_t rwbf_quirk;
 
 static void setup_dom0_devices(struct domain *d);
 static void setup_dom0_rmrr(struct domain *d);
@@ -482,16 +481,36 @@ static int inline iommu_flush_iotlb_glob
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline iommu_flush_iotlb_dsi(struct iommu *iommu, u16 did,
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status =  flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline get_alignment(u64 base, unsigned int size)
@@ -515,6 +534,7 @@ static int inline iommu_flush_iotlb_psi(
 {
     unsigned int align;
     struct iommu_flush *flush = iommu_get_flush(iommu);
+    int status;
 
     ASSERT(!(addr & (~PAGE_MASK_4K)));
     ASSERT(pages > 0);
@@ -535,8 +555,16 @@ static int inline iommu_flush_iotlb_psi(
     addr >>= PAGE_SHIFT_4K + align;
     addr <<= PAGE_SHIFT_4K + align;
 
-    return flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static void iommu_flush_all(void)
@@ -689,24 +717,13 @@ static int iommu_set_root_entry(struct i
     return 0;
 }
 
-#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 ( is_igd_drhd(drhd) && !is_igd_vt_enabled_quirk() ) 
     {
         if ( force_iommu )
             panic("BIOS did not enable IGD for VT properly, crash Xen for security purpose!\n");
@@ -718,6 +735,9 @@ static void iommu_enable_translation(str
         }
     }
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     if ( iommu_verbose )
         dprintk(VTDPREFIX,
                 "iommu_enable_translation: iommu->reg = %p\n", iommu->reg);
@@ -730,6 +750,9 @@ static void iommu_enable_translation(str
                   (sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
 
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
     /* Disable PMRs when VT-d engine takes effect per spec definition */
     disable_pmr(iommu);
 }
@@ -739,6 +762,9 @@ static void iommu_disable_translation(st
     u32 sts;
     unsigned long flags;
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     spin_lock_irqsave(&iommu->register_lock, flags);
     sts = dmar_readl(iommu->reg, DMAR_GSTS_REG);
     dmar_writel(iommu->reg, DMAR_GCMD_REG, sts & (~DMA_GCMD_TE));
@@ -747,6 +773,9 @@ static void iommu_disable_translation(st
     IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl,
                   !(sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
 }
 
 enum faulttype {
@@ -1090,6 +1119,7 @@ int __init iommu_alloc(struct acpi_drhd_
         xfree(iommu);
         return -ENOMEM;
     }
+    iommu->intel->drhd = drhd;
 
     iommu->reg = map_to_nocache_virt(nr_iommus, drhd->address);
     iommu->index = nr_iommus++;
@@ -1222,7 +1252,7 @@ static void intel_iommu_dom0_init(struct
     }
 }
 
-static int domain_context_mapping_one(
+int domain_context_mapping_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1324,6 +1354,8 @@ static int domain_context_mapping_one(
 
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1405,7 +1437,7 @@ static int domain_context_mapping(struct
     return ret;
 }
 
-static int domain_context_unmap_one(
+int domain_context_unmap_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1453,6 +1485,8 @@ static int domain_context_unmap_one(
     spin_unlock(&iommu->lock);
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1951,19 +1985,6 @@ static void setup_dom0_rmrr(struct domai
     spin_unlock(&pcidevs_lock);
 }
 
-static void __init platform_quirks(void)
-{
-    u32 id;
-
-    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
-    id = pci_conf_read32(0, 0, 0, 0);
-    if ( id == 0x2a408086 )
-    {
-        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
-        rwbf_quirk = 1;
-    }
-}
-
 int __init intel_vtd_setup(void)
 {
     struct acpi_drhd_unit *drhd;
@@ -1972,7 +1993,7 @@ int __init intel_vtd_setup(void)
     if ( list_empty(&acpi_drhd_units) )
         return -ENODEV;
 
-    platform_quirks();
+    platform_quirks_init();
 
     irq_to_iommu = xmalloc_array(struct iommu*, nr_irqs);
     BUG_ON(!irq_to_iommu);
diff -r 36a317494429 xen/drivers/passthrough/vtd/iommu.h
--- a/xen/drivers/passthrough/vtd/iommu.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.h	Wed Oct 27 08:25:39 2010 -0700
@@ -501,6 +501,7 @@ struct intel_iommu {
     struct qi_ctrl qi_ctrl;
     struct ir_ctrl ir_ctrl;
     struct iommu_flush flush;
+    struct acpi_drhd_unit *drhd;
 };
 
 #endif
diff -r 36a317494429 xen/drivers/passthrough/vtd/quirks.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/passthrough/vtd/quirks.c	Wed Oct 27 08:25:39 2010 -0700
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Allen Kay <allen.m.kay@intel.com>
+ */
+
+#include <xen/irq.h>
+#include <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <xen/domain_page.h>
+#include <xen/iommu.h>
+#include <asm/hvm/iommu.h>
+#include <xen/numa.h>
+#include <xen/softirq.h>
+#include <xen/time.h>
+#include <xen/pci.h>
+#include <xen/pci_regs.h>
+#include <xen/keyhandler.h>
+#include <asm/msi.h>
+#include <asm/irq.h>
+#include <mach_apic.h>
+#include "iommu.h"
+#include "dmar.h"
+#include "extern.h"
+#include "vtd.h"
+
+#define IGD_BAR_MASK 0xFFFF0000
+#define GGC 0x52
+#define GGC_MEMORY_VT_ENABLED  (0x8 << 8)
+
+#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)
+
+u32 dev0_id;
+bool_t rwbf_quirk;
+static int is_cantiga_b3;
+static u8 *igd_reg_va;
+
+/*
+ * QUIRK to workaround Xen boot issue on Calpella/Ironlake OEM BIOS
+ * not enabling VT-d properly in IGD.  The workaround is to not enabling
+ * IGD VT-d translation if VT is not enabled in IGD.
+ */
+int is_igd_vt_enabled_quirk(void)
+{
+    u16 ggc;
+
+    if ( !IS_ILK(dev0_id) )
+        return 1;
+
+    /* integrated graphics on Intel platforms is located at 0:2.0 */
+    ggc = pci_conf_read16(0, INTEL_IGD_DEV, 0, GGC);
+    return ( ggc & GGC_MEMORY_VT_ENABLED ? 1 : 0 );
+}
+
+/*
+ * QUIRK to workaround cantiga VT-d buffer flush issue.
+ * The workaround is to force write buffer flush even if
+ * VT-d capability indicates it is not required.
+ */
+static void cantiga_b3_errata_init(void)
+{
+    u16 vid;
+    u8 did_hi, rid;
+
+    vid = pci_conf_read16(0, INTEL_IGD_DEV, 0, 0);
+    if ( vid != 0x8086 )
+        return;
+
+    did_hi = pci_conf_read8(0, INTEL_IGD_DEV, 0, 3);
+    rid = pci_conf_read8(0, INTEL_IGD_DEV, 0, 8);
+
+    if ( (did_hi == 0x2A) && (rid == 0x7) )
+        is_cantiga_b3 = 1;
+}
+
+/*
+ * QUIRK to workaround Cantiga IGD VT-d low power errata.
+ * This errata impacts IGD assignment on Cantiga systems
+ * and can potentially cause VT-d operations to hang.
+ * The workaround is to access an IGD PCI config register
+ * to get IGD out of low power state before VT-d translation
+ * enable/disable and IOTLB flushes.
+ */
+
+/*
+ * map IGD MMIO+0x2000 page to allow Xen access to IGD 3D register.
+ */
+static int map_igd_reg(void)
+{
+    u64 igd_mmio, igd_reg;
+    int status;
+
+    if ( !is_cantiga_b3 || igd_reg_va != NULL )
+        return 0;
+
+    /* get IGD mmio address in PCI BAR */
+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
+
+    /* offset of IGD regster we want to access is in 0x2000 range */
+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
+
+    /* ioremap this physical page */
+    set_fixmap_nocache(FIX_IGD_MMIO, igd_reg);
+    igd_reg_va = (u8 *)fix_to_virt(FIX_IGD_MMIO);
+
+    return status;
+}
+
+/*
+ * force IGD to exit low power mode by accessing a IGD 3D regsiter.
+ */
+static int cantiga_vtd_ops_preamble(struct iommu* iommu)
+{
+    struct intel_iommu *intel = iommu->intel;
+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
+
+    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()
+     * already mapped page starting 0x2000, we just need to
+     * add page offset 0x0A4 to virtual address base.
+     */
+    return ( *((volatile int *)(igd_reg_va + 0x0A4)) );
+}
+
+/*
+ * call before VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_preamble_quirk(struct iommu* iommu)
+{
+    cantiga_vtd_ops_preamble(iommu);
+}
+
+/*
+ * call after VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_postamble_quirk(struct iommu* iommu)
+{
+    return;
+}
+
+/* initialize platform identification flags */
+void __init platform_quirks_init(void)
+{
+    dev0_id = pci_conf_read32(0, 0, 0, 0);
+
+    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
+    if ( dev0_id == 0x2a408086 )
+    {
+        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
+        rwbf_quirk = 1;
+    }
+
+    /* initialize cantiga B3 identification */
+    cantiga_b3_errata_init();
+
+    /* ioremap IGD MMIO+0x2000 page */
+    map_igd_reg();
+}
+
+/*
+ * QUIRK to workaround wifi direct assignment issue.  This issue
+ * impacts only cases where Intel integrated wifi device is directly
+ * is directly assigned to a guest.
+ *
+ * The workaround is to map ME phantom device 0:3.7 or 0:22.7
+ * to the ME vt-d engine if detect the user is trying to directly
+ * assigning Intel integrated wifi device to a guest.
+ */
+
+static void map_me_phantom_function(struct domain *domain, u32 dev, int map)
+{
+    struct acpi_drhd_unit *drhd;
+    struct pci_dev *pdev;
+
+    /* find ME VT-d engine base on a real ME device */
+    pdev = pci_get_pdev(0, PCI_DEVFN(dev, 0));
+    drhd = acpi_find_matched_drhd_unit(pdev);
+
+    /* map or unmap ME phantom function */
+    if ( map )
+        domain_context_mapping_one(domain, drhd->iommu, 0,
+                                   PCI_DEVFN(dev, 7));
+    else
+        domain_context_unmap_one(domain, drhd->iommu, 0,
+                                 PCI_DEVFN(dev, 7));
+}
+
+void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map)
+{
+    u32 id;
+
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( IS_CTG(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 3, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:3.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x42328086:
+            case 0x42358086:
+            case 0x42368086:
+            case 0x42378086:
+            case 0x423a8086:
+            case 0x423b8086:
+            case 0x423c8086:
+            case 0x423d8086:
+                map_me_phantom_function(domain, 3, map);
+                break;
+            default:
+                break;
+        }
+    }
+    else if ( IS_ILK(id) || IS_CPT(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 22, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:22.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x00878086:
+            case 0x00898086:
+            case 0x00828086:
+            case 0x00858086:
+            case 0x42388086:
+            case 0x422b8086:
+                map_me_phantom_function(domain, 22, map);
+                break;
+            default:
+                break;
+        }
+
+    }
+}
diff -r 36a317494429 xen/drivers/passthrough/vtd/vtd.h
--- a/xen/drivers/passthrough/vtd/vtd.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/vtd.h	Wed Oct 27 08:25:39 2010 -0700
@@ -23,6 +23,10 @@
 
 #include <xen/iommu.h>
 
+#define INTEL_IGD_DEV            0
+#define MAP_ME_PHANTOM_FUNC      1
+#define UNMAP_ME_PHANTOM_FUNC    0
+
 /* Accomodate both IOAPIC and IOSAPIC. */
 struct IO_xAPIC_route_entry {
     __u32   vector      :  8,
@@ -97,18 +101,4 @@ struct msi_msg_remap_entry {
     u32	data;		/* msi message data */
 };
 
-unsigned int get_cache_line_size(void);
-void cacheline_flush(char *);
-void flush_all_cache(void);
-u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
-void free_pgtable_maddr(u64 maddr);
-void *map_vtd_domain_page(u64 maddr);
-void unmap_vtd_domain_page(void *va);
-
-void iommu_flush_cache_entry(void *addr, unsigned int size);
-void iommu_flush_cache_page(void *addr, unsigned long npages);
-
-int iommu_alloc(struct acpi_drhd_unit *drhd);
-void iommu_free(struct acpi_drhd_unit *drhd);
-
 #endif // _VTD_H_
diff -r 36a317494429 xen/drivers/passthrough/vtd/x86/vtd.c
--- a/xen/drivers/passthrough/vtd/x86/vtd.c	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/x86/vtd.c	Wed Oct 27 08:25:39 2010 -0700
@@ -28,6 +28,7 @@
 #include "../iommu.h"
 #include "../dmar.h"
 #include "../vtd.h"
+#include "../extern.h"
 
 /*
  * iommu_inclusive_mapping: when set, all memory below 4GB is included in dom0
diff -r 36a317494429 xen/include/asm-x86/fixmap.h
--- a/xen/include/asm-x86/fixmap.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/include/asm-x86/fixmap.h	Wed Oct 27 08:25:39 2010 -0700
@@ -55,6 +55,7 @@ enum fixed_addresses {
     FIX_TBOOT_MAP_ADDRESS,
     FIX_APEI_RANGE_BASE,
     FIX_APEI_RANGE_END = FIX_APEI_RANGE_BASE + FIX_APEI_RANGE_MAX -1,
+    FIX_IGD_MMIO,
     __end_of_fixed_addresses
 };
 

[-- Attachment #3: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [RFC][PATCH][VTD][v3] consolidate VT-d quirks into a single file quirks.c
  2010-10-27 22:29       ` [RFC][PATCH][VTD][v3] " Kay, Allen M
@ 2010-10-28  6:57         ` Jan Beulich
  2010-10-28 15:50           ` [RFC][PATCH][VTD][v4] " Kay, Allen M
  0 siblings, 1 reply; 8+ messages in thread
From: Jan Beulich @ 2010-10-28  6:57 UTC (permalink / raw)
  To: Allen M Kay; +Cc: Xen-devel

>>> On 28.10.10 at 00:29, "Kay, Allen M" <allen.m.kay@intel.com> wrote:
>> The issue is not with doing a mapping, but with where (in virtual address 
> space)
>> you map to: You're passing a *physical* address for what is to be a 
> *virtual* one
>> (first argument to map_pages_to_xen()), i.e. as soon as there is any domain 
> the
>> mapping will conflict with the domain's use of virtual addresses.
> 
> Agree.  I fixed it in the attached v3 patch by using a fixmap entry for 
> doing ioremap.

Yes, this looks better now. However, map_igd_reg() now returns
"status" without ever initializing the variable. Didn't the compiler
warn (and the build fail because) of that? I think the function has
no need to return non-void anymore.

The non-void return value of cantiga_vtd_ops_preamble() also
looks bogus, btw.

One thing I may not have noticed in earlier versions is your use
of IGD_BAR_MASK - you define it as 0xFFFF0000 but then use
it to mask a 64-bit value (i.e. cutting of the top 32 bits).

Jan

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [RFC][PATCH][VTD][v4] consolidate VT-d quirks into a  single file quirks.c
  2010-10-28  6:57         ` Jan Beulich
@ 2010-10-28 15:50           ` Kay, Allen M
  2010-10-28 16:00             ` Jan Beulich
  0 siblings, 1 reply; 8+ messages in thread
From: Kay, Allen M @ 2010-10-28 15:50 UTC (permalink / raw)
  To: Jan Beulich; +Cc: Xen-devel

[-- Attachment #1: Type: text/plain, Size: 838 bytes --]

> Yes, this looks better now. However, map_igd_reg() now returns
> "status" without ever initializing the variable. Didn't the compiler
> warn (and the build fail because) of that? I think the function has
> no need to return non-void anymore.

I just did a build again, the compiler come with FC12 did not complain about it at all.  I have changed the function to void.

> The non-void return value of cantiga_vtd_ops_preamble() also
> looks bogus, btw.

The return value is not important in this case.  It is the requirement of the errata workaround to read the MMIO register.

> One thing I may not have noticed in earlier versions is your use
> of IGD_BAR_MASK - you define it as 0xFFFF0000 but then use
> it to mask a 64-bit value (i.e. cutting of the top 32 bits).

Fixed.

See attached for version 4.

Allen


[-- Attachment #2: quirks1028.patch --]
[-- Type: application/octet-stream, Size: 23442 bytes --]

diff -r 36a317494429 xen/drivers/passthrough/vtd/Makefile
--- a/xen/drivers/passthrough/vtd/Makefile	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/Makefile	Thu Oct 28 01:44:46 2010 -0700
@@ -6,3 +6,4 @@ obj-y += dmar.o
 obj-y += utils.o
 obj-y += qinval.o
 obj-y += intremap.o
+obj-y += quirks.o
diff -r 36a317494429 xen/drivers/passthrough/vtd/dmar.c
--- a/xen/drivers/passthrough/vtd/dmar.c	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/dmar.c	Thu Oct 28 01:44:46 2010 -0700
@@ -242,6 +242,9 @@ struct acpi_rhsa_unit * drhd_to_rhsa(str
 
 int is_igd_drhd(struct acpi_drhd_unit *drhd)
 {
+    if ( drhd == NULL )
+        return 0;
+
     return ( drhd->address == igd_drhd_address ? 1 : 0);
 }
 
@@ -278,8 +281,7 @@ static int scope_device_count(void *star
 
 
 static int __init acpi_parse_dev_scope(void *start, void *end,
-                                       void *acpi_entry, int type,
-                                       int *igd)
+                                       void *acpi_entry, int type)
 {
     struct dmar_scope *scope = acpi_entry;
     struct acpi_ioapic_unit *acpi_ioapic_unit;
@@ -340,8 +342,15 @@ static int __init acpi_parse_dev_scope(v
             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;
+
+            if ( type == DMAR_TYPE )
+            {
+                struct acpi_drhd_unit *drhd = acpi_entry;
+
+                if ( (bus == 0) && (path->dev == 2) && (path->fn == 0) )
+                    igd_drhd_address = drhd->address;
+            }
+
             break;
 
         case ACPI_DEV_IOAPIC:
@@ -388,7 +397,7 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
     void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
-    int ret, igd = 0;
+    int ret;
     static int include_all = 0;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*drhd))) != 0 )
@@ -413,10 +422,7 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
     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, &igd);
-
-    if ( igd )
-        igd_drhd_address = dmaru->address;
+                               dmaru, DMAR_TYPE);
 
     if ( dmaru->include_all )
     {
@@ -504,7 +510,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     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, igd = 0;
+    int ret;
 
     if ( (ret = acpi_dmar_check_length(header, sizeof(*rmrr))) != 0 )
         return ret;
@@ -536,7 +542,7 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     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, &igd);
+                               rmrru, RMRR_TYPE);
 
     if ( ret || (rmrru->scope.devices_cnt == 0) )
         xfree(rmrru);
@@ -601,7 +607,7 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
 {
     struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
     struct acpi_atsr_unit *atsru;
-    int ret, igd = 0;
+    int ret;
     static int all_ports;
     void *dev_scope_start, *dev_scope_end;
 
@@ -622,7 +628,7 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
         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, &igd);
+                                   atsru, ATSR_TYPE);
     }
     else
     {
diff -r 36a317494429 xen/drivers/passthrough/vtd/extern.h
--- a/xen/drivers/passthrough/vtd/extern.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/extern.h	Thu Oct 28 01:44:46 2010 -0700
@@ -26,6 +26,7 @@
 
 extern int qinval_enabled;
 extern int ats_enabled;
+extern bool_t rwbf_quirk;
 
 void print_iommu_regs(struct acpi_drhd_unit *drhd);
 void print_vtd_entries(struct iommu *iommu, int bus, int devfn, u64 gmfn);
@@ -35,6 +36,12 @@ int enable_qinval(struct iommu *iommu);
 void disable_qinval(struct iommu *iommu);
 int enable_intremap(struct iommu *iommu, int eim);
 void disable_intremap(struct iommu *iommu);
+
+void iommu_flush_cache_entry(void *addr, unsigned int size);
+void iommu_flush_cache_page(void *addr, unsigned long npages);
+int iommu_alloc(struct acpi_drhd_unit *drhd);
+void iommu_free(struct acpi_drhd_unit *drhd);
+
 int queue_invalidate_context(struct iommu *iommu,
     u16 did, u16 source_id, u8 function_mask, u8 granu);
 int queue_invalidate_iotlb(struct iommu *iommu,
@@ -45,18 +52,40 @@ int invalidate_sync(struct iommu *iommu)
 int iommu_flush_iec_global(struct iommu *iommu);
 int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx);
 void clear_fault_bits(struct iommu *iommu);
+
 struct iommu * ioapic_to_iommu(unsigned int apic_id);
 struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id);
 struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu);
 struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd);
+struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
 int ats_device(int seg, int bus, int devfn);
 int enable_ats_device(int seg, int bus, int devfn);
 int disable_ats_device(int seg, int bus, int devfn);
 int invalidate_ats_tcs(struct iommu *iommu);
+
 int qinval_device_iotlb(struct iommu *iommu,
                         u32 max_invs_pend, u16 sid, u16 size, u64 addr);
 int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
                          u64 addr, unsigned int size_order, u64 type);
-struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu);
+
+unsigned int get_cache_line_size(void);
+void cacheline_flush(char *);
+void flush_all_cache(void);
+
+u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
+void free_pgtable_maddr(u64 maddr);
+void *map_vtd_domain_page(u64 maddr);
+void unmap_vtd_domain_page(void *va);
+int domain_context_mapping_one(struct domain *domain, struct iommu *iommu,
+                               u8 bus, u8 devfn);
+int domain_context_unmap_one(struct domain *domain, struct iommu *iommu,
+                             u8 bus, u8 devfn);
+
+int is_igd_vt_enabled_quirk(void);
+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);
 
 #endif // _VTD_EXTERN_H_
diff -r 36a317494429 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c	Thu Oct 28 01:44:46 2010 -0700
@@ -44,7 +44,6 @@
 #endif
 
 int nr_iommus;
-static bool_t rwbf_quirk;
 
 static void setup_dom0_devices(struct domain *d);
 static void setup_dom0_rmrr(struct domain *d);
@@ -482,16 +481,36 @@ static int inline iommu_flush_iotlb_glob
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline iommu_flush_iotlb_dsi(struct iommu *iommu, u16 did,
     int flush_non_present_entry, int flush_dev_iotlb)
 {
     struct iommu_flush *flush = iommu_get_flush(iommu);
-    return flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
+    int status;
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status =  flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static int inline get_alignment(u64 base, unsigned int size)
@@ -515,6 +534,7 @@ static int inline iommu_flush_iotlb_psi(
 {
     unsigned int align;
     struct iommu_flush *flush = iommu_get_flush(iommu);
+    int status;
 
     ASSERT(!(addr & (~PAGE_MASK_4K)));
     ASSERT(pages > 0);
@@ -535,8 +555,16 @@ static int inline iommu_flush_iotlb_psi(
     addr >>= PAGE_SHIFT_4K + align;
     addr <<= PAGE_SHIFT_4K + align;
 
-    return flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
+    status = flush->iotlb(iommu, did, addr, align, DMA_TLB_PSI_FLUSH,
                         flush_non_present_entry, flush_dev_iotlb);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
+    return status;
 }
 
 static void iommu_flush_all(void)
@@ -689,24 +717,13 @@ static int iommu_set_root_entry(struct i
     return 0;
 }
 
-#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 ( is_igd_drhd(drhd) && !is_igd_vt_enabled_quirk() ) 
     {
         if ( force_iommu )
             panic("BIOS did not enable IGD for VT properly, crash Xen for security purpose!\n");
@@ -718,6 +735,9 @@ static void iommu_enable_translation(str
         }
     }
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     if ( iommu_verbose )
         dprintk(VTDPREFIX,
                 "iommu_enable_translation: iommu->reg = %p\n", iommu->reg);
@@ -730,6 +750,9 @@ static void iommu_enable_translation(str
                   (sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
 
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
+
     /* Disable PMRs when VT-d engine takes effect per spec definition */
     disable_pmr(iommu);
 }
@@ -739,6 +762,9 @@ static void iommu_disable_translation(st
     u32 sts;
     unsigned long flags;
 
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
+
     spin_lock_irqsave(&iommu->register_lock, flags);
     sts = dmar_readl(iommu->reg, DMAR_GSTS_REG);
     dmar_writel(iommu->reg, DMAR_GCMD_REG, sts & (~DMA_GCMD_TE));
@@ -747,6 +773,9 @@ static void iommu_disable_translation(st
     IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl,
                   !(sts & DMA_GSTS_TES), sts);
     spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+    /* undo platform specific errata workarounds */
+    vtd_ops_postamble_quirk(iommu);
 }
 
 enum faulttype {
@@ -1090,6 +1119,7 @@ int __init iommu_alloc(struct acpi_drhd_
         xfree(iommu);
         return -ENOMEM;
     }
+    iommu->intel->drhd = drhd;
 
     iommu->reg = map_to_nocache_virt(nr_iommus, drhd->address);
     iommu->index = nr_iommus++;
@@ -1222,7 +1252,7 @@ static void intel_iommu_dom0_init(struct
     }
 }
 
-static int domain_context_mapping_one(
+int domain_context_mapping_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1324,6 +1354,8 @@ static int domain_context_mapping_one(
 
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1405,7 +1437,7 @@ static int domain_context_mapping(struct
     return ret;
 }
 
-static int domain_context_unmap_one(
+int domain_context_unmap_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1453,6 +1485,8 @@ static int domain_context_unmap_one(
     spin_unlock(&iommu->lock);
     unmap_vtd_domain_page(context_entries);
 
+    me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC);
+
     return 0;
 }
 
@@ -1951,19 +1985,6 @@ static void setup_dom0_rmrr(struct domai
     spin_unlock(&pcidevs_lock);
 }
 
-static void __init platform_quirks(void)
-{
-    u32 id;
-
-    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
-    id = pci_conf_read32(0, 0, 0, 0);
-    if ( id == 0x2a408086 )
-    {
-        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
-        rwbf_quirk = 1;
-    }
-}
-
 int __init intel_vtd_setup(void)
 {
     struct acpi_drhd_unit *drhd;
@@ -1972,7 +1993,7 @@ int __init intel_vtd_setup(void)
     if ( list_empty(&acpi_drhd_units) )
         return -ENODEV;
 
-    platform_quirks();
+    platform_quirks_init();
 
     irq_to_iommu = xmalloc_array(struct iommu*, nr_irqs);
     BUG_ON(!irq_to_iommu);
diff -r 36a317494429 xen/drivers/passthrough/vtd/iommu.h
--- a/xen/drivers/passthrough/vtd/iommu.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.h	Thu Oct 28 01:44:46 2010 -0700
@@ -501,6 +501,7 @@ struct intel_iommu {
     struct qi_ctrl qi_ctrl;
     struct ir_ctrl ir_ctrl;
     struct iommu_flush flush;
+    struct acpi_drhd_unit *drhd;
 };
 
 #endif
diff -r 36a317494429 xen/drivers/passthrough/vtd/quirks.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/passthrough/vtd/quirks.c	Thu Oct 28 01:44:46 2010 -0700
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Allen Kay <allen.m.kay@intel.com>
+ */
+
+#include <xen/irq.h>
+#include <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <xen/domain_page.h>
+#include <xen/iommu.h>
+#include <asm/hvm/iommu.h>
+#include <xen/numa.h>
+#include <xen/softirq.h>
+#include <xen/time.h>
+#include <xen/pci.h>
+#include <xen/pci_regs.h>
+#include <xen/keyhandler.h>
+#include <asm/msi.h>
+#include <asm/irq.h>
+#include <mach_apic.h>
+#include "iommu.h"
+#include "dmar.h"
+#include "extern.h"
+#include "vtd.h"
+
+#define IGD_BAR_MASK 0xFFFFFFFFFFFF0000
+#define GGC 0x52
+#define GGC_MEMORY_VT_ENABLED  (0x8 << 8)
+
+#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)
+
+u32 dev0_id;
+bool_t rwbf_quirk;
+static int is_cantiga_b3;
+static u8 *igd_reg_va;
+
+/*
+ * QUIRK to workaround Xen boot issue on Calpella/Ironlake OEM BIOS
+ * not enabling VT-d properly in IGD.  The workaround is to not enabling
+ * IGD VT-d translation if VT is not enabled in IGD.
+ */
+int is_igd_vt_enabled_quirk(void)
+{
+    u16 ggc;
+
+    if ( !IS_ILK(dev0_id) )
+        return 1;
+
+    /* integrated graphics on Intel platforms is located at 0:2.0 */
+    ggc = pci_conf_read16(0, INTEL_IGD_DEV, 0, GGC);
+    return ( ggc & GGC_MEMORY_VT_ENABLED ? 1 : 0 );
+}
+
+/*
+ * QUIRK to workaround cantiga VT-d buffer flush issue.
+ * The workaround is to force write buffer flush even if
+ * VT-d capability indicates it is not required.
+ */
+static void cantiga_b3_errata_init(void)
+{
+    u16 vid;
+    u8 did_hi, rid;
+
+    vid = pci_conf_read16(0, INTEL_IGD_DEV, 0, 0);
+    if ( vid != 0x8086 )
+        return;
+
+    did_hi = pci_conf_read8(0, INTEL_IGD_DEV, 0, 3);
+    rid = pci_conf_read8(0, INTEL_IGD_DEV, 0, 8);
+
+    if ( (did_hi == 0x2A) && (rid == 0x7) )
+        is_cantiga_b3 = 1;
+}
+
+/*
+ * QUIRK to workaround Cantiga IGD VT-d low power errata.
+ * This errata impacts IGD assignment on Cantiga systems
+ * and can potentially cause VT-d operations to hang.
+ * The workaround is to access an IGD PCI config register
+ * to get IGD out of low power state before VT-d translation
+ * enable/disable and IOTLB flushes.
+ */
+
+/*
+ * map IGD MMIO+0x2000 page to allow Xen access to IGD 3D register.
+ */
+static void map_igd_reg(void)
+{
+    u64 igd_mmio, igd_reg;
+
+    if ( !is_cantiga_b3 || igd_reg_va != NULL )
+        return;
+
+    /* get IGD mmio address in PCI BAR */
+    igd_mmio = ((u64)pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x14) << 32) +
+                     pci_conf_read32(0, INTEL_IGD_DEV, 0, 0x10);
+
+    /* offset of IGD regster we want to access is in 0x2000 range */
+    igd_reg = (igd_mmio & IGD_BAR_MASK) + 0x2000;
+
+    /* ioremap this physical page */
+    set_fixmap_nocache(FIX_IGD_MMIO, igd_reg);
+    igd_reg_va = (u8 *)fix_to_virt(FIX_IGD_MMIO);
+}
+
+/*
+ * force IGD to exit low power mode by accessing a IGD 3D regsiter.
+ */
+static int cantiga_vtd_ops_preamble(struct iommu* iommu)
+{
+    struct intel_iommu *intel = iommu->intel;
+    struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL;
+
+    if ( !is_igd_drhd(drhd) || !is_cantiga_b3 )
+        return 0;
+
+    /*
+     * read IGD register at IGD MMIO + 0x20A4 to force IGD
+     * to exit low power state.  Since map_igd_reg()
+     * already mapped page starting 0x2000, we just need to
+     * add page offset 0x0A4 to virtual address base.
+     */
+    return ( *((volatile int *)(igd_reg_va + 0x0A4)) );
+}
+
+/*
+ * call before VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_preamble_quirk(struct iommu* iommu)
+{
+    cantiga_vtd_ops_preamble(iommu);
+}
+
+/*
+ * call after VT-d translation enable and IOTLB flush operations.
+ */
+void vtd_ops_postamble_quirk(struct iommu* iommu)
+{
+    return;
+}
+
+/* initialize platform identification flags */
+void __init platform_quirks_init(void)
+{
+    dev0_id = pci_conf_read32(0, 0, 0, 0);
+
+    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
+    if ( dev0_id == 0x2a408086 )
+    {
+        dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n");
+        rwbf_quirk = 1;
+    }
+
+    /* initialize cantiga B3 identification */
+    cantiga_b3_errata_init();
+
+    /* ioremap IGD MMIO+0x2000 page */
+    map_igd_reg();
+}
+
+/*
+ * QUIRK to workaround wifi direct assignment issue.  This issue
+ * impacts only cases where Intel integrated wifi device is directly
+ * is directly assigned to a guest.
+ *
+ * The workaround is to map ME phantom device 0:3.7 or 0:22.7
+ * to the ME vt-d engine if detect the user is trying to directly
+ * assigning Intel integrated wifi device to a guest.
+ */
+
+static void map_me_phantom_function(struct domain *domain, u32 dev, int map)
+{
+    struct acpi_drhd_unit *drhd;
+    struct pci_dev *pdev;
+
+    /* find ME VT-d engine base on a real ME device */
+    pdev = pci_get_pdev(0, PCI_DEVFN(dev, 0));
+    drhd = acpi_find_matched_drhd_unit(pdev);
+
+    /* map or unmap ME phantom function */
+    if ( map )
+        domain_context_mapping_one(domain, drhd->iommu, 0,
+                                   PCI_DEVFN(dev, 7));
+    else
+        domain_context_unmap_one(domain, drhd->iommu, 0,
+                                 PCI_DEVFN(dev, 7));
+}
+
+void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map)
+{
+    u32 id;
+
+    id = pci_conf_read32(0, 0, 0, 0);
+    if ( IS_CTG(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 3, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:3.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x42328086:
+            case 0x42358086:
+            case 0x42368086:
+            case 0x42378086:
+            case 0x423a8086:
+            case 0x423b8086:
+            case 0x423c8086:
+            case 0x423d8086:
+                map_me_phantom_function(domain, 3, map);
+                break;
+            default:
+                break;
+        }
+    }
+    else if ( IS_ILK(id) || IS_CPT(id) )
+    {
+        /* quit if ME does not exist */
+        if ( pci_conf_read32(0, 22, 0, 0) == 0xffffffff )
+            return;
+
+        /* if device is WLAN device, map ME phantom device 0:22.7 */
+        id = pci_conf_read32(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0);
+        switch (id)
+        {
+            case 0x00878086:
+            case 0x00898086:
+            case 0x00828086:
+            case 0x00858086:
+            case 0x42388086:
+            case 0x422b8086:
+                map_me_phantom_function(domain, 22, map);
+                break;
+            default:
+                break;
+        }
+
+    }
+}
diff -r 36a317494429 xen/drivers/passthrough/vtd/vtd.h
--- a/xen/drivers/passthrough/vtd/vtd.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/vtd.h	Thu Oct 28 01:44:46 2010 -0700
@@ -23,6 +23,10 @@
 
 #include <xen/iommu.h>
 
+#define INTEL_IGD_DEV            0
+#define MAP_ME_PHANTOM_FUNC      1
+#define UNMAP_ME_PHANTOM_FUNC    0
+
 /* Accomodate both IOAPIC and IOSAPIC. */
 struct IO_xAPIC_route_entry {
     __u32   vector      :  8,
@@ -97,18 +101,4 @@ struct msi_msg_remap_entry {
     u32	data;		/* msi message data */
 };
 
-unsigned int get_cache_line_size(void);
-void cacheline_flush(char *);
-void flush_all_cache(void);
-u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
-void free_pgtable_maddr(u64 maddr);
-void *map_vtd_domain_page(u64 maddr);
-void unmap_vtd_domain_page(void *va);
-
-void iommu_flush_cache_entry(void *addr, unsigned int size);
-void iommu_flush_cache_page(void *addr, unsigned long npages);
-
-int iommu_alloc(struct acpi_drhd_unit *drhd);
-void iommu_free(struct acpi_drhd_unit *drhd);
-
 #endif // _VTD_H_
diff -r 36a317494429 xen/drivers/passthrough/vtd/x86/vtd.c
--- a/xen/drivers/passthrough/vtd/x86/vtd.c	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/drivers/passthrough/vtd/x86/vtd.c	Thu Oct 28 01:44:46 2010 -0700
@@ -28,6 +28,7 @@
 #include "../iommu.h"
 #include "../dmar.h"
 #include "../vtd.h"
+#include "../extern.h"
 
 /*
  * iommu_inclusive_mapping: when set, all memory below 4GB is included in dom0
diff -r 36a317494429 xen/include/asm-x86/fixmap.h
--- a/xen/include/asm-x86/fixmap.h	Mon Oct 25 18:15:28 2010 +0100
+++ b/xen/include/asm-x86/fixmap.h	Thu Oct 28 01:44:46 2010 -0700
@@ -55,6 +55,7 @@ enum fixed_addresses {
     FIX_TBOOT_MAP_ADDRESS,
     FIX_APEI_RANGE_BASE,
     FIX_APEI_RANGE_END = FIX_APEI_RANGE_BASE + FIX_APEI_RANGE_MAX -1,
+    FIX_IGD_MMIO,
     __end_of_fixed_addresses
 };
 

[-- Attachment #3: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [RFC][PATCH][VTD][v4] consolidate VT-d quirks into a  single file quirks.c
  2010-10-28 15:50           ` [RFC][PATCH][VTD][v4] " Kay, Allen M
@ 2010-10-28 16:00             ` Jan Beulich
  0 siblings, 0 replies; 8+ messages in thread
From: Jan Beulich @ 2010-10-28 16:00 UTC (permalink / raw)
  To: Allen M Kay; +Cc: Xen-devel

>>> On 28.10.10 at 17:50, "Kay, Allen M" <allen.m.kay@intel.com> wrote:
>> The non-void return value of cantiga_vtd_ops_preamble() also
>> looks bogus, btw.
> 
> The return value is not important in this case.  It is the requirement of 
> the errata workaround to read the MMIO register.

Hmm, yes, but this could be expressed differently, as a non-void
return generally implies there's at least on caller wanting to look
at the value returned.

Anyway, looks good to me now. Thanks!

Jan

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-10-28 16:00 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-14  0:05 [RFC][PATCH][VTD] consolidate VT-d quirks into a single file quirks.c Kay, Allen M
2010-10-14  9:04 ` Jan Beulich
2010-10-15  0:25   ` [RFC][PATCH][VTD][v2] " Kay, Allen M
2010-10-15  8:47     ` Jan Beulich
2010-10-27 22:29       ` [RFC][PATCH][VTD][v3] " Kay, Allen M
2010-10-28  6:57         ` Jan Beulich
2010-10-28 15:50           ` [RFC][PATCH][VTD][v4] " Kay, Allen M
2010-10-28 16:00             ` Jan Beulich

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.