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