xref: /openbmc/linux/arch/x86/kernel/cpu/topology.c (revision ad3bc25a)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
22decb194SH. Peter Anvin /*
32decb194SH. Peter Anvin  * Check for extended topology enumeration cpuid leaf 0xb and if it
42decb194SH. Peter Anvin  * exists, use it for populating initial_apicid and cpu topology
52decb194SH. Peter Anvin  * detection.
62decb194SH. Peter Anvin  */
72decb194SH. Peter Anvin 
82decb194SH. Peter Anvin #include <linux/cpu.h>
92decb194SH. Peter Anvin #include <asm/apic.h>
102decb194SH. Peter Anvin #include <asm/pat.h>
112decb194SH. Peter Anvin #include <asm/processor.h>
122decb194SH. Peter Anvin 
13ad3bc25aSBorislav Petkov #include "cpu.h"
14ad3bc25aSBorislav Petkov 
152decb194SH. Peter Anvin /* leaf 0xb SMT level */
162decb194SH. Peter Anvin #define SMT_LEVEL	0
172decb194SH. Peter Anvin 
182decb194SH. Peter Anvin /* leaf 0xb sub-leaf types */
192decb194SH. Peter Anvin #define INVALID_TYPE	0
202decb194SH. Peter Anvin #define SMT_TYPE	1
212decb194SH. Peter Anvin #define CORE_TYPE	2
222decb194SH. Peter Anvin 
232decb194SH. Peter Anvin #define LEAFB_SUBTYPE(ecx)		(((ecx) >> 8) & 0xff)
242decb194SH. Peter Anvin #define BITS_SHIFT_NEXT_LEVEL(eax)	((eax) & 0x1f)
252decb194SH. Peter Anvin #define LEVEL_MAX_SIBLINGS(ebx)		((ebx) & 0xffff)
262decb194SH. Peter Anvin 
2795f3d39cSThomas Gleixner int detect_extended_topology_early(struct cpuinfo_x86 *c)
282decb194SH. Peter Anvin {
292decb194SH. Peter Anvin #ifdef CONFIG_SMP
3095f3d39cSThomas Gleixner 	unsigned int eax, ebx, ecx, edx;
312decb194SH. Peter Anvin 
322decb194SH. Peter Anvin 	if (c->cpuid_level < 0xb)
336c4f5abaSSuravee Suthikulpanit 		return -1;
342decb194SH. Peter Anvin 
352decb194SH. Peter Anvin 	cpuid_count(0xb, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
362decb194SH. Peter Anvin 
372decb194SH. Peter Anvin 	/*
382decb194SH. Peter Anvin 	 * check if the cpuid leaf 0xb is actually implemented.
392decb194SH. Peter Anvin 	 */
402decb194SH. Peter Anvin 	if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE))
416c4f5abaSSuravee Suthikulpanit 		return -1;
422decb194SH. Peter Anvin 
432decb194SH. Peter Anvin 	set_cpu_cap(c, X86_FEATURE_XTOPOLOGY);
442decb194SH. Peter Anvin 
452decb194SH. Peter Anvin 	/*
462decb194SH. Peter Anvin 	 * initial apic id, which also represents 32-bit extended x2apic id.
472decb194SH. Peter Anvin 	 */
482decb194SH. Peter Anvin 	c->initial_apicid = edx;
4995f3d39cSThomas Gleixner 	smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
5095f3d39cSThomas Gleixner #endif
5195f3d39cSThomas Gleixner 	return 0;
5295f3d39cSThomas Gleixner }
5395f3d39cSThomas Gleixner 
5495f3d39cSThomas Gleixner /*
5595f3d39cSThomas Gleixner  * Check for extended topology enumeration cpuid leaf 0xb and if it
5695f3d39cSThomas Gleixner  * exists, use it for populating initial_apicid and cpu topology
5795f3d39cSThomas Gleixner  * detection.
5895f3d39cSThomas Gleixner  */
5995f3d39cSThomas Gleixner int detect_extended_topology(struct cpuinfo_x86 *c)
6095f3d39cSThomas Gleixner {
6195f3d39cSThomas Gleixner #ifdef CONFIG_SMP
6295f3d39cSThomas Gleixner 	unsigned int eax, ebx, ecx, edx, sub_index;
6395f3d39cSThomas Gleixner 	unsigned int ht_mask_width, core_plus_mask_width;
6495f3d39cSThomas Gleixner 	unsigned int core_select_mask, core_level_siblings;
6595f3d39cSThomas Gleixner 
6695f3d39cSThomas Gleixner 	if (detect_extended_topology_early(c) < 0)
6795f3d39cSThomas Gleixner 		return -1;
682decb194SH. Peter Anvin 
692decb194SH. Peter Anvin 	/*
702decb194SH. Peter Anvin 	 * Populate HT related information from sub-leaf level 0.
712decb194SH. Peter Anvin 	 */
7295f3d39cSThomas Gleixner 	cpuid_count(0xb, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
732decb194SH. Peter Anvin 	core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
742decb194SH. Peter Anvin 	core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
752decb194SH. Peter Anvin 
762decb194SH. Peter Anvin 	sub_index = 1;
772decb194SH. Peter Anvin 	do {
782decb194SH. Peter Anvin 		cpuid_count(0xb, sub_index, &eax, &ebx, &ecx, &edx);
792decb194SH. Peter Anvin 
802decb194SH. Peter Anvin 		/*
812decb194SH. Peter Anvin 		 * Check for the Core type in the implemented sub leaves.
822decb194SH. Peter Anvin 		 */
832decb194SH. Peter Anvin 		if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) {
842decb194SH. Peter Anvin 			core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
852decb194SH. Peter Anvin 			core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
862decb194SH. Peter Anvin 			break;
872decb194SH. Peter Anvin 		}
882decb194SH. Peter Anvin 
892decb194SH. Peter Anvin 		sub_index++;
902decb194SH. Peter Anvin 	} while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE);
912decb194SH. Peter Anvin 
922decb194SH. Peter Anvin 	core_select_mask = (~(-1 << core_plus_mask_width)) >> ht_mask_width;
932decb194SH. Peter Anvin 
942decb194SH. Peter Anvin 	c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, ht_mask_width)
952decb194SH. Peter Anvin 						 & core_select_mask;
962decb194SH. Peter Anvin 	c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, core_plus_mask_width);
972decb194SH. Peter Anvin 	/*
982decb194SH. Peter Anvin 	 * Reinit the apicid, now that we have extended initial_apicid.
992decb194SH. Peter Anvin 	 */
1002decb194SH. Peter Anvin 	c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
1012decb194SH. Peter Anvin 
1022decb194SH. Peter Anvin 	c->x86_max_cores = (core_level_siblings / smp_num_siblings);
1032decb194SH. Peter Anvin #endif
1046c4f5abaSSuravee Suthikulpanit 	return 0;
1052decb194SH. Peter Anvin }
106