1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/cpu.h> 3 4 #include <asm/apic.h> 5 #include <asm/memtype.h> 6 #include <asm/processor.h> 7 8 #include "cpu.h" 9 10 enum topo_types { 11 INVALID_TYPE = 0, 12 SMT_TYPE = 1, 13 CORE_TYPE = 2, 14 MAX_TYPE_0B = 3, 15 MODULE_TYPE = 3, 16 AMD_CCD_TYPE = 3, 17 TILE_TYPE = 4, 18 AMD_SOCKET_TYPE = 4, 19 MAX_TYPE_80000026 = 5, 20 DIE_TYPE = 5, 21 DIEGRP_TYPE = 6, 22 MAX_TYPE_1F = 7, 23 }; 24 25 /* 26 * Use a lookup table for the case that there are future types > 6 which 27 * describe an intermediate domain level which does not exist today. 28 */ 29 static const unsigned int topo_domain_map_0b_1f[MAX_TYPE_1F] = { 30 [SMT_TYPE] = TOPO_SMT_DOMAIN, 31 [CORE_TYPE] = TOPO_CORE_DOMAIN, 32 [MODULE_TYPE] = TOPO_MODULE_DOMAIN, 33 [TILE_TYPE] = TOPO_TILE_DOMAIN, 34 [DIE_TYPE] = TOPO_DIE_DOMAIN, 35 [DIEGRP_TYPE] = TOPO_DIEGRP_DOMAIN, 36 }; 37 38 static const unsigned int topo_domain_map_80000026[MAX_TYPE_80000026] = { 39 [SMT_TYPE] = TOPO_SMT_DOMAIN, 40 [CORE_TYPE] = TOPO_CORE_DOMAIN, 41 [AMD_CCD_TYPE] = TOPO_TILE_DOMAIN, 42 [AMD_SOCKET_TYPE] = TOPO_DIE_DOMAIN, 43 }; 44 45 static inline bool topo_subleaf(struct topo_scan *tscan, u32 leaf, u32 subleaf, 46 unsigned int *last_dom) 47 { 48 unsigned int dom, maxtype; 49 const unsigned int *map; 50 struct { 51 // eax 52 u32 x2apic_shift : 5, // Number of bits to shift APIC ID right 53 // for the topology ID at the next level 54 : 27; // Reserved 55 // ebx 56 u32 num_processors : 16, // Number of processors at current level 57 : 16; // Reserved 58 // ecx 59 u32 level : 8, // Current topology level. Same as sub leaf number 60 type : 8, // Level type. If 0, invalid 61 : 16; // Reserved 62 // edx 63 u32 x2apic_id : 32; // X2APIC ID of the current logical processor 64 } sl; 65 66 switch (leaf) { 67 case 0x0b: maxtype = MAX_TYPE_0B; map = topo_domain_map_0b_1f; break; 68 case 0x1f: maxtype = MAX_TYPE_1F; map = topo_domain_map_0b_1f; break; 69 case 0x80000026: maxtype = MAX_TYPE_80000026; map = topo_domain_map_80000026; break; 70 default: return false; 71 } 72 73 cpuid_subleaf(leaf, subleaf, &sl); 74 75 if (!sl.num_processors || sl.type == INVALID_TYPE) 76 return false; 77 78 if (sl.type >= maxtype) { 79 pr_err_once("Topology: leaf 0x%x:%d Unknown domain type %u\n", 80 leaf, subleaf, sl.type); 81 /* 82 * It really would have been too obvious to make the domain 83 * type space sparse and leave a few reserved types between 84 * the points which might change instead of following the 85 * usual "this can be fixed in software" principle. 86 */ 87 dom = *last_dom + 1; 88 } else { 89 dom = map[sl.type]; 90 *last_dom = dom; 91 } 92 93 if (!dom) { 94 tscan->c->topo.initial_apicid = sl.x2apic_id; 95 } else if (tscan->c->topo.initial_apicid != sl.x2apic_id) { 96 pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf %d APIC ID mismatch %x != %x\n", 97 leaf, subleaf, tscan->c->topo.initial_apicid, sl.x2apic_id); 98 } 99 100 topology_set_dom(tscan, dom, sl.x2apic_shift, sl.num_processors); 101 return true; 102 } 103 104 static bool parse_topology_leaf(struct topo_scan *tscan, u32 leaf) 105 { 106 unsigned int last_dom; 107 u32 subleaf; 108 109 /* Read all available subleafs and populate the levels */ 110 for (subleaf = 0, last_dom = 0; topo_subleaf(tscan, leaf, subleaf, &last_dom); subleaf++); 111 112 /* If subleaf 0 failed to parse, give up */ 113 if (!subleaf) 114 return false; 115 116 /* 117 * There are machines in the wild which have shift 0 in the subleaf 118 * 0, but advertise 2 logical processors at that level. They are 119 * truly SMT. 120 */ 121 if (!tscan->dom_shifts[TOPO_SMT_DOMAIN] && tscan->dom_ncpus[TOPO_SMT_DOMAIN] > 1) { 122 unsigned int sft = get_count_order(tscan->dom_ncpus[TOPO_SMT_DOMAIN]); 123 124 pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf 0 has shift level 0 but %u CPUs. Fixing it up.\n", 125 leaf, tscan->dom_ncpus[TOPO_SMT_DOMAIN]); 126 topology_update_dom(tscan, TOPO_SMT_DOMAIN, sft, tscan->dom_ncpus[TOPO_SMT_DOMAIN]); 127 } 128 129 set_cpu_cap(tscan->c, X86_FEATURE_XTOPOLOGY); 130 return true; 131 } 132 133 bool cpu_parse_topology_ext(struct topo_scan *tscan) 134 { 135 /* Intel: Try leaf 0x1F first. */ 136 if (tscan->c->cpuid_level >= 0x1f && parse_topology_leaf(tscan, 0x1f)) 137 return true; 138 139 /* AMD: Try leaf 0x80000026 first. */ 140 if (tscan->c->extended_cpuid_level >= 0x80000026 && parse_topology_leaf(tscan, 0x80000026)) 141 return true; 142 143 /* Intel/AMD: Fall back to leaf 0xB if available */ 144 return tscan->c->cpuid_level >= 0x0b && parse_topology_leaf(tscan, 0x0b); 145 } 146
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.