]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1d67953f | 2 | /* |
2decb194 PA |
3 | * Check for extended topology enumeration cpuid leaf 0xb and if it |
4 | * exists, use it for populating initial_apicid and cpu topology | |
5 | * detection. | |
1d67953f | 6 | */ |
1d67953f | 7 | |
2decb194 PA |
8 | #include <linux/cpu.h> |
9 | #include <asm/apic.h> | |
eb243d1d | 10 | #include <asm/memtype.h> |
1d67953f VP |
11 | #include <asm/processor.h> |
12 | ||
ad3bc25a BP |
13 | #include "cpu.h" |
14 | ||
bbb65d2d SS |
15 | /* leaf 0xb SMT level */ |
16 | #define SMT_LEVEL 0 | |
17 | ||
7745f03e | 18 | /* extended topology sub-leaf types */ |
bbb65d2d SS |
19 | #define INVALID_TYPE 0 |
20 | #define SMT_TYPE 1 | |
21 | #define CORE_TYPE 2 | |
7745f03e | 22 | #define DIE_TYPE 5 |
bbb65d2d SS |
23 | |
24 | #define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff) | |
25 | #define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f) | |
26 | #define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff) | |
27 | ||
14d96d6c LB |
28 | unsigned int __max_die_per_package __read_mostly = 1; |
29 | EXPORT_SYMBOL(__max_die_per_package); | |
30 | ||
1eb8f690 | 31 | #ifdef CONFIG_SMP |
7745f03e LB |
32 | /* |
33 | * Check if given CPUID extended toplogy "leaf" is implemented | |
34 | */ | |
35 | static int check_extended_topology_leaf(int leaf) | |
36 | { | |
95f3d39c | 37 | unsigned int eax, ebx, ecx, edx; |
bbb65d2d | 38 | |
7745f03e LB |
39 | cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); |
40 | ||
41 | if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE)) | |
6c4f5aba | 42 | return -1; |
bbb65d2d | 43 | |
7745f03e LB |
44 | return 0; |
45 | } | |
46 | /* | |
47 | * Return best CPUID Extended Toplogy Leaf supported | |
48 | */ | |
49 | static int detect_extended_topology_leaf(struct cpuinfo_x86 *c) | |
50 | { | |
51 | if (c->cpuid_level >= 0x1f) { | |
52 | if (check_extended_topology_leaf(0x1f) == 0) | |
53 | return 0x1f; | |
54 | } | |
bbb65d2d | 55 | |
7745f03e LB |
56 | if (c->cpuid_level >= 0xb) { |
57 | if (check_extended_topology_leaf(0xb) == 0) | |
58 | return 0xb; | |
59 | } | |
60 | ||
61 | return -1; | |
62 | } | |
63 | #endif | |
64 | ||
65 | int detect_extended_topology_early(struct cpuinfo_x86 *c) | |
66 | { | |
67 | #ifdef CONFIG_SMP | |
68 | unsigned int eax, ebx, ecx, edx; | |
69 | int leaf; | |
70 | ||
71 | leaf = detect_extended_topology_leaf(c); | |
72 | if (leaf < 0) | |
6c4f5aba | 73 | return -1; |
bbb65d2d SS |
74 | |
75 | set_cpu_cap(c, X86_FEATURE_XTOPOLOGY); | |
76 | ||
7745f03e | 77 | cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); |
bbb65d2d SS |
78 | /* |
79 | * initial apic id, which also represents 32-bit extended x2apic id. | |
80 | */ | |
81 | c->initial_apicid = edx; | |
95f3d39c TG |
82 | smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); |
83 | #endif | |
84 | return 0; | |
85 | } | |
86 | ||
87 | /* | |
7745f03e | 88 | * Check for extended topology enumeration cpuid leaf, and if it |
95f3d39c TG |
89 | * exists, use it for populating initial_apicid and cpu topology |
90 | * detection. | |
91 | */ | |
92 | int detect_extended_topology(struct cpuinfo_x86 *c) | |
93 | { | |
94 | #ifdef CONFIG_SMP | |
95 | unsigned int eax, ebx, ecx, edx, sub_index; | |
7745f03e | 96 | unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width; |
95f3d39c | 97 | unsigned int core_select_mask, core_level_siblings; |
7745f03e | 98 | unsigned int die_select_mask, die_level_siblings; |
cb09a379 | 99 | bool die_level_present = false; |
7745f03e | 100 | int leaf; |
95f3d39c | 101 | |
7745f03e LB |
102 | leaf = detect_extended_topology_leaf(c); |
103 | if (leaf < 0) | |
95f3d39c | 104 | return -1; |
bbb65d2d SS |
105 | |
106 | /* | |
107 | * Populate HT related information from sub-leaf level 0. | |
108 | */ | |
7745f03e LB |
109 | cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); |
110 | c->initial_apicid = edx; | |
bbb65d2d SS |
111 | core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); |
112 | core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); | |
7745f03e LB |
113 | die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); |
114 | die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); | |
bbb65d2d SS |
115 | |
116 | sub_index = 1; | |
117 | do { | |
7745f03e | 118 | cpuid_count(leaf, sub_index, &eax, &ebx, &ecx, &edx); |
bbb65d2d SS |
119 | |
120 | /* | |
121 | * Check for the Core type in the implemented sub leaves. | |
122 | */ | |
123 | if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) { | |
124 | core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); | |
125 | core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); | |
7745f03e LB |
126 | die_level_siblings = core_level_siblings; |
127 | die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); | |
128 | } | |
129 | if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) { | |
cb09a379 | 130 | die_level_present = true; |
7745f03e LB |
131 | die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); |
132 | die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); | |
bbb65d2d SS |
133 | } |
134 | ||
135 | sub_index++; | |
136 | } while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE); | |
137 | ||
138 | core_select_mask = (~(-1 << core_plus_mask_width)) >> ht_mask_width; | |
7745f03e LB |
139 | die_select_mask = (~(-1 << die_plus_mask_width)) >> |
140 | core_plus_mask_width; | |
141 | ||
142 | c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, | |
143 | ht_mask_width) & core_select_mask; | |
cb09a379 YG |
144 | |
145 | if (die_level_present) { | |
146 | c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid, | |
147 | core_plus_mask_width) & die_select_mask; | |
148 | } | |
149 | ||
7745f03e LB |
150 | c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, |
151 | die_plus_mask_width); | |
345077cd SS |
152 | /* |
153 | * Reinit the apicid, now that we have extended initial_apicid. | |
154 | */ | |
cb8cc442 | 155 | c->apicid = apic->phys_pkg_id(c->initial_apicid, 0); |
d4c9a9f3 | 156 | |
bbb65d2d | 157 | c->x86_max_cores = (core_level_siblings / smp_num_siblings); |
14d96d6c | 158 | __max_die_per_package = (die_level_siblings / core_level_siblings); |
11c231a9 | 159 | #endif |
6c4f5aba | 160 | return 0; |
bbb65d2d | 161 | } |