aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorShaohua Li <shaohua.li@intel.com>2006-09-27 04:50:53 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-27 11:26:18 -0400
commit9a4b9efa1d39d7d31bed08fbe5a9b2a03b2759d4 (patch)
tree61a75eddfe151c4ba7212e69222f11cb6393b487 /arch/i386
parenta30a6a2cb0fdc2c9701d6ddfb21affeb8146c038 (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/Kconfig1
-rw-r--r--arch/i386/kernel/microcode.c154
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
402config MICROCODE 402config 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(&microcode_mutex); 602 mutex_unlock(&microcode_mutex);
603} 603}
604 604
605static 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(&microcode_mutex);
624 if (uci->valid)
625 err = cpu_request_microcode(cpu);
626 mutex_unlock(&microcode_mutex);
627 unlock_cpu_hotplug();
628 set_cpus_allowed(current, old);
629 }
630 if (err)
631 return err;
632 return sz;
633}
634
635static 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
642static 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
649static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
650static SYSDEV_ATTR(version, 0400, version_show, NULL);
651static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL);
652
653static struct attribute *mc_default_attrs[] = {
654 &attr_reload.attr,
655 &attr_version.attr,
656 &attr_processor_flags.attr,
657 NULL
658};
659
660static struct attribute_group mc_attr_group = {
661 .attrs = mc_default_attrs,
662 .name = "microcode",
663};
664
665static 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
680static 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
692static 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
704static 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
711static __cpuinit int
712mc_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
730static struct notifier_block mc_cpu_notifier = {
731 .notifier_call = mc_cpu_callback,
732};
733#endif
734
605static int __init microcode_init (void) 735static 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)
624static void __exit microcode_exit (void) 765static 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