diff options
Diffstat (limited to 'arch/x86/kernel/cpu/topology.c')
| -rw-r--r-- | arch/x86/kernel/cpu/topology.c | 60 | 
1 files changed, 55 insertions, 5 deletions
diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c index aaca8d235dc2..621a151ccf7d 100644 --- a/arch/x86/kernel/cpu/topology.c +++ b/arch/x86/kernel/cpu/topology.c @@ -123,12 +123,14 @@ static void topo_set_cpuids(unsigned int cpu, u32 apic_id, u32 acpi_id)  	early_per_cpu(x86_cpu_to_apicid, cpu) = apic_id;  	early_per_cpu(x86_cpu_to_acpiid, cpu) = acpi_id;  #endif -	set_cpu_possible(cpu, true);  	set_cpu_present(cpu, true);  }  static __init bool check_for_real_bsp(u32 apic_id)  { +	bool is_bsp = false, has_apic_base = boot_cpu_data.x86 >= 6; +	u64 msr; +  	/*  	 * There is no real good way to detect whether this a kdump()  	 * kernel, but except on the Voyager SMP monstrosity which is not @@ -145,17 +147,61 @@ static __init bool check_for_real_bsp(u32 apic_id)  	if (topo_info.real_bsp_apic_id != BAD_APICID)  		return false; +	/* +	 * Check whether the enumeration order is broken by evaluating the +	 * BSP bit in the APICBASE MSR. If the CPU does not have the +	 * APICBASE MSR then the BSP detection is not possible and the +	 * kernel must rely on the firmware enumeration order. +	 */ +	if (has_apic_base) { +		rdmsrl(MSR_IA32_APICBASE, msr); +		is_bsp = !!(msr & MSR_IA32_APICBASE_BSP); +	} +  	if (apic_id == topo_info.boot_cpu_apic_id) { -		topo_info.real_bsp_apic_id = apic_id; -		return false; +		/* +		 * If the boot CPU has the APIC BSP bit set then the +		 * firmware enumeration is agreeing. If the CPU does not +		 * have the APICBASE MSR then the only choice is to trust +		 * the enumeration order. +		 */ +		if (is_bsp || !has_apic_base) { +			topo_info.real_bsp_apic_id = apic_id; +			return false; +		} +		/* +		 * If the boot APIC is enumerated first, but the APICBASE +		 * MSR does not have the BSP bit set, then there is no way +		 * to discover the real BSP here. Assume a crash kernel and +		 * limit the number of CPUs to 1 as an INIT to the real BSP +		 * would reset the machine. +		 */ +		pr_warn("Enumerated BSP APIC %x is not marked in APICBASE MSR\n", apic_id); +		pr_warn("Assuming crash kernel. Limiting to one CPU to prevent machine INIT\n"); +		set_nr_cpu_ids(1); +		goto fwbug;  	} -	pr_warn("Boot CPU APIC ID not the first enumerated APIC ID: %x > %x\n", +	pr_warn("Boot CPU APIC ID not the first enumerated APIC ID: %x != %x\n",  		topo_info.boot_cpu_apic_id, apic_id); + +	if (is_bsp) { +		/* +		 * The boot CPU has the APIC BSP bit set. Use it and complain +		 * about the broken firmware enumeration. +		 */ +		topo_info.real_bsp_apic_id = topo_info.boot_cpu_apic_id; +		goto fwbug; +	} +  	pr_warn("Crash kernel detected. Disabling real BSP to prevent machine INIT\n");  	topo_info.real_bsp_apic_id = apic_id;  	return true; + +fwbug: +	pr_warn(FW_BUG "APIC enumeration order not specification compliant\n"); +	return false;  }  static unsigned int topo_unit_count(u32 lvlid, enum x86_topology_domains at_level, @@ -210,7 +256,11 @@ static __init void topo_register_apic(u32 apic_id, u32 acpi_id, bool present)  		topo_info.nr_disabled_cpus++;  	} -	/* Register present and possible CPUs in the domain maps */ +	/* +	 * Register present and possible CPUs in the domain +	 * maps. cpu_possible_map will be updated in +	 * topology_init_possible_cpus() after enumeration is done. +	 */  	for (dom = TOPO_SMT_DOMAIN; dom < TOPO_MAX_DOMAIN; dom++)  		set_bit(topo_apicid(apic_id, dom), apic_maps[dom].map);  }  |