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:
Linus Torvalds 2009-10-13 10:04:40 -07:00
commit 80fa680d22
5 changed files with 103 additions and 20 deletions

View file

@ -311,7 +311,7 @@ void pci_iommu_shutdown(void)
amd_iommu_shutdown(); amd_iommu_shutdown();
} }
/* Must execute after PCI subsystem */ /* Must execute after PCI subsystem */
fs_initcall(pci_iommu_init); rootfs_initcall(pci_iommu_init);
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
/* Many VIA bridges seem to corrupt data for DAC. Disable it here */ /* Many VIA bridges seem to corrupt data for DAC. Disable it here */

View file

@ -354,6 +354,7 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
struct acpi_dmar_hardware_unit *drhd; struct acpi_dmar_hardware_unit *drhd;
struct acpi_dmar_reserved_memory *rmrr; struct acpi_dmar_reserved_memory *rmrr;
struct acpi_dmar_atsr *atsr; struct acpi_dmar_atsr *atsr;
struct acpi_dmar_rhsa *rhsa;
switch (header->type) { switch (header->type) {
case ACPI_DMAR_TYPE_HARDWARE_UNIT: 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); atsr = container_of(header, struct acpi_dmar_atsr, header);
printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags); printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags);
break; 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); ret = dmar_parse_one_atsr(entry_header);
#endif #endif
break; break;
case ACPI_DMAR_HARDWARE_AFFINITY:
/* We don't do anything with RHSA (yet?) */
break;
default: default:
printk(KERN_WARNING PREFIX printk(KERN_WARNING PREFIX
"Unknown DMAR structure type\n"); "Unknown DMAR structure type %d\n",
entry_header->type);
ret = 0; /* for forward compatibility */ ret = 0; /* for forward compatibility */
break; break;
} }

View file

@ -48,6 +48,7 @@
#define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) #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_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_START (0xfee00000)
#define IOAPIC_RANGE_END (0xfeefffff) #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 */ /* global iommu list, set NULL for ignored DMAR units */
static struct intel_iommu **g_iommus; static struct intel_iommu **g_iommus;
static void __init check_tylersburg_isoch(void);
static int rwbf_quirk; static int rwbf_quirk;
/* /*
@ -1934,6 +1936,9 @@ error:
} }
static int iommu_identity_mapping; 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, static int iommu_domain_identity_map(struct dmar_domain *domain,
unsigned long long start, 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) static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
{ {
if (iommu_identity_mapping == 2) if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
return IS_GFX_DEVICE(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 * 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) if (iommu_pass_through)
iommu_identity_mapping = 1; iommu_identity_mapping |= IDENTMAP_ALL;
#ifdef CONFIG_DMAR_BROKEN_GFX_WA #ifdef CONFIG_DMAR_BROKEN_GFX_WA
else iommu_identity_mapping |= IDENTMAP_GFX;
iommu_identity_mapping = 2;
#endif #endif
check_tylersburg_isoch();
/* /*
* If pass through is not set or not enabled, setup context entries for * 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 * 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); 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);
}

View file

@ -2723,17 +2723,6 @@ int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev)
return 1; 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) static int __init pci_setup(char *str)
{ {
while (str) { while (str) {
@ -2771,8 +2760,6 @@ static int __init pci_setup(char *str)
} }
early_param("pci", pci_setup); early_param("pci", pci_setup);
device_initcall(pci_init);
EXPORT_SYMBOL(pci_reenable_device); EXPORT_SYMBOL(pci_reenable_device);
EXPORT_SYMBOL(pci_enable_device_io); EXPORT_SYMBOL(pci_enable_device_io);
EXPORT_SYMBOL(pci_enable_device_mem); EXPORT_SYMBOL(pci_enable_device_mem);

View file

@ -2591,6 +2591,19 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
} }
pci_do_fixups(dev, start, end); 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 #else
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {}
#endif #endif