diff options
Diffstat (limited to 'arch/x86/kernel/apic/apic.c')
| -rw-r--r-- | arch/x86/kernel/apic/apic.c | 113 | 
1 files changed, 84 insertions, 29 deletions
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index f98d84caf94c..2eec05b6d1b8 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -146,16 +146,26 @@ __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 int nox2apic;  static __init int setup_nox2apic(char *str)  {  	if (x2apic_enabled()) { -		pr_warning("Bios already enabled x2apic, " -			   "can't enforce nox2apic"); -		return 0; -	} +		int apicid = native_apic_msr_read(APIC_ID); + +		if (apicid >= 255) { +			pr_warning("Apicid: %08x, cannot enforce nox2apic\n", +				   apicid); +			return 0; +		} + +		pr_warning("x2apic already enabled. will disable it\n"); +	} else +		setup_clear_cpu_cap(X86_FEATURE_X2APIC); + +	nox2apic = 1; -	setup_clear_cpu_cap(X86_FEATURE_X2APIC);  	return 0;  }  early_param("nox2apic", setup_nox2apic); @@ -250,6 +260,7 @@ u32 native_safe_apic_wait_icr_idle(void)  		send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;  		if (!send_status)  			break; +		inc_irq_stat(icr_read_retry_count);  		udelay(100);  	} while (timeout++ < 1000); @@ -876,8 +887,8 @@ void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)  	 * Besides, if we don't timer interrupts ignore the global  	 * interrupt lock, which is the WrongThing (tm) to do.  	 */ -	exit_idle();  	irq_enter(); +	exit_idle();  	local_apic_timer_interrupt();  	irq_exit(); @@ -1431,6 +1442,45 @@ 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 __init 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); + +		if (nox2apic) { +			clear_cpu_cap(&cpu_data(0), X86_FEATURE_X2APIC); +			setup_clear_cpu_cap(X86_FEATURE_X2APIC); +		} + +		x2apic_disabled = 1; +		x2apic_mode = 0; + +		register_lapic_address(mp_lapic_addr); +	} +} +  void check_x2apic(void)  {  	if (x2apic_enabled()) { @@ -1441,15 +1491,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 */ @@ -1486,25 +1541,34 @@ 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);  	legacy_pic->mask_all();  	mask_ioapic_entries(); +	if (x2apic_preenabled && nox2apic) +		disable_x2apic(); +  	if (dmar_table_init_ret)  		ret = -1;  	else  		ret = enable_IR(); +	if (!x2apic_supported()) +		goto skip_x2apic; +  	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()) -			goto nox2apic; +		    !hypervisor_x2apic_available()) { +			if (x2apic_preenabled) +				disable_x2apic(); +			goto skip_x2apic; +		}  		/*  		 * without IR all CPUs can be addressed by IOAPIC/MSI  		 * only in physical mode @@ -1512,8 +1576,10 @@ void __init enable_IR_x2apic(void)  		x2apic_force_phys();  	} -	if (ret == IRQ_REMAP_XAPIC_MODE) -		goto nox2apic; +	if (ret == IRQ_REMAP_XAPIC_MODE) { +		pr_info("x2apic not enabled, IRQ remapping is in xapic mode\n"); +		goto skip_x2apic; +	}  	x2apic_enabled = 1; @@ -1523,22 +1589,11 @@ void __init enable_IR_x2apic(void)  		pr_info("Enabled x2apic\n");  	} -nox2apic: +skip_x2apic:  	if (ret < 0) /* IR enabling failed */  		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 @@ -1809,8 +1864,8 @@ void smp_spurious_interrupt(struct pt_regs *regs)  {  	u32 v; -	exit_idle();  	irq_enter(); +	exit_idle();  	/*  	 * Check if this really is a spurious interrupt and ACK it  	 * if it is a vectored one.  Just in case... @@ -1846,8 +1901,8 @@ void smp_error_interrupt(struct pt_regs *regs)  		"Illegal register address",	/* APIC Error Bit 7 */  	}; -	exit_idle();  	irq_enter(); +	exit_idle();  	/* First tickle the hardware, only then report what went on. -- REW */  	v0 = apic_read(APIC_ESR);  	apic_write(APIC_ESR, 0);  |