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