Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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
 10static 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
 46static 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
 61static bool parse_8000_001e(struct topo_scan *tscan, bool has_topoext)
 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. Only valid for family >= 0x17.
 88	 */
 89	if (!has_topoext && tscan->c->x86 >= 0x17) {
 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
122static void 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;
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}
140
141static void legacy_set_llc(struct topo_scan *tscan)
142{
143	unsigned int apicid = tscan->c->topo.initial_apicid;
144
145	/* If none of the parsers set LLC ID then use the die ID for it. */
146	if (tscan->c->topo.llc_id == BAD_APICID)
147		tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN];
148}
149
150static 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
170static void parse_topology_amd(struct topo_scan *tscan)
171{
172	bool has_topoext = false;
173
174	/*
175	 * If the extended topology leaf 0x8000_001e is available
176	 * try to get SMT, CORE, TILE, and DIE shifts from extended
177	 * CPUID leaf 0x8000_0026 on supported processors first. If
178	 * extended CPUID leaf 0x8000_0026 is not supported, try to
179	 * get SMT and CORE shift from leaf 0xb first, then try to
180	 * get the CORE shift from leaf 0x8000_0008.
181	 */
182	if (cpu_feature_enabled(X86_FEATURE_TOPOEXT))
183		has_topoext = cpu_parse_topology_ext(tscan);
184
185	if (cpu_feature_enabled(X86_FEATURE_AMD_HETEROGENEOUS_CORES))
186		tscan->c->topo.cpu_type = cpuid_ebx(0x80000026);
187
188	if (!has_topoext && !parse_8000_0008(tscan))
189		return;
190
191	/* Prefer leaf 0x8000001e if available */
192	if (parse_8000_001e(tscan, has_topoext))
193		return;
194
195	/* Try the NODEID MSR */
196	parse_fam10h_node_id(tscan);
197}
198
199void cpu_parse_topology_amd(struct topo_scan *tscan)
200{
201	tscan->amd_nodes_per_pkg = 1;
202	topoext_fixup(tscan);
203	parse_topology_amd(tscan);
204	legacy_set_llc(tscan);
205
206	if (tscan->amd_nodes_per_pkg > 1)
207		set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM);
208}
209
210void cpu_topology_fixup_amd(struct topo_scan *tscan)
211{
212	struct cpuinfo_x86 *c = tscan->c;
213
214	/*
215	 * Adjust the core_id relative to the node when there is more than
216	 * one node.
217	 */
218	if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1)
219		c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg;
220}