]>
Commit | Line | Data |
---|---|---|
59d5af67 | 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
321d628a FG |
2 | From: Andi Kleen <ak@linux.intel.com> |
3 | Date: Fri, 13 Oct 2017 14:56:42 -0700 | |
59d5af67 | 4 | Subject: [PATCH] x86/cpuid: Add generic table for CPUID dependencies |
321d628a FG |
5 | MIME-Version: 1.0 |
6 | Content-Type: text/plain; charset=UTF-8 | |
7 | Content-Transfer-Encoding: 8bit | |
8 | ||
9 | CVE-2017-5754 | |
10 | ||
11 | Some CPUID features depend on other features. Currently it's | |
12 | possible to to clear dependent features, but not clear the base features, | |
13 | which can cause various interesting problems. | |
14 | ||
15 | This patch implements a generic table to describe dependencies | |
16 | between CPUID features, to be used by all code that clears | |
17 | CPUID. | |
18 | ||
19 | Some subsystems (like XSAVE) had an own implementation of this, | |
20 | but it's better to do it all in a single place for everyone. | |
21 | ||
22 | Then clear_cpu_cap and setup_clear_cpu_cap always look up | |
23 | this table and clear all dependencies too. | |
24 | ||
25 | This is intended to be a practical table: only for features | |
26 | that make sense to clear. If someone for example clears FPU, | |
27 | or other features that are essentially part of the required | |
28 | base feature set, not much is going to work. Handling | |
29 | that is right now out of scope. We're only handling | |
30 | features which can be usefully cleared. | |
31 | ||
32 | Signed-off-by: Andi Kleen <ak@linux.intel.com> | |
33 | Reviewed-by: Thomas Gleixner <tglx@linutronix.de> | |
34 | Cc: Jonathan McDowell <noodles@earth.li> | |
35 | Cc: Linus Torvalds <torvalds@linux-foundation.org> | |
36 | Cc: Peter Zijlstra <peterz@infradead.org> | |
37 | Link: http://lkml.kernel.org/r/20171013215645.23166-3-andi@firstfloor.org | |
38 | Signed-off-by: Ingo Molnar <mingo@kernel.org> | |
39 | (cherry picked from commit 0b00de857a648dafe7020878c7a27cf776f5edf4) | |
40 | Signed-off-by: Andy Whitcroft <apw@canonical.com> | |
41 | Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com> | |
42 | (cherry picked from commit 35672522f2fc9a2e116ed1766f190bc08ef5582a) | |
43 | Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> | |
44 | --- | |
45 | arch/x86/kernel/cpu/Makefile | 1 + | |
46 | arch/x86/include/asm/cpufeature.h | 9 ++- | |
47 | arch/x86/include/asm/cpufeatures.h | 5 ++ | |
48 | arch/x86/kernel/cpu/cpuid-deps.c | 113 +++++++++++++++++++++++++++++++++++++ | |
49 | 4 files changed, 123 insertions(+), 5 deletions(-) | |
50 | create mode 100644 arch/x86/kernel/cpu/cpuid-deps.c | |
51 | ||
52 | diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile | |
53 | index e17942c131c8..de260fae1017 100644 | |
54 | --- a/arch/x86/kernel/cpu/Makefile | |
55 | +++ b/arch/x86/kernel/cpu/Makefile | |
56 | @@ -22,6 +22,7 @@ obj-y += rdrand.o | |
57 | obj-y += match.o | |
58 | obj-y += bugs.o | |
59 | obj-$(CONFIG_CPU_FREQ) += aperfmperf.o | |
60 | +obj-y += cpuid-deps.o | |
61 | ||
62 | obj-$(CONFIG_PROC_FS) += proc.o | |
63 | obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o | |
64 | diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h | |
65 | index d59c15c3defd..225fd8374fae 100644 | |
66 | --- a/arch/x86/include/asm/cpufeature.h | |
67 | +++ b/arch/x86/include/asm/cpufeature.h | |
68 | @@ -125,11 +125,10 @@ extern const char * const x86_bug_flags[NBUGINTS*32]; | |
69 | #define boot_cpu_has(bit) cpu_has(&boot_cpu_data, bit) | |
70 | ||
71 | #define set_cpu_cap(c, bit) set_bit(bit, (unsigned long *)((c)->x86_capability)) | |
72 | -#define clear_cpu_cap(c, bit) clear_bit(bit, (unsigned long *)((c)->x86_capability)) | |
73 | -#define setup_clear_cpu_cap(bit) do { \ | |
74 | - clear_cpu_cap(&boot_cpu_data, bit); \ | |
75 | - set_bit(bit, (unsigned long *)cpu_caps_cleared); \ | |
76 | -} while (0) | |
77 | + | |
78 | +extern void setup_clear_cpu_cap(unsigned int bit); | |
79 | +extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit); | |
80 | + | |
81 | #define setup_force_cpu_cap(bit) do { \ | |
82 | set_cpu_cap(&boot_cpu_data, bit); \ | |
83 | set_bit(bit, (unsigned long *)cpu_caps_set); \ | |
84 | diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h | |
85 | index 5a28e8e55e36..f4e145c4b06f 100644 | |
86 | --- a/arch/x86/include/asm/cpufeatures.h | |
87 | +++ b/arch/x86/include/asm/cpufeatures.h | |
88 | @@ -21,6 +21,11 @@ | |
89 | * this feature bit is not displayed in /proc/cpuinfo at all. | |
90 | */ | |
91 | ||
92 | +/* | |
93 | + * When adding new features here that depend on other features, | |
94 | + * please update the table in kernel/cpu/cpuid-deps.c | |
95 | + */ | |
96 | + | |
97 | /* Intel-defined CPU features, CPUID level 0x00000001 (edx), word 0 */ | |
98 | #define X86_FEATURE_FPU ( 0*32+ 0) /* Onboard FPU */ | |
99 | #define X86_FEATURE_VME ( 0*32+ 1) /* Virtual Mode Extensions */ | |
100 | diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c | |
101 | new file mode 100644 | |
102 | index 000000000000..e48eb7313120 | |
103 | --- /dev/null | |
104 | +++ b/arch/x86/kernel/cpu/cpuid-deps.c | |
105 | @@ -0,0 +1,113 @@ | |
106 | +/* Declare dependencies between CPUIDs */ | |
107 | +#include <linux/kernel.h> | |
108 | +#include <linux/init.h> | |
109 | +#include <linux/module.h> | |
110 | +#include <asm/cpufeature.h> | |
111 | + | |
112 | +struct cpuid_dep { | |
113 | + unsigned int feature; | |
114 | + unsigned int depends; | |
115 | +}; | |
116 | + | |
117 | +/* | |
118 | + * Table of CPUID features that depend on others. | |
119 | + * | |
120 | + * This only includes dependencies that can be usefully disabled, not | |
121 | + * features part of the base set (like FPU). | |
122 | + * | |
123 | + * Note this all is not __init / __initdata because it can be | |
124 | + * called from cpu hotplug. It shouldn't do anything in this case, | |
125 | + * but it's difficult to tell that to the init reference checker. | |
126 | + */ | |
127 | +const static struct cpuid_dep cpuid_deps[] = { | |
128 | + { X86_FEATURE_XSAVEOPT, X86_FEATURE_XSAVE }, | |
129 | + { X86_FEATURE_XSAVEC, X86_FEATURE_XSAVE }, | |
130 | + { X86_FEATURE_XSAVES, X86_FEATURE_XSAVE }, | |
131 | + { X86_FEATURE_AVX, X86_FEATURE_XSAVE }, | |
132 | + { X86_FEATURE_PKU, X86_FEATURE_XSAVE }, | |
133 | + { X86_FEATURE_MPX, X86_FEATURE_XSAVE }, | |
134 | + { X86_FEATURE_XGETBV1, X86_FEATURE_XSAVE }, | |
135 | + { X86_FEATURE_FXSR_OPT, X86_FEATURE_FXSR }, | |
136 | + { X86_FEATURE_XMM, X86_FEATURE_FXSR }, | |
137 | + { X86_FEATURE_XMM2, X86_FEATURE_XMM }, | |
138 | + { X86_FEATURE_XMM3, X86_FEATURE_XMM2 }, | |
139 | + { X86_FEATURE_XMM4_1, X86_FEATURE_XMM2 }, | |
140 | + { X86_FEATURE_XMM4_2, X86_FEATURE_XMM2 }, | |
141 | + { X86_FEATURE_XMM3, X86_FEATURE_XMM2 }, | |
142 | + { X86_FEATURE_PCLMULQDQ, X86_FEATURE_XMM2 }, | |
143 | + { X86_FEATURE_SSSE3, X86_FEATURE_XMM2, }, | |
144 | + { X86_FEATURE_F16C, X86_FEATURE_XMM2, }, | |
145 | + { X86_FEATURE_AES, X86_FEATURE_XMM2 }, | |
146 | + { X86_FEATURE_SHA_NI, X86_FEATURE_XMM2 }, | |
147 | + { X86_FEATURE_FMA, X86_FEATURE_AVX }, | |
148 | + { X86_FEATURE_AVX2, X86_FEATURE_AVX, }, | |
149 | + { X86_FEATURE_AVX512F, X86_FEATURE_AVX, }, | |
150 | + { X86_FEATURE_AVX512IFMA, X86_FEATURE_AVX512F }, | |
151 | + { X86_FEATURE_AVX512PF, X86_FEATURE_AVX512F }, | |
152 | + { X86_FEATURE_AVX512ER, X86_FEATURE_AVX512F }, | |
153 | + { X86_FEATURE_AVX512CD, X86_FEATURE_AVX512F }, | |
154 | + { X86_FEATURE_AVX512DQ, X86_FEATURE_AVX512F }, | |
155 | + { X86_FEATURE_AVX512BW, X86_FEATURE_AVX512F }, | |
156 | + { X86_FEATURE_AVX512VL, X86_FEATURE_AVX512F }, | |
157 | + { X86_FEATURE_AVX512VBMI, X86_FEATURE_AVX512F }, | |
158 | + { X86_FEATURE_AVX512_4VNNIW, X86_FEATURE_AVX512F }, | |
159 | + { X86_FEATURE_AVX512_4FMAPS, X86_FEATURE_AVX512F }, | |
160 | + { X86_FEATURE_AVX512_VPOPCNTDQ, X86_FEATURE_AVX512F }, | |
161 | + {} | |
162 | +}; | |
163 | + | |
164 | +static inline void __clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit) | |
165 | +{ | |
166 | + clear_bit32(bit, c->x86_capability); | |
167 | +} | |
168 | + | |
169 | +static inline void __setup_clear_cpu_cap(unsigned int bit) | |
170 | +{ | |
171 | + clear_cpu_cap(&boot_cpu_data, bit); | |
172 | + set_bit32(bit, cpu_caps_cleared); | |
173 | +} | |
174 | + | |
175 | +static inline void clear_feature(struct cpuinfo_x86 *c, unsigned int feature) | |
176 | +{ | |
177 | + if (!c) | |
178 | + __setup_clear_cpu_cap(feature); | |
179 | + else | |
180 | + __clear_cpu_cap(c, feature); | |
181 | +} | |
182 | + | |
183 | +static void do_clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature) | |
184 | +{ | |
185 | + bool changed; | |
186 | + DECLARE_BITMAP(disable, NCAPINTS * sizeof(u32) * 8); | |
187 | + const struct cpuid_dep *d; | |
188 | + | |
189 | + clear_feature(c, feature); | |
190 | + | |
191 | + /* Collect all features to disable, handling dependencies */ | |
192 | + memset(disable, 0, sizeof(disable)); | |
193 | + __set_bit(feature, disable); | |
194 | + | |
195 | + /* Loop until we get a stable state. */ | |
196 | + do { | |
197 | + changed = false; | |
198 | + for (d = cpuid_deps; d->feature; d++) { | |
199 | + if (!test_bit(d->depends, disable)) | |
200 | + continue; | |
201 | + if (__test_and_set_bit(d->feature, disable)) | |
202 | + continue; | |
203 | + | |
204 | + changed = true; | |
205 | + clear_feature(c, d->feature); | |
206 | + } | |
207 | + } while (changed); | |
208 | +} | |
209 | + | |
210 | +void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature) | |
211 | +{ | |
212 | + do_clear_cpu_cap(c, feature); | |
213 | +} | |
214 | + | |
215 | +void setup_clear_cpu_cap(unsigned int feature) | |
216 | +{ | |
217 | + do_clear_cpu_cap(NULL, feature); | |
218 | +} | |
219 | -- | |
220 | 2.14.2 | |
221 |