162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Check for extended topology enumeration cpuid leaf 0xb and if it 462306a36Sopenharmony_ci * exists, use it for populating initial_apicid and cpu topology 562306a36Sopenharmony_ci * detection. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/cpu.h> 962306a36Sopenharmony_ci#include <asm/apic.h> 1062306a36Sopenharmony_ci#include <asm/memtype.h> 1162306a36Sopenharmony_ci#include <asm/processor.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "cpu.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* leaf 0xb SMT level */ 1662306a36Sopenharmony_ci#define SMT_LEVEL 0 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* extended topology sub-leaf types */ 1962306a36Sopenharmony_ci#define INVALID_TYPE 0 2062306a36Sopenharmony_ci#define SMT_TYPE 1 2162306a36Sopenharmony_ci#define CORE_TYPE 2 2262306a36Sopenharmony_ci#define DIE_TYPE 5 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff) 2562306a36Sopenharmony_ci#define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f) 2662306a36Sopenharmony_ci#define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciunsigned int __max_die_per_package __read_mostly = 1; 2962306a36Sopenharmony_ciEXPORT_SYMBOL(__max_die_per_package); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef CONFIG_SMP 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * Check if given CPUID extended topology "leaf" is implemented 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistatic int check_extended_topology_leaf(int leaf) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned int eax, ebx, ecx, edx; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE)) 4262306a36Sopenharmony_ci return -1; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Return best CPUID Extended Topology Leaf supported 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic int detect_extended_topology_leaf(struct cpuinfo_x86 *c) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci if (c->cpuid_level >= 0x1f) { 5262306a36Sopenharmony_ci if (check_extended_topology_leaf(0x1f) == 0) 5362306a36Sopenharmony_ci return 0x1f; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (c->cpuid_level >= 0xb) { 5762306a36Sopenharmony_ci if (check_extended_topology_leaf(0xb) == 0) 5862306a36Sopenharmony_ci return 0xb; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return -1; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ciint detect_extended_topology_early(struct cpuinfo_x86 *c) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci#ifdef CONFIG_SMP 6862306a36Sopenharmony_ci unsigned int eax, ebx, ecx, edx; 6962306a36Sopenharmony_ci int leaf; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci leaf = detect_extended_topology_leaf(c); 7262306a36Sopenharmony_ci if (leaf < 0) 7362306a36Sopenharmony_ci return -1; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci set_cpu_cap(c, X86_FEATURE_XTOPOLOGY); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * initial apic id, which also represents 32-bit extended x2apic id. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci c->initial_apicid = edx; 8262306a36Sopenharmony_ci smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx)); 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * Check for extended topology enumeration cpuid leaf, and if it 8962306a36Sopenharmony_ci * exists, use it for populating initial_apicid and cpu topology 9062306a36Sopenharmony_ci * detection. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ciint detect_extended_topology(struct cpuinfo_x86 *c) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci#ifdef CONFIG_SMP 9562306a36Sopenharmony_ci unsigned int eax, ebx, ecx, edx, sub_index; 9662306a36Sopenharmony_ci unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width; 9762306a36Sopenharmony_ci unsigned int core_select_mask, core_level_siblings; 9862306a36Sopenharmony_ci unsigned int die_select_mask, die_level_siblings; 9962306a36Sopenharmony_ci unsigned int pkg_mask_width; 10062306a36Sopenharmony_ci bool die_level_present = false; 10162306a36Sopenharmony_ci int leaf; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci leaf = detect_extended_topology_leaf(c); 10462306a36Sopenharmony_ci if (leaf < 0) 10562306a36Sopenharmony_ci return -1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * Populate HT related information from sub-leaf level 0. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 11162306a36Sopenharmony_ci c->initial_apicid = edx; 11262306a36Sopenharmony_ci core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 11362306a36Sopenharmony_ci smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx)); 11462306a36Sopenharmony_ci core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 11562306a36Sopenharmony_ci die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 11662306a36Sopenharmony_ci pkg_mask_width = die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci sub_index = 1; 11962306a36Sopenharmony_ci while (true) { 12062306a36Sopenharmony_ci cpuid_count(leaf, sub_index, &eax, &ebx, &ecx, &edx); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * Check for the Core type in the implemented sub leaves. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) { 12662306a36Sopenharmony_ci core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 12762306a36Sopenharmony_ci core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 12862306a36Sopenharmony_ci die_level_siblings = core_level_siblings; 12962306a36Sopenharmony_ci die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) { 13262306a36Sopenharmony_ci die_level_present = true; 13362306a36Sopenharmony_ci die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 13462306a36Sopenharmony_ci die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (LEAFB_SUBTYPE(ecx) != INVALID_TYPE) 13862306a36Sopenharmony_ci pkg_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci sub_index++; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci core_select_mask = (~(-1 << pkg_mask_width)) >> ht_mask_width; 14662306a36Sopenharmony_ci die_select_mask = (~(-1 << die_plus_mask_width)) >> 14762306a36Sopenharmony_ci core_plus_mask_width; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, 15062306a36Sopenharmony_ci ht_mask_width) & core_select_mask; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (die_level_present) { 15362306a36Sopenharmony_ci c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid, 15462306a36Sopenharmony_ci core_plus_mask_width) & die_select_mask; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, 15862306a36Sopenharmony_ci pkg_mask_width); 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * Reinit the apicid, now that we have extended initial_apicid. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci c->apicid = apic->phys_pkg_id(c->initial_apicid, 0); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci c->x86_max_cores = (core_level_siblings / smp_num_siblings); 16562306a36Sopenharmony_ci __max_die_per_package = (die_level_siblings / core_level_siblings); 16662306a36Sopenharmony_ci#endif 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 169