/* * 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, see . * * Author: Allen Kay */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) /* SandyBridge IGD timeouts in milliseconds */ #define SNB_IGD_TIMEOUT_LEGACY 1000 #define SNB_IGD_TIMEOUT 670 static unsigned int snb_igd_timeout; static u32 __read_mostly ioh_id; static u32 __initdata igd_id; bool_t __read_mostly rwbf_quirk; static bool_t __read_mostly is_cantiga_b3; static bool_t __read_mostly is_snb_gfx; static u8 *__read_mostly igd_reg_va; static spinlock_t igd_lock; /* * 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, 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 __init cantiga_b3_errata_init(void) { u16 vid; u8 did_hi, rid; vid = pci_conf_read16(0, 0, IGD_DEV, 0, 0); if ( vid != 0x8086 ) return; did_hi = pci_conf_read8(0, 0, IGD_DEV, 0, 3); rid = pci_conf_read8(0, 0, IGD_DEV, 0, 8); if ( (did_hi == 0x2A) && (rid == 0x7) ) is_cantiga_b3 = 1; } /* check for Sandybridge IGD device ID's */ static void __init snb_errata_init(void) { is_snb_gfx = IS_SNB_GFX(igd_id); spin_lock_init(&igd_lock); } /* * QUIRK to workaround Cantiga IGD VT-d low power errata. * This errata impacts IGD assignment on Cantiga systems * 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 __init map_igd_reg(void) { u64 igd_mmio; if ( !is_cantiga_b3 && !is_snb_gfx ) return; if ( igd_reg_va ) return; igd_mmio = pci_conf_read32(0, 0, IGD_DEV, 0, PCI_BASE_ADDRESS_1); igd_mmio <<= 32; igd_mmio += pci_conf_read32(0, 0, IGD_DEV, 0, PCI_BASE_ADDRESS_0); igd_reg_va = ioremap(igd_mmio & IGD_BAR_MASK, 0x3000); } /* * 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 ( !igd_reg_va ) return 0; /* * Read IGD register at IGD MMIO + 0x20A4 to force IGD * to exit low power state. */ return *(volatile int *)(igd_reg_va + 0x20A4); } /* * Sandybridge RC6 power management inhibit state erratum. * This can cause power high power consumption. * Workaround is to prevent graphics get into RC6 * state when doing VT-d IOTLB operations, do the VT-d * IOTLB operation, and then re-enable RC6 state. * * This quirk is enabled with the snb_igd_quirk command * line parameter. Specifying snb_igd_quirk with no value * (or any of the standard boolean values) enables this * quirk and sets the timeout to the legacy timeout of * 1000 msec. Setting this parameter to the string * "cap" enables this quirk and sets the timeout to * the theoretical maximum of 670 msec. Setting this * parameter to a numerical value enables the quirk and * sets the timeout to that numerical number of msecs. */ static void snb_vtd_ops_preamble(struct iommu* iommu) { struct intel_iommu *intel = iommu->intel; struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL; s_time_t start_time; if ( !is_igd_drhd(drhd) || !is_snb_gfx ) return; if ( !igd_reg_va ) return; *(volatile u32 *)(igd_reg_va + 0x2054) = 0x000FFFFF; *(volatile u32 *)(igd_reg_va + 0x2700) = 0; start_time = NOW(); while ( (*(volatile u32 *)(igd_reg_va + 0x22AC) & 0xF) != 0 ) { if ( NOW() > start_time + snb_igd_timeout ) { dprintk(XENLOG_INFO VTDPREFIX, "snb_vtd_ops_preamble: failed to disable idle handshake\n"); break; } cpu_relax(); } *(volatile u32 *)(igd_reg_va + 0x2050) = 0x10001; } static void snb_vtd_ops_postamble(struct iommu* iommu) { struct intel_iommu *intel = iommu->intel; struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL; if ( !is_igd_drhd(drhd) || !is_snb_gfx ) return; if ( !igd_reg_va ) return; *(volatile u32 *)(igd_reg_va + 0x2054) = 0xA; *(volatile u32 *)(igd_reg_va + 0x2050) = 0x10000; } /* * call before VT-d translation enable and IOTLB flush operations. */ void vtd_ops_preamble_quirk(struct iommu* iommu) { cantiga_vtd_ops_preamble(iommu); if ( snb_igd_timeout != 0 ) { spin_lock(&igd_lock); /* match unlock in postamble */ snb_vtd_ops_preamble(iommu); } } /* * call after VT-d translation enable and IOTLB flush operations. */ void vtd_ops_postamble_quirk(struct iommu* iommu) { if ( snb_igd_timeout != 0 ) { snb_vtd_ops_postamble(iommu); /* match the lock in preamble */ spin_unlock(&igd_lock); } } static int __init parse_snb_timeout(const char *s) { int t; const char *q = NULL; t = parse_bool(s, NULL); if ( t < 0 ) { if ( *s == '\0' ) t = SNB_IGD_TIMEOUT_LEGACY; else if ( strcmp(s, "cap") == 0 ) t = SNB_IGD_TIMEOUT; else t = strtoul(s, &q, 0); } else t = t ? SNB_IGD_TIMEOUT_LEGACY : 0; snb_igd_timeout = MILLISECS(t); return (q && *q) ? -EINVAL : 0; } custom_param("snb_igd_quirk", parse_snb_timeout); /* 5500/5520/X58 Chipset Interrupt remapping errata, for stepping B-3. * Fixed in stepping C-2. */ static void __init tylersburg_intremap_quirk(void) { uint32_t bus, device; uint8_t rev; for ( bus = 0; bus < 0x100; bus++ ) { /* Match on System Management Registers on Device 20 Function 0 */ device = pci_conf_read32(0, bus, 20, 0, PCI_VENDOR_ID); rev = pci_conf_read8(0, bus, 20, 0, PCI_REVISION_ID); if ( rev == 0x13 && device == 0x342e8086 ) { printk(XENLOG_WARNING VTDPREFIX "Disabling IOMMU due to Intel 5500/5520/X58 Chipset errata #47, #53\n"); iommu_enable = 0; break; } } } /* initialize platform identification flags */ void __init platform_quirks_init(void) { ioh_id = pci_conf_read32(0, 0, IOH_DEV, 0, 0); igd_id = pci_conf_read32(0, 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(); snb_errata_init(); /* ioremap IGD MMIO+0x2000 page */ map_igd_reg(); /* Tylersburg interrupt remap quirk */ if ( iommu_intremap ) tylersburg_intremap_quirk(); } /* * 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 int __must_check map_me_phantom_function(struct domain *domain, u32 dev, int map) { struct acpi_drhd_unit *drhd; struct pci_dev *pdev; int rc; /* find ME VT-d engine base on a real ME device */ pdev = pci_get_pdev(0, 0, PCI_DEVFN(dev, 0)); drhd = acpi_find_matched_drhd_unit(pdev); /* map or unmap ME phantom function */ if ( map ) rc = domain_context_mapping_one(domain, drhd->iommu, 0, PCI_DEVFN(dev, 7), NULL); else rc = domain_context_unmap_one(domain, drhd->iommu, 0, PCI_DEVFN(dev, 7)); return rc; } int me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map) { u32 id; int rc = 0; id = pci_conf_read32(0, 0, 0, 0, 0); if ( IS_CTG(id) ) { /* quit if ME does not exist */ if ( pci_conf_read32(0, 0, 3, 0, 0) == 0xffffffff ) return 0; /* if device is WLAN device, map ME phantom device 0:3.7 */ id = pci_conf_read32(0, 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: rc = 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, 0, 22, 0, 0) == 0xffffffff ) return 0; /* if device is WLAN device, map ME phantom device 0:22.7 */ id = pci_conf_read32(0, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0); switch (id) { case 0x00878086: /* Kilmer Peak */ case 0x00898086: case 0x00828086: /* Taylor Peak */ case 0x00858086: case 0x008F8086: /* Rainbow Peak */ case 0x00908086: case 0x00918086: case 0x42388086: /* Puma Peak */ case 0x422b8086: case 0x422c8086: rc = map_me_phantom_function(domain, 22, map); break; default: break; } } return rc; } void pci_vtd_quirk(const struct pci_dev *pdev) { int seg = pdev->seg; int bus = pdev->bus; int dev = PCI_SLOT(pdev->devfn); int func = PCI_FUNC(pdev->devfn); int pos; bool_t ff; u32 val, val2; u64 bar; paddr_t pa; const char *action; if ( pci_conf_read16(seg, bus, dev, func, PCI_VENDOR_ID) != PCI_VENDOR_ID_INTEL ) return; switch ( pci_conf_read16(seg, bus, dev, func, PCI_DEVICE_ID) ) { /* * Mask reporting Intel VT-d faults to IOH core logic: * - Some platform escalates VT-d faults to platform errors. * - This can cause system failure upon non-fatal VT-d faults. * - Potential security issue if malicious guest trigger VT-d faults. */ case 0x342e: /* Tylersburg chipset (Nehalem / Westmere systems) */ case 0x3728: /* Xeon C5500/C3500 (JasperForest) */ case 0x3c28: /* Sandybridge */ val = pci_conf_read32(seg, bus, dev, func, 0x1AC); pci_conf_write32(seg, bus, dev, func, 0x1AC, val | (1 << 31)); printk(XENLOG_INFO "Masked VT-d error signaling on %04x:%02x:%02x.%u\n", seg, bus, dev, func); break; /* Tylersburg (EP)/Boxboro (MP) chipsets (NHM-EP/EX, WSM-EP/EX) */ case 0x3400 ... 0x3407: /* host bridges */ case 0x3408 ... 0x3411: case 0x3420 ... 0x3421: /* root ports */ /* JasperForest (Intel Xeon Processor C5500/C3500 */ case 0x3700 ... 0x370f: /* host bridges */ case 0x3720 ... 0x3724: /* root ports */ /* Sandybridge-EP (Romley) */ case 0x3c00: /* host bridge */ case 0x3c01 ... 0x3c0b: /* root ports */ pos = pci_find_ext_capability(seg, bus, pdev->devfn, PCI_EXT_CAP_ID_ERR); if ( !pos ) { pos = pci_find_ext_capability(seg, bus, pdev->devfn, PCI_EXT_CAP_ID_VNDR); while ( pos ) { val = pci_conf_read32(seg, bus, dev, func, pos + PCI_VNDR_HEADER); if ( PCI_VNDR_HEADER_ID(val) == 4 && PCI_VNDR_HEADER_REV(val) == 1 ) { pos += PCI_VNDR_HEADER; break; } pos = pci_find_next_ext_capability(seg, bus, pdev->devfn, pos, PCI_EXT_CAP_ID_VNDR); } ff = 0; } else ff = pcie_aer_get_firmware_first(pdev); if ( !pos ) { printk(XENLOG_WARNING "%04x:%02x:%02x.%u without AER capability?\n", seg, bus, dev, func); break; } val = pci_conf_read32(seg, bus, dev, func, pos + PCI_ERR_UNCOR_MASK); val2 = pci_conf_read32(seg, bus, dev, func, pos + PCI_ERR_COR_MASK); if ( (val & PCI_ERR_UNC_UNSUP) && (val2 & PCI_ERR_COR_ADV_NFAT) ) action = "Found masked"; else if ( !ff ) { pci_conf_write32(seg, bus, dev, func, pos + PCI_ERR_UNCOR_MASK, val | PCI_ERR_UNC_UNSUP); pci_conf_write32(seg, bus, dev, func, pos + PCI_ERR_COR_MASK, val2 | PCI_ERR_COR_ADV_NFAT); action = "Masked"; } else action = "Must not mask"; /* XPUNCERRMSK Send Completion with Unsupported Request */ val = pci_conf_read32(seg, bus, dev, func, 0x20c); pci_conf_write32(seg, bus, dev, func, 0x20c, val | (1 << 4)); printk(XENLOG_INFO "%s UR signaling on %04x:%02x:%02x.%u\n", action, seg, bus, dev, func); break; case 0x0040: case 0x0044: case 0x0048: /* Nehalem/Westmere */ case 0x0100: case 0x0104: case 0x0108: /* Sandybridge */ case 0x0150: case 0x0154: case 0x0158: /* Ivybridge */ case 0x0a00: case 0x0a04: case 0x0a08: case 0x0a0f: /* Haswell ULT */ case 0x0c00: case 0x0c04: case 0x0c08: case 0x0c0f: /* Haswell */ case 0x0d00: case 0x0d04: case 0x0d08: case 0x0d0f: /* Haswell */ case 0x1600: case 0x1604: case 0x1608: case 0x160f: /* Broadwell */ case 0x1610: case 0x1614: case 0x1618: /* Broadwell */ case 0x1900: case 0x1904: case 0x1908: case 0x190c: case 0x190f: /* Skylake */ case 0x1910: case 0x1918: case 0x191f: /* Skylake */ bar = pci_conf_read32(seg, bus, dev, func, 0x6c); bar = (bar << 32) | pci_conf_read32(seg, bus, dev, func, 0x68); pa = bar & 0x7ffffff000UL; /* bits 12...38 */ if ( (bar & 1) && pa && page_is_ram_type(paddr_to_pfn(pa), RAM_TYPE_RESERVED) ) { u32 __iomem *va = ioremap(pa, PAGE_SIZE); if ( va ) { __set_bit(0x1c8 * 8 + 20, va); iounmap(va); printk(XENLOG_INFO "Masked UR signaling on %04x:%02x:%02x.%u\n", seg, bus, dev, func); } else printk(XENLOG_ERR "Could not map %"PRIpaddr" for %04x:%02x:%02x.%u\n", pa, seg, bus, dev, func); } else printk(XENLOG_WARNING "Bogus DMIBAR %#"PRIx64" on %04x:%02x:%02x.%u\n", bar, seg, bus, dev, func); break; } }