162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Check for extended topology enumeration cpuid leaf 0xb and if it
462306a36Sopenharmony_ci * exists, use it for populating initial_apicid and cpu topology
562306a36Sopenharmony_ci * detection.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/cpu.h>
962306a36Sopenharmony_ci#include <asm/apic.h>
1062306a36Sopenharmony_ci#include <asm/memtype.h>
1162306a36Sopenharmony_ci#include <asm/processor.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "cpu.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* leaf 0xb SMT level */
1662306a36Sopenharmony_ci#define SMT_LEVEL	0
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* extended topology sub-leaf types */
1962306a36Sopenharmony_ci#define INVALID_TYPE	0
2062306a36Sopenharmony_ci#define SMT_TYPE	1
2162306a36Sopenharmony_ci#define CORE_TYPE	2
2262306a36Sopenharmony_ci#define DIE_TYPE	5
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define LEAFB_SUBTYPE(ecx)		(((ecx) >> 8) & 0xff)
2562306a36Sopenharmony_ci#define BITS_SHIFT_NEXT_LEVEL(eax)	((eax) & 0x1f)
2662306a36Sopenharmony_ci#define LEVEL_MAX_SIBLINGS(ebx)		((ebx) & 0xffff)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciunsigned int __max_die_per_package __read_mostly = 1;
2962306a36Sopenharmony_ciEXPORT_SYMBOL(__max_die_per_package);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef CONFIG_SMP
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Check if given CPUID extended topology "leaf" is implemented
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic int check_extended_topology_leaf(int leaf)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	unsigned int eax, ebx, ecx, edx;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE))
4262306a36Sopenharmony_ci		return -1;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Return best CPUID Extended Topology Leaf supported
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic int detect_extended_topology_leaf(struct cpuinfo_x86 *c)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (c->cpuid_level >= 0x1f) {
5262306a36Sopenharmony_ci		if (check_extended_topology_leaf(0x1f) == 0)
5362306a36Sopenharmony_ci			return 0x1f;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (c->cpuid_level >= 0xb) {
5762306a36Sopenharmony_ci		if (check_extended_topology_leaf(0xb) == 0)
5862306a36Sopenharmony_ci			return 0xb;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return -1;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci#endif
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint detect_extended_topology_early(struct cpuinfo_x86 *c)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci#ifdef CONFIG_SMP
6862306a36Sopenharmony_ci	unsigned int eax, ebx, ecx, edx;
6962306a36Sopenharmony_ci	int leaf;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	leaf = detect_extended_topology_leaf(c);
7262306a36Sopenharmony_ci	if (leaf < 0)
7362306a36Sopenharmony_ci		return -1;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	set_cpu_cap(c, X86_FEATURE_XTOPOLOGY);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
7862306a36Sopenharmony_ci	/*
7962306a36Sopenharmony_ci	 * initial apic id, which also represents 32-bit extended x2apic id.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	c->initial_apicid = edx;
8262306a36Sopenharmony_ci	smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
8362306a36Sopenharmony_ci#endif
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * Check for extended topology enumeration cpuid leaf, and if it
8962306a36Sopenharmony_ci * exists, use it for populating initial_apicid and cpu topology
9062306a36Sopenharmony_ci * detection.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciint detect_extended_topology(struct cpuinfo_x86 *c)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci#ifdef CONFIG_SMP
9562306a36Sopenharmony_ci	unsigned int eax, ebx, ecx, edx, sub_index;
9662306a36Sopenharmony_ci	unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width;
9762306a36Sopenharmony_ci	unsigned int core_select_mask, core_level_siblings;
9862306a36Sopenharmony_ci	unsigned int die_select_mask, die_level_siblings;
9962306a36Sopenharmony_ci	unsigned int pkg_mask_width;
10062306a36Sopenharmony_ci	bool die_level_present = false;
10162306a36Sopenharmony_ci	int leaf;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	leaf = detect_extended_topology_leaf(c);
10462306a36Sopenharmony_ci	if (leaf < 0)
10562306a36Sopenharmony_ci		return -1;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/*
10862306a36Sopenharmony_ci	 * Populate HT related information from sub-leaf level 0.
10962306a36Sopenharmony_ci	 */
11062306a36Sopenharmony_ci	cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
11162306a36Sopenharmony_ci	c->initial_apicid = edx;
11262306a36Sopenharmony_ci	core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
11362306a36Sopenharmony_ci	smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
11462306a36Sopenharmony_ci	core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
11562306a36Sopenharmony_ci	die_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
11662306a36Sopenharmony_ci	pkg_mask_width = die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	sub_index = 1;
11962306a36Sopenharmony_ci	while (true) {
12062306a36Sopenharmony_ci		cpuid_count(leaf, sub_index, &eax, &ebx, &ecx, &edx);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		/*
12362306a36Sopenharmony_ci		 * Check for the Core type in the implemented sub leaves.
12462306a36Sopenharmony_ci		 */
12562306a36Sopenharmony_ci		if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) {
12662306a36Sopenharmony_ci			core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
12762306a36Sopenharmony_ci			core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
12862306a36Sopenharmony_ci			die_level_siblings = core_level_siblings;
12962306a36Sopenharmony_ci			die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci		if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) {
13262306a36Sopenharmony_ci			die_level_present = true;
13362306a36Sopenharmony_ci			die_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
13462306a36Sopenharmony_ci			die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
13562306a36Sopenharmony_ci		}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		if (LEAFB_SUBTYPE(ecx) != INVALID_TYPE)
13862306a36Sopenharmony_ci			pkg_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
13962306a36Sopenharmony_ci		else
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		sub_index++;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	core_select_mask = (~(-1 << pkg_mask_width)) >> ht_mask_width;
14662306a36Sopenharmony_ci	die_select_mask = (~(-1 << die_plus_mask_width)) >>
14762306a36Sopenharmony_ci				core_plus_mask_width;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid,
15062306a36Sopenharmony_ci				ht_mask_width) & core_select_mask;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (die_level_present) {
15362306a36Sopenharmony_ci		c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid,
15462306a36Sopenharmony_ci					core_plus_mask_width) & die_select_mask;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid,
15862306a36Sopenharmony_ci				pkg_mask_width);
15962306a36Sopenharmony_ci	/*
16062306a36Sopenharmony_ci	 * Reinit the apicid, now that we have extended initial_apicid.
16162306a36Sopenharmony_ci	 */
16262306a36Sopenharmony_ci	c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	c->x86_max_cores = (core_level_siblings / smp_num_siblings);
16562306a36Sopenharmony_ci	__max_die_per_package = (die_level_siblings / core_level_siblings);
16662306a36Sopenharmony_ci#endif
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci}
169