| 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 | /* |
| 28 | * This file implements the APIC-ID-based CPU topology enumeration logic, |
| 29 | * documented at the following document: |
| 30 | * Intel® 64 Architecture Processor Topology Enumeration |
| 31 | * http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ |
| 32 | * |
| 33 | * This code should be compatible with AMD's "Extended Method" described at: |
| 34 | * AMD CPUID Specification (Publication #25481) |
| 35 | * Section 3: Multiple Core Calculation |
| 36 | * as long as: |
| 37 | * nr_threads is set to 1; |
| 38 | * OFFSET_IDX is assumed to be 0; |
| 39 | * CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width(). |
| 40 | */ |
| 41 | |
| 42 | #include "qapi/qapi-types-machine-common.h" |
| 43 | #include "qemu/bitops.h" |
| 44 | |
| 45 | /* |
| 46 | * APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support |
| 47 | */ |
| 48 | typedef uint32_t apic_id_t; |
| 49 | |
| 50 | typedef struct X86CPUTopoIDs { |
| 51 | unsigned pkg_id; |
| 52 | unsigned die_id; |
| 53 | unsigned module_id; |
| 54 | unsigned core_id; |
| 55 | unsigned smt_id; |
| 56 | } X86CPUTopoIDs; |
| 57 | |
| 58 | typedef struct X86CPUTopoInfo { |
| 59 | unsigned dies_per_pkg; |
| 60 | unsigned modules_per_die; |
| 61 | unsigned cores_per_module; |
| 62 | unsigned threads_per_core; |
| 63 | } X86CPUTopoInfo; |
| 64 | |
| 65 | #define CPU_TOPOLOGY_LEVEL_INVALID CPU_TOPOLOGY_LEVEL__MAX |
| 66 | |
| 67 | /* Return the bit width needed for 'count' IDs */ |
| 68 | static unsigned apicid_bitwidth_for_count(unsigned count) |
| 69 | { |
| 70 | g_assert(count >= 1); |
| 71 | count -= 1; |
| 72 | return count ? 32 - clz32(count) : 0; |
| 73 | } |
| 74 | |
| 75 | /* Bit width of the SMT_ID (thread ID) field on the APIC ID */ |
| 76 | static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info) |
| 77 | { |
| 78 | return apicid_bitwidth_for_count(topo_info->threads_per_core); |
| 79 | } |
| 80 | |
| 81 | /* Bit width of the Core_ID field */ |
| 82 | static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info) |
| 83 | { |
| 84 | return apicid_bitwidth_for_count(topo_info->cores_per_module); |
| 85 | } |
| 86 | |
| 87 | /* Bit width of the Module_ID field */ |
| 88 | static inline unsigned apicid_module_width(X86CPUTopoInfo *topo_info) |
| 89 | { |
| 90 | return apicid_bitwidth_for_count(topo_info->modules_per_die); |
| 91 | } |
| 92 | |
| 93 | /* Bit width of the Die_ID field */ |
| 94 | static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info) |
| 95 | { |
| 96 | return apicid_bitwidth_for_count(topo_info->dies_per_pkg); |
| 97 | } |
| 98 | |
| 99 | /* Bit offset of the Core_ID field */ |
| 100 | static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info) |
| 101 | { |
| 102 | return apicid_smt_width(topo_info); |
| 103 | } |
| 104 | |
| 105 | /* Bit offset of the Module_ID field */ |
| 106 | static inline unsigned apicid_module_offset(X86CPUTopoInfo *topo_info) |
| 107 | { |
| 108 | return apicid_core_offset(topo_info) + apicid_core_width(topo_info); |
| 109 | } |
| 110 | |
| 111 | /* Bit offset of the Die_ID field */ |
| 112 | static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) |
| 113 | { |
| 114 | return apicid_module_offset(topo_info) + apicid_module_width(topo_info); |
| 115 | } |
| 116 | |
| 117 | /* Bit offset of the Pkg_ID (socket ID) field */ |
| 118 | static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) |
| 119 | { |
| 120 | return apicid_die_offset(topo_info) + apicid_die_width(topo_info); |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | * Make APIC ID for the CPU based on topology and IDs of each topology level. |
| 125 | * |
| 126 | * The caller must make sure the ID of each level doesn't exceed the width of |
| 127 | * the level. |
| 128 | */ |
| 129 | static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, |
| 130 | const X86CPUTopoIDs *topo_ids) |
| 131 | { |
| 132 | return (topo_ids->pkg_id << apicid_pkg_offset(topo_info)) | |
| 133 | (topo_ids->die_id << apicid_die_offset(topo_info)) | |
| 134 | (topo_ids->module_id << apicid_module_offset(topo_info)) | |
| 135 | (topo_ids->core_id << apicid_core_offset(topo_info)) | |
| 136 | topo_ids->smt_id; |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | * Calculate thread/core/package IDs for a specific topology, |
| 141 | * based on (contiguous) CPU index |
| 142 | */ |
| 143 | static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, |
| 144 | unsigned cpu_index, |
| 145 | X86CPUTopoIDs *topo_ids) |
| 146 | { |
| 147 | unsigned nr_dies = topo_info->dies_per_pkg; |
| 148 | unsigned nr_modules = topo_info->modules_per_die; |
| 149 | unsigned nr_cores = topo_info->cores_per_module; |
| 150 | unsigned nr_threads = topo_info->threads_per_core; |
| 151 | |
| 152 | topo_ids->pkg_id = cpu_index / (nr_dies * nr_modules * |
| 153 | nr_cores * nr_threads); |
| 154 | topo_ids->die_id = cpu_index / (nr_modules * nr_cores * |
| 155 | nr_threads) % nr_dies; |
| 156 | topo_ids->module_id = cpu_index / (nr_cores * nr_threads) % |
| 157 | nr_modules; |
| 158 | topo_ids->core_id = cpu_index / nr_threads % nr_cores; |
| 159 | topo_ids->smt_id = cpu_index % nr_threads; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * Calculate thread/core/package IDs for a specific topology, |
| 164 | * based on APIC ID |
| 165 | */ |
| 166 | static inline void x86_topo_ids_from_apicid(apic_id_t apicid, |
| 167 | X86CPUTopoInfo *topo_info, |
| 168 | X86CPUTopoIDs *topo_ids) |
| 169 | { |
| 170 | topo_ids->smt_id = apicid & |
| 171 | ~(0xFFFFFFFFUL << apicid_smt_width(topo_info)); |
| 172 | topo_ids->core_id = |
| 173 | (apicid >> apicid_core_offset(topo_info)) & |
| 174 | ~(0xFFFFFFFFUL << apicid_core_width(topo_info)); |
| 175 | topo_ids->module_id = |
| 176 | (apicid >> apicid_module_offset(topo_info)) & |
| 177 | ~(0xFFFFFFFFUL << apicid_module_width(topo_info)); |
| 178 | topo_ids->die_id = |
| 179 | (apicid >> apicid_die_offset(topo_info)) & |
| 180 | ~(0xFFFFFFFFUL << apicid_die_width(topo_info)); |
| 181 | topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info); |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | * Make APIC ID for the CPU 'cpu_index' |
| 186 | * |
| 187 | * 'cpu_index' is a sequential, contiguous ID for the CPU. |
| 188 | */ |
| 189 | static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info, |
| 190 | unsigned cpu_index) |
| 191 | { |
| 192 | X86CPUTopoIDs topo_ids; |
| 193 | x86_topo_ids_from_idx(topo_info, cpu_index, &topo_ids); |
| 194 | return x86_apicid_from_topo_ids(topo_info, &topo_ids); |
| 195 | } |
| 196 | |
| 197 | /* |
| 198 | * Check whether there's extended topology level (module or die)? |
| 199 | */ |
| 200 | static inline bool x86_has_extended_topo(unsigned long *topo_bitmap) |
| 201 | { |
| 202 | return test_bit(CPU_TOPOLOGY_LEVEL_MODULE, topo_bitmap) || |
| 203 | test_bit(CPU_TOPOLOGY_LEVEL_DIE, topo_bitmap); |
| 204 | } |
| 205 | |
| 206 | static inline unsigned x86_module_per_pkg(X86CPUTopoInfo *topo_info) |
| 207 | { |
| 208 | return topo_info->modules_per_die * topo_info->dies_per_pkg; |
| 209 | } |
| 210 | |
| 211 | static inline unsigned x86_cores_per_pkg(X86CPUTopoInfo *topo_info) |
| 212 | { |
| 213 | return topo_info->cores_per_module * x86_module_per_pkg(topo_info); |
| 214 | } |
| 215 | |
| 216 | static inline unsigned x86_threads_per_pkg(X86CPUTopoInfo *topo_info) |
| 217 | { |
| 218 | return topo_info->threads_per_core * x86_cores_per_pkg(topo_info); |
| 219 | } |
| 220 | |
| 221 | static inline unsigned x86_threads_per_module(X86CPUTopoInfo *topo_info) |
| 222 | { |
| 223 | return topo_info->threads_per_core * topo_info->cores_per_module; |
| 224 | } |
| 225 | |
| 226 | static inline unsigned x86_threads_per_die(X86CPUTopoInfo *topo_info) |
| 227 | { |
| 228 | return x86_threads_per_module(topo_info) * topo_info->modules_per_die; |
| 229 | } |
| 230 | |
| 231 | #endif /* HW_I386_TOPOLOGY_H */ |