Skip to content

Commit e0fc7e0

Browse files
David WoodhouseDavid Woodhouse
authored andcommitted
intel-iommu: Yet another BIOS workaround: Isoch DMAR unit with no TLB space
Asus decided to ship a BIOS which configures sound DMA to go via the dedicated IOMMU unit, but assigns precisely zero TLB entries to that unit. Which causes the whole thing to deadlock, including the DMA traffic on the _other_ IOMMU units. Nice one. Signed-off-by: David Woodhouse <[email protected]>
1 parent 17b6097 commit e0fc7e0

File tree

1 file changed

+77
-5
lines changed

1 file changed

+77
-5
lines changed

drivers/pci/intel-iommu.c

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
#define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
5050
#define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
51+
#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
5152

5253
#define IOAPIC_RANGE_START (0xfee00000)
5354
#define IOAPIC_RANGE_END (0xfeefffff)
@@ -94,6 +95,7 @@ static inline unsigned long virt_to_dma_pfn(void *p)
9495
/* global iommu list, set NULL for ignored DMAR units */
9596
static struct intel_iommu **g_iommus;
9697

98+
static void __init check_tylersburg_isoch(void);
9799
static int rwbf_quirk;
98100

99101
/*
@@ -1934,6 +1936,9 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
19341936
}
19351937

19361938
static int iommu_identity_mapping;
1939+
#define IDENTMAP_ALL 1
1940+
#define IDENTMAP_GFX 2
1941+
#define IDENTMAP_AZALIA 4
19371942

19381943
static int iommu_domain_identity_map(struct dmar_domain *domain,
19391944
unsigned long long start,
@@ -2151,8 +2156,14 @@ static int domain_add_dev_info(struct dmar_domain *domain,
21512156

21522157
static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
21532158
{
2154-
if (iommu_identity_mapping == 2)
2155-
return IS_GFX_DEVICE(pdev);
2159+
if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(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;
21562167

21572168
/*
21582169
* We want to start off with all devices in the 1:1 domain, and
@@ -2332,11 +2343,14 @@ int __init init_dmars(void)
23322343
}
23332344

23342345
if (iommu_pass_through)
2335-
iommu_identity_mapping = 1;
2346+
iommu_identity_mapping |= IDENTMAP_ALL;
2347+
23362348
#ifdef CONFIG_DMAR_BROKEN_GFX_WA
2337-
else
2338-
iommu_identity_mapping = 2;
2349+
iommu_identity_mapping |= IDENTMAP_GFX;
23392350
#endif
2351+
2352+
check_tylersburg_isoch();
2353+
23402354
/*
23412355
* If pass through is not set or not enabled, setup context entries for
23422356
* 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)
36703684
}
36713685

36723686
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+
}

0 commit comments

Comments
 (0)