diff --git a/xen/arch/x86/apic.c b/xen/arch/x86/apic.c index 64d0c88c5b..cfdb13abf6 100644 --- a/xen/arch/x86/apic.c +++ b/xen/arch/x86/apic.c @@ -516,7 +516,7 @@ static void resume_x2apic(void) mask_8259A(); mask_IO_APIC_setup(ioapic_entries); - iommu_enable_IR(); + iommu_enable_x2apic_IR(); __enable_x2apic(); restore_IO_APIC_setup(ioapic_entries); @@ -735,7 +735,7 @@ int lapic_suspend(void) local_irq_save(flags); disable_local_APIC(); - iommu_disable_IR(); + iommu_disable_x2apic_IR(); local_irq_restore(flags); return 0; } @@ -958,7 +958,7 @@ void __init x2apic_bsp_setup(void) mask_8259A(); mask_IO_APIC_setup(ioapic_entries); - if ( iommu_enable_IR() ) + if ( iommu_enable_x2apic_IR() ) { if ( x2apic_enabled ) panic("Interrupt remapping could not be enabled while " diff --git a/xen/drivers/passthrough/vtd/dmar.c b/xen/drivers/passthrough/vtd/dmar.c index dfaf6804fa..aadbe1b058 100644 --- a/xen/drivers/passthrough/vtd/dmar.c +++ b/xen/drivers/passthrough/vtd/dmar.c @@ -47,6 +47,7 @@ static LIST_HEAD_READ_MOSTLY(acpi_atsr_units); static LIST_HEAD_READ_MOSTLY(acpi_rhsa_units); static struct acpi_table_header *__read_mostly dmar_table; +static int __read_mostly dmar_flags; static u64 __read_mostly igd_drhd_address; static void __init dmar_scope_add_buses(struct dmar_scope *scope, u16 sec_bus, @@ -761,7 +762,11 @@ out: int __init acpi_dmar_init(void) { + struct acpi_table_dmar *dmar; + acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_table); + dmar = (struct acpi_table_dmar *) dmar_table; + dmar_flags = dmar->flags; return parse_dmar_table(acpi_parse_dmar); } @@ -781,3 +786,22 @@ void acpi_dmar_zap(void) dmar_table->signature[0] = 'X'; dmar_table->checksum -= 'X'-'D'; } + +int __init platform_supports_intremap(void) +{ + unsigned int flags = 0; + + flags = DMAR_INTR_REMAP; + return ((dmar_flags & flags) == DMAR_INTR_REMAP); +} + +int __init platform_supports_x2apic(void) +{ + unsigned int flags = 0; + + if (!cpu_has_x2apic) + return 0; + + flags = DMAR_INTR_REMAP | DMAR_X2APIC_OPT_OUT; + return ((dmar_flags & flags) == DMAR_INTR_REMAP); +} diff --git a/xen/drivers/passthrough/vtd/extern.h b/xen/drivers/passthrough/vtd/extern.h index ea80fccf2a..a9c3f1c339 100644 --- a/xen/drivers/passthrough/vtd/extern.h +++ b/xen/drivers/passthrough/vtd/extern.h @@ -119,5 +119,7 @@ void vtd_ops_preamble_quirk(struct iommu* iommu); void vtd_ops_postamble_quirk(struct iommu* iommu); void me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map); void pci_vtd_quirk(struct pci_dev *pdev); +int __init platform_supports_intremap(void); +int __init platform_supports_x2apic(void); #endif // _VTD_EXTERN_H_ diff --git a/xen/drivers/passthrough/vtd/intremap.c b/xen/drivers/passthrough/vtd/intremap.c index 7e194a4927..09af71f8f3 100644 --- a/xen/drivers/passthrough/vtd/intremap.c +++ b/xen/drivers/passthrough/vtd/intremap.c @@ -735,6 +735,13 @@ int enable_intremap(struct iommu *iommu, int eim) ASSERT(ecap_intr_remap(iommu->ecap) && iommu_intremap); + if ( !platform_supports_intremap() ) + { + dprintk(XENLOG_ERR VTDPREFIX, + "Platform firmware does not support interrupt remapping\n"); + return -EINVAL; + } + ir_ctrl = iommu_ir_ctrl(iommu); sts = dmar_readl(iommu->reg, DMAR_GSTS_REG); @@ -821,10 +828,10 @@ out: } /* - * This function is used to enable Interrutp remapping when + * This function is used to enable Interrupt remapping when * enable x2apic */ -int iommu_enable_IR(void) +int iommu_enable_x2apic_IR(void) { struct acpi_drhd_unit *drhd; struct iommu *iommu; @@ -832,6 +839,9 @@ int iommu_enable_IR(void) if ( !iommu_supports_eim() ) return -1; + if ( !platform_supports_x2apic() ) + return -1; + for_each_drhd_unit ( drhd ) { struct qi_ctrl *qi_ctrl = NULL; @@ -881,7 +891,7 @@ int iommu_enable_IR(void) * This function is used to disable Interrutp remapping when * suspend local apic */ -void iommu_disable_IR(void) +void iommu_disable_x2apic_IR(void) { struct acpi_drhd_unit *drhd; diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c index b69d5e3cc0..52d1585987 100644 --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -2014,7 +2014,7 @@ static int init_vtd_hw(void) if ( enable_intremap(iommu, 0) != 0 ) { dprintk(XENLOG_WARNING VTDPREFIX, - "Failed to enable Interrupt Remapping!\n"); + "Interrupt Remapping not enabled\n"); break; } } diff --git a/xen/drivers/passthrough/vtd/iommu.h b/xen/drivers/passthrough/vtd/iommu.h index 6e689a5889..950267569c 100644 --- a/xen/drivers/passthrough/vtd/iommu.h +++ b/xen/drivers/passthrough/vtd/iommu.h @@ -22,6 +22,10 @@ #include +/* DMAR Flags bits */ +#define DMAR_INTR_REMAP 0x1 +#define DMAR_X2APIC_OPT_OUT 0x2 + /* * Intel IOMMU register specification per version 1.0 public spec. */ diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 4b93c31e38..b7f20ed61f 100644 --- a/xen/include/xen/iommu.h +++ b/xen/include/xen/iommu.h @@ -63,8 +63,8 @@ struct iommu { int iommu_setup(void); int iommu_supports_eim(void); -int iommu_enable_IR(void); -void iommu_disable_IR(void); +int iommu_enable_x2apic_IR(void); +void iommu_disable_x2apic_IR(void); int iommu_add_device(struct pci_dev *pdev); int iommu_remove_device(struct pci_dev *pdev);