Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/kernel/cpu/topology_amd.c
26493 views
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
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
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
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
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
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
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
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
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
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
228