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