summaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu/cpuid-deps.c
blob: a2fbea0be535fea51ebe1d7175ece80e489d2479 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/* Declare dependencies between CPUIDs */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/cpufeature.h>

struct cpuid_dep {
	unsigned int	feature;
	unsigned int	depends;
};

/*
 * Table of CPUID features that depend on others.
 *
 * This only includes dependencies that can be usefully disabled, not
 * features part of the base set (like FPU).
 *
 * Note this all is not __init / __initdata because it can be
 * called from cpu hotplug. It shouldn't do anything in this case,
 * but it's difficult to tell that to the init reference checker.
 */
static const struct cpuid_dep cpuid_deps[] = {
	{ X86_FEATURE_FXSR,			X86_FEATURE_FPU	      },
	{ X86_FEATURE_XSAVEOPT,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_XSAVEC,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_XSAVES,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_AVX,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_PKU,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_MPX,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_XGETBV1,			X86_FEATURE_XSAVE     },
	{ X86_FEATURE_CMOV,			X86_FEATURE_FXSR      },
	{ X86_FEATURE_MMX,			X86_FEATURE_FXSR      },
	{ X86_FEATURE_MMXEXT,			X86_FEATURE_MMX       },
	{ X86_FEATURE_FXSR_OPT,			X86_FEATURE_FXSR      },
	{ X86_FEATURE_XSAVE,			X86_FEATURE_FXSR      },
	{ X86_FEATURE_XMM,			X86_FEATURE_FXSR      },
	{ X86_FEATURE_XMM2,			X86_FEATURE_XMM       },
	{ X86_FEATURE_XMM3,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_XMM4_1,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_XMM4_2,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_XMM3,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_PCLMULQDQ,		X86_FEATURE_XMM2      },
	{ X86_FEATURE_SSSE3,			X86_FEATURE_XMM2,     },
	{ X86_FEATURE_F16C,			X86_FEATURE_XMM2,     },
	{ X86_FEATURE_AES,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_SHA_NI,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_GFNI,			X86_FEATURE_XMM2      },
	{ X86_FEATURE_AVX_VNNI,			X86_FEATURE_AVX       },
	{ X86_FEATURE_FMA,			X86_FEATURE_AVX       },
	{ X86_FEATURE_VAES,			X86_FEATURE_AVX       },
	{ X86_FEATURE_VPCLMULQDQ,		X86_FEATURE_AVX       },
	{ X86_FEATURE_AVX2,			X86_FEATURE_AVX,      },
	{ X86_FEATURE_AVX512F,			X86_FEATURE_AVX,      },
	{ X86_FEATURE_AVX512IFMA,		X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512PF,			X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512ER,			X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512CD,			X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512DQ,			X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512BW,			X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512VL,			X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512VBMI,		X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512_VBMI2,		X86_FEATURE_AVX512VL  },
	{ X86_FEATURE_AVX512_VNNI,		X86_FEATURE_AVX512VL  },
	{ X86_FEATURE_AVX512_BITALG,		X86_FEATURE_AVX512VL  },
	{ X86_FEATURE_AVX512_4VNNIW,		X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512_4FMAPS,		X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512_VPOPCNTDQ,		X86_FEATURE_AVX512F   },
	{ X86_FEATURE_AVX512_VP2INTERSECT,	X86_FEATURE_AVX512VL  },
	{ X86_FEATURE_CQM_OCCUP_LLC,		X86_FEATURE_CQM_LLC   },
	{ X86_FEATURE_CQM_MBM_TOTAL,		X86_FEATURE_CQM_LLC   },
	{ X86_FEATURE_CQM_MBM_LOCAL,		X86_FEATURE_CQM_LLC   },
	{ X86_FEATURE_BMEC,			X86_FEATURE_CQM_MBM_TOTAL   },
	{ X86_FEATURE_BMEC,			X86_FEATURE_CQM_MBM_LOCAL   },
	{ X86_FEATURE_AVX512_BF16,		X86_FEATURE_AVX512VL  },
	{ X86_FEATURE_AVX512_FP16,		X86_FEATURE_AVX512BW  },
	{ X86_FEATURE_ENQCMD,			X86_FEATURE_XSAVES    },
	{ X86_FEATURE_PER_THREAD_MBA,		X86_FEATURE_MBA       },
	{ X86_FEATURE_SGX_LC,			X86_FEATURE_SGX	      },
	{ X86_FEATURE_SGX1,			X86_FEATURE_SGX       },
	{ X86_FEATURE_SGX2,			X86_FEATURE_SGX1      },
	{ X86_FEATURE_SGX_EDECCSSA,		X86_FEATURE_SGX1      },
	{ X86_FEATURE_XFD,			X86_FEATURE_XSAVES    },
	{ X86_FEATURE_XFD,			X86_FEATURE_XGETBV1   },
	{ X86_FEATURE_AMX_TILE,			X86_FEATURE_XFD       },
	{ X86_FEATURE_SHSTK,			X86_FEATURE_XSAVES    },
	{ X86_FEATURE_FRED,			X86_FEATURE_LKGS      },
	{}
};

static inline void clear_feature(struct cpuinfo_x86 *c, unsigned int feature)
{
	/*
	 * Note: This could use the non atomic __*_bit() variants, but the
	 * rest of the cpufeature code uses atomics as well, so keep it for
	 * consistency. Cleanup all of it separately.
	 */
	if (!c) {
		clear_cpu_cap(&boot_cpu_data, feature);
		set_bit(feature, (unsigned long *)cpu_caps_cleared);
	} else {
		clear_bit(feature, (unsigned long *)c->x86_capability);
	}
}

/* Take the capabilities and the BUG bits into account */
#define MAX_FEATURE_BITS ((NCAPINTS + NBUGINTS) * sizeof(u32) * 8)

static void do_clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
{
	DECLARE_BITMAP(disable, MAX_FEATURE_BITS);
	const struct cpuid_dep *d;
	bool changed;

	if (WARN_ON(feature >= MAX_FEATURE_BITS))
		return;

	if (boot_cpu_has(feature))
		WARN_ON(alternatives_patched);

	clear_feature(c, feature);

	/* Collect all features to disable, handling dependencies */
	memset(disable, 0, sizeof(disable));
	__set_bit(feature, disable);

	/* Loop until we get a stable state. */
	do {
		changed = false;
		for (d = cpuid_deps; d->feature; d++) {
			if (!test_bit(d->depends, disable))
				continue;
			if (__test_and_set_bit(d->feature, disable))
				continue;

			changed = true;
			clear_feature(c, d->feature);
		}
	} while (changed);
}

void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
{
	do_clear_cpu_cap(c, feature);
}

void setup_clear_cpu_cap(unsigned int feature)
{
	do_clear_cpu_cap(NULL, feature);
}

/*
 * Return the feature "name" if available, otherwise return
 * the X86_FEATURE_* numerals to make it easier to identify
 * the feature.
 */
static const char *x86_feature_name(unsigned int feature, char *buf)
{
	if (x86_cap_flags[feature])
		return x86_cap_flags[feature];

	snprintf(buf, 16, "%d*32+%2d", feature / 32, feature % 32);

	return buf;
}

void check_cpufeature_deps(struct cpuinfo_x86 *c)
{
	char feature_buf[16], depends_buf[16];
	const struct cpuid_dep *d;

	for (d = cpuid_deps; d->feature; d++) {
		if (cpu_has(c, d->feature) && !cpu_has(c, d->depends)) {
			/*
			 * Only warn about the first unmet dependency on the
			 * first CPU where it is encountered to avoid spamming
			 * the kernel log.
			 */
			pr_warn_once("x86 CPU feature dependency check failure: CPU%d has '%s' enabled but '%s' disabled. Kernel might be fine, but no guarantees.\n",
				     smp_processor_id(),
				     x86_feature_name(d->feature, feature_buf),
				     x86_feature_name(d->depends, depends_buf));
		}
	}
}