Commit 9cd4d78e21cfdc709b1af516214ec4f69ee0e6bd
Committed by
H. Peter Anvin
1 parent
0d91ea86a8
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
x86/microcode_intel.h: Define functions and macros for early loading ucode
Define some functions and macros that will be used in early loading ucode. Some of them are moved from microcode_intel.c driver in order to be called in early boot phase before module can be called. Signed-off-by: Fenghua Yu <fenghua.yu@intel.com> Link: http://lkml.kernel.org/r/1356075872-3054-3-git-send-email-fenghua.yu@intel.com Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Showing 4 changed files with 122 additions and 171 deletions Side-by-side Diff
arch/x86/include/asm/microcode_intel.h
1 | +#ifndef _ASM_X86_MICROCODE_INTEL_H | |
2 | +#define _ASM_X86_MICROCODE_INTEL_H | |
3 | + | |
4 | +#include <asm/microcode.h> | |
5 | + | |
6 | +struct microcode_header_intel { | |
7 | + unsigned int hdrver; | |
8 | + unsigned int rev; | |
9 | + unsigned int date; | |
10 | + unsigned int sig; | |
11 | + unsigned int cksum; | |
12 | + unsigned int ldrver; | |
13 | + unsigned int pf; | |
14 | + unsigned int datasize; | |
15 | + unsigned int totalsize; | |
16 | + unsigned int reserved[3]; | |
17 | +}; | |
18 | + | |
19 | +struct microcode_intel { | |
20 | + struct microcode_header_intel hdr; | |
21 | + unsigned int bits[0]; | |
22 | +}; | |
23 | + | |
24 | +/* microcode format is extended from prescott processors */ | |
25 | +struct extended_signature { | |
26 | + unsigned int sig; | |
27 | + unsigned int pf; | |
28 | + unsigned int cksum; | |
29 | +}; | |
30 | + | |
31 | +struct extended_sigtable { | |
32 | + unsigned int count; | |
33 | + unsigned int cksum; | |
34 | + unsigned int reserved[3]; | |
35 | + struct extended_signature sigs[0]; | |
36 | +}; | |
37 | + | |
38 | +#define DEFAULT_UCODE_DATASIZE (2000) | |
39 | +#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel)) | |
40 | +#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) | |
41 | +#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) | |
42 | +#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) | |
43 | +#define DWSIZE (sizeof(u32)) | |
44 | + | |
45 | +#define get_totalsize(mc) \ | |
46 | + (((struct microcode_intel *)mc)->hdr.totalsize ? \ | |
47 | + ((struct microcode_intel *)mc)->hdr.totalsize : \ | |
48 | + DEFAULT_UCODE_TOTALSIZE) | |
49 | + | |
50 | +#define get_datasize(mc) \ | |
51 | + (((struct microcode_intel *)mc)->hdr.datasize ? \ | |
52 | + ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) | |
53 | + | |
54 | +#define sigmatch(s1, s2, p1, p2) \ | |
55 | + (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) | |
56 | + | |
57 | +#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) | |
58 | + | |
59 | +extern int | |
60 | +get_matching_microcode(unsigned int csig, int cpf, void *mc, int rev); | |
61 | +extern int microcode_sanity_check(void *mc, int print_err); | |
62 | +extern int get_matching_sig(unsigned int csig, int cpf, void *mc, int rev); | |
63 | +extern int | |
64 | +update_match_revision(struct microcode_header_intel *mc_header, int rev); | |
65 | + | |
66 | +#ifdef CONFIG_MICROCODE_INTEL_EARLY | |
67 | +extern void __init load_ucode_intel_bsp(void); | |
68 | +extern void __cpuinit load_ucode_intel_ap(void); | |
69 | +extern void show_ucode_info_early(void); | |
70 | +#else | |
71 | +static inline __init void load_ucode_intel_bsp(void) {} | |
72 | +static inline __cpuinit void load_ucode_intel_ap(void) {} | |
73 | +static inline void show_ucode_info_early(void) {} | |
74 | +#endif | |
75 | + | |
76 | +#if defined(CONFIG_MICROCODE_INTEL_EARLY) && defined(CONFIG_HOTPLUG_CPU) | |
77 | +extern int save_mc_for_early(u8 *mc); | |
78 | +#else | |
79 | +static inline int save_mc_for_early(u8 *mc) | |
80 | +{ | |
81 | + return 0; | |
82 | +} | |
83 | +#endif | |
84 | + | |
85 | +#endif /* _ASM_X86_MICROCODE_INTEL_H */ |
arch/x86/kernel/Makefile
... | ... | @@ -88,6 +88,9 @@ |
88 | 88 | |
89 | 89 | obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o |
90 | 90 | |
91 | +obj-$(CONFIG_MICROCODE_EARLY) += microcode_core_early.o | |
92 | +obj-$(CONFIG_MICROCODE_INTEL_EARLY) += microcode_intel_early.o | |
93 | +obj-$(CONFIG_MICROCODE_INTEL_LIB) += microcode_intel_lib.o | |
91 | 94 | microcode-y := microcode_core.o |
92 | 95 | microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o |
93 | 96 | microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o |
arch/x86/kernel/microcode_core.c
... | ... | @@ -364,10 +364,7 @@ |
364 | 364 | |
365 | 365 | static void microcode_fini_cpu(int cpu) |
366 | 366 | { |
367 | - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
368 | - | |
369 | 367 | microcode_ops->microcode_fini_cpu(cpu); |
370 | - uci->valid = 0; | |
371 | 368 | } |
372 | 369 | |
373 | 370 | static enum ucode_state microcode_resume_cpu(int cpu) |
... | ... | @@ -383,6 +380,10 @@ |
383 | 380 | static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw) |
384 | 381 | { |
385 | 382 | enum ucode_state ustate; |
383 | + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
384 | + | |
385 | + if (uci && uci->valid) | |
386 | + return UCODE_OK; | |
386 | 387 | |
387 | 388 | if (collect_cpu_info(cpu)) |
388 | 389 | return UCODE_ERROR; |
arch/x86/kernel/microcode_intel.c
... | ... | @@ -79,7 +79,7 @@ |
79 | 79 | #include <linux/module.h> |
80 | 80 | #include <linux/vmalloc.h> |
81 | 81 | |
82 | -#include <asm/microcode.h> | |
82 | +#include <asm/microcode_intel.h> | |
83 | 83 | #include <asm/processor.h> |
84 | 84 | #include <asm/msr.h> |
85 | 85 | |
... | ... | @@ -87,59 +87,6 @@ |
87 | 87 | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); |
88 | 88 | MODULE_LICENSE("GPL"); |
89 | 89 | |
90 | -struct microcode_header_intel { | |
91 | - unsigned int hdrver; | |
92 | - unsigned int rev; | |
93 | - unsigned int date; | |
94 | - unsigned int sig; | |
95 | - unsigned int cksum; | |
96 | - unsigned int ldrver; | |
97 | - unsigned int pf; | |
98 | - unsigned int datasize; | |
99 | - unsigned int totalsize; | |
100 | - unsigned int reserved[3]; | |
101 | -}; | |
102 | - | |
103 | -struct microcode_intel { | |
104 | - struct microcode_header_intel hdr; | |
105 | - unsigned int bits[0]; | |
106 | -}; | |
107 | - | |
108 | -/* microcode format is extended from prescott processors */ | |
109 | -struct extended_signature { | |
110 | - unsigned int sig; | |
111 | - unsigned int pf; | |
112 | - unsigned int cksum; | |
113 | -}; | |
114 | - | |
115 | -struct extended_sigtable { | |
116 | - unsigned int count; | |
117 | - unsigned int cksum; | |
118 | - unsigned int reserved[3]; | |
119 | - struct extended_signature sigs[0]; | |
120 | -}; | |
121 | - | |
122 | -#define DEFAULT_UCODE_DATASIZE (2000) | |
123 | -#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel)) | |
124 | -#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) | |
125 | -#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) | |
126 | -#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) | |
127 | -#define DWSIZE (sizeof(u32)) | |
128 | - | |
129 | -#define get_totalsize(mc) \ | |
130 | - (((struct microcode_intel *)mc)->hdr.totalsize ? \ | |
131 | - ((struct microcode_intel *)mc)->hdr.totalsize : \ | |
132 | - DEFAULT_UCODE_TOTALSIZE) | |
133 | - | |
134 | -#define get_datasize(mc) \ | |
135 | - (((struct microcode_intel *)mc)->hdr.datasize ? \ | |
136 | - ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) | |
137 | - | |
138 | -#define sigmatch(s1, s2, p1, p2) \ | |
139 | - (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) | |
140 | - | |
141 | -#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) | |
142 | - | |
143 | 90 | static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) |
144 | 91 | { |
145 | 92 | struct cpuinfo_x86 *c = &cpu_data(cpu_num); |
146 | 93 | |
147 | 94 | |
148 | 95 | |
149 | 96 | |
150 | 97 | |
151 | 98 | |
... | ... | @@ -162,128 +109,25 @@ |
162 | 109 | return 0; |
163 | 110 | } |
164 | 111 | |
165 | -static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf) | |
166 | -{ | |
167 | - return (!sigmatch(sig, csig->sig, pf, csig->pf)) ? 0 : 1; | |
168 | -} | |
169 | - | |
170 | -static inline int | |
171 | -update_match_revision(struct microcode_header_intel *mc_header, int rev) | |
172 | -{ | |
173 | - return (mc_header->rev <= rev) ? 0 : 1; | |
174 | -} | |
175 | - | |
176 | -static int microcode_sanity_check(void *mc) | |
177 | -{ | |
178 | - unsigned long total_size, data_size, ext_table_size; | |
179 | - struct microcode_header_intel *mc_header = mc; | |
180 | - struct extended_sigtable *ext_header = NULL; | |
181 | - int sum, orig_sum, ext_sigcount = 0, i; | |
182 | - struct extended_signature *ext_sig; | |
183 | - | |
184 | - total_size = get_totalsize(mc_header); | |
185 | - data_size = get_datasize(mc_header); | |
186 | - | |
187 | - if (data_size + MC_HEADER_SIZE > total_size) { | |
188 | - pr_err("error! Bad data size in microcode data file\n"); | |
189 | - return -EINVAL; | |
190 | - } | |
191 | - | |
192 | - if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { | |
193 | - pr_err("error! Unknown microcode update format\n"); | |
194 | - return -EINVAL; | |
195 | - } | |
196 | - ext_table_size = total_size - (MC_HEADER_SIZE + data_size); | |
197 | - if (ext_table_size) { | |
198 | - if ((ext_table_size < EXT_HEADER_SIZE) | |
199 | - || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { | |
200 | - pr_err("error! Small exttable size in microcode data file\n"); | |
201 | - return -EINVAL; | |
202 | - } | |
203 | - ext_header = mc + MC_HEADER_SIZE + data_size; | |
204 | - if (ext_table_size != exttable_size(ext_header)) { | |
205 | - pr_err("error! Bad exttable size in microcode data file\n"); | |
206 | - return -EFAULT; | |
207 | - } | |
208 | - ext_sigcount = ext_header->count; | |
209 | - } | |
210 | - | |
211 | - /* check extended table checksum */ | |
212 | - if (ext_table_size) { | |
213 | - int ext_table_sum = 0; | |
214 | - int *ext_tablep = (int *)ext_header; | |
215 | - | |
216 | - i = ext_table_size / DWSIZE; | |
217 | - while (i--) | |
218 | - ext_table_sum += ext_tablep[i]; | |
219 | - if (ext_table_sum) { | |
220 | - pr_warning("aborting, bad extended signature table checksum\n"); | |
221 | - return -EINVAL; | |
222 | - } | |
223 | - } | |
224 | - | |
225 | - /* calculate the checksum */ | |
226 | - orig_sum = 0; | |
227 | - i = (MC_HEADER_SIZE + data_size) / DWSIZE; | |
228 | - while (i--) | |
229 | - orig_sum += ((int *)mc)[i]; | |
230 | - if (orig_sum) { | |
231 | - pr_err("aborting, bad checksum\n"); | |
232 | - return -EINVAL; | |
233 | - } | |
234 | - if (!ext_table_size) | |
235 | - return 0; | |
236 | - /* check extended signature checksum */ | |
237 | - for (i = 0; i < ext_sigcount; i++) { | |
238 | - ext_sig = (void *)ext_header + EXT_HEADER_SIZE + | |
239 | - EXT_SIGNATURE_SIZE * i; | |
240 | - sum = orig_sum | |
241 | - - (mc_header->sig + mc_header->pf + mc_header->cksum) | |
242 | - + (ext_sig->sig + ext_sig->pf + ext_sig->cksum); | |
243 | - if (sum) { | |
244 | - pr_err("aborting, bad checksum\n"); | |
245 | - return -EINVAL; | |
246 | - } | |
247 | - } | |
248 | - return 0; | |
249 | -} | |
250 | - | |
251 | 112 | /* |
252 | 113 | * return 0 - no update found |
253 | 114 | * return 1 - found update |
254 | 115 | */ |
255 | -static int | |
256 | -get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev) | |
116 | +static int get_matching_mc(struct microcode_intel *mc_intel, int cpu) | |
257 | 117 | { |
258 | - struct microcode_header_intel *mc_header = mc; | |
259 | - struct extended_sigtable *ext_header; | |
260 | - unsigned long total_size = get_totalsize(mc_header); | |
261 | - int ext_sigcount, i; | |
262 | - struct extended_signature *ext_sig; | |
118 | + struct cpu_signature cpu_sig; | |
119 | + unsigned int csig, cpf, crev; | |
263 | 120 | |
264 | - if (!update_match_revision(mc_header, rev)) | |
265 | - return 0; | |
121 | + collect_cpu_info(cpu, &cpu_sig); | |
266 | 122 | |
267 | - if (update_match_cpu(cpu_sig, mc_header->sig, mc_header->pf)) | |
268 | - return 1; | |
123 | + csig = cpu_sig.sig; | |
124 | + cpf = cpu_sig.pf; | |
125 | + crev = cpu_sig.rev; | |
269 | 126 | |
270 | - /* Look for ext. headers: */ | |
271 | - if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) | |
272 | - return 0; | |
273 | - | |
274 | - ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; | |
275 | - ext_sigcount = ext_header->count; | |
276 | - ext_sig = (void *)ext_header + EXT_HEADER_SIZE; | |
277 | - | |
278 | - for (i = 0; i < ext_sigcount; i++) { | |
279 | - if (update_match_cpu(cpu_sig, ext_sig->sig, ext_sig->pf)) | |
280 | - return 1; | |
281 | - ext_sig++; | |
282 | - } | |
283 | - return 0; | |
127 | + return get_matching_microcode(csig, cpf, mc_intel, crev); | |
284 | 128 | } |
285 | 129 | |
286 | -static int apply_microcode(int cpu) | |
130 | +int apply_microcode(int cpu) | |
287 | 131 | { |
288 | 132 | struct microcode_intel *mc_intel; |
289 | 133 | struct ucode_cpu_info *uci; |
... | ... | @@ -300,6 +144,14 @@ |
300 | 144 | if (mc_intel == NULL) |
301 | 145 | return 0; |
302 | 146 | |
147 | + /* | |
148 | + * Microcode on this CPU could be updated earlier. Only apply the | |
149 | + * microcode patch in mc_intel when it is newer than the one on this | |
150 | + * CPU. | |
151 | + */ | |
152 | + if (get_matching_mc(mc_intel, cpu) == 0) | |
153 | + return 0; | |
154 | + | |
303 | 155 | /* write microcode via MSR 0x79 */ |
304 | 156 | wrmsr(MSR_IA32_UCODE_WRITE, |
305 | 157 | (unsigned long) mc_intel->bits, |
... | ... | @@ -338,6 +190,7 @@ |
338 | 190 | unsigned int leftover = size; |
339 | 191 | enum ucode_state state = UCODE_OK; |
340 | 192 | unsigned int curr_mc_size = 0; |
193 | + unsigned int csig, cpf; | |
341 | 194 | |
342 | 195 | while (leftover) { |
343 | 196 | struct microcode_header_intel mc_header; |
344 | 197 | |
... | ... | @@ -362,11 +215,13 @@ |
362 | 215 | } |
363 | 216 | |
364 | 217 | if (get_ucode_data(mc, ucode_ptr, mc_size) || |
365 | - microcode_sanity_check(mc) < 0) { | |
218 | + microcode_sanity_check(mc, 1) < 0) { | |
366 | 219 | break; |
367 | 220 | } |
368 | 221 | |
369 | - if (get_matching_microcode(&uci->cpu_sig, mc, new_rev)) { | |
222 | + csig = uci->cpu_sig.sig; | |
223 | + cpf = uci->cpu_sig.pf; | |
224 | + if (get_matching_microcode(csig, cpf, mc, new_rev)) { | |
370 | 225 | vfree(new_mc); |
371 | 226 | new_rev = mc_header.rev; |
372 | 227 | new_mc = mc; |
... | ... | @@ -392,6 +247,13 @@ |
392 | 247 | |
393 | 248 | vfree(uci->mc); |
394 | 249 | uci->mc = (struct microcode_intel *)new_mc; |
250 | + | |
251 | + /* | |
252 | + * If early loading microcode is supported, save this mc into | |
253 | + * permanent memory. So it will be loaded early when a CPU is hot added | |
254 | + * or resumes. | |
255 | + */ | |
256 | + save_mc_for_early(new_mc); | |
395 | 257 | |
396 | 258 | pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n", |
397 | 259 | cpu, new_rev, uci->cpu_sig.rev); |