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 187745f03eSLen Brown /* extended topology sub-leaf types */ 192decb194SH. Peter Anvin #define INVALID_TYPE 0 202decb194SH. Peter Anvin #define SMT_TYPE 1 212decb194SH. Peter Anvin #define CORE_TYPE 2 227745f03eSLen Brown #define DIE_TYPE 5 232decb194SH. Peter Anvin 242decb194SH. Peter Anvin #define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff) 252decb194SH. Peter Anvin #define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f) 262decb194SH. Peter Anvin #define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff) 272decb194SH. Peter Anvin 287745f03eSLen Brown #ifdef CONFIG_SMP 297745f03eSLen Brown /* 307745f03eSLen Brown * Check if given CPUID extended toplogy "leaf" is implemented 317745f03eSLen Brown */ 327745f03eSLen Brown static int check_extended_topology_leaf(int leaf) 337745f03eSLen Brown { 347745f03eSLen Brown unsigned int eax, ebx, ecx, edx; 357745f03eSLen Brown 367745f03eSLen Brown cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 377745f03eSLen Brown 387745f03eSLen Brown if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE)) 397745f03eSLen Brown return -1; 407745f03eSLen Brown 417745f03eSLen Brown return 0; 427745f03eSLen Brown } 437745f03eSLen Brown /* 447745f03eSLen Brown * Return best CPUID Extended Toplogy Leaf supported 457745f03eSLen Brown */ 467745f03eSLen Brown static int detect_extended_topology_leaf(struct cpuinfo_x86 *c) 477745f03eSLen Brown { 487745f03eSLen Brown if (c->cpuid_level >= 0x1f) { 497745f03eSLen Brown if (check_extended_topology_leaf(0x1f) == 0) 507745f03eSLen Brown return 0x1f; 517745f03eSLen Brown } 527745f03eSLen Brown 537745f03eSLen Brown if (c->cpuid_level >= 0xb) { 547745f03eSLen Brown if (check_extended_topology_leaf(0xb) == 0) 557745f03eSLen Brown return 0xb; 567745f03eSLen Brown } 577745f03eSLen Brown 587745f03eSLen Brown return -1; 597745f03eSLen Brown } 607745f03eSLen Brown #endif 617745f03eSLen Brown 6295f3d39cSThomas Gleixner int detect_extended_topology_early(struct cpuinfo_x86 *c) 632decb194SH. Peter Anvin { 642decb194SH. Peter Anvin #ifdef CONFIG_SMP 6595f3d39cSThomas Gleixner unsigned int eax, ebx, ecx, edx; 667745f03eSLen Brown int leaf; 672decb194SH. Peter Anvin 687745f03eSLen Brown leaf = detect_extended_topology_leaf(c); 697745f03eSLen Brown if (leaf < 0) 706c4f5abaSSuravee Suthikulpanit return -1; 712decb194SH. Peter Anvin 722decb194SH. Peter Anvin set_cpu_cap(c, X86_FEATURE_XTOPOLOGY); 732decb194SH. Peter Anvin 747745f03eSLen Brown cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 752decb194SH. Peter Anvin /* 762decb194SH. Peter Anvin * initial apic id, which also represents 32-bit extended x2apic id. 772decb194SH. Peter Anvin */ 782decb194SH. Peter Anvin c->initial_apicid = edx; 7995f3d39cSThomas Gleixner smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); 8095f3d39cSThomas Gleixner #endif 8195f3d39cSThomas Gleixner return 0; 8295f3d39cSThomas Gleixner } 8395f3d39cSThomas Gleixner 8495f3d39cSThomas Gleixner /* 857745f03eSLen Brown * Check for extended topology enumeration cpuid leaf, and if it 8695f3d39cSThomas Gleixner * exists, use it for populating initial_apicid and cpu topology 8795f3d39cSThomas Gleixner * detection. 8895f3d39cSThomas Gleixner */ 8995f3d39cSThomas Gleixner int detect_extended_topology(struct cpuinfo_x86 *c) 9095f3d39cSThomas Gleixner { 9195f3d39cSThomas Gleixner #ifdef CONFIG_SMP 9295f3d39cSThomas Gleixner unsigned int eax, ebx, ecx, edx, sub_index; 937745f03eSLen Brown unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width; 9495f3d39cSThomas Gleixner unsigned int core_select_mask, core_level_siblings; 957745f03eSLen Brown unsigned int die_select_mask, die_level_siblings; 967745f03eSLen Brown int leaf; 9795f3d39cSThomas Gleixner 987745f03eSLen Brown leaf = detect_extended_topology_leaf(c); 997745f03eSLen Brown if (leaf < 0) 10095f3d39cSThomas Gleixner return -1; 1012decb194SH. Peter Anvin 1022decb194SH. Peter Anvin /* 1032decb194SH. Peter Anvin * Populate HT related information from sub-leaf level 0. 1042decb194SH. Peter Anvin */ 1057745f03eSLen Brown cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 1067745f03eSLen Brown c->initial_apicid = edx; 1072decb194SH. Peter Anvin core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); 1082decb194SH. Peter Anvin core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1097745f03eSLen Brown die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 1107745f03eSLen Brown die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1112decb194SH. Peter Anvin 1122decb194SH. Peter Anvin sub_index = 1; 1132decb194SH. Peter Anvin do { 1147745f03eSLen Brown cpuid_count(leaf, sub_index, &eax, &ebx, &ecx, &edx); 1152decb194SH. Peter Anvin 1162decb194SH. Peter Anvin /* 1172decb194SH. Peter Anvin * Check for the Core type in the implemented sub leaves. 1182decb194SH. Peter Anvin */ 1192decb194SH. Peter Anvin if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) { 1202decb194SH. Peter Anvin core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 1212decb194SH. Peter Anvin core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1227745f03eSLen Brown die_level_siblings = core_level_siblings; 1237745f03eSLen Brown die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1247745f03eSLen Brown } 1257745f03eSLen Brown if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) { 1267745f03eSLen Brown die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 1277745f03eSLen Brown die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1282decb194SH. Peter Anvin } 1292decb194SH. Peter Anvin 1302decb194SH. Peter Anvin sub_index++; 1312decb194SH. Peter Anvin } while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE); 1322decb194SH. Peter Anvin 1332decb194SH. Peter Anvin core_select_mask = (~(-1 << core_plus_mask_width)) >> ht_mask_width; 1347745f03eSLen Brown die_select_mask = (~(-1 << die_plus_mask_width)) >> 1357745f03eSLen Brown core_plus_mask_width; 1362decb194SH. Peter Anvin 1377745f03eSLen Brown c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, 1387745f03eSLen Brown ht_mask_width) & core_select_mask; 1397745f03eSLen Brown c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid, 1407745f03eSLen Brown core_plus_mask_width) & die_select_mask; 1417745f03eSLen Brown c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, 1427745f03eSLen Brown die_plus_mask_width); 1432decb194SH. Peter Anvin /* 1442decb194SH. Peter Anvin * Reinit the apicid, now that we have extended initial_apicid. 1452decb194SH. Peter Anvin */ 1462decb194SH. Peter Anvin c->apicid = apic->phys_pkg_id(c->initial_apicid, 0); 1472decb194SH. Peter Anvin 1482decb194SH. Peter Anvin c->x86_max_cores = (core_level_siblings / smp_num_siblings); 1497745f03eSLen Brown c->x86_max_dies = (die_level_siblings / core_level_siblings); 1502decb194SH. Peter Anvin #endif 1516c4f5abaSSuravee Suthikulpanit return 0; 1522decb194SH. Peter Anvin } 153