summaryrefslogtreecommitdiff
path: root/arch/x86/kernel/cpu/topology_ext.c
blob: e477228cd5b2b4f717fc17ac37cf742fc9a633b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: GPL-2.0
#include <linux/cpu.h>

#include <asm/apic.h>
#include <asm/memtype.h>
#include <asm/processor.h>

#include "cpu.h"

enum topo_types {
	INVALID_TYPE		= 0,
	SMT_TYPE		= 1,
	CORE_TYPE		= 2,
	MAX_TYPE_0B		= 3,
	MODULE_TYPE		= 3,
	TILE_TYPE		= 4,
	DIE_TYPE		= 5,
	DIEGRP_TYPE		= 6,
	MAX_TYPE_1F		= 7,
};

/*
 * Use a lookup table for the case that there are future types > 6 which
 * describe an intermediate domain level which does not exist today.
 */
static const unsigned int topo_domain_map_0b_1f[MAX_TYPE_1F] = {
	[SMT_TYPE]	= TOPO_SMT_DOMAIN,
	[CORE_TYPE]	= TOPO_CORE_DOMAIN,
	[MODULE_TYPE]	= TOPO_MODULE_DOMAIN,
	[TILE_TYPE]	= TOPO_TILE_DOMAIN,
	[DIE_TYPE]	= TOPO_DIE_DOMAIN,
	[DIEGRP_TYPE]	= TOPO_DIEGRP_DOMAIN,
};

static inline bool topo_subleaf(struct topo_scan *tscan, u32 leaf, u32 subleaf,
				unsigned int *last_dom)
{
	unsigned int dom, maxtype;
	const unsigned int *map;
	struct {
		// eax
		u32	x2apic_shift	:  5, // Number of bits to shift APIC ID right
					      // for the topology ID at the next level
					: 27; // Reserved
		// ebx
		u32	num_processors	: 16, // Number of processors at current level
					: 16; // Reserved
		// ecx
		u32	level		:  8, // Current topology level. Same as sub leaf number
			type		:  8, // Level type. If 0, invalid
					: 16; // Reserved
		// edx
		u32	x2apic_id	: 32; // X2APIC ID of the current logical processor
	} sl;

	switch (leaf) {
	case 0x0b: maxtype = MAX_TYPE_0B; map = topo_domain_map_0b_1f; break;
	case 0x1f: maxtype = MAX_TYPE_1F; map = topo_domain_map_0b_1f; break;
	default: return false;
	}

	cpuid_subleaf(leaf, subleaf, &sl);

	if (!sl.num_processors || sl.type == INVALID_TYPE)
		return false;

	if (sl.type >= maxtype) {
		pr_err_once("Topology: leaf 0x%x:%d Unknown domain type %u\n",
			    leaf, subleaf, sl.type);
		/*
		 * It really would have been too obvious to make the domain
		 * type space sparse and leave a few reserved types between
		 * the points which might change instead of following the
		 * usual "this can be fixed in software" principle.
		 */
		dom = *last_dom + 1;
	} else {
		dom = map[sl.type];
		*last_dom = dom;
	}

	if (!dom) {
		tscan->c->topo.initial_apicid = sl.x2apic_id;
	} else if (tscan->c->topo.initial_apicid != sl.x2apic_id) {
		pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf %d APIC ID mismatch %x != %x\n",
			     leaf, subleaf, tscan->c->topo.initial_apicid, sl.x2apic_id);
	}

	topology_set_dom(tscan, dom, sl.x2apic_shift, sl.num_processors);
	return true;
}

static bool parse_topology_leaf(struct topo_scan *tscan, u32 leaf)
{
	unsigned int last_dom;
	u32 subleaf;

	/* Read all available subleafs and populate the levels */
	for (subleaf = 0, last_dom = 0; topo_subleaf(tscan, leaf, subleaf, &last_dom); subleaf++);

	/* If subleaf 0 failed to parse, give up */
	if (!subleaf)
		return false;

	/*
	 * There are machines in the wild which have shift 0 in the subleaf
	 * 0, but advertise 2 logical processors at that level. They are
	 * truly SMT.
	 */
	if (!tscan->dom_shifts[TOPO_SMT_DOMAIN] && tscan->dom_ncpus[TOPO_SMT_DOMAIN] > 1) {
		unsigned int sft = get_count_order(tscan->dom_ncpus[TOPO_SMT_DOMAIN]);

		pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf 0 has shift level 0 but %u CPUs. Fixing it up.\n",
			     leaf, tscan->dom_ncpus[TOPO_SMT_DOMAIN]);
		topology_update_dom(tscan, TOPO_SMT_DOMAIN, sft, tscan->dom_ncpus[TOPO_SMT_DOMAIN]);
	}

	set_cpu_cap(tscan->c, X86_FEATURE_XTOPOLOGY);
	return true;
}

bool cpu_parse_topology_ext(struct topo_scan *tscan)
{
	/* Intel: Try leaf 0x1F first. */
	if (tscan->c->cpuid_level >= 0x1f && parse_topology_leaf(tscan, 0x1f))
		return true;

	/* Intel/AMD: Fall back to leaf 0xB if available */
	return tscan->c->cpuid_level >= 0x0b && parse_topology_leaf(tscan, 0x0b);
}