aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/microcode_amd.c
diff options
context:
space:
mode:
authorDmitry Adamushko <dmitry.adamushko@gmail.com>2009-05-11 17:48:27 -0400
committerIngo Molnar <mingo@elte.hu>2009-05-12 04:36:44 -0400
commit871b72dd1e12afc3f024479531d25a9339d2e3f9 (patch)
tree132f309ab587561f4da1cfe6592c570fbad67d53 /arch/x86/kernel/microcode_amd.c
parenta4d7749be5de4a7261bcbe3c7d96c748792ec455 (diff)
x86: microcode: use smp_call_function_single instead of set_cpus_allowed, cleanup of synchronization logic
* Solve issues described in 6f66cbc63081fd70e3191b4dbb796746780e5ae1 in a way that doesn't resort to set_cpus_allowed(); * in fact, only collect_cpu_info and apply_microcode callbacks must run on a target cpu, others will do just fine on any other. smp_call_function_single() (as suggested by Ingo) is used to run these callbacks on a target cpu. * cleanup of synchronization logic of the 'microcode_core' part The generic 'microcode_core' part guarantees that only a single cpu (be it a full-fledged cpu, one of the cores or HT) is being updated at any particular moment of time. In general, there is no need for any additional sync. mechanism in arch-specific parts (the patch removes existing spinlocks). See also the "Synchronization" section in microcode_core.c. * return -EINVAL instead of -1 (which is translated into -EPERM) in microcode_write(), reload_cpu() and mc_sysdev_add(). Other suggestions for an error code? * use 'enum ucode_state' as return value of request_microcode_{fw, user} to gain more flexibility by distinguishing between real error cases and situations when an appropriate ucode was not found (which is not an error per-se). * some minor cleanups Thanks a lot to Hugh Dickins for review/suggestions/testing! Reference: http://marc.info/?l=linux-kernel&m=124025889012541&w=2 [ Impact: refactor and clean up microcode driver locking code ] Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com> Acked-by: Hugh Dickins <hugh@veritas.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Andreas Herrmann <andreas.herrmann3@amd.com> Cc: Peter Oruba <peter.oruba@amd.com> Cc: Arjan van de Ven <arjan@infradead.org> LKML-Reference: <1242078507.5560.9.camel@earth> [ did some more cleanups ] Signed-off-by: Ingo Molnar <mingo@elte.hu> arch/x86/include/asm/microcode.h | 25 ++ arch/x86/kernel/microcode_amd.c | 58 ++---- arch/x86/kernel/microcode_core.c | 326 +++++++++++++++++++++----------------- arch/x86/kernel/microcode_intel.c | 92 +++------- 4 files changed, 261 insertions(+), 240 deletions(-) (~20 new comment lines)
Diffstat (limited to 'arch/x86/kernel/microcode_amd.c')
-rw-r--r--arch/x86/kernel/microcode_amd.c58
1 files changed, 22 insertions, 36 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 453b5795a5c6..c8be20f16447 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -13,25 +13,13 @@
13 * Licensed under the terms of the GNU General Public 13 * Licensed under the terms of the GNU General Public
14 * License version 2. See file COPYING for details. 14 * License version 2. See file COPYING for details.
15 */ 15 */
16#include <linux/platform_device.h>
17#include <linux/capability.h>
18#include <linux/miscdevice.h>
19#include <linux/firmware.h> 16#include <linux/firmware.h>
20#include <linux/spinlock.h>
21#include <linux/cpumask.h>
22#include <linux/pci_ids.h> 17#include <linux/pci_ids.h>
23#include <linux/uaccess.h> 18#include <linux/uaccess.h>
24#include <linux/vmalloc.h> 19#include <linux/vmalloc.h>
25#include <linux/kernel.h> 20#include <linux/kernel.h>
26#include <linux/module.h> 21#include <linux/module.h>
27#include <linux/mutex.h>
28#include <linux/sched.h>
29#include <linux/init.h>
30#include <linux/slab.h>
31#include <linux/cpu.h>
32#include <linux/pci.h> 22#include <linux/pci.h>
33#include <linux/fs.h>
34#include <linux/mm.h>
35 23
36#include <asm/microcode.h> 24#include <asm/microcode.h>
37#include <asm/processor.h> 25#include <asm/processor.h>
@@ -79,9 +67,6 @@ struct microcode_amd {
79#define UCODE_CONTAINER_SECTION_HDR 8 67#define UCODE_CONTAINER_SECTION_HDR 8
80#define UCODE_CONTAINER_HEADER_SIZE 12 68#define UCODE_CONTAINER_HEADER_SIZE 12
81 69
82/* serialize access to the physical write */
83static DEFINE_SPINLOCK(microcode_update_lock);
84
85static struct equiv_cpu_entry *equiv_cpu_table; 70static struct equiv_cpu_entry *equiv_cpu_table;
86 71
87static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) 72static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
@@ -144,9 +129,8 @@ static int get_matching_microcode(int cpu, void *mc, int rev)
144 return 1; 129 return 1;
145} 130}
146 131
147static void apply_microcode_amd(int cpu) 132static int apply_microcode_amd(int cpu)
148{ 133{
149 unsigned long flags;
150 u32 rev, dummy; 134 u32 rev, dummy;
151 int cpu_num = raw_smp_processor_id(); 135 int cpu_num = raw_smp_processor_id();
152 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; 136 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
@@ -156,25 +140,25 @@ static void apply_microcode_amd(int cpu)
156 BUG_ON(cpu_num != cpu); 140 BUG_ON(cpu_num != cpu);
157 141
158 if (mc_amd == NULL) 142 if (mc_amd == NULL)
159 return; 143 return 0;
160 144
161 spin_lock_irqsave(&microcode_update_lock, flags);
162 wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code); 145 wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code);
163 /* get patch id after patching */ 146 /* get patch id after patching */
164 rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); 147 rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
165 spin_unlock_irqrestore(&microcode_update_lock, flags);
166 148
167 /* check current patch id and patch's id for match */ 149 /* check current patch id and patch's id for match */
168 if (rev != mc_amd->hdr.patch_id) { 150 if (rev != mc_amd->hdr.patch_id) {
169 printk(KERN_ERR "microcode: CPU%d: update failed " 151 printk(KERN_ERR "microcode: CPU%d: update failed "
170 "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id); 152 "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id);
171 return; 153 return -1;
172 } 154 }
173 155
174 printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n", 156 printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n",
175 cpu, rev); 157 cpu, rev);
176 158
177 uci->cpu_sig.rev = rev; 159 uci->cpu_sig.rev = rev;
160
161 return 0;
178} 162}
179 163
180static int get_ucode_data(void *to, const u8 *from, size_t n) 164static int get_ucode_data(void *to, const u8 *from, size_t n)
@@ -263,7 +247,8 @@ static void free_equiv_cpu_table(void)
263 } 247 }
264} 248}
265 249
266static int generic_load_microcode(int cpu, const u8 *data, size_t size) 250static enum ucode_state
251generic_load_microcode(int cpu, const u8 *data, size_t size)
267{ 252{
268 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 253 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
269 const u8 *ucode_ptr = data; 254 const u8 *ucode_ptr = data;
@@ -272,12 +257,13 @@ static int generic_load_microcode(int cpu, const u8 *data, size_t size)
272 int new_rev = uci->cpu_sig.rev; 257 int new_rev = uci->cpu_sig.rev;
273 unsigned int leftover; 258 unsigned int leftover;
274 unsigned long offset; 259 unsigned long offset;
260 enum ucode_state state = UCODE_OK;
275 261
276 offset = install_equiv_cpu_table(ucode_ptr); 262 offset = install_equiv_cpu_table(ucode_ptr);
277 if (!offset) { 263 if (!offset) {
278 printk(KERN_ERR "microcode: failed to create " 264 printk(KERN_ERR "microcode: failed to create "
279 "equivalent cpu table\n"); 265 "equivalent cpu table\n");
280 return -EINVAL; 266 return UCODE_ERROR;
281 } 267 }
282 268
283 ucode_ptr += offset; 269 ucode_ptr += offset;
@@ -312,28 +298,27 @@ static int generic_load_microcode(int cpu, const u8 *data, size_t size)
312 pr_debug("microcode: CPU%d found a matching microcode " 298 pr_debug("microcode: CPU%d found a matching microcode "
313 "update with version 0x%x (current=0x%x)\n", 299 "update with version 0x%x (current=0x%x)\n",
314 cpu, new_rev, uci->cpu_sig.rev); 300 cpu, new_rev, uci->cpu_sig.rev);
315 } else 301 } else {
316 vfree(new_mc); 302 vfree(new_mc);
317 } 303 state = UCODE_ERROR;
304 }
305 } else
306 state = UCODE_NFOUND;
318 307
319 free_equiv_cpu_table(); 308 free_equiv_cpu_table();
320 309
321 return (int)leftover; 310 return state;
322} 311}
323 312
324static int request_microcode_fw(int cpu, struct device *device) 313static enum ucode_state request_microcode_fw(int cpu, struct device *device)
325{ 314{
326 const char *fw_name = "amd-ucode/microcode_amd.bin"; 315 const char *fw_name = "amd-ucode/microcode_amd.bin";
327 const struct firmware *firmware; 316 const struct firmware *firmware;
328 int ret; 317 enum ucode_state ret;
329
330 /* We should bind the task to the CPU */
331 BUG_ON(cpu != raw_smp_processor_id());
332 318
333 ret = request_firmware(&firmware, fw_name, device); 319 if (request_firmware(&firmware, fw_name, device)) {
334 if (ret) {
335 printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); 320 printk(KERN_ERR "microcode: failed to load file %s\n", fw_name);
336 return ret; 321 return UCODE_NFOUND;
337 } 322 }
338 323
339 ret = generic_load_microcode(cpu, firmware->data, firmware->size); 324 ret = generic_load_microcode(cpu, firmware->data, firmware->size);
@@ -343,11 +328,12 @@ static int request_microcode_fw(int cpu, struct device *device)
343 return ret; 328 return ret;
344} 329}
345 330
346static int request_microcode_user(int cpu, const void __user *buf, size_t size) 331static enum ucode_state
332request_microcode_user(int cpu, const void __user *buf, size_t size)
347{ 333{
348 printk(KERN_INFO "microcode: AMD microcode update via " 334 printk(KERN_INFO "microcode: AMD microcode update via "
349 "/dev/cpu/microcode not supported\n"); 335 "/dev/cpu/microcode not supported\n");
350 return -1; 336 return UCODE_ERROR;
351} 337}
352 338
353static void microcode_fini_cpu_amd(int cpu) 339static void microcode_fini_cpu_amd(int cpu)