diff options
author | Shaohua Li <shaohua.li@intel.com> | 2006-09-27 04:50:53 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-27 11:26:18 -0400 |
commit | 9a4b9efa1d39d7d31bed08fbe5a9b2a03b2759d4 (patch) | |
tree | 61a75eddfe151c4ba7212e69222f11cb6393b487 /arch/i386 | |
parent | a30a6a2cb0fdc2c9701d6ddfb21affeb8146c038 (diff) |
[PATCH] x86 microcode: add sysfs and hotplug support
Add sysfs support. Currently each CPU has three microcode related
attributes. One is 'version' which shows current ucode version of CPU.
Tools can use the attribute do validation or show CPU ucode status. one is
'reload' which allows manually reloading ucode. Another is
'processor_flags', which exports processor flags, so we can write tools to
check if CPU has latest ucode. Also add suspend/resume and CPU hotplug
support.
[akpm@osdl.org: cleanups, build fix]
[bunk@stusta.de: Kconfig fixes]
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: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/Kconfig | 1 | ||||
-rw-r--r-- | arch/i386/kernel/microcode.c | 154 |
2 files changed, 152 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 | ||