diff options
-rw-r--r-- | arch/i386/Kconfig | 1 | ||||
-rw-r--r-- | arch/i386/kernel/microcode.c | 154 | ||||
-rw-r--r-- | arch/x86_64/Kconfig | 1 |
3 files changed, 153 insertions, 3 deletions
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 0cbeb26ac3fb..3fd2f256f2be 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig | |||
@@ -401,6 +401,7 @@ config X86_REBOOTFIXUPS | |||
401 | 401 | ||
402 | config MICROCODE | 402 | config MICROCODE |
403 | tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support" | 403 | tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support" |
404 | select FW_LOADER | ||
404 | ---help--- | 405 | ---help--- |
405 | If you say Y here and also to "/dev file system support" in the | 406 | If you say Y here and also to "/dev file system support" in the |
406 | 'File systems' section, you will be able to update the microcode on | 407 | 'File systems' section, you will be able to update the microcode on |
diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index f8f1ec5f26c1..467901ebb992 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c | |||
@@ -219,7 +219,7 @@ static int microcode_sanity_check(void *mc) | |||
219 | /* check extended table checksum */ | 219 | /* check extended table checksum */ |
220 | if (ext_table_size) { | 220 | if (ext_table_size) { |
221 | int ext_table_sum = 0; | 221 | int ext_table_sum = 0; |
222 | int * ext_tablep = (int *)ext_header; | 222 | int *ext_tablep = (int *)ext_header; |
223 | 223 | ||
224 | i = ext_table_size / DWSIZE; | 224 | i = ext_table_size / DWSIZE; |
225 | while (i--) | 225 | while (i--) |
@@ -386,7 +386,7 @@ static int do_microcode_update (void) | |||
386 | { | 386 | { |
387 | long cursor = 0; | 387 | long cursor = 0; |
388 | int error = 0; | 388 | int error = 0; |
389 | void * new_mc; | 389 | void *new_mc; |
390 | int cpu; | 390 | int cpu; |
391 | cpumask_t old; | 391 | cpumask_t old; |
392 | 392 | ||
@@ -531,7 +531,7 @@ static int cpu_request_microcode(int cpu) | |||
531 | char name[30]; | 531 | char name[30]; |
532 | struct cpuinfo_x86 *c = cpu_data + cpu; | 532 | struct cpuinfo_x86 *c = cpu_data + cpu; |
533 | const struct firmware *firmware; | 533 | const struct firmware *firmware; |
534 | void * buf; | 534 | void *buf; |
535 | unsigned long size; | 535 | unsigned long size; |
536 | long offset = 0; | 536 | long offset = 0; |
537 | int error; | 537 | int error; |
@@ -602,6 +602,136 @@ static void microcode_fini_cpu(int cpu) | |||
602 | mutex_unlock(µcode_mutex); | 602 | mutex_unlock(µcode_mutex); |
603 | } | 603 | } |
604 | 604 | ||
605 | static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) | ||
606 | { | ||
607 | struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; | ||
608 | char *end; | ||
609 | unsigned long val = simple_strtoul(buf, &end, 0); | ||
610 | int err = 0; | ||
611 | int cpu = dev->id; | ||
612 | |||
613 | if (end == buf) | ||
614 | return -EINVAL; | ||
615 | if (val == 1) { | ||
616 | cpumask_t old; | ||
617 | |||
618 | old = current->cpus_allowed; | ||
619 | |||
620 | lock_cpu_hotplug(); | ||
621 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | ||
622 | |||
623 | mutex_lock(µcode_mutex); | ||
624 | if (uci->valid) | ||
625 | err = cpu_request_microcode(cpu); | ||
626 | mutex_unlock(µcode_mutex); | ||
627 | unlock_cpu_hotplug(); | ||
628 | set_cpus_allowed(current, old); | ||
629 | } | ||
630 | if (err) | ||
631 | return err; | ||
632 | return sz; | ||
633 | } | ||
634 | |||
635 | static ssize_t version_show(struct sys_device *dev, char *buf) | ||
636 | { | ||
637 | struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; | ||
638 | |||
639 | return sprintf(buf, "0x%x\n", uci->rev); | ||
640 | } | ||
641 | |||
642 | static ssize_t pf_show(struct sys_device *dev, char *buf) | ||
643 | { | ||
644 | struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; | ||
645 | |||
646 | return sprintf(buf, "0x%x\n", uci->pf); | ||
647 | } | ||
648 | |||
649 | static SYSDEV_ATTR(reload, 0200, NULL, reload_store); | ||
650 | static SYSDEV_ATTR(version, 0400, version_show, NULL); | ||
651 | static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL); | ||
652 | |||
653 | static struct attribute *mc_default_attrs[] = { | ||
654 | &attr_reload.attr, | ||
655 | &attr_version.attr, | ||
656 | &attr_processor_flags.attr, | ||
657 | NULL | ||
658 | }; | ||
659 | |||
660 | static struct attribute_group mc_attr_group = { | ||
661 | .attrs = mc_default_attrs, | ||
662 | .name = "microcode", | ||
663 | }; | ||
664 | |||
665 | static int mc_sysdev_add(struct sys_device *sys_dev) | ||
666 | { | ||
667 | int cpu = sys_dev->id; | ||
668 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | ||
669 | |||
670 | if (!cpu_online(cpu)) | ||
671 | return 0; | ||
672 | pr_debug("Microcode:CPU %d added\n", cpu); | ||
673 | memset(uci, 0, sizeof(*uci)); | ||
674 | sysfs_create_group(&sys_dev->kobj, &mc_attr_group); | ||
675 | |||
676 | microcode_init_cpu(cpu); | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | static int mc_sysdev_remove(struct sys_device *sys_dev) | ||
681 | { | ||
682 | int cpu = sys_dev->id; | ||
683 | |||
684 | if (!cpu_online(cpu)) | ||
685 | return 0; | ||
686 | pr_debug("Microcode:CPU %d removed\n", cpu); | ||
687 | microcode_fini_cpu(cpu); | ||
688 | sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | static int mc_sysdev_resume(struct sys_device *dev) | ||
693 | { | ||
694 | int cpu = dev->id; | ||
695 | |||
696 | if (!cpu_online(cpu)) | ||
697 | return 0; | ||
698 | pr_debug("Microcode:CPU %d resumed\n", cpu); | ||
699 | /* only CPU 0 will apply ucode here */ | ||
700 | apply_microcode(0); | ||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static struct sysdev_driver mc_sysdev_driver = { | ||
705 | .add = mc_sysdev_add, | ||
706 | .remove = mc_sysdev_remove, | ||
707 | .resume = mc_sysdev_resume, | ||
708 | }; | ||
709 | |||
710 | #ifdef CONFIG_HOTPLUG_CPU | ||
711 | static __cpuinit int | ||
712 | mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) | ||
713 | { | ||
714 | unsigned int cpu = (unsigned long)hcpu; | ||
715 | struct sys_device *sys_dev; | ||
716 | |||
717 | sys_dev = get_cpu_sysdev(cpu); | ||
718 | switch (action) { | ||
719 | case CPU_ONLINE: | ||
720 | case CPU_DOWN_FAILED: | ||
721 | mc_sysdev_add(sys_dev); | ||
722 | break; | ||
723 | case CPU_DOWN_PREPARE: | ||
724 | mc_sysdev_remove(sys_dev); | ||
725 | break; | ||
726 | } | ||
727 | return NOTIFY_OK; | ||
728 | } | ||
729 | |||
730 | static struct notifier_block mc_cpu_notifier = { | ||
731 | .notifier_call = mc_cpu_callback, | ||
732 | }; | ||
733 | #endif | ||
734 | |||
605 | static int __init microcode_init (void) | 735 | static int __init microcode_init (void) |
606 | { | 736 | { |
607 | int error; | 737 | int error; |
@@ -616,6 +746,17 @@ static int __init microcode_init (void) | |||
616 | return PTR_ERR(microcode_pdev); | 746 | return PTR_ERR(microcode_pdev); |
617 | } | 747 | } |
618 | 748 | ||
749 | lock_cpu_hotplug(); | ||
750 | error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); | ||
751 | unlock_cpu_hotplug(); | ||
752 | if (error) { | ||
753 | microcode_dev_exit(); | ||
754 | platform_device_unregister(microcode_pdev); | ||
755 | return error; | ||
756 | } | ||
757 | |||
758 | register_hotcpu_notifier(&mc_cpu_notifier); | ||
759 | |||
619 | printk(KERN_INFO | 760 | printk(KERN_INFO |
620 | "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@veritas.com>\n"); | 761 | "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@veritas.com>\n"); |
621 | return 0; | 762 | return 0; |
@@ -624,6 +765,13 @@ static int __init microcode_init (void) | |||
624 | static void __exit microcode_exit (void) | 765 | static void __exit microcode_exit (void) |
625 | { | 766 | { |
626 | microcode_dev_exit(); | 767 | microcode_dev_exit(); |
768 | |||
769 | unregister_hotcpu_notifier(&mc_cpu_notifier); | ||
770 | |||
771 | lock_cpu_hotplug(); | ||
772 | sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); | ||
773 | unlock_cpu_hotplug(); | ||
774 | |||
627 | platform_device_unregister(microcode_pdev); | 775 | platform_device_unregister(microcode_pdev); |
628 | } | 776 | } |
629 | 777 | ||
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index ab4cbcb9e3b3..32ae1378f35c 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig | |||
@@ -171,6 +171,7 @@ config X86_GOOD_APIC | |||
171 | 171 | ||
172 | config MICROCODE | 172 | config MICROCODE |
173 | tristate "/dev/cpu/microcode - Intel CPU microcode support" | 173 | tristate "/dev/cpu/microcode - Intel CPU microcode support" |
174 | select FW_LOADER | ||
174 | ---help--- | 175 | ---help--- |
175 | If you say Y here the 'File systems' section, you will be | 176 | If you say Y here the 'File systems' section, you will be |
176 | able to update the microcode on Intel processors. You will | 177 | able to update the microcode on Intel processors. You will |