Merge git://git.infradead.org/~dwmw2/iommu-2.6.32
* git://git.infradead.org/~dwmw2/iommu-2.6.32: x86: Move pci_iommu_init to rootfs_initcall() Run pci_apply_final_quirks() sooner. Mark pci_apply_final_quirks() __init rather than __devinit Rename pci_init() to pci_apply_final_quirks(), move it to quirks.c intel-iommu: Yet another BIOS workaround: Isoch DMAR unit with no TLB space intel-iommu: Decode (and ignore) RHSA entries intel-iommu: Make "Unknown DMAR structure" message more informative
This commit is contained in:
commit
80fa680d22
@ -311,7 +311,7 @@ void pci_iommu_shutdown(void)
|
||||
amd_iommu_shutdown();
|
||||
}
|
||||
/* Must execute after PCI subsystem */
|
||||
fs_initcall(pci_iommu_init);
|
||||
rootfs_initcall(pci_iommu_init);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/* Many VIA bridges seem to corrupt data for DAC. Disable it here */
|
||||
|
@ -354,6 +354,7 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
|
||||
struct acpi_dmar_hardware_unit *drhd;
|
||||
struct acpi_dmar_reserved_memory *rmrr;
|
||||
struct acpi_dmar_atsr *atsr;
|
||||
struct acpi_dmar_rhsa *rhsa;
|
||||
|
||||
switch (header->type) {
|
||||
case ACPI_DMAR_TYPE_HARDWARE_UNIT:
|
||||
@ -375,6 +376,12 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
|
||||
atsr = container_of(header, struct acpi_dmar_atsr, header);
|
||||
printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags);
|
||||
break;
|
||||
case ACPI_DMAR_HARDWARE_AFFINITY:
|
||||
rhsa = container_of(header, struct acpi_dmar_rhsa, header);
|
||||
printk(KERN_INFO PREFIX "RHSA base: %#016Lx proximity domain: %#x\n",
|
||||
(unsigned long long)rhsa->base_address,
|
||||
rhsa->proximity_domain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -459,9 +466,13 @@ parse_dmar_table(void)
|
||||
ret = dmar_parse_one_atsr(entry_header);
|
||||
#endif
|
||||
break;
|
||||
case ACPI_DMAR_HARDWARE_AFFINITY:
|
||||
/* We don't do anything with RHSA (yet?) */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING PREFIX
|
||||
"Unknown DMAR structure type\n");
|
||||
"Unknown DMAR structure type %d\n",
|
||||
entry_header->type);
|
||||
ret = 0; /* for forward compatibility */
|
||||
break;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
|
||||
#define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
|
||||
#define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
|
||||
#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
|
||||
|
||||
#define IOAPIC_RANGE_START (0xfee00000)
|
||||
#define IOAPIC_RANGE_END (0xfeefffff)
|
||||
@ -94,6 +95,7 @@ static inline unsigned long virt_to_dma_pfn(void *p)
|
||||
/* global iommu list, set NULL for ignored DMAR units */
|
||||
static struct intel_iommu **g_iommus;
|
||||
|
||||
static void __init check_tylersburg_isoch(void);
|
||||
static int rwbf_quirk;
|
||||
|
||||
/*
|
||||
@ -1934,6 +1936,9 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
|
||||
}
|
||||
|
||||
static int iommu_identity_mapping;
|
||||
#define IDENTMAP_ALL 1
|
||||
#define IDENTMAP_GFX 2
|
||||
#define IDENTMAP_AZALIA 4
|
||||
|
||||
static int iommu_domain_identity_map(struct dmar_domain *domain,
|
||||
unsigned long long start,
|
||||
@ -2151,8 +2156,14 @@ static int domain_add_dev_info(struct dmar_domain *domain,
|
||||
|
||||
static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
|
||||
{
|
||||
if (iommu_identity_mapping == 2)
|
||||
return IS_GFX_DEVICE(pdev);
|
||||
if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
|
||||
return 1;
|
||||
|
||||
if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
|
||||
return 1;
|
||||
|
||||
if (!(iommu_identity_mapping & IDENTMAP_ALL))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We want to start off with all devices in the 1:1 domain, and
|
||||
@ -2332,11 +2343,14 @@ int __init init_dmars(void)
|
||||
}
|
||||
|
||||
if (iommu_pass_through)
|
||||
iommu_identity_mapping = 1;
|
||||
iommu_identity_mapping |= IDENTMAP_ALL;
|
||||
|
||||
#ifdef CONFIG_DMAR_BROKEN_GFX_WA
|
||||
else
|
||||
iommu_identity_mapping = 2;
|
||||
iommu_identity_mapping |= IDENTMAP_GFX;
|
||||
#endif
|
||||
|
||||
check_tylersburg_isoch();
|
||||
|
||||
/*
|
||||
* If pass through is not set or not enabled, setup context entries for
|
||||
* 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)
|
||||
}
|
||||
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf);
|
||||
|
||||
/* On Tylersburg chipsets, some BIOSes have been known to enable the
|
||||
ISOCH DMAR unit for the Azalia sound device, but not give it any
|
||||
TLB entries, which causes it to deadlock. Check for that. We do
|
||||
this in a function called from init_dmars(), instead of in a PCI
|
||||
quirk, because we don't want to print the obnoxious "BIOS broken"
|
||||
message if VT-d is actually disabled.
|
||||
*/
|
||||
static void __init check_tylersburg_isoch(void)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
uint32_t vtisochctrl;
|
||||
|
||||
/* If there's no Azalia in the system anyway, forget it. */
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x3a3e, NULL);
|
||||
if (!pdev)
|
||||
return;
|
||||
pci_dev_put(pdev);
|
||||
|
||||
/* System Management Registers. Might be hidden, in which case
|
||||
we can't do the sanity check. But that's OK, because the
|
||||
known-broken BIOSes _don't_ actually hide it, so far. */
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x342e, NULL);
|
||||
if (!pdev)
|
||||
return;
|
||||
|
||||
if (pci_read_config_dword(pdev, 0x188, &vtisochctrl)) {
|
||||
pci_dev_put(pdev);
|
||||
return;
|
||||
}
|
||||
|
||||
pci_dev_put(pdev);
|
||||
|
||||
/* If Azalia DMA is routed to the non-isoch DMAR unit, fine. */
|
||||
if (vtisochctrl & 1)
|
||||
return;
|
||||
|
||||
/* Drop all bits other than the number of TLB entries */
|
||||
vtisochctrl &= 0x1c;
|
||||
|
||||
/* If we have the recommended number of TLB entries (16), fine. */
|
||||
if (vtisochctrl == 0x10)
|
||||
return;
|
||||
|
||||
/* Zero TLB entries? You get to ride the short bus to school. */
|
||||
if (!vtisochctrl) {
|
||||
WARN(1, "Your BIOS is broken; DMA routed to ISOCH DMAR unit but no TLB space.\n"
|
||||
"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
|
||||
dmi_get_system_info(DMI_BIOS_VENDOR),
|
||||
dmi_get_system_info(DMI_BIOS_VERSION),
|
||||
dmi_get_system_info(DMI_PRODUCT_VERSION));
|
||||
iommu_identity_mapping |= IDENTMAP_AZALIA;
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_WARNING "DMAR: Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n",
|
||||
vtisochctrl);
|
||||
}
|
||||
|
@ -2723,17 +2723,6 @@ int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __devinit pci_init(void)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
|
||||
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
pci_fixup_device(pci_fixup_final, dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init pci_setup(char *str)
|
||||
{
|
||||
while (str) {
|
||||
@ -2771,8 +2760,6 @@ static int __init pci_setup(char *str)
|
||||
}
|
||||
early_param("pci", pci_setup);
|
||||
|
||||
device_initcall(pci_init);
|
||||
|
||||
EXPORT_SYMBOL(pci_reenable_device);
|
||||
EXPORT_SYMBOL(pci_enable_device_io);
|
||||
EXPORT_SYMBOL(pci_enable_device_mem);
|
||||
|
@ -2591,6 +2591,19 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
|
||||
}
|
||||
pci_do_fixups(dev, start, end);
|
||||
}
|
||||
|
||||
static int __init pci_apply_final_quirks(void)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
|
||||
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
pci_fixup_device(pci_fixup_final, dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs_initcall_sync(pci_apply_final_quirks);
|
||||
#else
|
||||
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user