1 #include <stdbool.h>
2 #include <stdint.h>
3
4 #include <cpuinfo.h>
5 #include <cpuinfo/log.h>
6 #include <cpuinfo/utils.h>
7 #include <x86/api.h>
8 #include <x86/cpuid.h>
9
10 enum topology_type {
11 topology_type_invalid = 0,
12 topology_type_smt = 1,
13 topology_type_core = 2,
14 };
15
cpuinfo_x86_detect_topology(uint32_t max_base_index,uint32_t max_extended_index,struct cpuid_regs leaf1,struct cpuinfo_x86_topology * topology)16 void cpuinfo_x86_detect_topology(
17 uint32_t max_base_index,
18 uint32_t max_extended_index,
19 struct cpuid_regs leaf1,
20 struct cpuinfo_x86_topology* topology) {
21 /*
22 * HTT: indicates multi-core/hyper-threading support on this core.
23 * - Intel, AMD: edx[bit 28] in basic info.
24 */
25 const bool htt = !!(leaf1.edx & UINT32_C(0x10000000));
26
27 uint32_t apic_id = 0;
28 if (htt) {
29 apic_id = leaf1.ebx >> 24;
30 bool amd_cmp_legacy = false;
31 if (max_extended_index >= UINT32_C(0x80000001)) {
32 const struct cpuid_regs leaf0x80000001 = cpuid(UINT32_C(0x80000001));
33 /*
34 * CmpLegacy: core multi-processing legacy mode.
35 * - AMD: ecx[bit 1] in extended info (reserved bit on
36 * Intel CPUs).
37 */
38 amd_cmp_legacy = !!(leaf0x80000001.ecx & UINT32_C(0x00000002));
39 }
40 if (amd_cmp_legacy) {
41 if (max_extended_index >= UINT32_C(0x80000008)) {
42 const struct cpuid_regs leaf0x80000008 = cpuid(UINT32_C(0x80000008));
43 /*
44 * NC: number of physical cores - 1. The number
45 * of cores in the processor is NC+1.
46 * - AMD: ecx[bits 0-7] in leaf 0x80000008
47 * (reserved zero bits on Intel CPUs).
48 */
49 const uint32_t cores_per_processor = 1 + (leaf0x80000008.ecx & UINT32_C(0x000000FF));
50 topology->core_bits_length = bit_length(cores_per_processor);
51 cpuinfo_log_debug(
52 "HTT: APIC ID = %08" PRIx32 ", cores per processor = %" PRIu32,
53 apic_id,
54 cores_per_processor);
55 } else {
56 /*
57 * LogicalProcessorCount: the number of cores
58 * per processor.
59 * - AMD: ebx[bits 16-23] in basic info
60 * (different interpretation on Intel CPUs).
61 */
62 const uint32_t cores_per_processor = (leaf1.ebx >> 16) & UINT32_C(0x000000FF);
63 if (cores_per_processor != 0) {
64 topology->core_bits_length = bit_length(cores_per_processor);
65 }
66 cpuinfo_log_debug(
67 "HTT: APIC ID = %08" PRIx32 ", cores per processor = %" PRIu32,
68 apic_id,
69 cores_per_processor);
70 }
71 } else {
72 /*
73 * Maximum number of addressable IDs for logical
74 * processors in this physical package.
75 * - Intel: ebx[bits 16-23] in basic info (different
76 * interpretation on AMD CPUs).
77 */
78 const uint32_t logical_processors = (leaf1.ebx >> 16) & UINT32_C(0x000000FF);
79 if (logical_processors != 0) {
80 const uint32_t log2_max_logical_processors = bit_length(logical_processors);
81 const uint32_t log2_max_threads_per_core =
82 log2_max_logical_processors - topology->core_bits_length;
83 topology->core_bits_offset = log2_max_threads_per_core;
84 topology->thread_bits_length = log2_max_threads_per_core;
85 }
86 cpuinfo_log_debug(
87 "HTT: APIC ID = %08" PRIx32 ", logical processors = %" PRIu32,
88 apic_id,
89 logical_processors);
90 }
91 }
92
93 /*
94 * x2APIC: indicated support for x2APIC feature.
95 * - Intel: ecx[bit 21] in basic info (reserved bit on AMD CPUs).
96 */
97 const bool x2apic = !!(leaf1.ecx & UINT32_C(0x00200000));
98 if (x2apic && (max_base_index >= UINT32_C(0xB))) {
99 uint32_t level = 0;
100 uint32_t type;
101 uint32_t total_shift = 0;
102 topology->thread_bits_offset = topology->thread_bits_length = 0;
103 topology->core_bits_offset = topology->core_bits_length = 0;
104 do {
105 const struct cpuid_regs leafB = cpuidex(UINT32_C(0xB), level);
106 type = (leafB.ecx >> 8) & UINT32_C(0x000000FF);
107 const uint32_t level_shift = leafB.eax & UINT32_C(0x0000001F);
108 const uint32_t x2apic_id = leafB.edx;
109 apic_id = x2apic_id;
110 switch (type) {
111 case topology_type_invalid:
112 break;
113 case topology_type_smt:
114 cpuinfo_log_debug(
115 "x2 level %" PRIu32 ": APIC ID = %08" PRIx32
116 ", "
117 "type SMT, shift %" PRIu32 ", total shift %" PRIu32,
118 level,
119 apic_id,
120 level_shift,
121 total_shift);
122 topology->thread_bits_offset = total_shift;
123 topology->thread_bits_length = level_shift;
124 break;
125 case topology_type_core:
126 cpuinfo_log_debug(
127 "x2 level %" PRIu32 ": APIC ID = %08" PRIx32
128 ", "
129 "type core, shift %" PRIu32 ", total shift %" PRIu32,
130 level,
131 apic_id,
132 level_shift,
133 total_shift);
134 topology->core_bits_offset = total_shift;
135 topology->core_bits_length = level_shift;
136 break;
137 default:
138 cpuinfo_log_warning(
139 "unexpected topology type %" PRIu32 " (offset %" PRIu32
140 ", length %" PRIu32
141 ") "
142 "reported in leaf 0x0000000B is ignored",
143 type,
144 total_shift,
145 level_shift);
146 break;
147 }
148 total_shift += level_shift;
149 level += 1;
150 } while (type != 0);
151 cpuinfo_log_debug(
152 "x2APIC ID 0x%08" PRIx32
153 ", "
154 "SMT offset %" PRIu32 " length %" PRIu32 ", core offset %" PRIu32 " length %" PRIu32,
155 apic_id,
156 topology->thread_bits_offset,
157 topology->thread_bits_length,
158 topology->core_bits_offset,
159 topology->core_bits_length);
160 }
161
162 topology->apic_id = apic_id;
163 }
164