x86, x2apic: Enable the bios request for x2apic optout

On the platforms which are x2apic and interrupt-remapping
capable, Linux kernel is enabling x2apic even if the BIOS
doesn't. This is to take advantage of the features that x2apic
brings in.

Some of the OEM platforms are running into issues because of
this, as their bios is not x2apic aware. For example, this was
resulting in interrupt migration issues on one of the platforms.
Also if the BIOS SMI handling uses APIC interface to send SMI's,
then the BIOS need to be aware of x2apic mode that OS has
enabled.

On some of these platforms, BIOS doesn't have a HW mechanism to
turnoff the x2apic feature to prevent OS from enabling it.

To resolve this mess, recent changes to the VT-d2 specification:

 http://download.intel.com/technology/computing/vptech/Intel(r)_VT_for_Direct_IO.pdf

includes a mechanism that provides BIOS a way to request system
software to opt out of enabling x2apic mode.

Look at the x2apic optout flag in the DMAR tables before
enabling the x2apic mode in the platform. Also print a warning
that we have disabled x2apic based on the BIOS request.

Kernel boot parameter "intremap=no_x2apic_optout" can be used to
override the BIOS x2apic optout request.

Signed-off-by: Youquan Song <youquan.song@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: yinghai@kernel.org
Cc: joerg.roedel@amd.com
Cc: tony.luck@intel.com
Cc: dwmw2@infradead.org
Link: http://lkml.kernel.org/r/20110824001456.171766616@sbsiddha-desk.sc.intel.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Suresh Siddha 2011-08-23 17:05:18 -07:00 committed by Ingo Molnar
parent 9d037a7776
commit 41750d31fc
5 changed files with 66 additions and 28 deletions

View File

@ -1014,10 +1014,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
has the capability. With this option, super page will has the capability. With this option, super page will
not be supported. not be supported.
intremap= [X86-64, Intel-IOMMU] intremap= [X86-64, Intel-IOMMU]
Format: { on (default) | off | nosid }
on enable Interrupt Remapping (default) on enable Interrupt Remapping (default)
off disable Interrupt Remapping off disable Interrupt Remapping
nosid disable Source ID checking nosid disable Source ID checking
no_x2apic_optout
BIOS x2APIC opt-out request will be ignored
inttest= [IA-64] inttest= [IA-64]

View File

@ -1440,24 +1440,18 @@ int __init enable_IR(void)
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
if (!intr_remapping_supported()) { if (!intr_remapping_supported()) {
pr_debug("intr-remapping not supported\n"); pr_debug("intr-remapping not supported\n");
return 0; return -1;
} }
if (!x2apic_preenabled && skip_ioapic_setup) { if (!x2apic_preenabled && skip_ioapic_setup) {
pr_info("Skipped enabling intr-remap because of skipping " pr_info("Skipped enabling intr-remap because of skipping "
"io-apic setup\n"); "io-apic setup\n");
return 0; return -1;
} }
if (enable_intr_remapping(x2apic_supported())) return enable_intr_remapping();
return 0;
pr_info("Enabled Interrupt-remapping\n");
return 1;
#endif #endif
return 0; return -1;
} }
void __init enable_IR_x2apic(void) void __init enable_IR_x2apic(void)
@ -1481,11 +1475,11 @@ void __init enable_IR_x2apic(void)
mask_ioapic_entries(); mask_ioapic_entries();
if (dmar_table_init_ret) if (dmar_table_init_ret)
ret = 0; ret = -1;
else else
ret = enable_IR(); ret = enable_IR();
if (!ret) { if (ret < 0) {
/* IR is required if there is APIC ID > 255 even when running /* IR is required if there is APIC ID > 255 even when running
* under KVM * under KVM
*/ */
@ -1499,6 +1493,9 @@ void __init enable_IR_x2apic(void)
x2apic_force_phys(); x2apic_force_phys();
} }
if (ret == IRQ_REMAP_XAPIC_MODE)
goto nox2apic;
x2apic_enabled = 1; x2apic_enabled = 1;
if (x2apic_supported() && !x2apic_mode) { if (x2apic_supported() && !x2apic_mode) {
@ -1508,19 +1505,21 @@ void __init enable_IR_x2apic(void)
} }
nox2apic: nox2apic:
if (!ret) /* IR enabling failed */ if (ret < 0) /* IR enabling failed */
restore_ioapic_entries(); restore_ioapic_entries();
legacy_pic->restore_mask(); legacy_pic->restore_mask();
local_irq_restore(flags); local_irq_restore(flags);
out: out:
if (x2apic_enabled) if (x2apic_enabled || !x2apic_supported())
return; return;
if (x2apic_preenabled) if (x2apic_preenabled)
panic("x2apic: enabled by BIOS but kernel init failed."); panic("x2apic: enabled by BIOS but kernel init failed.");
else if (cpu_has_x2apic) else if (ret == IRQ_REMAP_XAPIC_MODE)
pr_info("Not enabling x2apic, Intr-remapping init failed.\n"); pr_info("x2apic not enabled, IRQ remapping is in xapic mode\n");
else if (ret < 0)
pr_info("x2apic not enabled, IRQ remapping init failed\n");
} }
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64

View File

@ -46,7 +46,7 @@
*/ */
LIST_HEAD(dmar_drhd_units); LIST_HEAD(dmar_drhd_units);
static struct acpi_table_header * __initdata dmar_tbl; struct acpi_table_header * __initdata dmar_tbl;
static acpi_size dmar_tbl_size; static acpi_size dmar_tbl_size;
static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)

View File

@ -21,6 +21,7 @@ int intr_remapping_enabled;
static int disable_intremap; static int disable_intremap;
static int disable_sourceid_checking; static int disable_sourceid_checking;
static int no_x2apic_optout;
static __init int setup_nointremap(char *str) static __init int setup_nointremap(char *str)
{ {
@ -34,12 +35,20 @@ static __init int setup_intremap(char *str)
if (!str) if (!str)
return -EINVAL; return -EINVAL;
if (!strncmp(str, "on", 2)) while (*str) {
disable_intremap = 0; if (!strncmp(str, "on", 2))
else if (!strncmp(str, "off", 3)) disable_intremap = 0;
disable_intremap = 1; else if (!strncmp(str, "off", 3))
else if (!strncmp(str, "nosid", 5)) disable_intremap = 1;
disable_sourceid_checking = 1; else if (!strncmp(str, "nosid", 5))
disable_sourceid_checking = 1;
else if (!strncmp(str, "no_x2apic_optout", 16))
no_x2apic_optout = 1;
str += strcspn(str, ",");
while (*str == ',')
str++;
}
return 0; return 0;
} }
@ -501,6 +510,15 @@ end:
spin_unlock_irqrestore(&iommu->register_lock, flags); spin_unlock_irqrestore(&iommu->register_lock, flags);
} }
static int __init dmar_x2apic_optout(void)
{
struct acpi_table_dmar *dmar;
dmar = (struct acpi_table_dmar *)dmar_tbl;
if (!dmar || no_x2apic_optout)
return 0;
return dmar->flags & DMAR_X2APIC_OPT_OUT;
}
int __init intr_remapping_supported(void) int __init intr_remapping_supported(void)
{ {
struct dmar_drhd_unit *drhd; struct dmar_drhd_unit *drhd;
@ -521,16 +539,25 @@ int __init intr_remapping_supported(void)
return 1; return 1;
} }
int __init enable_intr_remapping(int eim) int __init enable_intr_remapping(void)
{ {
struct dmar_drhd_unit *drhd; struct dmar_drhd_unit *drhd;
int setup = 0; int setup = 0;
int eim = 0;
if (parse_ioapics_under_ir() != 1) { if (parse_ioapics_under_ir() != 1) {
printk(KERN_INFO "Not enable interrupt remapping\n"); printk(KERN_INFO "Not enable interrupt remapping\n");
return -1; return -1;
} }
if (x2apic_supported()) {
eim = !dmar_x2apic_optout();
WARN(!eim, KERN_WARNING
"Your BIOS is broken and requested that x2apic be disabled\n"
"This will leave your machine vulnerable to irq-injection attacks\n"
"Use 'intremap=no_x2apic_optout' to override BIOS request\n");
}
for_each_drhd_unit(drhd) { for_each_drhd_unit(drhd) {
struct intel_iommu *iommu = drhd->iommu; struct intel_iommu *iommu = drhd->iommu;
@ -606,8 +633,9 @@ int __init enable_intr_remapping(int eim)
goto error; goto error;
intr_remapping_enabled = 1; intr_remapping_enabled = 1;
pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");
return 0; return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
error: error:
/* /*

View File

@ -26,8 +26,13 @@
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
/* DMAR Flags */
#define DMAR_INTR_REMAP 0x1
#define DMAR_X2APIC_OPT_OUT 0x2
struct intel_iommu; struct intel_iommu;
#if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP) #if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP)
extern struct acpi_table_header *dmar_tbl;
struct dmar_drhd_unit { struct dmar_drhd_unit {
struct list_head list; /* list of drhd units */ struct list_head list; /* list of drhd units */
struct acpi_dmar_header *hdr; /* ACPI header */ struct acpi_dmar_header *hdr; /* ACPI header */
@ -110,7 +115,7 @@ struct irte {
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
extern int intr_remapping_enabled; extern int intr_remapping_enabled;
extern int intr_remapping_supported(void); extern int intr_remapping_supported(void);
extern int enable_intr_remapping(int); extern int enable_intr_remapping(void);
extern void disable_intr_remapping(void); extern void disable_intr_remapping(void);
extern int reenable_intr_remapping(int); extern int reenable_intr_remapping(int);
@ -177,7 +182,7 @@ static inline int set_msi_sid(struct irte *irte, struct pci_dev *dev)
#define intr_remapping_enabled (0) #define intr_remapping_enabled (0)
static inline int enable_intr_remapping(int eim) static inline int enable_intr_remapping(void)
{ {
return -1; return -1;
} }
@ -192,6 +197,11 @@ static inline int reenable_intr_remapping(int eim)
} }
#endif #endif
enum {
IRQ_REMAP_XAPIC_MODE,
IRQ_REMAP_X2APIC_MODE,
};
/* Can't use the common MSI interrupt functions /* Can't use the common MSI interrupt functions
* since DMAR is not a pci device * since DMAR is not a pci device
*/ */