xref: /linux/arch/x86/kernel/cpu/topology_amd.c (revision 5c3b3264e5858813632031ba58bcd6e1eeb3b214)
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 
parse_8000_0008(struct topo_scan * tscan)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(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, TOPO_SMT_DOMAIN, 0, 1);
43 	topology_set_dom(tscan, TOPO_CORE_DOMAIN, sft, ecx.cpu_nthreads + 1);
44 	return true;
45 }
46 
store_node(struct topo_scan * tscan,u16 nr_nodes,u16 node_id)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 
parse_8000_001e(struct topo_scan * tscan,bool has_topoext)62 static bool parse_8000_001e(struct topo_scan *tscan, bool has_topoext)
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 (!has_topoext) {
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, TOPO_SMT_DOMAIN,
101 					    get_count_order(nthreads), nthreads);
102 		}
103 	}
104 
105 	store_node(tscan, leaf.nnodes_per_socket + 1, 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(tscan->c, 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, TOPO_CORE_DOMAIN, 6,
121 					 tscan->dom_ncpus[TOPO_CORE_DOMAIN]);
122 		}
123 		cacheinfo_hygon_init_llc_id(tscan->c);
124 	}
125 	return true;
126 }
127 
parse_fam10h_node_id(struct topo_scan * tscan)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, nid.nodes_per_pkg + 1, nid.node_id);
144 	tscan->c->topo.llc_id = nid.node_id;
145 }
146 
legacy_set_llc(struct topo_scan * tscan)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 
topoext_fixup(struct topo_scan * tscan)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(0xc0011005, 54) <= 0)
167 		return;
168 
169 	rdmsrq(0xc0011005, msrval);
170 	if (msrval & BIT_64(54)) {
171 		set_cpu_cap(c, X86_FEATURE_TOPOEXT);
172 		pr_info_once(FW_INFO "CPU: Re-enabling disabled Topology Extensions Support.\n");
173 	}
174 }
175 
parse_topology_amd(struct topo_scan * tscan)176 static void parse_topology_amd(struct topo_scan *tscan)
177 {
178 	bool has_topoext = false;
179 
180 	/*
181 	 * If the extended topology leaf 0x8000_001e is available
182 	 * try to get SMT, CORE, TILE, and DIE shifts from extended
183 	 * CPUID leaf 0x8000_0026 on supported processors first. If
184 	 * extended CPUID leaf 0x8000_0026 is not supported, try to
185 	 * get SMT and CORE shift from leaf 0xb first, then try to
186 	 * get the CORE shift from leaf 0x8000_0008.
187 	 */
188 	if (cpu_feature_enabled(X86_FEATURE_TOPOEXT))
189 		has_topoext = cpu_parse_topology_ext(tscan);
190 
191 	if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
192 		tscan->c->topo.cpu_type = cpuid_ebx(0x80000026);
193 
194 	if (!has_topoext && !parse_8000_0008(tscan))
195 		return;
196 
197 	/* Prefer leaf 0x8000001e if available */
198 	if (parse_8000_001e(tscan, has_topoext))
199 		return;
200 
201 	/* Try the NODEID MSR */
202 	parse_fam10h_node_id(tscan);
203 }
204 
cpu_parse_topology_amd(struct topo_scan * tscan)205 void cpu_parse_topology_amd(struct topo_scan *tscan)
206 {
207 	tscan->amd_nodes_per_pkg = 1;
208 	topoext_fixup(tscan);
209 	parse_topology_amd(tscan);
210 	legacy_set_llc(tscan);
211 
212 	if (tscan->amd_nodes_per_pkg > 1)
213 		set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM);
214 }
215 
cpu_topology_fixup_amd(struct topo_scan * tscan)216 void cpu_topology_fixup_amd(struct topo_scan *tscan)
217 {
218 	struct cpuinfo_x86 *c = tscan->c;
219 
220 	/*
221 	 * Adjust the core_id relative to the node when there is more than
222 	 * one node.
223 	 */
224 	if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1)
225 		c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg;
226 }
227