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> 10eb243d1dSIngo Molnar #include <asm/memtype.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 2914d96d6cSLen Brown unsigned int __max_die_per_package __read_mostly = 1; 3014d96d6cSLen Brown EXPORT_SYMBOL(__max_die_per_package); 3114d96d6cSLen Brown 327745f03eSLen Brown /* 337745f03eSLen Brown * Check if given CPUID extended toplogy "leaf" is implemented 347745f03eSLen Brown */ 357745f03eSLen Brown static int check_extended_topology_leaf(int leaf) 367745f03eSLen Brown { 377745f03eSLen Brown unsigned int eax, ebx, ecx, edx; 387745f03eSLen Brown 397745f03eSLen Brown cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 407745f03eSLen Brown 417745f03eSLen Brown if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE)) 427745f03eSLen Brown return -1; 437745f03eSLen Brown 447745f03eSLen Brown return 0; 457745f03eSLen Brown } 467745f03eSLen Brown /* 477745f03eSLen Brown * Return best CPUID Extended Toplogy Leaf supported 487745f03eSLen Brown */ 497745f03eSLen Brown static int detect_extended_topology_leaf(struct cpuinfo_x86 *c) 507745f03eSLen Brown { 517745f03eSLen Brown if (c->cpuid_level >= 0x1f) { 527745f03eSLen Brown if (check_extended_topology_leaf(0x1f) == 0) 537745f03eSLen Brown return 0x1f; 547745f03eSLen Brown } 557745f03eSLen Brown 567745f03eSLen Brown if (c->cpuid_level >= 0xb) { 577745f03eSLen Brown if (check_extended_topology_leaf(0xb) == 0) 587745f03eSLen Brown return 0xb; 597745f03eSLen Brown } 607745f03eSLen Brown 617745f03eSLen Brown return -1; 627745f03eSLen Brown } 637745f03eSLen Brown #endif 647745f03eSLen Brown 6595f3d39cSThomas Gleixner int detect_extended_topology_early(struct cpuinfo_x86 *c) 662decb194SH. Peter Anvin { 672decb194SH. Peter Anvin #ifdef CONFIG_SMP 6895f3d39cSThomas Gleixner unsigned int eax, ebx, ecx, edx; 697745f03eSLen Brown int leaf; 702decb194SH. Peter Anvin 717745f03eSLen Brown leaf = detect_extended_topology_leaf(c); 727745f03eSLen Brown if (leaf < 0) 736c4f5abaSSuravee Suthikulpanit return -1; 742decb194SH. Peter Anvin 752decb194SH. Peter Anvin set_cpu_cap(c, X86_FEATURE_XTOPOLOGY); 762decb194SH. Peter Anvin 777745f03eSLen Brown cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 782decb194SH. Peter Anvin /* 792decb194SH. Peter Anvin * initial apic id, which also represents 32-bit extended x2apic id. 802decb194SH. Peter Anvin */ 812decb194SH. Peter Anvin c->initial_apicid = edx; 8295f3d39cSThomas Gleixner smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); 8395f3d39cSThomas Gleixner #endif 8495f3d39cSThomas Gleixner return 0; 8595f3d39cSThomas Gleixner } 8695f3d39cSThomas Gleixner 8795f3d39cSThomas Gleixner /* 887745f03eSLen Brown * Check for extended topology enumeration cpuid leaf, and if it 8995f3d39cSThomas Gleixner * exists, use it for populating initial_apicid and cpu topology 9095f3d39cSThomas Gleixner * detection. 9195f3d39cSThomas Gleixner */ 9295f3d39cSThomas Gleixner int detect_extended_topology(struct cpuinfo_x86 *c) 9395f3d39cSThomas Gleixner { 9495f3d39cSThomas Gleixner #ifdef CONFIG_SMP 9595f3d39cSThomas Gleixner unsigned int eax, ebx, ecx, edx, sub_index; 967745f03eSLen Brown unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width; 9795f3d39cSThomas Gleixner unsigned int core_select_mask, core_level_siblings; 987745f03eSLen Brown unsigned int die_select_mask, die_level_siblings; 99*cb09a379SYazen Ghannam bool die_level_present = false; 1007745f03eSLen Brown int leaf; 10195f3d39cSThomas Gleixner 1027745f03eSLen Brown leaf = detect_extended_topology_leaf(c); 1037745f03eSLen Brown if (leaf < 0) 10495f3d39cSThomas Gleixner return -1; 1052decb194SH. Peter Anvin 1062decb194SH. Peter Anvin /* 1072decb194SH. Peter Anvin * Populate HT related information from sub-leaf level 0. 1082decb194SH. Peter Anvin */ 1097745f03eSLen Brown cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); 1107745f03eSLen Brown c->initial_apicid = edx; 1112decb194SH. Peter Anvin core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); 1122decb194SH. Peter Anvin core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1137745f03eSLen Brown die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 1147745f03eSLen Brown die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1152decb194SH. Peter Anvin 1162decb194SH. Peter Anvin sub_index = 1; 1172decb194SH. Peter Anvin do { 1187745f03eSLen Brown cpuid_count(leaf, sub_index, &eax, &ebx, &ecx, &edx); 1192decb194SH. Peter Anvin 1202decb194SH. Peter Anvin /* 1212decb194SH. Peter Anvin * Check for the Core type in the implemented sub leaves. 1222decb194SH. Peter Anvin */ 1232decb194SH. Peter Anvin if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) { 1242decb194SH. Peter Anvin core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 1252decb194SH. Peter Anvin core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1267745f03eSLen Brown die_level_siblings = core_level_siblings; 1277745f03eSLen Brown die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1287745f03eSLen Brown } 1297745f03eSLen Brown if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) { 130*cb09a379SYazen Ghannam die_level_present = true; 1317745f03eSLen Brown die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); 1327745f03eSLen Brown die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); 1332decb194SH. Peter Anvin } 1342decb194SH. Peter Anvin 1352decb194SH. Peter Anvin sub_index++; 1362decb194SH. Peter Anvin } while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE); 1372decb194SH. Peter Anvin 1382decb194SH. Peter Anvin core_select_mask = (~(-1 << core_plus_mask_width)) >> ht_mask_width; 1397745f03eSLen Brown die_select_mask = (~(-1 << die_plus_mask_width)) >> 1407745f03eSLen Brown core_plus_mask_width; 1412decb194SH. Peter Anvin 1427745f03eSLen Brown c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, 1437745f03eSLen Brown ht_mask_width) & core_select_mask; 144*cb09a379SYazen Ghannam 145*cb09a379SYazen Ghannam if (die_level_present) { 1467745f03eSLen Brown c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid, 1477745f03eSLen Brown core_plus_mask_width) & die_select_mask; 148*cb09a379SYazen Ghannam } 149*cb09a379SYazen Ghannam 1507745f03eSLen Brown c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, 1517745f03eSLen Brown die_plus_mask_width); 1522decb194SH. Peter Anvin /* 1532decb194SH. Peter Anvin * Reinit the apicid, now that we have extended initial_apicid. 1542decb194SH. Peter Anvin */ 1552decb194SH. Peter Anvin c->apicid = apic->phys_pkg_id(c->initial_apicid, 0); 1562decb194SH. Peter Anvin 1572decb194SH. Peter Anvin c->x86_max_cores = (core_level_siblings / smp_num_siblings); 15814d96d6cSLen Brown __max_die_per_package = (die_level_siblings / core_level_siblings); 1592decb194SH. Peter Anvin #endif 1606c4f5abaSSuravee Suthikulpanit return 0; 1612decb194SH. Peter Anvin } 162