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