xref: /linux/arch/x86/kernel/cpu/topology_amd.c (revision eb01fe7abbe2d0b38824d2a93fdb4cc3eaf2ccc1)
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