diff options
Diffstat (limited to 'arch/x86/kernel/microcode_intel.c')
-rw-r--r-- | arch/x86/kernel/microcode_intel.c | 90 |
1 files changed, 33 insertions, 57 deletions
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c index 149b9ec7c1ab..0d334ddd0a96 100644 --- a/arch/x86/kernel/microcode_intel.c +++ b/arch/x86/kernel/microcode_intel.c | |||
@@ -70,24 +70,11 @@ | |||
70 | * Fix sigmatch() macro to handle old CPUs with pf == 0. | 70 | * Fix sigmatch() macro to handle old CPUs with pf == 0. |
71 | * Thanks to Stuart Swales for pointing out this bug. | 71 | * Thanks to Stuart Swales for pointing out this bug. |
72 | */ | 72 | */ |
73 | #include <linux/platform_device.h> | ||
74 | #include <linux/capability.h> | ||
75 | #include <linux/miscdevice.h> | ||
76 | #include <linux/firmware.h> | 73 | #include <linux/firmware.h> |
77 | #include <linux/smp_lock.h> | ||
78 | #include <linux/spinlock.h> | ||
79 | #include <linux/cpumask.h> | ||
80 | #include <linux/uaccess.h> | 74 | #include <linux/uaccess.h> |
81 | #include <linux/vmalloc.h> | ||
82 | #include <linux/kernel.h> | 75 | #include <linux/kernel.h> |
83 | #include <linux/module.h> | 76 | #include <linux/module.h> |
84 | #include <linux/mutex.h> | 77 | #include <linux/vmalloc.h> |
85 | #include <linux/sched.h> | ||
86 | #include <linux/init.h> | ||
87 | #include <linux/slab.h> | ||
88 | #include <linux/cpu.h> | ||
89 | #include <linux/fs.h> | ||
90 | #include <linux/mm.h> | ||
91 | 78 | ||
92 | #include <asm/microcode.h> | 79 | #include <asm/microcode.h> |
93 | #include <asm/processor.h> | 80 | #include <asm/processor.h> |
@@ -150,13 +137,9 @@ struct extended_sigtable { | |||
150 | 137 | ||
151 | #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) | 138 | #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) |
152 | 139 | ||
153 | /* serialize access to the physical write to MSR 0x79 */ | ||
154 | static DEFINE_SPINLOCK(microcode_update_lock); | ||
155 | |||
156 | static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) | 140 | static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) |
157 | { | 141 | { |
158 | struct cpuinfo_x86 *c = &cpu_data(cpu_num); | 142 | struct cpuinfo_x86 *c = &cpu_data(cpu_num); |
159 | unsigned long flags; | ||
160 | unsigned int val[2]; | 143 | unsigned int val[2]; |
161 | 144 | ||
162 | memset(csig, 0, sizeof(*csig)); | 145 | memset(csig, 0, sizeof(*csig)); |
@@ -176,18 +159,14 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) | |||
176 | csig->pf = 1 << ((val[1] >> 18) & 7); | 159 | csig->pf = 1 << ((val[1] >> 18) & 7); |
177 | } | 160 | } |
178 | 161 | ||
179 | /* serialize access to the physical write to MSR 0x79 */ | ||
180 | spin_lock_irqsave(µcode_update_lock, flags); | ||
181 | |||
182 | wrmsr(MSR_IA32_UCODE_REV, 0, 0); | 162 | wrmsr(MSR_IA32_UCODE_REV, 0, 0); |
183 | /* see notes above for revision 1.07. Apparent chip bug */ | 163 | /* see notes above for revision 1.07. Apparent chip bug */ |
184 | sync_core(); | 164 | sync_core(); |
185 | /* get the current revision from MSR 0x8B */ | 165 | /* get the current revision from MSR 0x8B */ |
186 | rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev); | 166 | rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev); |
187 | spin_unlock_irqrestore(µcode_update_lock, flags); | ||
188 | 167 | ||
189 | pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n", | 168 | printk(KERN_INFO "microcode: CPU%d sig=0x%x, pf=0x%x, revision=0x%x\n", |
190 | csig->sig, csig->pf, csig->rev); | 169 | cpu_num, csig->sig, csig->pf, csig->rev); |
191 | 170 | ||
192 | return 0; | 171 | return 0; |
193 | } | 172 | } |
@@ -318,11 +297,10 @@ get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev) | |||
318 | return 0; | 297 | return 0; |
319 | } | 298 | } |
320 | 299 | ||
321 | static void apply_microcode(int cpu) | 300 | static int apply_microcode(int cpu) |
322 | { | 301 | { |
323 | struct microcode_intel *mc_intel; | 302 | struct microcode_intel *mc_intel; |
324 | struct ucode_cpu_info *uci; | 303 | struct ucode_cpu_info *uci; |
325 | unsigned long flags; | ||
326 | unsigned int val[2]; | 304 | unsigned int val[2]; |
327 | int cpu_num; | 305 | int cpu_num; |
328 | 306 | ||
@@ -334,10 +312,7 @@ static void apply_microcode(int cpu) | |||
334 | BUG_ON(cpu_num != cpu); | 312 | BUG_ON(cpu_num != cpu); |
335 | 313 | ||
336 | if (mc_intel == NULL) | 314 | if (mc_intel == NULL) |
337 | return; | 315 | return 0; |
338 | |||
339 | /* serialize access to the physical write to MSR 0x79 */ | ||
340 | spin_lock_irqsave(µcode_update_lock, flags); | ||
341 | 316 | ||
342 | /* write microcode via MSR 0x79 */ | 317 | /* write microcode via MSR 0x79 */ |
343 | wrmsr(MSR_IA32_UCODE_WRITE, | 318 | wrmsr(MSR_IA32_UCODE_WRITE, |
@@ -351,30 +326,32 @@ static void apply_microcode(int cpu) | |||
351 | /* get the current revision from MSR 0x8B */ | 326 | /* get the current revision from MSR 0x8B */ |
352 | rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); | 327 | rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); |
353 | 328 | ||
354 | spin_unlock_irqrestore(µcode_update_lock, flags); | ||
355 | if (val[1] != mc_intel->hdr.rev) { | 329 | if (val[1] != mc_intel->hdr.rev) { |
356 | printk(KERN_ERR "microcode: CPU%d update from revision " | 330 | printk(KERN_ERR "microcode: CPU%d update " |
357 | "0x%x to 0x%x failed\n", | 331 | "to revision 0x%x failed\n", |
358 | cpu_num, uci->cpu_sig.rev, val[1]); | 332 | cpu_num, mc_intel->hdr.rev); |
359 | return; | 333 | return -1; |
360 | } | 334 | } |
361 | printk(KERN_INFO "microcode: CPU%d updated from revision " | 335 | printk(KERN_INFO "microcode: CPU%d updated to revision " |
362 | "0x%x to 0x%x, date = %04x-%02x-%02x \n", | 336 | "0x%x, date = %04x-%02x-%02x \n", |
363 | cpu_num, uci->cpu_sig.rev, val[1], | 337 | cpu_num, val[1], |
364 | mc_intel->hdr.date & 0xffff, | 338 | mc_intel->hdr.date & 0xffff, |
365 | mc_intel->hdr.date >> 24, | 339 | mc_intel->hdr.date >> 24, |
366 | (mc_intel->hdr.date >> 16) & 0xff); | 340 | (mc_intel->hdr.date >> 16) & 0xff); |
367 | 341 | ||
368 | uci->cpu_sig.rev = val[1]; | 342 | uci->cpu_sig.rev = val[1]; |
343 | |||
344 | return 0; | ||
369 | } | 345 | } |
370 | 346 | ||
371 | static int generic_load_microcode(int cpu, void *data, size_t size, | 347 | static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, |
372 | int (*get_ucode_data)(void *, const void *, size_t)) | 348 | int (*get_ucode_data)(void *, const void *, size_t)) |
373 | { | 349 | { |
374 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 350 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
375 | u8 *ucode_ptr = data, *new_mc = NULL, *mc; | 351 | u8 *ucode_ptr = data, *new_mc = NULL, *mc; |
376 | int new_rev = uci->cpu_sig.rev; | 352 | int new_rev = uci->cpu_sig.rev; |
377 | unsigned int leftover = size; | 353 | unsigned int leftover = size; |
354 | enum ucode_state state = UCODE_OK; | ||
378 | 355 | ||
379 | while (leftover) { | 356 | while (leftover) { |
380 | struct microcode_header_intel mc_header; | 357 | struct microcode_header_intel mc_header; |
@@ -412,11 +389,15 @@ static int generic_load_microcode(int cpu, void *data, size_t size, | |||
412 | leftover -= mc_size; | 389 | leftover -= mc_size; |
413 | } | 390 | } |
414 | 391 | ||
415 | if (!new_mc) | 392 | if (leftover) { |
393 | if (new_mc) | ||
394 | vfree(new_mc); | ||
395 | state = UCODE_ERROR; | ||
416 | goto out; | 396 | goto out; |
397 | } | ||
417 | 398 | ||
418 | if (leftover) { | 399 | if (!new_mc) { |
419 | vfree(new_mc); | 400 | state = UCODE_NFOUND; |
420 | goto out; | 401 | goto out; |
421 | } | 402 | } |
422 | 403 | ||
@@ -427,9 +408,8 @@ static int generic_load_microcode(int cpu, void *data, size_t size, | |||
427 | pr_debug("microcode: CPU%d found a matching microcode update with" | 408 | pr_debug("microcode: CPU%d found a matching microcode update with" |
428 | " version 0x%x (current=0x%x)\n", | 409 | " version 0x%x (current=0x%x)\n", |
429 | cpu, new_rev, uci->cpu_sig.rev); | 410 | cpu, new_rev, uci->cpu_sig.rev); |
430 | 411 | out: | |
431 | out: | 412 | return state; |
432 | return (int)leftover; | ||
433 | } | 413 | } |
434 | 414 | ||
435 | static int get_ucode_fw(void *to, const void *from, size_t n) | 415 | static int get_ucode_fw(void *to, const void *from, size_t n) |
@@ -438,21 +418,19 @@ static int get_ucode_fw(void *to, const void *from, size_t n) | |||
438 | return 0; | 418 | return 0; |
439 | } | 419 | } |
440 | 420 | ||
441 | static int request_microcode_fw(int cpu, struct device *device) | 421 | static enum ucode_state request_microcode_fw(int cpu, struct device *device) |
442 | { | 422 | { |
443 | char name[30]; | 423 | char name[30]; |
444 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 424 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
445 | const struct firmware *firmware; | 425 | const struct firmware *firmware; |
446 | int ret; | 426 | enum ucode_state ret; |
447 | 427 | ||
448 | /* We should bind the task to the CPU */ | ||
449 | BUG_ON(cpu != raw_smp_processor_id()); | ||
450 | sprintf(name, "intel-ucode/%02x-%02x-%02x", | 428 | sprintf(name, "intel-ucode/%02x-%02x-%02x", |
451 | c->x86, c->x86_model, c->x86_mask); | 429 | c->x86, c->x86_model, c->x86_mask); |
452 | ret = request_firmware(&firmware, name, device); | 430 | |
453 | if (ret) { | 431 | if (request_firmware(&firmware, name, device)) { |
454 | pr_debug("microcode: data file %s load failed\n", name); | 432 | pr_debug("microcode: data file %s load failed\n", name); |
455 | return ret; | 433 | return UCODE_NFOUND; |
456 | } | 434 | } |
457 | 435 | ||
458 | ret = generic_load_microcode(cpu, (void *)firmware->data, | 436 | ret = generic_load_microcode(cpu, (void *)firmware->data, |
@@ -468,11 +446,9 @@ static int get_ucode_user(void *to, const void *from, size_t n) | |||
468 | return copy_from_user(to, from, n); | 446 | return copy_from_user(to, from, n); |
469 | } | 447 | } |
470 | 448 | ||
471 | static int request_microcode_user(int cpu, const void __user *buf, size_t size) | 449 | static enum ucode_state |
450 | request_microcode_user(int cpu, const void __user *buf, size_t size) | ||
472 | { | 451 | { |
473 | /* We should bind the task to the CPU */ | ||
474 | BUG_ON(cpu != raw_smp_processor_id()); | ||
475 | |||
476 | return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); | 452 | return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); |
477 | } | 453 | } |
478 | 454 | ||