# HG changeset patch
# User Keir Fraser <keir@xen.org>
# Date 1286028261 -3600
# Node ID 4beee577912215c734b79cb84bfe3fb20c1afbfc
# Parent  aed9fd361340158daf2d7160d1b367478b6312d6
Vt-d: fix dom0 graphics problem on Levnovo T410.
References: bnc#652935

The patch is derived from a similar quirk in Linux kernel by David
Woodhouse and Adam Jackson.  It checks for VT enabling bit in IGD GGC
register.  If VT is not enabled correctly in the IGD, Xen does not
enable VT-d translation for IGD VT-d engine.  In case where iommu boot
parameter is set to force, Xen calls panic().

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

# HG changeset patch
# User Keir Fraser <keir@xen.org>
# Date 1288344554 -3600
# Node ID b48d8f27fca251c2df0222d195ffcb772d6a1128
# Parent  2d5e8f4ac43a120bbb5d4c52d08f6980848f0166
vtd: consolidate VT-d quirks into a single file quirks.c

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>
Reviewed-by: Jan Beulich <JBeulich@novell.com>

# HG changeset patch
# User Keir Fraser <keir@xen.org>
# Date 1288888517 0
# Node ID fedcd4cbcc1eb3e210628bdf95766ca0c400fc18
# Parent  d508b18a68447f91cd879b79a498f06536d89f8e
[VTD] fix a typo and some minor cleanup of quirks.c

Fixed a typo for IGD_DEV define and some minor cleanup to ease future
enhancement.

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

# HG changeset patch
# User Keir Fraser <keir@xen.org>
# Date 1294221021 0
# Node ID e635e6641c07ee2da66b16f46f45442c9a46821d
# Parent  76d897a06b316bf2278220b006d578faf31ce3fb
[VTD] added WLAN device ID on Fujitsu's platform in quirks.c

Added WLAN device ID 0x422C that was found on Fujitsu's Calpella
system to WLAN quirk.

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

--- a/xen/drivers/passthrough/vtd/Makefile
+++ b/xen/drivers/passthrough/vtd/Makefile
@@ -6,3 +6,4 @@ obj-y += dmar.o
 obj-y += utils.o
 obj-y += qinval.o
 obj-y += intremap.o
+obj-y += quirks.o
--- 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 && (drhd->address == igd_drhd_address);
+}
+
 /*
  * 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:
--- a/xen/drivers/passthrough/vtd/dmar.h
+++ b/xen/drivers/passthrough/vtd/dmar.h
@@ -114,5 +114,6 @@ void *map_to_nocache_virt(int nr_iommus,
 int vtd_hw_check(void);
 void disable_pmr(struct iommu *iommu);
 int is_usb_device(u8 bus, u8 devfn);
+int is_igd_drhd(struct acpi_drhd_unit *drhd);
 
 #endif /* _DMAR_H_ */
--- a/xen/drivers/passthrough/vtd/extern.h
+++ b/xen/drivers/passthrough/vtd/extern.h
@@ -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,
@@ -44,19 +51,41 @@ int queue_invalidate_iec(struct iommu *i
 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);
-void clear_fault_bits(struct iommu *iommu);
+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_
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -43,7 +43,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);
@@ -481,16 +480,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)
@@ -514,6 +533,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);
@@ -534,8 +554,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)
@@ -688,10 +716,26 @@ static int iommu_set_root_entry(struct i
     return 0;
 }
 
-static void iommu_enable_translation(struct iommu *iommu)
+static void iommu_enable_translation(struct acpi_drhd_unit *drhd)
 {
     u32 sts;
     unsigned long flags;
+    struct iommu *iommu = drhd->iommu;
+
+    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");
+        else
+        {
+            dprintk(XENLOG_WARNING VTDPREFIX,
+                    "BIOS did not enable IGD for VT properly.  Disabling IGD VT-d engine.\n");
+            return;
+        }
+    }
+
+    /* apply platform specific errata workarounds */
+    vtd_ops_preamble_quirk(iommu);
 
     if ( iommu_verbose )
         dprintk(VTDPREFIX,
@@ -705,6 +749,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);
 }
@@ -714,6 +761,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));
@@ -722,6 +772,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 {
@@ -1065,6 +1118,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++;
@@ -1178,7 +1232,6 @@ static int intel_iommu_domain_init(struc
 
 static void intel_iommu_dom0_init(struct domain *d)
 {
-    struct iommu *iommu;
     struct acpi_drhd_unit *drhd;
 
     if ( !iommu_passthrough && !need_iommu(d) )
@@ -1194,12 +1247,11 @@ static void intel_iommu_dom0_init(struct
 
     for_each_drhd_unit ( drhd )
     {
-        iommu = drhd->iommu;
-        iommu_enable_translation(iommu);
+        iommu_enable_translation(drhd);
     }
 }
 
-static int domain_context_mapping_one(
+int domain_context_mapping_one(
     struct domain *domain,
     struct iommu *iommu,
     u8 bus, u8 devfn)
@@ -1301,6 +1353,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;
 }
 
@@ -1382,7 +1436,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)
@@ -1430,6 +1484,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;
 }
 
@@ -1928,19 +1984,6 @@ static void setup_dom0_rmrr(struct domai
     spin_unlock(&pcidevs_lock);
 }
 
-static void 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 intel_vtd_setup(void)
 {
     struct acpi_drhd_unit *drhd;
@@ -1949,7 +1992,7 @@ int 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);
@@ -2164,7 +2207,7 @@ static void vtd_resume(void)
                     (u32) iommu_state[i][DMAR_FEUADDR_REG]);
         spin_unlock_irqrestore(&iommu->register_lock, flags);
 
-        iommu_enable_translation(iommu);
+        iommu_enable_translation(drhd);
     }
 }
 
--- a/xen/drivers/passthrough/vtd/iommu.h
+++ b/xen/drivers/passthrough/vtd/iommu.h
@@ -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
--- /dev/null
+++ b/xen/drivers/passthrough/vtd/quirks.c
@@ -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 IOH_DEV      0
+#define IGD_DEV      2
+
+#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 ioh_id;
+u32 igd_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(ioh_id) )
+        return 1;
+
+    /* integrated graphics on Intel platforms is located at 0:2.0 */
+    ggc = pci_conf_read16(0, 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, IGD_DEV, 0, 0);
+    if ( vid != 0x8086 )
+        return;
+
+    did_hi = pci_conf_read8(0, IGD_DEV, 0, 3);
+    rid = pci_conf_read8(0, 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, IGD_DEV, 0, 0x14) << 32) +
+                     pci_conf_read32(0, 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)
+{
+    ioh_id = pci_conf_read32(0, IOH_DEV, 0, 0);
+    igd_id = pci_conf_read32(0, IGD_DEV, 0, 0);
+
+    /* Mobile 4 Series Chipset neglects to set RWBF capability. */
+    if ( ioh_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:
+            case 0x422c8086:
+                map_me_phantom_function(domain, 22, map);
+                break;
+            default:
+                break;
+        }
+
+    }
+}
--- a/xen/drivers/passthrough/vtd/vtd.h
+++ b/xen/drivers/passthrough/vtd/vtd.h
@@ -23,6 +23,9 @@
 
 #include <xen/iommu.h>
 
+#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 +100,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_
--- a/xen/drivers/passthrough/vtd/x86/vtd.c
+++ b/xen/drivers/passthrough/vtd/x86/vtd.c
@@ -27,6 +27,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
--- a/xen/include/asm-x86/fixmap.h
+++ b/xen/include/asm-x86/fixmap.h
@@ -52,6 +52,7 @@ enum fixed_addresses {
     FIX_MSIX_IO_RESERV_BASE,
     FIX_MSIX_IO_RESERV_END = FIX_MSIX_IO_RESERV_BASE + FIX_MSIX_MAX_PAGES -1,
     FIX_TBOOT_MAP_ADDRESS,
+    FIX_IGD_MMIO,
     __end_of_fixed_addresses
 };
 
