File Index
Files and directories
Sign in
FILE

topology_amd.c

File preview with syntax highlighting.

linux-6.19.14/arch/x86/kernel/cpu/topology_amd.c C
  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
 11static 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
 47static 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
 62static 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, 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
128static 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
147static 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
156static 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
177static 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(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
206void 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
217void 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}
~/ Read-only file preview.