qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

topology.h (5909B)


      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 die_id;
     51     unsigned core_id;
     52     unsigned smt_id;
     53 } X86CPUTopoIDs;
     54 
     55 typedef struct X86CPUTopoInfo {
     56     unsigned dies_per_pkg;
     57     unsigned cores_per_die;
     58     unsigned threads_per_core;
     59 } X86CPUTopoInfo;
     60 
     61 /* Return the bit width needed for 'count' IDs
     62  */
     63 static unsigned apicid_bitwidth_for_count(unsigned count)
     64 {
     65     g_assert(count >= 1);
     66     count -= 1;
     67     return count ? 32 - clz32(count) : 0;
     68 }
     69 
     70 /* Bit width of the SMT_ID (thread ID) field on the APIC ID
     71  */
     72 static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info)
     73 {
     74     return apicid_bitwidth_for_count(topo_info->threads_per_core);
     75 }
     76 
     77 /* Bit width of the Core_ID field
     78  */
     79 static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info)
     80 {
     81     return apicid_bitwidth_for_count(topo_info->cores_per_die);
     82 }
     83 
     84 /* Bit width of the Die_ID field */
     85 static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info)
     86 {
     87     return apicid_bitwidth_for_count(topo_info->dies_per_pkg);
     88 }
     89 
     90 /* Bit offset of the Core_ID field
     91  */
     92 static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info)
     93 {
     94     return apicid_smt_width(topo_info);
     95 }
     96 
     97 /* Bit offset of the Die_ID field */
     98 static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info)
     99 {
    100     return apicid_core_offset(topo_info) + apicid_core_width(topo_info);
    101 }
    102 
    103 /* Bit offset of the Pkg_ID (socket ID) field
    104  */
    105 static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info)
    106 {
    107     return apicid_die_offset(topo_info) + apicid_die_width(topo_info);
    108 }
    109 
    110 /* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID
    111  *
    112  * The caller must make sure core_id < nr_cores and smt_id < nr_threads.
    113  */
    114 static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info,
    115                                                  const X86CPUTopoIDs *topo_ids)
    116 {
    117     return (topo_ids->pkg_id  << apicid_pkg_offset(topo_info)) |
    118            (topo_ids->die_id  << apicid_die_offset(topo_info)) |
    119            (topo_ids->core_id << apicid_core_offset(topo_info)) |
    120            topo_ids->smt_id;
    121 }
    122 
    123 /* Calculate thread/core/package IDs for a specific topology,
    124  * based on (contiguous) CPU index
    125  */
    126 static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info,
    127                                          unsigned cpu_index,
    128                                          X86CPUTopoIDs *topo_ids)
    129 {
    130     unsigned nr_dies = topo_info->dies_per_pkg;
    131     unsigned nr_cores = topo_info->cores_per_die;
    132     unsigned nr_threads = topo_info->threads_per_core;
    133 
    134     topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads);
    135     topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies;
    136     topo_ids->core_id = cpu_index / nr_threads % nr_cores;
    137     topo_ids->smt_id = cpu_index % nr_threads;
    138 }
    139 
    140 /* Calculate thread/core/package IDs for a specific topology,
    141  * based on APIC ID
    142  */
    143 static inline void x86_topo_ids_from_apicid(apic_id_t apicid,
    144                                             X86CPUTopoInfo *topo_info,
    145                                             X86CPUTopoIDs *topo_ids)
    146 {
    147     topo_ids->smt_id = apicid &
    148             ~(0xFFFFFFFFUL << apicid_smt_width(topo_info));
    149     topo_ids->core_id =
    150             (apicid >> apicid_core_offset(topo_info)) &
    151             ~(0xFFFFFFFFUL << apicid_core_width(topo_info));
    152     topo_ids->die_id =
    153             (apicid >> apicid_die_offset(topo_info)) &
    154             ~(0xFFFFFFFFUL << apicid_die_width(topo_info));
    155     topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info);
    156 }
    157 
    158 /* Make APIC ID for the CPU 'cpu_index'
    159  *
    160  * 'cpu_index' is a sequential, contiguous ID for the CPU.
    161  */
    162 static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info,
    163                                                 unsigned cpu_index)
    164 {
    165     X86CPUTopoIDs topo_ids;
    166     x86_topo_ids_from_idx(topo_info, cpu_index, &topo_ids);
    167     return x86_apicid_from_topo_ids(topo_info, &topo_ids);
    168 }
    169 
    170 #endif /* HW_I386_TOPOLOGY_H */