mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
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
5 changed files with 103 additions and 20 deletions
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue