diff options
author | Shaohua Li <shaohua.li@intel.com> | 2006-09-27 04:50:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-27 11:26:18 -0400 |
commit | a30a6a2cb0fdc2c9701d6ddfb21affeb8146c038 (patch) | |
tree | 72502de7266fbed2e4791cc162ef7607710f7cd0 /arch/i386/kernel | |
parent | 9a3110bf4bb0466b43b898533bfd4952001bc38f (diff) |
[PATCH] x86 microcode: using request_firmware to pull microcode
Using request_firmware to pull ucode from userspace, so we don't need the
application 'microcode_ctl' to assist. We name each ucode file according
to CPU's info as intel-ucode/family-model-stepping. In this way we could
split ucode file as small one. This has a lot of advantages such as
selectively update and validate microcode for specific models, better
manage microcode file, easily write tools for administerators and so on.
with the changes, we should put all intel-ucode/xx-xx-xx microcode files
into the firmware dir (I had a tool to split previous big data file into
small one and later we will release new style data file). The init script
should be changed to just loading the driver without unloading
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Acked-by: Tigran Aivazian <tigran@veritas.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r-- | arch/i386/kernel/microcode.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 00bdc3dd693e..f8f1ec5f26c1 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c | |||
@@ -83,6 +83,9 @@ | |||
83 | #include <linux/spinlock.h> | 83 | #include <linux/spinlock.h> |
84 | #include <linux/mm.h> | 84 | #include <linux/mm.h> |
85 | #include <linux/mutex.h> | 85 | #include <linux/mutex.h> |
86 | #include <linux/cpu.h> | ||
87 | #include <linux/firmware.h> | ||
88 | #include <linux/platform_device.h> | ||
86 | 89 | ||
87 | #include <asm/msr.h> | 90 | #include <asm/msr.h> |
88 | #include <asm/uaccess.h> | 91 | #include <asm/uaccess.h> |
@@ -493,6 +496,112 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); | |||
493 | #define microcode_dev_exit() do { } while(0) | 496 | #define microcode_dev_exit() do { } while(0) |
494 | #endif | 497 | #endif |
495 | 498 | ||
499 | static long get_next_ucode_from_buffer(void **mc, void *buf, | ||
500 | unsigned long size, long offset) | ||
501 | { | ||
502 | microcode_header_t *mc_header; | ||
503 | unsigned long total_size; | ||
504 | |||
505 | /* No more data */ | ||
506 | if (offset >= size) | ||
507 | return 0; | ||
508 | mc_header = (microcode_header_t *)(buf + offset); | ||
509 | total_size = get_totalsize(mc_header); | ||
510 | |||
511 | if ((offset + total_size > size) | ||
512 | || (total_size < DEFAULT_UCODE_TOTALSIZE)) { | ||
513 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | |||
517 | *mc = vmalloc(total_size); | ||
518 | if (!*mc) { | ||
519 | printk(KERN_ERR "microcode: error! Can not allocate memory\n"); | ||
520 | return -ENOMEM; | ||
521 | } | ||
522 | memcpy(*mc, buf + offset, total_size); | ||
523 | return offset + total_size; | ||
524 | } | ||
525 | |||
526 | /* fake device for request_firmware */ | ||
527 | static struct platform_device *microcode_pdev; | ||
528 | |||
529 | static int cpu_request_microcode(int cpu) | ||
530 | { | ||
531 | char name[30]; | ||
532 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
533 | const struct firmware *firmware; | ||
534 | void * buf; | ||
535 | unsigned long size; | ||
536 | long offset = 0; | ||
537 | int error; | ||
538 | void *mc; | ||
539 | |||
540 | /* We should bind the task to the CPU */ | ||
541 | BUG_ON(cpu != raw_smp_processor_id()); | ||
542 | sprintf(name,"intel-ucode/%02x-%02x-%02x", | ||
543 | c->x86, c->x86_model, c->x86_mask); | ||
544 | error = request_firmware(&firmware, name, µcode_pdev->dev); | ||
545 | if (error) { | ||
546 | pr_debug("ucode data file %s load failed\n", name); | ||
547 | return error; | ||
548 | } | ||
549 | buf = (void *)firmware->data; | ||
550 | size = firmware->size; | ||
551 | while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) | ||
552 | > 0) { | ||
553 | error = microcode_sanity_check(mc); | ||
554 | if (error) | ||
555 | break; | ||
556 | error = get_maching_microcode(mc, cpu); | ||
557 | if (error < 0) | ||
558 | break; | ||
559 | /* | ||
560 | * It's possible the data file has multiple matching ucode, | ||
561 | * lets keep searching till the latest version | ||
562 | */ | ||
563 | if (error == 1) { | ||
564 | apply_microcode(cpu); | ||
565 | error = 0; | ||
566 | } | ||
567 | vfree(mc); | ||
568 | } | ||
569 | if (offset > 0) | ||
570 | vfree(mc); | ||
571 | if (offset < 0) | ||
572 | error = offset; | ||
573 | release_firmware(firmware); | ||
574 | |||
575 | return error; | ||
576 | } | ||
577 | |||
578 | static void microcode_init_cpu(int cpu) | ||
579 | { | ||
580 | cpumask_t old; | ||
581 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | ||
582 | |||
583 | old = current->cpus_allowed; | ||
584 | |||
585 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | ||
586 | mutex_lock(µcode_mutex); | ||
587 | collect_cpu_info(cpu); | ||
588 | if (uci->valid) | ||
589 | cpu_request_microcode(cpu); | ||
590 | mutex_unlock(µcode_mutex); | ||
591 | set_cpus_allowed(current, old); | ||
592 | } | ||
593 | |||
594 | static void microcode_fini_cpu(int cpu) | ||
595 | { | ||
596 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | ||
597 | |||
598 | mutex_lock(µcode_mutex); | ||
599 | uci->valid = 0; | ||
600 | vfree(uci->mc); | ||
601 | uci->mc = NULL; | ||
602 | mutex_unlock(µcode_mutex); | ||
603 | } | ||
604 | |||
496 | static int __init microcode_init (void) | 605 | static int __init microcode_init (void) |
497 | { | 606 | { |
498 | int error; | 607 | int error; |
@@ -500,6 +609,12 @@ static int __init microcode_init (void) | |||
500 | error = microcode_dev_init(); | 609 | error = microcode_dev_init(); |
501 | if (error) | 610 | if (error) |
502 | return error; | 611 | return error; |
612 | microcode_pdev = platform_device_register_simple("microcode", -1, | ||
613 | NULL, 0); | ||
614 | if (IS_ERR(microcode_pdev)) { | ||
615 | microcode_dev_exit(); | ||
616 | return PTR_ERR(microcode_pdev); | ||
617 | } | ||
503 | 618 | ||
504 | printk(KERN_INFO | 619 | printk(KERN_INFO |
505 | "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@veritas.com>\n"); | 620 | "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@veritas.com>\n"); |
@@ -509,6 +624,7 @@ static int __init microcode_init (void) | |||
509 | static void __exit microcode_exit (void) | 624 | static void __exit microcode_exit (void) |
510 | { | 625 | { |
511 | microcode_dev_exit(); | 626 | microcode_dev_exit(); |
627 | platform_device_unregister(microcode_pdev); | ||
512 | } | 628 | } |
513 | 629 | ||
514 | module_init(microcode_init) | 630 | module_init(microcode_init) |