diff options
-rw-r--r-- | arch/i386/kernel/microcode.c | 71 | ||||
-rw-r--r-- | include/linux/cpu.h | 4 | ||||
-rw-r--r-- | kernel/cpu.c | 32 |
3 files changed, 87 insertions, 20 deletions
diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index b8f16633a6ec..cbe7ec8dbb9f 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c | |||
@@ -567,6 +567,53 @@ static int cpu_request_microcode(int cpu) | |||
567 | return error; | 567 | return error; |
568 | } | 568 | } |
569 | 569 | ||
570 | static int apply_microcode_on_cpu(int cpu) | ||
571 | { | ||
572 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
573 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | ||
574 | cpumask_t old; | ||
575 | unsigned int val[2]; | ||
576 | int err = 0; | ||
577 | |||
578 | if (!uci->mc) | ||
579 | return -EINVAL; | ||
580 | |||
581 | old = current->cpus_allowed; | ||
582 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | ||
583 | |||
584 | /* Check if the microcode we have in memory matches the CPU */ | ||
585 | if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || | ||
586 | cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001)) | ||
587 | err = -EINVAL; | ||
588 | |||
589 | if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) { | ||
590 | /* get processor flags from MSR 0x17 */ | ||
591 | rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); | ||
592 | if (uci->pf != (1 << ((val[1] >> 18) & 7))) | ||
593 | err = -EINVAL; | ||
594 | } | ||
595 | |||
596 | if (!err) { | ||
597 | wrmsr(MSR_IA32_UCODE_REV, 0, 0); | ||
598 | /* see notes above for revision 1.07. Apparent chip bug */ | ||
599 | sync_core(); | ||
600 | /* get the current revision from MSR 0x8B */ | ||
601 | rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); | ||
602 | if (uci->rev != val[1]) | ||
603 | err = -EINVAL; | ||
604 | } | ||
605 | |||
606 | if (!err) | ||
607 | apply_microcode(cpu); | ||
608 | else | ||
609 | printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:" | ||
610 | " sig=0x%x, pf=0x%x, rev=0x%x\n", | ||
611 | cpu, uci->sig, uci->pf, uci->rev); | ||
612 | |||
613 | set_cpus_allowed(current, old); | ||
614 | return err; | ||
615 | } | ||
616 | |||
570 | static void microcode_init_cpu(int cpu) | 617 | static void microcode_init_cpu(int cpu) |
571 | { | 618 | { |
572 | cpumask_t old; | 619 | cpumask_t old; |
@@ -577,7 +624,8 @@ static void microcode_init_cpu(int cpu) | |||
577 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | 624 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); |
578 | mutex_lock(µcode_mutex); | 625 | mutex_lock(µcode_mutex); |
579 | collect_cpu_info(cpu); | 626 | collect_cpu_info(cpu); |
580 | if (uci->valid && system_state == SYSTEM_RUNNING) | 627 | if (uci->valid && system_state == SYSTEM_RUNNING && |
628 | !suspend_cpu_hotplug) | ||
581 | cpu_request_microcode(cpu); | 629 | cpu_request_microcode(cpu); |
582 | mutex_unlock(µcode_mutex); | 630 | mutex_unlock(µcode_mutex); |
583 | set_cpus_allowed(current, old); | 631 | set_cpus_allowed(current, old); |
@@ -663,13 +711,24 @@ static int mc_sysdev_add(struct sys_device *sys_dev) | |||
663 | return 0; | 711 | return 0; |
664 | 712 | ||
665 | pr_debug("Microcode:CPU %d added\n", cpu); | 713 | pr_debug("Microcode:CPU %d added\n", cpu); |
666 | memset(uci, 0, sizeof(*uci)); | 714 | /* If suspend_cpu_hotplug is set, the system is resuming and we should |
715 | * use the data from before the suspend. | ||
716 | */ | ||
717 | if (suspend_cpu_hotplug) { | ||
718 | err = apply_microcode_on_cpu(cpu); | ||
719 | if (err) | ||
720 | microcode_fini_cpu(cpu); | ||
721 | } | ||
722 | if (!uci->valid) | ||
723 | memset(uci, 0, sizeof(*uci)); | ||
667 | 724 | ||
668 | err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); | 725 | err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); |
669 | if (err) | 726 | if (err) |
670 | return err; | 727 | return err; |
671 | 728 | ||
672 | microcode_init_cpu(cpu); | 729 | if (!uci->valid) |
730 | microcode_init_cpu(cpu); | ||
731 | |||
673 | return 0; | 732 | return 0; |
674 | } | 733 | } |
675 | 734 | ||
@@ -680,7 +739,11 @@ static int mc_sysdev_remove(struct sys_device *sys_dev) | |||
680 | if (!cpu_online(cpu)) | 739 | if (!cpu_online(cpu)) |
681 | return 0; | 740 | return 0; |
682 | pr_debug("Microcode:CPU %d removed\n", cpu); | 741 | pr_debug("Microcode:CPU %d removed\n", cpu); |
683 | microcode_fini_cpu(cpu); | 742 | /* If suspend_cpu_hotplug is set, the system is suspending and we should |
743 | * keep the microcode in memory for the resume. | ||
744 | */ | ||
745 | if (!suspend_cpu_hotplug) | ||
746 | microcode_fini_cpu(cpu); | ||
684 | sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); | 747 | sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); |
685 | return 0; | 748 | return 0; |
686 | } | 749 | } |
diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 769ddc6df492..c22b0dfcbcd2 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h | |||
@@ -127,9 +127,13 @@ static inline int cpu_is_offline(int cpu) { return 0; } | |||
127 | #endif /* CONFIG_HOTPLUG_CPU */ | 127 | #endif /* CONFIG_HOTPLUG_CPU */ |
128 | 128 | ||
129 | #ifdef CONFIG_SUSPEND_SMP | 129 | #ifdef CONFIG_SUSPEND_SMP |
130 | extern int suspend_cpu_hotplug; | ||
131 | |||
130 | extern int disable_nonboot_cpus(void); | 132 | extern int disable_nonboot_cpus(void); |
131 | extern void enable_nonboot_cpus(void); | 133 | extern void enable_nonboot_cpus(void); |
132 | #else | 134 | #else |
135 | #define suspend_cpu_hotplug 0 | ||
136 | |||
133 | static inline int disable_nonboot_cpus(void) { return 0; } | 137 | static inline int disable_nonboot_cpus(void) { return 0; } |
134 | static inline void enable_nonboot_cpus(void) {} | 138 | static inline void enable_nonboot_cpus(void) {} |
135 | #endif | 139 | #endif |
diff --git a/kernel/cpu.c b/kernel/cpu.c index 3d4206ada5c9..36e70845cfc3 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -254,6 +254,12 @@ int __cpuinit cpu_up(unsigned int cpu) | |||
254 | } | 254 | } |
255 | 255 | ||
256 | #ifdef CONFIG_SUSPEND_SMP | 256 | #ifdef CONFIG_SUSPEND_SMP |
257 | /* Needed to prevent the microcode driver from requesting firmware in its CPU | ||
258 | * hotplug notifier during the suspend/resume. | ||
259 | */ | ||
260 | int suspend_cpu_hotplug; | ||
261 | EXPORT_SYMBOL(suspend_cpu_hotplug); | ||
262 | |||
257 | static cpumask_t frozen_cpus; | 263 | static cpumask_t frozen_cpus; |
258 | 264 | ||
259 | int disable_nonboot_cpus(void) | 265 | int disable_nonboot_cpus(void) |
@@ -261,16 +267,8 @@ int disable_nonboot_cpus(void) | |||
261 | int cpu, first_cpu, error = 0; | 267 | int cpu, first_cpu, error = 0; |
262 | 268 | ||
263 | mutex_lock(&cpu_add_remove_lock); | 269 | mutex_lock(&cpu_add_remove_lock); |
264 | first_cpu = first_cpu(cpu_present_map); | 270 | suspend_cpu_hotplug = 1; |
265 | if (!cpu_online(first_cpu)) { | 271 | first_cpu = first_cpu(cpu_online_map); |
266 | error = _cpu_up(first_cpu); | ||
267 | if (error) { | ||
268 | printk(KERN_ERR "Could not bring CPU%d up.\n", | ||
269 | first_cpu); | ||
270 | goto out; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* We take down all of the non-boot CPUs in one shot to avoid races | 272 | /* We take down all of the non-boot CPUs in one shot to avoid races |
275 | * with the userspace trying to use the CPU hotplug at the same time | 273 | * with the userspace trying to use the CPU hotplug at the same time |
276 | */ | 274 | */ |
@@ -296,7 +294,7 @@ int disable_nonboot_cpus(void) | |||
296 | } else { | 294 | } else { |
297 | printk(KERN_ERR "Non-boot CPUs are not disabled\n"); | 295 | printk(KERN_ERR "Non-boot CPUs are not disabled\n"); |
298 | } | 296 | } |
299 | out: | 297 | suspend_cpu_hotplug = 0; |
300 | mutex_unlock(&cpu_add_remove_lock); | 298 | mutex_unlock(&cpu_add_remove_lock); |
301 | return error; | 299 | return error; |
302 | } | 300 | } |
@@ -308,20 +306,22 @@ void enable_nonboot_cpus(void) | |||
308 | /* Allow everyone to use the CPU hotplug again */ | 306 | /* Allow everyone to use the CPU hotplug again */ |
309 | mutex_lock(&cpu_add_remove_lock); | 307 | mutex_lock(&cpu_add_remove_lock); |
310 | cpu_hotplug_disabled = 0; | 308 | cpu_hotplug_disabled = 0; |
311 | mutex_unlock(&cpu_add_remove_lock); | ||
312 | if (cpus_empty(frozen_cpus)) | 309 | if (cpus_empty(frozen_cpus)) |
313 | return; | 310 | goto out; |
314 | 311 | ||
312 | suspend_cpu_hotplug = 1; | ||
315 | printk("Enabling non-boot CPUs ...\n"); | 313 | printk("Enabling non-boot CPUs ...\n"); |
316 | for_each_cpu_mask(cpu, frozen_cpus) { | 314 | for_each_cpu_mask(cpu, frozen_cpus) { |
317 | error = cpu_up(cpu); | 315 | error = _cpu_up(cpu); |
318 | if (!error) { | 316 | if (!error) { |
319 | printk("CPU%d is up\n", cpu); | 317 | printk("CPU%d is up\n", cpu); |
320 | continue; | 318 | continue; |
321 | } | 319 | } |
322 | printk(KERN_WARNING "Error taking CPU%d up: %d\n", | 320 | printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error); |
323 | cpu, error); | ||
324 | } | 321 | } |
325 | cpus_clear(frozen_cpus); | 322 | cpus_clear(frozen_cpus); |
323 | suspend_cpu_hotplug = 0; | ||
324 | out: | ||
325 | mutex_unlock(&cpu_add_remove_lock); | ||
326 | } | 326 | } |
327 | #endif | 327 | #endif |