aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/microcode_amd.c
diff options
context:
space:
mode:
authorDmitry Adamushko <dmitry.adamushko@gmail.com>2008-09-11 17:27:52 -0400
committerIngo Molnar <mingo@elte.hu>2008-09-12 06:20:27 -0400
commita0a29b62a9cac6b7d83b7514679f2ed8d33d4372 (patch)
treee07ab66cbe3c90da70a7195104413cae00104de6 /arch/x86/kernel/microcode_amd.c
parent5b792d320f28ff83dd4c13f984807e26235f7703 (diff)
x86, microcode rework, v2
this is a rework of the microcode splitup in tip/x86/microcode (1) I think this new interface is cleaner (look at the changes in 'struct microcode_ops' in microcode.h); (2) it's -64 lines of code; Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/microcode_amd.c')
-rw-r--r--arch/x86/kernel/microcode_amd.c329
1 files changed, 155 insertions, 174 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index d606a05545ca..6815837a7753 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -59,7 +59,7 @@ MODULE_LICENSE("GPL v2");
59/* serialize access to the physical write */ 59/* serialize access to the physical write */
60static DEFINE_SPINLOCK(microcode_update_lock); 60static DEFINE_SPINLOCK(microcode_update_lock);
61 61
62struct equiv_cpu_entry *equiv_cpu_table; 62static struct equiv_cpu_entry *equiv_cpu_table;
63 63
64static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) 64static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
65{ 65{
@@ -83,36 +83,37 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
83 return 0; 83 return 0;
84} 84}
85 85
86static int get_matching_microcode_amd(void *mc, int cpu) 86static int get_matching_microcode(int cpu, void *mc, int rev)
87{ 87{
88 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
89 struct microcode_header_amd *mc_header = mc; 88 struct microcode_header_amd *mc_header = mc;
90 unsigned long total_size = get_totalsize(mc_header);
91 void *new_mc;
92 struct pci_dev *nb_pci_dev, *sb_pci_dev; 89 struct pci_dev *nb_pci_dev, *sb_pci_dev;
93 unsigned int current_cpu_id; 90 unsigned int current_cpu_id;
94 unsigned int equiv_cpu_id = 0x00; 91 unsigned int equiv_cpu_id = 0x00;
95 unsigned int i = 0; 92 unsigned int i = 0;
96 93
97 /* We should bind the task to the CPU */ 94 /*
98 BUG_ON(cpu != raw_smp_processor_id()); 95 * dimm: do we need this? Why an update via /dev/... is different
99 96 * from the one via firmware?
100 /* This is a tricky part. We might be called from a write operation */ 97 *
101 /* to the device file instead of the usual process of firmware */ 98 * This is a tricky part. We might be called from a write operation
102 /* loading. This routine needs to be able to distinguish both */ 99 * to the device file instead of the usual process of firmware
103/* cases. This is done by checking if there alread is a equivalent */ 100 * loading. This routine needs to be able to distinguish both
104 /* CPU table installed. If not, we're written through */ 101 * cases. This is done by checking if there alread is a equivalent
105 /* /dev/cpu/microcode. */ 102 * CPU table installed. If not, we're written through
106/* Since we ignore all checks. The error case in which going through */ 103 * /dev/cpu/microcode.
107/* firmware loading and that table is not loaded has already been */ 104 * Since we ignore all checks. The error case in which going through
108 /* checked earlier. */ 105 * firmware loading and that table is not loaded has already been
106 * checked earlier.
107 */
108 BUG_ON(equiv_cpu_table == NULL);
109#if 0
109 if (equiv_cpu_table == NULL) { 110 if (equiv_cpu_table == NULL) {
110 printk(KERN_INFO "microcode: CPU%d microcode update with " 111 printk(KERN_INFO "microcode: CPU%d microcode update with "
111 "version 0x%x (current=0x%x)\n", 112 "version 0x%x (current=0x%x)\n",
112 cpu, mc_header->patch_id, uci->cpu_sig.rev); 113 cpu, mc_header->patch_id, uci->cpu_sig.rev);
113 goto out; 114 goto out;
114 } 115 }
115 116#endif
116 current_cpu_id = cpuid_eax(0x00000001); 117 current_cpu_id = cpuid_eax(0x00000001);
117 118
118 while (equiv_cpu_table[i].installed_cpu != 0) { 119 while (equiv_cpu_table[i].installed_cpu != 0) {
@@ -175,27 +176,9 @@ static int get_matching_microcode_amd(void *mc, int cpu)
175 pci_dev_put(sb_pci_dev); 176 pci_dev_put(sb_pci_dev);
176 } 177 }
177 178
178 if (mc_header->patch_id <= uci->cpu_sig.rev) 179 if (mc_header->patch_id <= rev)
179 return 0; 180 return 0;
180 181
181 printk(KERN_INFO "microcode: CPU%d found a matching microcode "
182 "update with version 0x%x (current=0x%x)\n",
183 cpu, mc_header->patch_id, uci->cpu_sig.rev);
184
185out:
186 new_mc = vmalloc(UCODE_MAX_SIZE);
187 if (!new_mc) {
188 printk(KERN_ERR "microcode: error, can't allocate memory\n");
189 return -ENOMEM;
190 }
191 memset(new_mc, 0, UCODE_MAX_SIZE);
192
193 /* free previous update file */
194 vfree(uci->mc.mc_amd);
195
196 memcpy(new_mc, mc, total_size);
197
198 uci->mc.mc_amd = new_mc;
199 return 1; 182 return 1;
200} 183}
201 184
@@ -245,104 +228,65 @@ static void apply_microcode_amd(int cpu)
245 uci->cpu_sig.rev = rev; 228 uci->cpu_sig.rev = rev;
246} 229}
247 230
248#ifdef CONFIG_MICROCODE_OLD_INTERFACE 231static void * get_next_ucode(u8 *buf, unsigned int size,
249extern void __user *user_buffer; /* user area microcode data buffer */ 232 int (*get_ucode_data)(void *, const void *, size_t),
250extern unsigned int user_buffer_size; /* it's size */ 233 unsigned int *mc_size)
251
252static long get_next_ucode_amd(void **mc, long offset)
253{
254 struct microcode_header_amd mc_header;
255 unsigned long total_size;
256
257 /* No more data */
258 if (offset >= user_buffer_size)
259 return 0;
260 if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) {
261 printk(KERN_ERR "microcode: error! Can not read user data\n");
262 return -EFAULT;
263 }
264 total_size = get_totalsize(&mc_header);
265 if (offset + total_size > user_buffer_size) {
266 printk(KERN_ERR "microcode: error! Bad total size in microcode "
267 "data file\n");
268 return -EINVAL;
269 }
270 *mc = vmalloc(UCODE_MAX_SIZE);
271 if (!*mc)
272 return -ENOMEM;
273 memset(*mc, 0, UCODE_MAX_SIZE);
274
275 if (copy_from_user(*mc, user_buffer + offset, total_size)) {
276 printk(KERN_ERR "microcode: error! Can not read user data\n");
277 vfree(*mc);
278 return -EFAULT;
279 }
280 return offset + total_size;
281}
282#else
283#define get_next_ucode_amd() NULL
284#endif
285
286static long get_next_ucode_from_buffer_amd(void **mc, void *buf,
287 unsigned long size, long offset)
288{ 234{
289 struct microcode_header_amd *mc_header; 235 unsigned int total_size;
290 unsigned long total_size; 236#define UCODE_UNKNOWN_HDR 8
291 unsigned char *buf_pos = buf; 237 u8 hdr[UCODE_UNKNOWN_HDR];
238 void *mc;
292 239
293 /* No more data */ 240 if (get_ucode_data(hdr, buf, UCODE_UNKNOWN_HDR))
294 if (offset >= size) 241 return NULL;
295 return 0;
296 242
297 if (buf_pos[offset] != UCODE_UCODE_TYPE) { 243 if (hdr[0] != UCODE_UCODE_TYPE) {
298 printk(KERN_ERR "microcode: error! " 244 printk(KERN_ERR "microcode: error! "
299 "Wrong microcode payload type field\n"); 245 "Wrong microcode payload type field\n");
300 return -EINVAL; 246 return NULL;
301 } 247 }
302 248
303 mc_header = (struct microcode_header_amd *)(&buf_pos[offset+8]); 249 /* Why not by means of get_totalsize(hdr)? */
250 total_size = (unsigned long) (hdr[4] + (hdr[5] << 8));
304 251
305 total_size = (unsigned long) (buf_pos[offset+4] + 252 printk(KERN_INFO "microcode: size %u, total_size %u\n",
306 (buf_pos[offset+5] << 8)); 253 size, total_size);
307 254
308 printk(KERN_INFO "microcode: size %lu, total_size %lu, offset %ld\n", 255 if (total_size > size || total_size > UCODE_MAX_SIZE) {
309 size, total_size, offset);
310
311 if (offset + total_size > size) {
312 printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); 256 printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
313 return -EINVAL; 257 return NULL;
314 } 258 }
315 259
316 *mc = vmalloc(UCODE_MAX_SIZE); 260 mc = vmalloc(UCODE_MAX_SIZE);
317 if (!*mc) { 261 if (mc) {
318 printk(KERN_ERR "microcode: error! " 262 memset(mc, 0, UCODE_MAX_SIZE);
319 "Can not allocate memory for microcode patch\n"); 263 if (get_ucode_data(mc, buf + UCODE_UNKNOWN_HDR, total_size)) {
320 return -ENOMEM; 264 vfree(mc);
265 mc = NULL;
266 } else
267 *mc_size = total_size + UCODE_UNKNOWN_HDR;
321 } 268 }
322 269#undef UCODE_UNKNOWN_HDR
323 memset(*mc, 0, UCODE_MAX_SIZE); 270 return mc;
324 memcpy(*mc, buf + offset + 8, total_size);
325
326 return offset + total_size + 8;
327} 271}
328 272
329static long install_equiv_cpu_table(void *buf, unsigned long size, long offset) 273
274static int install_equiv_cpu_table(u8 *buf,
275 int (*get_ucode_data)(void *, const void *, size_t))
330{ 276{
331 unsigned int *buf_pos = buf; 277#define UCODE_HEADER_SIZE 12
278 u8 *hdr[UCODE_HEADER_SIZE];
279 unsigned int *buf_pos = (unsigned int *)hdr;
280 unsigned long size;
332 281
333 /* No more data */ 282 if (get_ucode_data(&hdr, buf, UCODE_HEADER_SIZE))
334 if (offset >= size)
335 return 0; 283 return 0;
336 284
337 if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE) { 285 size = buf_pos[2];
338 printk(KERN_ERR "microcode: error! "
339 "Wrong microcode equivalnet cpu table type field\n");
340 return 0;
341 }
342 286
343 if (size == 0) { 287 if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) {
344 printk(KERN_ERR "microcode: error! " 288 printk(KERN_ERR "microcode: error! "
345 "Wrong microcode equivalnet cpu table length\n"); 289 "Wrong microcode equivalnet cpu table\n");
346 return 0; 290 return 0;
347 } 291 }
348 292
@@ -352,79 +296,118 @@ static long install_equiv_cpu_table(void *buf, unsigned long size, long offset)
352 return 0; 296 return 0;
353 } 297 }
354 298
355 memset(equiv_cpu_table, 0, size); 299 buf += UCODE_HEADER_SIZE;
356 memcpy(equiv_cpu_table, &buf_pos[3], size); 300 if (get_ucode_data(equiv_cpu_table, buf, size)) {
301 vfree(equiv_cpu_table);
302 return 0;
303 }
357 304
358 return size + 12; /* add header length */ 305 return size + UCODE_HEADER_SIZE; /* add header length */
306#undef UCODE_HEADER_SIZE
359} 307}
360 308
361/* fake device for request_firmware */ 309static void free_equiv_cpu_table(void)
362extern struct platform_device *microcode_pdev;
363
364static int cpu_request_microcode_amd(int cpu)
365{ 310{
366 char name[30]; 311 if (equiv_cpu_table) {
367 const struct firmware *firmware; 312 vfree(equiv_cpu_table);
368 void *buf; 313 equiv_cpu_table = NULL;
369 unsigned int *buf_pos;
370 unsigned long size;
371 long offset = 0;
372 int error;
373 void *mc;
374
375 /* We should bind the task to the CPU */
376 BUG_ON(cpu != raw_smp_processor_id());
377
378 sprintf(name, "amd-ucode/microcode_amd.bin");
379 error = request_firmware(&firmware, "amd-ucode/microcode_amd.bin",
380 &microcode_pdev->dev);
381 if (error) {
382 printk(KERN_ERR "microcode: ucode data file %s load failed\n",
383 name);
384 return error;
385 }
386
387 buf_pos = (unsigned int *)firmware->data;
388 buf = (void *)firmware->data;
389 size = firmware->size;
390
391 if (buf_pos[0] != UCODE_MAGIC) {
392 printk(KERN_ERR "microcode: error! Wrong microcode patch file magic\n");
393 return -EINVAL;
394 } 314 }
315}
395 316
396 offset = install_equiv_cpu_table(buf, buf_pos[2], offset); 317static int generic_load_microcode(int cpu, void *data, size_t size,
318 int (*get_ucode_data)(void *, const void *, size_t))
319{
320 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
321 u8 *ucode_ptr = data, *new_mc = NULL, *mc;
322 int new_rev = uci->cpu_sig.rev;
323 unsigned int leftover;
324 unsigned long offset;
397 325
326 offset = install_equiv_cpu_table(ucode_ptr, get_ucode_data);
398 if (!offset) { 327 if (!offset) {
399 printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); 328 printk(KERN_ERR "microcode: installing equivalent cpu table failed\n");
400 return -EINVAL; 329 return -EINVAL;
401 } 330 }
402 331
403 while ((offset = 332 ucode_ptr += offset;
404 get_next_ucode_from_buffer_amd(&mc, buf, size, offset)) > 0) { 333 leftover = size - offset;
405 error = get_matching_microcode_amd(mc, cpu); 334
406 if (error < 0) 335 while (leftover) {
336 unsigned int mc_size;
337 struct microcode_header_amd *mc_header;
338
339 mc = get_next_ucode(ucode_ptr, leftover, get_ucode_data, &mc_size);
340 if (!mc)
407 break; 341 break;
408 /* 342
409 * It's possible the data file has multiple matching ucode, 343 mc_header = (struct microcode_header_amd *)mc;
410 * lets keep searching till the latest version 344 if (get_matching_microcode(cpu, mc, new_rev)) {
411 */ 345 new_rev = mc_header->patch_id;
412 if (error == 1) { 346 new_mc = mc;
413 apply_microcode_amd(cpu); 347 } else
414 error = 0; 348 vfree(mc);
415 } 349
416 vfree(mc); 350 ucode_ptr += mc_size;
351 leftover -= mc_size;
417 } 352 }
418 if (offset > 0) { 353
419 vfree(mc); 354 if (new_mc) {
420 vfree(equiv_cpu_table); 355 if (!leftover) {
421 equiv_cpu_table = NULL; 356 if (uci->mc.mc_amd)
357 vfree(uci->mc.mc_amd);
358 uci->mc.mc_amd = (struct microcode_amd *)new_mc;
359 pr_debug("microcode: CPU%d found a matching microcode update with"
360 " version 0x%x (current=0x%x)\n",
361 cpu, uci->mc.mc_amd->hdr.patch_id, uci->cpu_sig.rev);
362 } else
363 vfree(new_mc);
422 } 364 }
423 if (offset < 0) 365
424 error = offset; 366 free_equiv_cpu_table();
367
368 return (int)leftover;
369}
370
371static int get_ucode_fw(void *to, const void *from, size_t n)
372{
373 memcpy(to, from, n);
374 return 0;
375}
376
377static int request_microcode_fw(int cpu, struct device *device)
378{
379 const char *fw_name = "amd-ucode/microcode_amd.bin";
380 const struct firmware *firmware;
381 int ret;
382
383 /* We should bind the task to the CPU */
384 BUG_ON(cpu != raw_smp_processor_id());
385
386 ret = request_firmware(&firmware, fw_name, device);
387 if (ret) {
388 printk(KERN_ERR "microcode: ucode data file %s load failed\n", fw_name);
389 return ret;
390 }
391
392 ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size,
393 &get_ucode_fw);
394
425 release_firmware(firmware); 395 release_firmware(firmware);
426 396
427 return error; 397 return ret;
398}
399
400static int get_ucode_user(void *to, const void *from, size_t n)
401{
402 return copy_from_user(to, from, n);
403}
404
405static int request_microcode_user(int cpu, const void __user *buf, size_t size)
406{
407 /* We should bind the task to the CPU */
408 BUG_ON(cpu != raw_smp_processor_id());
409
410 return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user);
428} 411}
429 412
430static void microcode_fini_cpu_amd(int cpu) 413static void microcode_fini_cpu_amd(int cpu)
@@ -436,10 +419,8 @@ static void microcode_fini_cpu_amd(int cpu)
436} 419}
437 420
438static struct microcode_ops microcode_amd_ops = { 421static struct microcode_ops microcode_amd_ops = {
439 .get_next_ucode = get_next_ucode_amd, 422 .request_microcode_user = request_microcode_user,
440 .get_matching_microcode = get_matching_microcode_amd, 423 .request_microcode_fw = request_microcode_fw,
441 .microcode_sanity_check = NULL,
442 .cpu_request_microcode = cpu_request_microcode_amd,
443 .collect_cpu_info = collect_cpu_info_amd, 424 .collect_cpu_info = collect_cpu_info_amd,
444 .apply_microcode = apply_microcode_amd, 425 .apply_microcode = apply_microcode_amd,
445 .microcode_fini_cpu = microcode_fini_cpu_amd, 426 .microcode_fini_cpu = microcode_fini_cpu_amd,