Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb209bd8 authored by Yinghai Lu's avatar Yinghai Lu Committed by H. Peter Anvin
Browse files

x86, x2apic: Fallback to xapic when BIOS doesn't setup interrupt-remapping



On some of the recent Intel SNB platforms, by default bios is pre-enabling
x2apic mode in the cpu with out setting up interrupt-remapping.
This case was resulting in the kernel to panic as the cpu is already in
x2apic mode but the OS was not able to enable interrupt-remapping (which
is a pre-req for using x2apic capability).

On these platforms all the apic-ids are < 255 and the kernel can fallback to
xapic mode if the bios has not enabled interrupt-remapping (which is
mostly the case if the bios has not exported interrupt-remapping tables to the
OS).

Reported-by: default avatarBerck E. Nash <flyboy@gmail.com>
Signed-off-by: default avatarYinghai Lu <yinghai@kernel.org>
Link: http://lkml.kernel.org/r/20111222014632.600418637@sbsiddha-desk.sc.intel.com


Signed-off-by: default avatarSuresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
parent a35fd282
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -176,6 +176,7 @@ static inline u64 native_x2apic_icr_read(void)
}

extern int x2apic_phys;
extern int x2apic_preenabled;
extern void check_x2apic(void);
extern void enable_x2apic(void);
extern void x2apic_icr_write(u32 low, u32 id);
@@ -198,6 +199,9 @@ static inline void x2apic_force_phys(void)
	x2apic_phys = 1;
}
#else
static inline void disable_x2apic(void)
{
}
static inline void check_x2apic(void)
{
}
+1 −0
Original line number Diff line number Diff line
@@ -144,6 +144,7 @@

#define APIC_BASE (fix_to_virt(FIX_APIC_BASE))
#define APIC_BASE_MSR	0x800
#define XAPIC_ENABLE	(1UL << 11)
#define X2APIC_ENABLE	(1UL << 10)

#ifdef CONFIG_X86_32
+55 −18
Original line number Diff line number Diff line
@@ -146,7 +146,8 @@ __setup("apicpmtimer", setup_apicpmtimer);
int x2apic_mode;
#ifdef CONFIG_X86_X2APIC
/* x2apic enabled before OS handover */
static int x2apic_preenabled;
int x2apic_preenabled;
static int x2apic_disabled;
static __init int setup_nox2apic(char *str)
{
	if (x2apic_enabled()) {
@@ -1432,6 +1433,40 @@ void __init bsp_end_local_APIC_setup(void)
}

#ifdef CONFIG_X86_X2APIC
/*
 * Need to disable xapic and x2apic at the same time and then enable xapic mode
 */
static inline void __disable_x2apic(u64 msr)
{
	wrmsrl(MSR_IA32_APICBASE,
	       msr & ~(X2APIC_ENABLE | XAPIC_ENABLE));
	wrmsrl(MSR_IA32_APICBASE, msr & ~X2APIC_ENABLE);
}

static void disable_x2apic(void)
{
	u64 msr;

	if (!cpu_has_x2apic)
		return;

	rdmsrl(MSR_IA32_APICBASE, msr);
	if (msr & X2APIC_ENABLE) {
		u32 x2apic_id = read_apic_id();

		if (x2apic_id >= 255)
			panic("Cannot disable x2apic, id: %08x\n", x2apic_id);

		pr_info("Disabling x2apic\n");
		__disable_x2apic(msr);

		x2apic_disabled = 1;
		x2apic_mode = 0;

		register_lapic_address(mp_lapic_addr);
	}
}

void check_x2apic(void)
{
	if (x2apic_enabled()) {
@@ -1442,15 +1477,20 @@ void check_x2apic(void)

void enable_x2apic(void)
{
	int msr, msr2;
	u64 msr;

	rdmsrl(MSR_IA32_APICBASE, msr);
	if (x2apic_disabled) {
		__disable_x2apic(msr);
		return;
	}

	if (!x2apic_mode)
		return;

	rdmsr(MSR_IA32_APICBASE, msr, msr2);
	if (!(msr & X2APIC_ENABLE)) {
		printk_once(KERN_INFO "Enabling x2apic\n");
		wrmsr(MSR_IA32_APICBASE, msr | X2APIC_ENABLE, msr2);
		wrmsrl(MSR_IA32_APICBASE, msr | X2APIC_ENABLE);
	}
}
#endif /* CONFIG_X86_X2APIC */
@@ -1487,7 +1527,7 @@ void __init enable_IR_x2apic(void)
	ret = save_ioapic_entries();
	if (ret) {
		pr_info("Saving IO-APIC state failed: %d\n", ret);
		goto out;
		return;
	}

	local_irq_save(flags);
@@ -1499,13 +1539,19 @@ void __init enable_IR_x2apic(void)
	else
		ret = enable_IR();

	if (!x2apic_supported())
		goto nox2apic;

	if (ret < 0) {
		/* IR is required if there is APIC ID > 255 even when running
		 * under KVM
		 */
		if (max_physical_apicid > 255 ||
		    !hypervisor_x2apic_available())
		    !hypervisor_x2apic_available()) {
			if (x2apic_preenabled)
				disable_x2apic();
			goto nox2apic;
		}
		/*
		 * without IR all CPUs can be addressed by IOAPIC/MSI
		 * only in physical mode
@@ -1513,8 +1559,10 @@ void __init enable_IR_x2apic(void)
		x2apic_force_phys();
	}

	if (ret == IRQ_REMAP_XAPIC_MODE)
	if (ret == IRQ_REMAP_XAPIC_MODE) {
		pr_info("x2apic not enabled, IRQ remapping is in xapic mode\n");
		goto nox2apic;
	}

	x2apic_enabled = 1;

@@ -1529,17 +1577,6 @@ nox2apic:
		restore_ioapic_entries();
	legacy_pic->restore_mask();
	local_irq_restore(flags);

out:
	if (x2apic_enabled || !x2apic_supported())
		return;

	if (x2apic_preenabled)
		panic("x2apic: enabled by BIOS but kernel init failed.");
	else if (ret == IRQ_REMAP_XAPIC_MODE)
		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
+4 −0
Original line number Diff line number Diff line
@@ -2948,6 +2948,10 @@ static inline void __init check_timer(void)
	}
	local_irq_disable();
	apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n");
	if (x2apic_preenabled)
		apic_printk(APIC_QUIET, KERN_INFO
			    "Perhaps problem with the pre-enabled x2apic mode\n"
			    "Try booting with x2apic and interrupt-remapping disabled in the bios.\n");
	panic("IO-APIC + timer doesn't work!  Boot with apic=debug and send a "
		"report.  Then try booting with the 'noapic' option.\n");
out: