diff options
Diffstat (limited to 'drivers/pci/intel-iommu.c')
| -rw-r--r-- | drivers/pci/intel-iommu.c | 82 |
1 files changed, 77 insertions, 5 deletions
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 855dd7ca47f3..b1e97e682500 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
| @@ -48,6 +48,7 @@ | |||
| 48 | 48 | ||
| 49 | #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) | 49 | #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) |
| 50 | #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) | 50 | #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) |
| 51 | #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) | ||
| 51 | 52 | ||
| 52 | #define IOAPIC_RANGE_START (0xfee00000) | 53 | #define IOAPIC_RANGE_START (0xfee00000) |
| 53 | #define IOAPIC_RANGE_END (0xfeefffff) | 54 | #define IOAPIC_RANGE_END (0xfeefffff) |
| @@ -94,6 +95,7 @@ static inline unsigned long virt_to_dma_pfn(void *p) | |||
| 94 | /* global iommu list, set NULL for ignored DMAR units */ | 95 | /* global iommu list, set NULL for ignored DMAR units */ |
| 95 | static struct intel_iommu **g_iommus; | 96 | static struct intel_iommu **g_iommus; |
| 96 | 97 | ||
| 98 | static void __init check_tylersburg_isoch(void); | ||
| 97 | static int rwbf_quirk; | 99 | static int rwbf_quirk; |
| 98 | 100 | ||
| 99 | /* | 101 | /* |
| @@ -1934,6 +1936,9 @@ error: | |||
| 1934 | } | 1936 | } |
| 1935 | 1937 | ||
| 1936 | static int iommu_identity_mapping; | 1938 | static int iommu_identity_mapping; |
| 1939 | #define IDENTMAP_ALL 1 | ||
| 1940 | #define IDENTMAP_GFX 2 | ||
| 1941 | #define IDENTMAP_AZALIA 4 | ||
| 1937 | 1942 | ||
| 1938 | static int iommu_domain_identity_map(struct dmar_domain *domain, | 1943 | static int iommu_domain_identity_map(struct dmar_domain *domain, |
| 1939 | unsigned long long start, | 1944 | unsigned long long start, |
| @@ -2151,8 +2156,14 @@ static int domain_add_dev_info(struct dmar_domain *domain, | |||
| 2151 | 2156 | ||
| 2152 | static int iommu_should_identity_map(struct pci_dev *pdev, int startup) | 2157 | static int iommu_should_identity_map(struct pci_dev *pdev, int startup) |
| 2153 | { | 2158 | { |
| 2154 | if (iommu_identity_mapping == 2) | 2159 | if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) |
| 2155 | return IS_GFX_DEVICE(pdev); | 2160 | return 1; |
| 2161 | |||
| 2162 | if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev)) | ||
| 2163 | return 1; | ||
| 2164 | |||
| 2165 | if (!(iommu_identity_mapping & IDENTMAP_ALL)) | ||
| 2166 | return 0; | ||
| 2156 | 2167 | ||
| 2157 | /* | 2168 | /* |
| 2158 | * We want to start off with all devices in the 1:1 domain, and | 2169 | * We want to start off with all devices in the 1:1 domain, and |
| @@ -2332,11 +2343,14 @@ int __init init_dmars(void) | |||
| 2332 | } | 2343 | } |
| 2333 | 2344 | ||
| 2334 | if (iommu_pass_through) | 2345 | if (iommu_pass_through) |
| 2335 | iommu_identity_mapping = 1; | 2346 | iommu_identity_mapping |= IDENTMAP_ALL; |
| 2347 | |||
| 2336 | #ifdef CONFIG_DMAR_BROKEN_GFX_WA | 2348 | #ifdef CONFIG_DMAR_BROKEN_GFX_WA |
| 2337 | else | 2349 | iommu_identity_mapping |= IDENTMAP_GFX; |
| 2338 | iommu_identity_mapping = 2; | ||
| 2339 | #endif | 2350 | #endif |
| 2351 | |||
| 2352 | check_tylersburg_isoch(); | ||
| 2353 | |||
| 2340 | /* | 2354 | /* |
| 2341 | * If pass through is not set or not enabled, setup context entries for | 2355 | * If pass through is not set or not enabled, setup context entries for |
| 2342 | * identity mappings for rmrr, gfx, and isa and may fall back to static | 2356 | * identity mappings for rmrr, gfx, and isa and may fall back to static |
| @@ -3670,3 +3684,61 @@ static void __devinit quirk_iommu_rwbf(struct pci_dev *dev) | |||
| 3670 | } | 3684 | } |
| 3671 | 3685 | ||
| 3672 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); | 3686 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); |
| 3687 | |||
| 3688 | /* On Tylersburg chipsets, some BIOSes have been known to enable the | ||
| 3689 | ISOCH DMAR unit for the Azalia sound device, but not give it any | ||
| 3690 | TLB entries, which causes it to deadlock. Check for that. We do | ||
| 3691 | this in a function called from init_dmars(), instead of in a PCI | ||
| 3692 | quirk, because we don't want to print the obnoxious "BIOS broken" | ||
| 3693 | message if VT-d is actually disabled. | ||
| 3694 | */ | ||
| 3695 | static void __init check_tylersburg_isoch(void) | ||
| 3696 | { | ||
| 3697 | struct pci_dev *pdev; | ||
| 3698 | uint32_t vtisochctrl; | ||
| 3699 | |||
| 3700 | /* If there's no Azalia in the system anyway, forget it. */ | ||
| 3701 | pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x3a3e, NULL); | ||
| 3702 | if (!pdev) | ||
| 3703 | return; | ||
| 3704 | pci_dev_put(pdev); | ||
| 3705 | |||
| 3706 | /* System Management Registers. Might be hidden, in which case | ||
| 3707 | we can't do the sanity check. But that's OK, because the | ||
| 3708 | known-broken BIOSes _don't_ actually hide it, so far. */ | ||
| 3709 | pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x342e, NULL); | ||
| 3710 | if (!pdev) | ||
| 3711 | return; | ||
| 3712 | |||
| 3713 | if (pci_read_config_dword(pdev, 0x188, &vtisochctrl)) { | ||
| 3714 | pci_dev_put(pdev); | ||
| 3715 | return; | ||
| 3716 | } | ||
| 3717 | |||
| 3718 | pci_dev_put(pdev); | ||
| 3719 | |||
| 3720 | /* If Azalia DMA is routed to the non-isoch DMAR unit, fine. */ | ||
| 3721 | if (vtisochctrl & 1) | ||
| 3722 | return; | ||
| 3723 | |||
| 3724 | /* Drop all bits other than the number of TLB entries */ | ||
| 3725 | vtisochctrl &= 0x1c; | ||
| 3726 | |||
| 3727 | /* If we have the recommended number of TLB entries (16), fine. */ | ||
| 3728 | if (vtisochctrl == 0x10) | ||
| 3729 | return; | ||
| 3730 | |||
| 3731 | /* Zero TLB entries? You get to ride the short bus to school. */ | ||
| 3732 | if (!vtisochctrl) { | ||
| 3733 | WARN(1, "Your BIOS is broken; DMA routed to ISOCH DMAR unit but no TLB space.\n" | ||
| 3734 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
| 3735 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
| 3736 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
| 3737 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
| 3738 | iommu_identity_mapping |= IDENTMAP_AZALIA; | ||
| 3739 | return; | ||
| 3740 | } | ||
| 3741 | |||
| 3742 | printk(KERN_WARNING "DMAR: Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n", | ||
| 3743 | vtisochctrl); | ||
| 3744 | } | ||
