• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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