aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/microcode_intel.c
diff options
context:
space:
mode:
authorDmitry Adamushko <dmitry.adamushko@gmail.com>2008-09-11 17:27:52 -0400
committerIngo Molnar <mingo@elte.hu>2008-09-12 06:20:27 -0400
commita0a29b62a9cac6b7d83b7514679f2ed8d33d4372 (patch)
treee07ab66cbe3c90da70a7195104413cae00104de6 /arch/x86/kernel/microcode_intel.c
parent5b792d320f28ff83dd4c13f984807e26235f7703 (diff)
x86, microcode rework, v2
this is a rework of the microcode splitup in tip/x86/microcode (1) I think this new interface is cleaner (look at the changes in 'struct microcode_ops' in microcode.h); (2) it's -64 lines of code; Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/microcode_intel.c')
-rw-r--r--arch/x86/kernel/microcode_intel.c220
1 files changed, 96 insertions, 124 deletions
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c
index c9b53202ba3d..f4930b55c6a0 100644
--- a/arch/x86/kernel/microcode_intel.c
+++ b/arch/x86/kernel/microcode_intel.c
@@ -155,15 +155,15 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
155 return 0; 155 return 0;
156} 156}
157 157
158static inline int microcode_update_match(int cpu_num, 158static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf)
159 struct microcode_header_intel *mc_header, int sig, int pf)
160{ 159{
161 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; 160 return (!sigmatch(sig, csig->sig, pf, csig->pf)) ? 0 : 1;
161}
162 162
163 if (!sigmatch(sig, uci->cpu_sig.sig, pf, uci->cpu_sig.pf) 163static inline int
164 || mc_header->rev <= uci->cpu_sig.rev) 164update_match_revision(struct microcode_header_intel *mc_header, int rev)
165 return 0; 165{
166 return 1; 166 return (mc_header->rev <= rev) ? 0 : 1;
167} 167}
168 168
169static int microcode_sanity_check(void *mc) 169static int microcode_sanity_check(void *mc)
@@ -248,51 +248,36 @@ static int microcode_sanity_check(void *mc)
248/* 248/*
249 * return 0 - no update found 249 * return 0 - no update found
250 * return 1 - found update 250 * return 1 - found update
251 * return < 0 - error
252 */ 251 */
253static int get_matching_microcode(void *mc, int cpu) 252static int
253get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev)
254{ 254{
255 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
256 struct microcode_header_intel *mc_header = mc; 255 struct microcode_header_intel *mc_header = mc;
257 struct extended_sigtable *ext_header; 256 struct extended_sigtable *ext_header;
258 unsigned long total_size = get_totalsize(mc_header); 257 unsigned long total_size = get_totalsize(mc_header);
259 int ext_sigcount, i; 258 int ext_sigcount, i;
260 struct extended_signature *ext_sig; 259 struct extended_signature *ext_sig;
261 void *new_mc;
262 260
263 if (microcode_update_match(cpu, mc_header, 261 if (!update_match_revision(mc_header, rev))
264 mc_header->sig, mc_header->pf)) 262 return 0;
265 goto find; 263
264 if (update_match_cpu(cpu_sig, mc_header->sig, mc_header->pf))
265 return 1;
266 266
267 /* Look for ext. headers: */
267 if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) 268 if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE)
268 return 0; 269 return 0;
269 270
270 ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; 271 ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE;
271 ext_sigcount = ext_header->count; 272 ext_sigcount = ext_header->count;
272 ext_sig = (void *)ext_header + EXT_HEADER_SIZE; 273 ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
274
273 for (i = 0; i < ext_sigcount; i++) { 275 for (i = 0; i < ext_sigcount; i++) {
274 if (microcode_update_match(cpu, mc_header, 276 if (update_match_cpu(cpu_sig, ext_sig->sig, ext_sig->pf))
275 ext_sig->sig, ext_sig->pf)) 277 return 1;
276 goto find;
277 ext_sig++; 278 ext_sig++;
278 } 279 }
279 return 0; 280 return 0;
280find:
281 pr_debug("microcode: CPU%d found a matching microcode update with"
282 " version 0x%x (current=0x%x)\n",
283 cpu, mc_header->rev, uci->cpu_sig.rev);
284 new_mc = vmalloc(total_size);
285 if (!new_mc) {
286 printk(KERN_ERR "microcode: error! Can not allocate memory\n");
287 return -ENOMEM;
288 }
289
290 /* free previous update file */
291 vfree(uci->mc.mc_intel);
292
293 memcpy(new_mc, mc, total_size);
294 uci->mc.mc_intel = new_mc;
295 return 1;
296} 281}
297 282
298static void apply_microcode(int cpu) 283static void apply_microcode(int cpu)
@@ -300,7 +285,7 @@ static void apply_microcode(int cpu)
300 unsigned long flags; 285 unsigned long flags;
301 unsigned int val[2]; 286 unsigned int val[2];
302 int cpu_num = raw_smp_processor_id(); 287 int cpu_num = raw_smp_processor_id();
303 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; 288 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
304 289
305 /* We should bind the task to the CPU */ 290 /* We should bind the task to the CPU */
306 BUG_ON(cpu_num != cpu); 291 BUG_ON(cpu_num != cpu);
@@ -338,116 +323,105 @@ static void apply_microcode(int cpu)
338 uci->cpu_sig.rev = val[1]; 323 uci->cpu_sig.rev = val[1];
339} 324}
340 325
341#ifdef CONFIG_MICROCODE_OLD_INTERFACE 326static int generic_load_microcode(int cpu, void *data, size_t size,
342extern void __user *user_buffer; /* user area microcode data buffer */ 327 int (*get_ucode_data)(void *, const void *, size_t))
343extern unsigned int user_buffer_size; /* it's size */
344
345static long get_next_ucode(void **mc, long offset)
346{ 328{
347 struct microcode_header_intel mc_header; 329 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
348 unsigned long total_size; 330 u8 *ucode_ptr = data, *new_mc = NULL, *mc;
331 int new_rev = uci->cpu_sig.rev;
332 unsigned int leftover = size;
349 333
350 /* No more data */ 334 while (leftover) {
351 if (offset >= user_buffer_size) 335 struct microcode_header_intel mc_header;
352 return 0; 336 unsigned int mc_size;
353 if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) {
354 printk(KERN_ERR "microcode: error! Can not read user data\n");
355 return -EFAULT;
356 }
357 total_size = get_totalsize(&mc_header);
358 if (offset + total_size > user_buffer_size) {
359 printk(KERN_ERR "microcode: error! Bad total size in microcode "
360 "data file\n");
361 return -EINVAL;
362 }
363 *mc = vmalloc(total_size);
364 if (!*mc)
365 return -ENOMEM;
366 if (copy_from_user(*mc, user_buffer + offset, total_size)) {
367 printk(KERN_ERR "microcode: error! Can not read user data\n");
368 vfree(*mc);
369 return -EFAULT;
370 }
371 return offset + total_size;
372}
373#endif
374 337
375static long get_next_ucode_from_buffer(void **mc, const u8 *buf, 338 if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header)))
376 unsigned long size, long offset) 339 break;
377{
378 struct microcode_header_intel *mc_header;
379 unsigned long total_size;
380 340
381 /* No more data */ 341 mc_size = get_totalsize(&mc_header);
382 if (offset >= size) 342 if (!mc_size || mc_size > leftover) {
383 return 0; 343 printk(KERN_ERR "microcode: error!"
384 mc_header = (struct microcode_header_intel *)(buf + offset); 344 "Bad data in microcode data file\n");
385 total_size = get_totalsize(mc_header); 345 break;
346 }
386 347
387 if (offset + total_size > size) { 348 mc = vmalloc(mc_size);
388 printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); 349 if (!mc)
389 return -EINVAL; 350 break;
351
352 if (get_ucode_data(mc, ucode_ptr, mc_size) ||
353 microcode_sanity_check(mc) < 0) {
354 vfree(mc);
355 break;
356 }
357
358 if (get_matching_microcode(&uci->cpu_sig, mc, new_rev)) {
359 new_rev = mc_header.rev;
360 new_mc = mc;
361 } else
362 vfree(mc);
363
364 ucode_ptr += mc_size;
365 leftover -= mc_size;
390 } 366 }
391 367
392 *mc = vmalloc(total_size); 368 if (new_mc) {
393 if (!*mc) { 369 if (!leftover) {
394 printk(KERN_ERR "microcode: error! Can not allocate memory\n"); 370 if (uci->mc.mc_intel)
395 return -ENOMEM; 371 vfree(uci->mc.mc_intel);
372 uci->mc.mc_intel = (struct microcode_intel *)new_mc;
373 pr_debug("microcode: CPU%d found a matching microcode update with"
374 " version 0x%x (current=0x%x)\n",
375 cpu, uci->mc.mc_intel->hdr.rev, uci->cpu_sig.rev);
376 } else
377 vfree(new_mc);
396 } 378 }
397 memcpy(*mc, buf + offset, total_size); 379
398 return offset + total_size; 380 return (int)leftover;
399} 381}
400 382
401/* fake device for request_firmware */ 383static int get_ucode_fw(void *to, const void *from, size_t n)
402extern struct platform_device *microcode_pdev; 384{
385 memcpy(to, from, n);
386 return 0;
387}
403 388
404static int cpu_request_microcode(int cpu) 389static int request_microcode_fw(int cpu, struct device *device)
405{ 390{
406 char name[30]; 391 char name[30];
407 struct cpuinfo_x86 *c = &cpu_data(cpu); 392 struct cpuinfo_x86 *c = &cpu_data(cpu);
408 const struct firmware *firmware; 393 const struct firmware *firmware;
409 const u8 *buf; 394 int ret;
410 unsigned long size;
411 long offset = 0;
412 int error;
413 void *mc;
414 395
415 /* We should bind the task to the CPU */ 396 /* We should bind the task to the CPU */
416 BUG_ON(cpu != raw_smp_processor_id()); 397 BUG_ON(cpu != raw_smp_processor_id());
417 sprintf(name, "intel-ucode/%02x-%02x-%02x", 398 sprintf(name, "intel-ucode/%02x-%02x-%02x",
418 c->x86, c->x86_model, c->x86_mask); 399 c->x86, c->x86_model, c->x86_mask);
419 error = request_firmware(&firmware, name, &microcode_pdev->dev); 400 ret = request_firmware(&firmware, name, device);
420 if (error) { 401 if (ret) {
421 pr_debug("microcode: data file %s load failed\n", name); 402 pr_debug("microcode: data file %s load failed\n", name);
422 return error; 403 return ret;
423 }
424 buf = firmware->data;
425 size = firmware->size;
426 while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset))
427 > 0) {
428 error = microcode_sanity_check(mc);
429 if (error)
430 break;
431 error = get_matching_microcode(mc, cpu);
432 if (error < 0)
433 break;
434 /*
435 * It's possible the data file has multiple matching ucode,
436 * lets keep searching till the latest version
437 */
438 if (error == 1) {
439 apply_microcode(cpu);
440 error = 0;
441 }
442 vfree(mc);
443 } 404 }
444 if (offset > 0) 405
445 vfree(mc); 406 ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size,
446 if (offset < 0) 407 &get_ucode_fw);
447 error = offset; 408
448 release_firmware(firmware); 409 release_firmware(firmware);
449 410
450 return error; 411 return ret;
412}
413
414static int get_ucode_user(void *to, const void *from, size_t n)
415{
416 return copy_from_user(to, from, n);
417}
418
419static int request_microcode_user(int cpu, const void __user *buf, size_t size)
420{
421 /* We should bind the task to the CPU */
422 BUG_ON(cpu != raw_smp_processor_id());
423
424 return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user);
451} 425}
452 426
453static void microcode_fini_cpu(int cpu) 427static void microcode_fini_cpu(int cpu)
@@ -459,10 +433,8 @@ static void microcode_fini_cpu(int cpu)
459} 433}
460 434
461static struct microcode_ops microcode_intel_ops = { 435static struct microcode_ops microcode_intel_ops = {
462 .get_next_ucode = get_next_ucode, 436 .request_microcode_user = request_microcode_user,
463 .get_matching_microcode = get_matching_microcode, 437 .request_microcode_fw = request_microcode_fw,
464 .microcode_sanity_check = microcode_sanity_check,
465 .cpu_request_microcode = cpu_request_microcode,
466 .collect_cpu_info = collect_cpu_info, 438 .collect_cpu_info = collect_cpu_info,
467 .apply_microcode = apply_microcode, 439 .apply_microcode = apply_microcode,
468 .microcode_fini_cpu = microcode_fini_cpu, 440 .microcode_fini_cpu = microcode_fini_cpu,