xref: /qemu/include/hw/i386/topology.h (revision a976ed3f)
1 /*
2  *  x86 CPU topology data structures and functions
3  *
4  *  Copyright (c) 2012 Red Hat Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #ifndef HW_I386_TOPOLOGY_H
25 #define HW_I386_TOPOLOGY_H
26 
27 /* This file implements the APIC-ID-based CPU topology enumeration logic,
28  * documented at the following document:
29  *   Intel® 64 Architecture Processor Topology Enumeration
30  *   http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/
31  *
32  * This code should be compatible with AMD's "Extended Method" described at:
33  *   AMD CPUID Specification (Publication #25481)
34  *   Section 3: Multiple Core Calcuation
35  * as long as:
36  *  nr_threads is set to 1;
37  *  OFFSET_IDX is assumed to be 0;
38  *  CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width().
39  */
40 
41 
42 #include "qemu/bitops.h"
43 
44 /* APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support
45  */
46 typedef uint32_t apic_id_t;
47 
48 typedef struct X86CPUTopoIDs {
49     unsigned pkg_id;
50     unsigned node_id;
51     unsigned die_id;
52     unsigned core_id;
53     unsigned smt_id;
54 } X86CPUTopoIDs;
55 
56 typedef struct X86CPUTopoInfo {
57     unsigned nodes_per_pkg;
58     unsigned dies_per_pkg;
59     unsigned cores_per_die;
60     unsigned threads_per_core;
61 } X86CPUTopoInfo;
62 
63 /* Return the bit width needed for 'count' IDs
64  */
65 static unsigned apicid_bitwidth_for_count(unsigned count)
66 {
67     g_assert(count >= 1);
68     count -= 1;
69     return count ? 32 - clz32(count) : 0;
70 }
71 
72 /* Bit width of the SMT_ID (thread ID) field on the APIC ID
73  */
74 static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info)
75 {
76     return apicid_bitwidth_for_count(topo_info->threads_per_core);
77 }
78 
79 /* Bit width of the Core_ID field
80  */
81 static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info)
82 {
83     return apicid_bitwidth_for_count(topo_info->cores_per_die);
84 }
85 
86 /* Bit width of the Die_ID field */
87 static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info)
88 {
89     return apicid_bitwidth_for_count(topo_info->dies_per_pkg);
90 }
91 
92 /* Bit width of the node_id field per socket */
93 static inline unsigned apicid_node_width_epyc(X86CPUTopoInfo *topo_info)
94 {
95     return apicid_bitwidth_for_count(MAX(topo_info->nodes_per_pkg, 1));
96 }
97 /* Bit offset of the Core_ID field
98  */
99 static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info)
100 {
101     return apicid_smt_width(topo_info);
102 }
103 
104 /* Bit offset of the Die_ID field */
105 static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info)
106 {
107     return apicid_core_offset(topo_info) + apicid_core_width(topo_info);
108 }
109 
110 /* Bit offset of the Pkg_ID (socket ID) field
111  */
112 static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info)
113 {
114     return apicid_die_offset(topo_info) + apicid_die_width(topo_info);
115 }
116 
117 #define NODE_ID_OFFSET 3 /* Minimum node_id offset if numa configured */
118 
119 /*
120  * Bit offset of the node_id field
121  *
122  * Make sure nodes_per_pkg >  0 if numa configured else zero.
123  */
124 static inline unsigned apicid_node_offset_epyc(X86CPUTopoInfo *topo_info)
125 {
126     unsigned offset = apicid_die_offset(topo_info) +
127                       apicid_die_width(topo_info);
128 
129     if (topo_info->nodes_per_pkg) {
130         return MAX(NODE_ID_OFFSET, offset);
131     } else {
132         return offset;
133     }
134 }
135 
136 /* Bit offset of the Pkg_ID (socket ID) field */
137 static inline unsigned apicid_pkg_offset_epyc(X86CPUTopoInfo *topo_info)
138 {
139     return apicid_node_offset_epyc(topo_info) +
140            apicid_node_width_epyc(topo_info);
141 }
142 
143 /*
144  * Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID
145  *
146  * The caller must make sure core_id < nr_cores and smt_id < nr_threads.
147  */
148 static inline apic_id_t
149 x86_apicid_from_topo_ids_epyc(X86CPUTopoInfo *topo_info,
150                               const X86CPUTopoIDs *topo_ids)
151 {
152     return (topo_ids->pkg_id  << apicid_pkg_offset_epyc(topo_info)) |
153            (topo_ids->node_id << apicid_node_offset_epyc(topo_info)) |
154            (topo_ids->die_id  << apicid_die_offset(topo_info)) |
155            (topo_ids->core_id << apicid_core_offset(topo_info)) |
156            topo_ids->smt_id;
157 }
158 
159 static inline void x86_topo_ids_from_idx_epyc(X86CPUTopoInfo *topo_info,
160                                               unsigned cpu_index,
161                                               X86CPUTopoIDs *topo_ids)
162 {
163     unsigned nr_nodes = MAX(topo_info->nodes_per_pkg, 1);
164     unsigned nr_dies = topo_info->dies_per_pkg;
165     unsigned nr_cores = topo_info->cores_per_die;
166     unsigned nr_threads = topo_info->threads_per_core;
167     unsigned cores_per_node = DIV_ROUND_UP((nr_dies * nr_cores * nr_threads),
168                                             nr_nodes);
169 
170     topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads);
171     topo_ids->node_id = (cpu_index / cores_per_node) % nr_nodes;
172     topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies;
173     topo_ids->core_id = cpu_index / nr_threads % nr_cores;
174     topo_ids->smt_id = cpu_index % nr_threads;
175 }
176 
177 /*
178  * Calculate thread/core/package IDs for a specific topology,
179  * based on APIC ID
180  */
181 static inline void x86_topo_ids_from_apicid_epyc(apic_id_t apicid,
182                                             X86CPUTopoInfo *topo_info,
183                                             X86CPUTopoIDs *topo_ids)
184 {
185     topo_ids->smt_id = apicid &
186             ~(0xFFFFFFFFUL << apicid_smt_width(topo_info));
187     topo_ids->core_id =
188             (apicid >> apicid_core_offset(topo_info)) &
189             ~(0xFFFFFFFFUL << apicid_core_width(topo_info));
190     topo_ids->die_id =
191             (apicid >> apicid_die_offset(topo_info)) &
192             ~(0xFFFFFFFFUL << apicid_die_width(topo_info));
193     topo_ids->node_id =
194             (apicid >> apicid_node_offset_epyc(topo_info)) &
195             ~(0xFFFFFFFFUL << apicid_node_width_epyc(topo_info));
196     topo_ids->pkg_id = apicid >> apicid_pkg_offset_epyc(topo_info);
197 }
198 
199 /*
200  * Make APIC ID for the CPU 'cpu_index'
201  *
202  * 'cpu_index' is a sequential, contiguous ID for the CPU.
203  */
204 static inline apic_id_t x86_apicid_from_cpu_idx_epyc(X86CPUTopoInfo *topo_info,
205                                                      unsigned cpu_index)
206 {
207     X86CPUTopoIDs topo_ids;
208     x86_topo_ids_from_idx_epyc(topo_info, cpu_index, &topo_ids);
209     return x86_apicid_from_topo_ids_epyc(topo_info, &topo_ids);
210 }
211 /* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID
212  *
213  * The caller must make sure core_id < nr_cores and smt_id < nr_threads.
214  */
215 static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info,
216                                                  const X86CPUTopoIDs *topo_ids)
217 {
218     return (topo_ids->pkg_id  << apicid_pkg_offset(topo_info)) |
219            (topo_ids->die_id  << apicid_die_offset(topo_info)) |
220            (topo_ids->core_id << apicid_core_offset(topo_info)) |
221            topo_ids->smt_id;
222 }
223 
224 /* Calculate thread/core/package IDs for a specific topology,
225  * based on (contiguous) CPU index
226  */
227 static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info,
228                                          unsigned cpu_index,
229                                          X86CPUTopoIDs *topo_ids)
230 {
231     unsigned nr_dies = topo_info->dies_per_pkg;
232     unsigned nr_cores = topo_info->cores_per_die;
233     unsigned nr_threads = topo_info->threads_per_core;
234 
235     topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads);
236     topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies;
237     topo_ids->core_id = cpu_index / nr_threads % nr_cores;
238     topo_ids->smt_id = cpu_index % nr_threads;
239 }
240 
241 /* Calculate thread/core/package IDs for a specific topology,
242  * based on APIC ID
243  */
244 static inline void x86_topo_ids_from_apicid(apic_id_t apicid,
245                                             X86CPUTopoInfo *topo_info,
246                                             X86CPUTopoIDs *topo_ids)
247 {
248     topo_ids->smt_id = apicid &
249             ~(0xFFFFFFFFUL << apicid_smt_width(topo_info));
250     topo_ids->core_id =
251             (apicid >> apicid_core_offset(topo_info)) &
252             ~(0xFFFFFFFFUL << apicid_core_width(topo_info));
253     topo_ids->die_id =
254             (apicid >> apicid_die_offset(topo_info)) &
255             ~(0xFFFFFFFFUL << apicid_die_width(topo_info));
256     topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info);
257 }
258 
259 /* Make APIC ID for the CPU 'cpu_index'
260  *
261  * 'cpu_index' is a sequential, contiguous ID for the CPU.
262  */
263 static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info,
264                                                 unsigned cpu_index)
265 {
266     X86CPUTopoIDs topo_ids;
267     x86_topo_ids_from_idx(topo_info, cpu_index, &topo_ids);
268     return x86_apicid_from_topo_ids(topo_info, &topo_ids);
269 }
270 
271 #endif /* HW_I386_TOPOLOGY_H */
272