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 topology_set_dom(tscan, TOPO_SMT_DOMAIN, sft, ecx.cpu_nthreads + 1); 33 return true; 34 } 35 36 static void store_node(struct topo_scan *tscan, unsigned int nr_nodes, u16 node_id) 37 { 38 /* 39 * Starting with Fam 17h the DIE domain could probably be used to 40 * retrieve the node info on AMD/HYGON. Analysis of CPUID dumps 41 * suggests it's the topmost bit(s) of the CPU cores area, but 42 * that's guess work and neither enumerated nor documented. 43 * 44 * Up to Fam 16h this does not work at all and the legacy node ID 45 * has to be used. 46 */ 47 tscan->amd_nodes_per_pkg = nr_nodes; 48 tscan->amd_node_id = node_id; 49 } 50 51 static bool parse_8000_001e(struct topo_scan *tscan, bool has_0xb) 52 { 53 struct { 54 // eax 55 u32 ext_apic_id : 32; // Extended APIC ID 56 // ebx 57 u32 core_id : 8, // Unique per-socket logical core unit ID 58 core_nthreads : 8, // #Threads per core (zero-based) 59 : 16; // Reserved 60 // ecx 61 u32 node_id : 8, // Node (die) ID of invoking logical CPU 62 nnodes_per_socket : 3, // #nodes in invoking logical CPU's package/socket 63 : 21; // Reserved 64 // edx 65 u32 : 32; // Reserved 66 } leaf; 67 68 if (!boot_cpu_has(X86_FEATURE_TOPOEXT)) 69 return false; 70 71 cpuid_leaf(0x8000001e, &leaf); 72 73 tscan->c->topo.initial_apicid = leaf.ext_apic_id; 74 75 /* 76 * If leaf 0xb is available, then SMT shift is set already. If not 77 * take it from ecx.threads_per_core and use topo_update_dom() - 78 * topology_set_dom() would propagate and overwrite the already 79 * propagated CORE level. 80 */ 81 if (!has_0xb) { 82 unsigned int nthreads = leaf.core_nthreads + 1; 83 84 topology_update_dom(tscan, TOPO_SMT_DOMAIN, get_count_order(nthreads), nthreads); 85 } 86 87 store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id); 88 89 if (tscan->c->x86_vendor == X86_VENDOR_AMD) { 90 if (tscan->c->x86 == 0x15) 91 tscan->c->topo.cu_id = leaf.core_id; 92 93 cacheinfo_amd_init_llc_id(tscan->c, leaf.node_id); 94 } else { 95 /* 96 * Package ID is ApicId[6..] on certain Hygon CPUs. See 97 * commit e0ceeae708ce for explanation. The topology info 98 * is screwed up: The package shift is always 6 and the 99 * node ID is bit [4:5]. 100 */ 101 if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && tscan->c->x86_model <= 0x3) { 102 topology_set_dom(tscan, TOPO_CORE_DOMAIN, 6, 103 tscan->dom_ncpus[TOPO_CORE_DOMAIN]); 104 } 105 cacheinfo_hygon_init_llc_id(tscan->c); 106 } 107 return true; 108 } 109 110 static bool parse_fam10h_node_id(struct topo_scan *tscan) 111 { 112 struct { 113 union { 114 u64 node_id : 3, 115 nodes_per_pkg : 3, 116 unused : 58; 117 u64 msr; 118 }; 119 } nid; 120 121 if (!boot_cpu_has(X86_FEATURE_NODEID_MSR)) 122 return false; 123 124 rdmsrl(MSR_FAM10H_NODE_ID, nid.msr); 125 store_node(tscan, nid.nodes_per_pkg + 1, nid.node_id); 126 tscan->c->topo.llc_id = nid.node_id; 127 return true; 128 } 129 130 static void legacy_set_llc(struct topo_scan *tscan) 131 { 132 unsigned int apicid = tscan->c->topo.initial_apicid; 133 134 /* parse_8000_0008() set everything up except llc_id */ 135 tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN]; 136 } 137 138 static void parse_topology_amd(struct topo_scan *tscan) 139 { 140 bool has_0xb = false; 141 142 /* 143 * If the extended topology leaf 0x8000_001e is available 144 * try to get SMT and CORE shift from leaf 0xb first, then 145 * try to get the CORE shift from leaf 0x8000_0008. 146 */ 147 if (cpu_feature_enabled(X86_FEATURE_TOPOEXT)) 148 has_0xb = cpu_parse_topology_ext(tscan); 149 150 if (!has_0xb && !parse_8000_0008(tscan)) 151 return; 152 153 /* Prefer leaf 0x8000001e if available */ 154 if (parse_8000_001e(tscan, has_0xb)) 155 return; 156 157 /* Try the NODEID MSR */ 158 if (parse_fam10h_node_id(tscan)) 159 return; 160 161 legacy_set_llc(tscan); 162 } 163 164 void cpu_parse_topology_amd(struct topo_scan *tscan) 165 { 166 tscan->amd_nodes_per_pkg = 1; 167 parse_topology_amd(tscan); 168 169 if (tscan->amd_nodes_per_pkg > 1) 170 set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM); 171 } 172 173 void cpu_topology_fixup_amd(struct topo_scan *tscan) 174 { 175 struct cpuinfo_x86 *c = tscan->c; 176 177 /* 178 * Adjust the core_id relative to the node when there is more than 179 * one node. 180 */ 181 if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1) 182 c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg; 183 } 184