diff options
Diffstat (limited to 'arch/x86/kernel/apic/apic.c')
| -rw-r--r-- | arch/x86/kernel/apic/apic.c | 44 | 
1 files changed, 40 insertions, 4 deletions
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 6d303d1d276c..c6876d3ea4b1 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -61,6 +61,7 @@  #include <asm/cpu_device_id.h>  #include <asm/intel-family.h>  #include <asm/irq_regs.h> +#include <asm/cpu.h>  unsigned int num_processors; @@ -1751,11 +1752,26 @@ EXPORT_SYMBOL_GPL(x2apic_mode);  enum {  	X2APIC_OFF, -	X2APIC_ON,  	X2APIC_DISABLED, +	/* All states below here have X2APIC enabled */ +	X2APIC_ON, +	X2APIC_ON_LOCKED  };  static int x2apic_state; +static bool x2apic_hw_locked(void) +{ +	u64 ia32_cap; +	u64 msr; + +	ia32_cap = x86_read_arch_cap_msr(); +	if (ia32_cap & ARCH_CAP_XAPIC_DISABLE) { +		rdmsrl(MSR_IA32_XAPIC_DISABLE_STATUS, msr); +		return (msr & LEGACY_XAPIC_DISABLED); +	} +	return false; +} +  static void __x2apic_disable(void)  {  	u64 msr; @@ -1793,6 +1809,10 @@ static int __init setup_nox2apic(char *str)  				apicid);  			return 0;  		} +		if (x2apic_hw_locked()) { +			pr_warn("APIC locked in x2apic mode, can't disable\n"); +			return 0; +		}  		pr_warn("x2apic already enabled.\n");  		__x2apic_disable();  	} @@ -1807,10 +1827,18 @@ early_param("nox2apic", setup_nox2apic);  void x2apic_setup(void)  {  	/* -	 * If x2apic is not in ON state, disable it if already enabled +	 * Try to make the AP's APIC state match that of the BSP,  but if the +	 * BSP is unlocked and the AP is locked then there is a state mismatch. +	 * Warn about the mismatch in case a GP fault occurs due to a locked AP +	 * trying to be turned off. +	 */ +	if (x2apic_state != X2APIC_ON_LOCKED && x2apic_hw_locked()) +		pr_warn("x2apic lock mismatch between BSP and AP.\n"); +	/* +	 * If x2apic is not in ON or LOCKED state, disable it if already enabled  	 * from BIOS.  	 */ -	if (x2apic_state != X2APIC_ON) { +	if (x2apic_state < X2APIC_ON) {  		__x2apic_disable();  		return;  	} @@ -1831,6 +1859,11 @@ static __init void x2apic_disable(void)  	if (x2apic_id >= 255)  		panic("Cannot disable x2apic, id: %08x\n", x2apic_id); +	if (x2apic_hw_locked()) { +		pr_warn("Cannot disable locked x2apic, id: %08x\n", x2apic_id); +		return; +	} +  	__x2apic_disable();  	register_lapic_address(mp_lapic_addr);  } @@ -1889,7 +1922,10 @@ void __init check_x2apic(void)  	if (x2apic_enabled()) {  		pr_info("x2apic: enabled by BIOS, switching to x2apic ops\n");  		x2apic_mode = 1; -		x2apic_state = X2APIC_ON; +		if (x2apic_hw_locked()) +			x2apic_state = X2APIC_ON_LOCKED; +		else +			x2apic_state = X2APIC_ON;  	} else if (!boot_cpu_has(X86_FEATURE_X2APIC)) {  		x2apic_state = X2APIC_DISABLED;  	}  |