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 static bool parse_8000_0008(struct topo_scan *tscan) 11 { 12 struct { 13 // ecx 14 u32 cpu_nthreads : 8, // Number of physical threads - 1 15 : 4, // Reserved 16 apicid_coreid_len : 4, // Number of thread core ID bits (shift) in APIC ID 17 perf_tsc_len : 2, // Performance time-stamp counter size 18 : 14; // Reserved 19 } ecx; 20 unsigned int sft; 21 22 if (tscan->c->extended_cpuid_level < 0x80000008) 23 return false; 24 25 cpuid_leaf_reg(0x80000008, CPUID_ECX, &ecx); 26 27 /* If the thread bits are 0, then get the shift value from ecx.cpu_nthreads */ 28 sft = ecx.apicid_coreid_len; 29 if (!sft) 30 sft = get_count_order(ecx.cpu_nthreads + 1); 31 32 /* 33 * cpu_nthreads describes the number of threads in the package 34 * sft is the number of APIC ID bits per package 35 * 36 * As the number of actual threads per core is not described in 37 * this leaf, just set the CORE domain shift and let the later 38 * parsers set SMT shift. Assume one thread per core by default 39 * which is correct if there are no other CPUID leafs to parse. 40 */ 41 topology_update_dom(tscan, TOPO_SMT_DOMAIN, 0, 1); 42 topology_set_dom(tscan, TOPO_CORE_DOMAIN, sft, ecx.cpu_nthreads + 1); 43 return true; 44 } 45 46 static void store_node(struct topo_scan *tscan, u16 nr_nodes, u16 node_id) 47 { 48 /* 49 * Starting with Fam 17h the DIE domain could probably be used to 50 * retrieve the node info on AMD/HYGON. Analysis of CPUID dumps 51 * suggests it's the topmost bit(s) of the CPU cores area, but 52 * that's guess work and neither enumerated nor documented. 53 * 54 * Up to Fam 16h this does not work at all and the legacy node ID 55 * has to be used. 56 */ 57 tscan->amd_nodes_per_pkg = nr_nodes; 58 tscan->amd_node_id = node_id; 59 } 60 61 static bool parse_8000_001e(struct topo_scan *tscan, bool has_0xb) 62 { 63 struct { 64 // eax 65 u32 ext_apic_id : 32; // Extended APIC ID 66 // ebx 67 u32 core_id : 8, // Unique per-socket logical core unit ID 68 core_nthreads : 8, // #Threads per core (zero-based) 69 : 16; // Reserved 70 // ecx 71 u32 node_id : 8, // Node (die) ID of invoking logical CPU 72 nnodes_per_socket : 3, // #nodes in invoking logical CPU's package/socket 73 : 21; // Reserved 74 // edx 75 u32 : 32; // Reserved 76 } leaf; 77 78 if (!boot_cpu_has(X86_FEATURE_TOPOEXT)) 79 return false; 80 81 cpuid_leaf(0x8000001e, &leaf); 82 83 tscan->c->topo.initial_apicid = leaf.ext_apic_id; 84 85 /* 86 * If leaf 0xb is available, then the domain shifts are set 87 * already and nothing to do here. 88 */ 89 if (!has_0xb) { 90 /* 91 * Leaf 0x80000008 set the CORE domain shift already. 92 * Update the SMT domain, but do not propagate it. 93 */ 94 unsigned int nthreads = leaf.core_nthreads + 1; 95 96 topology_update_dom(tscan, TOPO_SMT_DOMAIN, get_count_order(nthreads), nthreads); 97 } 98 99 store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id); 100 101 if (tscan->c->x86_vendor == X86_VENDOR_AMD) { 102 if (tscan->c->x86 == 0x15) 103 tscan->c->topo.cu_id = leaf.core_id; 104 105 cacheinfo_amd_init_llc_id(tscan->c, leaf.node_id); 106 } else { 107 /* 108 * Package ID is ApicId[6..] on certain Hygon CPUs. See 109 * commit e0ceeae708ce for explanation. The topology info 110 * is screwed up: The package shift is always 6 and the 111 * node ID is bit [4:5]. 112 */ 113 if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && tscan->c->x86_model <= 0x3) { 114 topology_set_dom(tscan, TOPO_CORE_DOMAIN, 6, 115 tscan->dom_ncpus[TOPO_CORE_DOMAIN]); 116 } 117 cacheinfo_hygon_init_llc_id(tscan->c); 118 } 119 return true; 120 } 121 122 static bool parse_fam10h_node_id(struct topo_scan *tscan) 123 { 124 union { 125 struct { 126 u64 node_id : 3, 127 nodes_per_pkg : 3, 128 unused : 58; 129 }; 130 u64 msr; 131 } nid; 132 133 if (!boot_cpu_has(X86_FEATURE_NODEID_MSR)) 134 return false; 135 136 rdmsrl(MSR_FAM10H_NODE_ID, nid.msr); 137 store_node(tscan, nid.nodes_per_pkg + 1, nid.node_id); 138 tscan->c->topo.llc_id = nid.node_id; 139 return true; 140 } 141 142 static void legacy_set_llc(struct topo_scan *tscan) 143 { 144 unsigned int apicid = tscan->c->topo.initial_apicid; 145 146 /* parse_8000_0008() set everything up except llc_id */ 147 tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN]; 148 } 149 150 static void topoext_fixup(struct topo_scan *tscan) 151 { 152 struct cpuinfo_x86 *c = tscan->c; 153 u64 msrval; 154 155 /* Try to re-enable TopologyExtensions if switched off by BIOS */ 156 if (cpu_has(c, X86_FEATURE_TOPOEXT) || c->x86_vendor != X86_VENDOR_AMD || 157 c->x86 != 0x15 || c->x86_model < 0x10 || c->x86_model > 0x6f) 158 return; 159 160 if (msr_set_bit(0xc0011005, 54) <= 0) 161 return; 162 163 rdmsrl(0xc0011005, msrval); 164 if (msrval & BIT_64(54)) { 165 set_cpu_cap(c, X86_FEATURE_TOPOEXT); 166 pr_info_once(FW_INFO "CPU: Re-enabling disabled Topology Extensions Support.\n"); 167 } 168 } 169 170 static void parse_topology_amd(struct topo_scan *tscan) 171 { 172 bool has_0xb = false; 173 174 /* 175 * If the extended topology leaf 0x8000_001e is available 176 * try to get SMT and CORE shift from leaf 0xb first, then 177 * try to get the CORE shift from leaf 0x8000_0008. 178 */ 179 if (cpu_feature_enabled(X86_FEATURE_TOPOEXT)) 180 has_0xb = cpu_parse_topology_ext(tscan); 181 182 if (!has_0xb && !parse_8000_0008(tscan)) 183 return; 184 185 /* Prefer leaf 0x8000001e if available */ 186 if (parse_8000_001e(tscan, has_0xb)) 187 return; 188 189 /* Try the NODEID MSR */ 190 if (parse_fam10h_node_id(tscan)) 191 return; 192 193 legacy_set_llc(tscan); 194 } 195 196 void cpu_parse_topology_amd(struct topo_scan *tscan) 197 { 198 tscan->amd_nodes_per_pkg = 1; 199 topoext_fixup(tscan); 200 parse_topology_amd(tscan); 201 202 if (tscan->amd_nodes_per_pkg > 1) 203 set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM); 204 } 205 206 void cpu_topology_fixup_amd(struct topo_scan *tscan) 207 { 208 struct cpuinfo_x86 *c = tscan->c; 209 210 /* 211 * Adjust the core_id relative to the node when there is more than 212 * one node. 213 */ 214 if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1) 215 c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg; 216 } 217