1 // SPDX-License-Identifier: GPL-2.0
7 #include <asm/processor.h>
12 struct x86_topology_system x86_topo_system __ro_after_init;
14 unsigned int __amd_nodes_per_pkg __ro_after_init;
15 EXPORT_SYMBOL_GPL(__amd_nodes_per_pkg);
17 void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
18 unsigned int shift, unsigned int ncpus)
20 topology_update_dom(tscan, dom, shift, ncpus);
22 /* Propagate to the upper levels */
23 for (dom++; dom < TOPO_MAX_DOMAIN; dom++) {
24 tscan->dom_shifts[dom] = tscan->dom_shifts[dom - 1];
25 tscan->dom_ncpus[dom] = tscan->dom_ncpus[dom - 1];
29 static unsigned int __maybe_unused parse_num_cores_legacy(struct cpuinfo_x86 *c)
37 if (c->cpuid_level < 4)
40 cpuid_subleaf_reg(4, 0, CPUID_EAX, &eax);
44 return eax.ncores + 1;
47 static void parse_legacy(struct topo_scan *tscan)
49 unsigned int cores, core_shift, smt_shift = 0;
50 struct cpuinfo_x86 *c = tscan->c;
52 cores = parse_num_cores_legacy(c);
53 core_shift = get_count_order(cores);
55 if (cpu_has(c, X86_FEATURE_HT)) {
56 if (!WARN_ON_ONCE(tscan->ebx1_nproc_shift < core_shift))
57 smt_shift = tscan->ebx1_nproc_shift - core_shift;
59 * The parser expects leaf 0xb/0x1f format, which means
60 * the number of logical processors at core level is
63 core_shift += smt_shift;
67 topology_set_dom(tscan, TOPO_SMT_DOMAIN, smt_shift, 1U << smt_shift);
68 topology_set_dom(tscan, TOPO_CORE_DOMAIN, core_shift, cores);
71 static bool fake_topology(struct topo_scan *tscan)
74 * Preset the CORE level shift for CPUID less systems and XEN_PV,
75 * which has useless CPUID information.
77 topology_set_dom(tscan, TOPO_SMT_DOMAIN, 0, 1);
78 topology_set_dom(tscan, TOPO_CORE_DOMAIN, 0, 1);
80 return tscan->c->cpuid_level < 1 || xen_pv_domain();
83 static void parse_topology(struct topo_scan *tscan, bool early)
85 const struct cpuinfo_topology topo_defaults = {
90 struct cpuinfo_x86 *c = tscan->c;
97 c->topo = topo_defaults;
99 if (fake_topology(tscan))
102 /* Preset Initial APIC ID from CPUID leaf 1 */
103 cpuid_leaf_reg(1, CPUID_EBX, &ebx);
104 c->topo.initial_apicid = ebx.apicid;
107 * The initial invocation from early_identify_cpu() happens before
108 * the APIC is mapped or X2APIC enabled. For establishing the
109 * topology, that's not required. Use the initial APIC ID.
112 c->topo.apicid = c->topo.initial_apicid;
114 c->topo.apicid = read_apic_id();
116 /* The above is sufficient for UP */
117 if (!IS_ENABLED(CONFIG_SMP))
120 tscan->ebx1_nproc_shift = get_count_order(ebx.nproc);
122 switch (c->x86_vendor) {
124 if (IS_ENABLED(CONFIG_CPU_SUP_AMD))
125 cpu_parse_topology_amd(tscan);
127 case X86_VENDOR_CENTAUR:
128 case X86_VENDOR_ZHAOXIN:
131 case X86_VENDOR_INTEL:
132 if (!IS_ENABLED(CONFIG_CPU_SUP_INTEL) || !cpu_parse_topology_ext(tscan))
135 case X86_VENDOR_HYGON:
136 if (IS_ENABLED(CONFIG_CPU_SUP_HYGON))
137 cpu_parse_topology_amd(tscan);
142 static void topo_set_ids(struct topo_scan *tscan)
144 struct cpuinfo_x86 *c = tscan->c;
145 u32 apicid = c->topo.apicid;
147 c->topo.pkg_id = topo_shift_apicid(apicid, TOPO_PKG_DOMAIN);
148 c->topo.die_id = topo_shift_apicid(apicid, TOPO_DIE_DOMAIN);
150 /* Package relative core ID */
151 c->topo.core_id = (apicid & topo_domain_mask(TOPO_PKG_DOMAIN)) >>
152 x86_topo_system.dom_shifts[TOPO_SMT_DOMAIN];
154 /* Temporary workaround */
155 if (tscan->amd_nodes_per_pkg)
156 c->topo.amd_node_id = c->topo.die_id = tscan->amd_node_id;
158 if (c->x86_vendor == X86_VENDOR_AMD)
159 cpu_topology_fixup_amd(tscan);
162 static void topo_set_max_cores(struct topo_scan *tscan)
165 * Bug compatible for now. This is broken on hybrid systems:
166 * 8 cores SMT + 8 cores w/o SMT
167 * tscan.dom_ncpus[TOPO_DIEGRP_DOMAIN] = 24; 24 / 2 = 12 !!
169 * Cannot be fixed without further topology enumeration changes.
171 tscan->c->x86_max_cores = tscan->dom_ncpus[TOPO_DIEGRP_DOMAIN] >>
172 x86_topo_system.dom_shifts[TOPO_SMT_DOMAIN];
175 void cpu_parse_topology(struct cpuinfo_x86 *c)
177 unsigned int dom, cpu = smp_processor_id();
178 struct topo_scan tscan = { .c = c, };
180 parse_topology(&tscan, false);
182 for (dom = TOPO_SMT_DOMAIN; dom < TOPO_MAX_DOMAIN; dom++) {
183 if (tscan.dom_shifts[dom] == x86_topo_system.dom_shifts[dom])
185 pr_err(FW_BUG "CPU%d: Topology domain %u shift %u != %u\n", cpu, dom,
186 tscan.dom_shifts[dom], x86_topo_system.dom_shifts[dom]);
189 /* Bug compatible with the existing parsers */
190 if (tscan.dom_ncpus[TOPO_SMT_DOMAIN] > smp_num_siblings) {
191 if (system_state == SYSTEM_BOOTING) {
192 pr_warn_once("CPU%d: SMT detected and enabled late\n", cpu);
193 smp_num_siblings = tscan.dom_ncpus[TOPO_SMT_DOMAIN];
195 pr_warn_once("CPU%d: SMT detected after init. Too late!\n", cpu);
199 topo_set_ids(&tscan);
200 topo_set_max_cores(&tscan);
203 void __init cpu_init_topology(struct cpuinfo_x86 *c)
205 struct topo_scan tscan = { .c = c, };
206 unsigned int dom, sft;
208 parse_topology(&tscan, true);
210 /* Copy the shift values and calculate the unit sizes. */
211 memcpy(x86_topo_system.dom_shifts, tscan.dom_shifts, sizeof(x86_topo_system.dom_shifts));
213 dom = TOPO_SMT_DOMAIN;
214 x86_topo_system.dom_size[dom] = 1U << x86_topo_system.dom_shifts[dom];
216 for (dom++; dom < TOPO_MAX_DOMAIN; dom++) {
217 sft = x86_topo_system.dom_shifts[dom] - x86_topo_system.dom_shifts[dom - 1];
218 x86_topo_system.dom_size[dom] = 1U << sft;
221 topo_set_ids(&tscan);
222 topo_set_max_cores(&tscan);
225 * Bug compatible with the existing code. If the boot CPU does not
226 * have SMT this ends up with one sibling. This needs way deeper
227 * changes further down the road to get it right during early boot.
229 smp_num_siblings = tscan.dom_ncpus[TOPO_SMT_DOMAIN];
232 * Neither it's clear whether there are as many dies as the APIC
233 * space indicating die level is. But assume that the actual number
234 * of CPUs gives a proper indication for now to stay bug compatible.
236 __max_die_per_package = tscan.dom_ncpus[TOPO_DIE_DOMAIN] /
237 tscan.dom_ncpus[TOPO_DIE_DOMAIN - 1];
239 * AMD systems have Nodes per package which cannot be mapped to
242 if (c->x86_vendor == X86_VENDOR_AMD || c->x86_vendor == X86_VENDOR_HYGON)
243 __amd_nodes_per_pkg = __max_die_per_package = tscan.amd_nodes_per_pkg;