aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/microcode.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/microcode.c')
-rw-r--r--arch/x86/kernel/microcode.c155
1 files changed, 102 insertions, 53 deletions
diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c
index ad136ad99cb3..b2f84ce5eed3 100644
--- a/arch/x86/kernel/microcode.c
+++ b/arch/x86/kernel/microcode.c
@@ -71,7 +71,7 @@
71 * Thanks to Stuart Swales for pointing out this bug. 71 * Thanks to Stuart Swales for pointing out this bug.
72 */ 72 */
73 73
74/*#define DEBUG pr_debug */ 74/* #define DEBUG pr_debug */
75#include <linux/capability.h> 75#include <linux/capability.h>
76#include <linux/kernel.h> 76#include <linux/kernel.h>
77#include <linux/init.h> 77#include <linux/init.h>
@@ -104,8 +104,7 @@ MODULE_LICENSE("GPL");
104struct microcode_ops *microcode_ops; 104struct microcode_ops *microcode_ops;
105 105
106/* no concurrent ->write()s are allowed on /dev/cpu/microcode */ 106/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
107DEFINE_MUTEX(microcode_mutex); 107static DEFINE_MUTEX(microcode_mutex);
108EXPORT_SYMBOL_GPL(microcode_mutex);
109 108
110struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; 109struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
111EXPORT_SYMBOL_GPL(ucode_cpu_info); 110EXPORT_SYMBOL_GPL(ucode_cpu_info);
@@ -234,22 +233,6 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
234struct platform_device *microcode_pdev; 233struct platform_device *microcode_pdev;
235EXPORT_SYMBOL_GPL(microcode_pdev); 234EXPORT_SYMBOL_GPL(microcode_pdev);
236 235
237static void microcode_init_cpu(int cpu, int resume)
238{
239 cpumask_t old;
240 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
241
242 old = current->cpus_allowed;
243
244 set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
245 mutex_lock(&microcode_mutex);
246 microcode_ops->collect_cpu_info(cpu);
247 if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
248 microcode_ops->cpu_request_microcode(cpu);
249 mutex_unlock(&microcode_mutex);
250 set_cpus_allowed_ptr(current, &old);
251}
252
253static ssize_t reload_store(struct sys_device *dev, 236static ssize_t reload_store(struct sys_device *dev,
254 struct sysdev_attribute *attr, 237 struct sysdev_attribute *attr,
255 const char *buf, size_t sz) 238 const char *buf, size_t sz)
@@ -266,14 +249,15 @@ static ssize_t reload_store(struct sys_device *dev,
266 cpumask_t old = current->cpus_allowed; 249 cpumask_t old = current->cpus_allowed;
267 250
268 get_online_cpus(); 251 get_online_cpus();
269 set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); 252 if (cpu_online(cpu)) {
270 253 set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
271 mutex_lock(&microcode_mutex); 254 mutex_lock(&microcode_mutex);
272 if (uci->valid) 255 if (uci->valid)
273 err = microcode_ops->cpu_request_microcode(cpu); 256 err = microcode_ops->cpu_request_microcode(cpu);
274 mutex_unlock(&microcode_mutex); 257 mutex_unlock(&microcode_mutex);
258 set_cpus_allowed_ptr(current, &old);
259 }
275 put_online_cpus(); 260 put_online_cpus();
276 set_cpus_allowed_ptr(current, &old);
277 } 261 }
278 if (err) 262 if (err)
279 return err; 263 return err;
@@ -285,7 +269,7 @@ static ssize_t version_show(struct sys_device *dev,
285{ 269{
286 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; 270 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
287 271
288 return sprintf(buf, "0x%x\n", uci->rev); 272 return sprintf(buf, "0x%x\n", uci->cpu_sig.rev);
289} 273}
290 274
291static ssize_t pf_show(struct sys_device *dev, 275static ssize_t pf_show(struct sys_device *dev,
@@ -293,7 +277,7 @@ static ssize_t pf_show(struct sys_device *dev,
293{ 277{
294 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; 278 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
295 279
296 return sprintf(buf, "0x%x\n", uci->pf); 280 return sprintf(buf, "0x%x\n", uci->cpu_sig.pf);
297} 281}
298 282
299static SYSDEV_ATTR(reload, 0200, NULL, reload_store); 283static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
@@ -312,7 +296,85 @@ static struct attribute_group mc_attr_group = {
312 .name = "microcode", 296 .name = "microcode",
313}; 297};
314 298
315static int __mc_sysdev_add(struct sys_device *sys_dev, int resume) 299static void microcode_fini_cpu(int cpu)
300{
301 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
302
303 mutex_lock(&microcode_mutex);
304 microcode_ops->microcode_fini_cpu(cpu);
305 uci->valid = 0;
306 mutex_unlock(&microcode_mutex);
307}
308
309static void collect_cpu_info(int cpu)
310{
311 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
312
313 memset(uci, 0, sizeof(*uci));
314 if (!microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig))
315 uci->valid = 1;
316}
317
318static void microcode_resume_cpu(int cpu)
319{
320 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
321 struct cpu_signature nsig;
322
323 pr_debug("microcode: CPU%d resumed\n", cpu);
324
325 if (!uci->mc.valid_mc)
326 return;
327
328 /*
329 * Let's verify that the 'cached' ucode does belong
330 * to this cpu (a bit of paranoia):
331 */
332 if (microcode_ops->collect_cpu_info(cpu, &nsig)) {
333 microcode_fini_cpu(cpu);
334 return;
335 }
336
337 if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) {
338 microcode_fini_cpu(cpu);
339 /* Should we look for a new ucode here? */
340 return;
341 }
342
343 microcode_ops->apply_microcode(cpu);
344}
345
346void microcode_update_cpu(int cpu)
347{
348 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
349
350 /* We should bind the task to the CPU */
351 BUG_ON(raw_smp_processor_id() != cpu);
352
353 mutex_lock(&microcode_mutex);
354 /*
355 * Check if the system resume is in progress (uci->valid != NULL),
356 * otherwise just request a firmware:
357 */
358 if (uci->valid) {
359 microcode_resume_cpu(cpu);
360 } else {
361 collect_cpu_info(cpu);
362 if (uci->valid && system_state == SYSTEM_RUNNING)
363 microcode_ops->cpu_request_microcode(cpu);
364 }
365 mutex_unlock(&microcode_mutex);
366}
367
368static void microcode_init_cpu(int cpu)
369{
370 cpumask_t old = current->cpus_allowed;
371
372 set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
373 microcode_update_cpu(cpu);
374 set_cpus_allowed_ptr(current, &old);
375}
376
377static int mc_sysdev_add(struct sys_device *sys_dev)
316{ 378{
317 int err, cpu = sys_dev->id; 379 int err, cpu = sys_dev->id;
318 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 380 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
@@ -327,16 +389,10 @@ static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
327 if (err) 389 if (err)
328 return err; 390 return err;
329 391
330 microcode_init_cpu(cpu, resume); 392 microcode_init_cpu(cpu);
331
332 return 0; 393 return 0;
333} 394}
334 395
335static int mc_sysdev_add(struct sys_device *sys_dev)
336{
337 return __mc_sysdev_add(sys_dev, 0);
338}
339
340static int mc_sysdev_remove(struct sys_device *sys_dev) 396static int mc_sysdev_remove(struct sys_device *sys_dev)
341{ 397{
342 int cpu = sys_dev->id; 398 int cpu = sys_dev->id;
@@ -345,7 +401,7 @@ static int mc_sysdev_remove(struct sys_device *sys_dev)
345 return 0; 401 return 0;
346 402
347 pr_debug("microcode: CPU%d removed\n", cpu); 403 pr_debug("microcode: CPU%d removed\n", cpu);
348 microcode_ops->microcode_fini_cpu(cpu); 404 microcode_fini_cpu(cpu);
349 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); 405 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
350 return 0; 406 return 0;
351} 407}
@@ -376,33 +432,26 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
376 432
377 sys_dev = get_cpu_sysdev(cpu); 433 sys_dev = get_cpu_sysdev(cpu);
378 switch (action) { 434 switch (action) {
379 case CPU_UP_CANCELED_FROZEN:
380 /* The CPU refused to come up during a system resume */
381 microcode_ops->microcode_fini_cpu(cpu);
382 break;
383 case CPU_ONLINE: 435 case CPU_ONLINE:
384 case CPU_DOWN_FAILED:
385 mc_sysdev_add(sys_dev);
386 break;
387 case CPU_ONLINE_FROZEN: 436 case CPU_ONLINE_FROZEN:
388 /* System-wide resume is in progress, try to apply microcode */ 437 microcode_init_cpu(cpu);
389 if (microcode_ops->apply_microcode_check_cpu(cpu)) { 438 case CPU_DOWN_FAILED:
390 /* The application of microcode failed */
391 microcode_ops->microcode_fini_cpu(cpu);
392 __mc_sysdev_add(sys_dev, 1);
393 break;
394 }
395 case CPU_DOWN_FAILED_FROZEN: 439 case CPU_DOWN_FAILED_FROZEN:
440 pr_debug("microcode: CPU%d added\n", cpu);
396 if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) 441 if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
397 printk(KERN_ERR "microcode: Failed to create the sysfs " 442 printk(KERN_ERR "microcode: Failed to create the sysfs "
398 "group for CPU%d\n", cpu); 443 "group for CPU%d\n", cpu);
399 break; 444 break;
400 case CPU_DOWN_PREPARE: 445 case CPU_DOWN_PREPARE:
401 mc_sysdev_remove(sys_dev);
402 break;
403 case CPU_DOWN_PREPARE_FROZEN: 446 case CPU_DOWN_PREPARE_FROZEN:
404 /* Suspend is in progress, only remove the interface */ 447 /* Suspend is in progress, only remove the interface */
405 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); 448 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
449 pr_debug("microcode: CPU%d removed\n", cpu);
450 break;
451 case CPU_DEAD:
452 case CPU_UP_CANCELED_FROZEN:
453 /* The CPU refused to come up during a system resume */
454 microcode_fini_cpu(cpu);
406 break; 455 break;
407 } 456 }
408 return NOTIFY_OK; 457 return NOTIFY_OK;