Commit | Line | Data |
---|---|---|
80cc9f10 PO |
1 | /* |
2 | * AMD CPU Microcode Update Driver for Linux | |
3 | * Copyright (C) 2008 Advanced Micro Devices Inc. | |
4 | * | |
5 | * Author: Peter Oruba <peter.oruba@amd.com> | |
6 | * | |
7 | * Based on work by: | |
8 | * Tigran Aivazian <tigran@aivazian.fsnet.co.uk> | |
9 | * | |
10 | * This driver allows to upgrade microcode on AMD | |
11 | * family 0x10 and 0x11 processors. | |
12 | * | |
13 | * Licensed unter the terms of the GNU General Public | |
14 | * License version 2. See file COPYING for details. | |
15 | */ | |
16 | ||
17 | #include <linux/capability.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/cpumask.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/vmalloc.h> | |
25 | #include <linux/miscdevice.h> | |
26 | #include <linux/spinlock.h> | |
27 | #include <linux/mm.h> | |
28 | #include <linux/fs.h> | |
29 | #include <linux/mutex.h> | |
30 | #include <linux/cpu.h> | |
31 | #include <linux/firmware.h> | |
32 | #include <linux/platform_device.h> | |
33 | #include <linux/pci.h> | |
34 | #include <linux/pci_ids.h> | |
35 | ||
36 | #include <asm/msr.h> | |
37 | #include <asm/uaccess.h> | |
38 | #include <asm/processor.h> | |
39 | #include <asm/microcode.h> | |
40 | ||
41 | MODULE_DESCRIPTION("AMD Microcode Update Driver"); | |
42 | MODULE_AUTHOR("Peter Oruba <peter.oruba@amd.com>"); | |
43 | MODULE_LICENSE("GPLv2"); | |
44 | ||
45 | #define UCODE_MAGIC 0x00414d44 | |
46 | #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 | |
47 | #define UCODE_UCODE_TYPE 0x00000001 | |
48 | ||
49 | #define UCODE_MAX_SIZE (2048) | |
50 | #define DEFAULT_UCODE_DATASIZE (896) /* 896 bytes */ | |
51 | #define MC_HEADER_SIZE (sizeof(struct microcode_header_amd)) /* 64 bytes */ | |
52 | #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 960 bytes */ | |
53 | #define DWSIZE (sizeof(u32)) | |
54 | /* For now we support a fixed ucode total size only */ | |
55 | #define get_totalsize(mc) \ | |
56 | ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \ | |
57 | + MC_HEADER_SIZE) | |
58 | ||
59 | extern int microcode_init(void *opaque, struct module *module); | |
60 | extern void microcode_exit(void); | |
61 | ||
62 | /* serialize access to the physical write */ | |
63 | static DEFINE_SPINLOCK(microcode_update_lock); | |
64 | ||
65 | /* no concurrent ->write()s are allowed on /dev/cpu/microcode */ | |
66 | extern struct mutex (microcode_mutex); | |
67 | ||
68 | struct equiv_cpu_entry *equiv_cpu_table; | |
69 | ||
70 | extern struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; | |
71 | ||
72 | static void collect_cpu_info_amd(int cpu) | |
73 | { | |
74 | struct cpuinfo_x86 *c = &cpu_data(cpu); | |
75 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
76 | ||
77 | /* We should bind the task to the CPU */ | |
78 | BUG_ON(raw_smp_processor_id() != cpu); | |
79 | uci->rev = 0; | |
80 | uci->pf = 0; | |
81 | uci->mc.mc_amd = NULL; | |
82 | uci->valid = 1; | |
83 | ||
84 | if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { | |
85 | printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n", | |
86 | cpu); | |
87 | uci->valid = 0; | |
88 | return; | |
89 | } | |
90 | ||
91 | asm volatile("movl %1, %%ecx; rdmsr" | |
92 | : "=a" (uci->rev) | |
93 | : "i" (0x0000008B) : "ecx"); | |
94 | ||
95 | printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n", | |
96 | uci->rev); | |
97 | } | |
98 | ||
99 | static int get_matching_microcode_amd(void *mc, int cpu) | |
100 | { | |
101 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
102 | struct microcode_header_amd *mc_header = mc; | |
103 | unsigned long total_size = get_totalsize(mc_header); | |
104 | void *new_mc; | |
105 | struct pci_dev *nb_pci_dev, *sb_pci_dev; | |
106 | unsigned int current_cpu_id; | |
107 | unsigned int equiv_cpu_id = 0x00; | |
108 | unsigned int i = 0; | |
109 | ||
110 | /* We should bind the task to the CPU */ | |
111 | BUG_ON(cpu != raw_smp_processor_id()); | |
112 | ||
113 | /* This is a tricky part. We might be called from a write operation */ | |
114 | /* to the device file instead of the usual process of firmware */ | |
115 | /* loading. This routine needs to be able to distinguish both */ | |
116 | /* cases. This is done by checking if there alread is a equivalent */ | |
117 | /* CPU table installed. If not, we're written through */ | |
118 | /* /dev/cpu/microcode. */ | |
119 | /* Since we ignore all checks. The error case in which going through */ | |
120 | /* firmware loading and that table is not loaded has already been */ | |
121 | /* checked earlier. */ | |
122 | if (equiv_cpu_table == NULL) { | |
123 | printk(KERN_INFO "microcode: CPU%d microcode update with " | |
124 | "version 0x%x (current=0x%x)\n", | |
125 | cpu, mc_header->patch_id, uci->rev); | |
126 | goto out; | |
127 | } | |
128 | ||
129 | current_cpu_id = cpuid_eax(0x00000001); | |
130 | ||
131 | while (equiv_cpu_table[i].installed_cpu != 0) { | |
132 | if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { | |
133 | equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; | |
134 | break; | |
135 | } | |
136 | i++; | |
137 | } | |
138 | ||
139 | if (!equiv_cpu_id) { | |
140 | printk(KERN_ERR "microcode: CPU%d cpu_id " | |
141 | "not found in equivalent cpu table \n", cpu); | |
142 | return 0; | |
143 | } | |
144 | ||
145 | if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) { | |
146 | printk(KERN_ERR | |
147 | "microcode: CPU%d patch does not match " | |
148 | "(patch is %x, cpu extended is %x) \n", | |
149 | cpu, mc_header->processor_rev_id[0], | |
150 | (equiv_cpu_id & 0xff)); | |
151 | return 0; | |
152 | } | |
153 | ||
154 | if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) { | |
155 | printk(KERN_ERR "microcode: CPU%d patch does not match " | |
156 | "(patch is %x, cpu base id is %x) \n", | |
157 | cpu, mc_header->processor_rev_id[1], | |
158 | ((equiv_cpu_id >> 16) & 0xff)); | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
163 | /* ucode may be northbridge specific */ | |
164 | if (mc_header->nb_dev_id) { | |
165 | nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, | |
166 | (mc_header->nb_dev_id & 0xff), | |
167 | NULL); | |
168 | if ((!nb_pci_dev) || | |
169 | (mc_header->nb_rev_id != nb_pci_dev->revision)) { | |
170 | printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu); | |
171 | pci_dev_put(nb_pci_dev); | |
172 | return 0; | |
173 | } | |
174 | pci_dev_put(nb_pci_dev); | |
175 | } | |
176 | ||
177 | /* ucode may be southbridge specific */ | |
178 | if (mc_header->sb_dev_id) { | |
179 | sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, | |
180 | (mc_header->sb_dev_id & 0xff), | |
181 | NULL); | |
182 | if ((!sb_pci_dev) || | |
183 | (mc_header->sb_rev_id != sb_pci_dev->revision)) { | |
184 | printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu); | |
185 | pci_dev_put(sb_pci_dev); | |
186 | return 0; | |
187 | } | |
188 | pci_dev_put(sb_pci_dev); | |
189 | } | |
190 | ||
191 | if (mc_header->patch_id <= uci->rev) | |
192 | return 0; | |
193 | ||
194 | printk(KERN_INFO "microcode: CPU%d found a matching microcode " | |
195 | "update with version 0x%x (current=0x%x)\n", | |
196 | cpu, mc_header->patch_id, uci->rev); | |
197 | ||
198 | out: | |
199 | new_mc = vmalloc(UCODE_MAX_SIZE); | |
200 | if (!new_mc) { | |
201 | printk(KERN_ERR "microcode: error, can't allocate memory\n"); | |
202 | return -ENOMEM; | |
203 | } | |
204 | memset(new_mc, 0, UCODE_MAX_SIZE); | |
205 | ||
206 | /* free previous update file */ | |
207 | vfree(uci->mc.mc_amd); | |
208 | ||
209 | memcpy(new_mc, mc, total_size); | |
210 | ||
211 | uci->mc.mc_amd = new_mc; | |
212 | return 1; | |
213 | } | |
214 | ||
215 | static void apply_microcode_amd(int cpu) | |
216 | { | |
217 | unsigned long flags; | |
218 | unsigned int eax, edx; | |
219 | unsigned int rev; | |
220 | int cpu_num = raw_smp_processor_id(); | |
221 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
222 | ||
223 | /* We should bind the task to the CPU */ | |
224 | BUG_ON(cpu_num != cpu); | |
225 | ||
226 | if (uci->mc.mc_amd == NULL) | |
227 | return; | |
228 | ||
229 | spin_lock_irqsave(µcode_update_lock, flags); | |
230 | ||
231 | edx = (unsigned int)(((unsigned long) | |
232 | &(uci->mc.mc_amd->hdr.data_code)) >> 32); | |
233 | eax = (unsigned int)(((unsigned long) | |
234 | &(uci->mc.mc_amd->hdr.data_code)) & 0xffffffffL); | |
235 | ||
236 | asm volatile("movl %0, %%ecx; wrmsr" : | |
237 | : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx"); | |
238 | ||
239 | /* get patch id after patching */ | |
240 | asm volatile("movl %1, %%ecx; rdmsr" | |
241 | : "=a" (rev) | |
242 | : "i" (0x0000008B) : "ecx"); | |
243 | ||
244 | spin_unlock_irqrestore(µcode_update_lock, flags); | |
245 | ||
246 | /* check current patch id and patch's id for match */ | |
247 | if (rev != uci->mc.mc_amd->hdr.patch_id) { | |
248 | printk(KERN_ERR "microcode: CPU%d update from revision " | |
249 | "0x%x to 0x%x failed\n", cpu_num, | |
250 | uci->mc.mc_amd->hdr.patch_id, rev); | |
251 | return; | |
252 | } | |
253 | ||
254 | printk(KERN_INFO "microcode: CPU%d updated from revision " | |
255 | "0x%x to 0x%x \n", | |
256 | cpu_num, uci->rev, uci->mc.mc_amd->hdr.patch_id); | |
257 | ||
258 | uci->rev = rev; | |
259 | } | |
260 | ||
261 | #ifdef CONFIG_MICROCODE_OLD_INTERFACE | |
262 | extern void __user *user_buffer; /* user area microcode data buffer */ | |
263 | extern unsigned int user_buffer_size; /* it's size */ | |
264 | ||
265 | static long get_next_ucode_amd(void **mc, long offset) | |
266 | { | |
267 | struct microcode_header_amd mc_header; | |
268 | unsigned long total_size; | |
269 | ||
270 | /* No more data */ | |
271 | if (offset >= user_buffer_size) | |
272 | return 0; | |
273 | if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) { | |
274 | printk(KERN_ERR "microcode: error! Can not read user data\n"); | |
275 | return -EFAULT; | |
276 | } | |
277 | total_size = get_totalsize(&mc_header); | |
278 | if (offset + total_size > user_buffer_size) { | |
279 | printk(KERN_ERR "microcode: error! Bad total size in microcode " | |
280 | "data file\n"); | |
281 | return -EINVAL; | |
282 | } | |
283 | *mc = vmalloc(UCODE_MAX_SIZE); | |
284 | if (!*mc) | |
285 | return -ENOMEM; | |
286 | memset(*mc, 0, UCODE_MAX_SIZE); | |
287 | ||
288 | if (copy_from_user(*mc, user_buffer + offset, total_size)) { | |
289 | printk(KERN_ERR "microcode: error! Can not read user data\n"); | |
290 | vfree(*mc); | |
291 | return -EFAULT; | |
292 | } | |
293 | return offset + total_size; | |
294 | } | |
295 | #else | |
296 | #define get_next_ucode_amd() NULL | |
297 | #endif | |
298 | ||
299 | static long get_next_ucode_from_buffer_amd(void **mc, void *buf, | |
300 | unsigned long size, long offset) | |
301 | { | |
302 | struct microcode_header_amd *mc_header; | |
303 | unsigned long total_size; | |
304 | unsigned char *buf_pos = buf; | |
305 | ||
306 | /* No more data */ | |
307 | if (offset >= size) | |
308 | return 0; | |
309 | ||
310 | if (buf_pos[offset] != UCODE_UCODE_TYPE) { | |
311 | printk(KERN_ERR "microcode: error! " | |
312 | "Wrong microcode payload type field\n"); | |
313 | return -EINVAL; | |
314 | } | |
315 | ||
316 | mc_header = (struct microcode_header_amd *)(&buf_pos[offset+8]); | |
317 | ||
318 | total_size = (unsigned long) (buf_pos[offset+4] + | |
319 | (buf_pos[offset+5] << 8)); | |
320 | ||
321 | printk(KERN_INFO "microcode: size %lu, total_size %lu, offset %ld\n", | |
322 | size, total_size, offset); | |
323 | ||
324 | if (offset + total_size > size) { | |
325 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); | |
326 | return -EINVAL; | |
327 | } | |
328 | ||
329 | *mc = vmalloc(UCODE_MAX_SIZE); | |
330 | if (!*mc) { | |
331 | printk(KERN_ERR "microcode: error! " | |
332 | "Can not allocate memory for microcode patch\n"); | |
333 | return -ENOMEM; | |
334 | } | |
335 | ||
336 | memset(*mc, 0, UCODE_MAX_SIZE); | |
337 | memcpy(*mc, buf + offset + 8, total_size); | |
338 | ||
339 | return offset + total_size + 8; | |
340 | } | |
341 | ||
342 | static long install_equiv_cpu_table(void *buf, unsigned long size, long offset) | |
343 | { | |
344 | unsigned int *buf_pos = buf; | |
345 | ||
346 | /* No more data */ | |
347 | if (offset >= size) | |
348 | return 0; | |
349 | ||
350 | if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE) { | |
351 | printk(KERN_ERR "microcode: error! " | |
352 | "Wrong microcode equivalnet cpu table type field\n"); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | if (size == 0) { | |
357 | printk(KERN_ERR "microcode: error! " | |
358 | "Wrong microcode equivalnet cpu table length\n"); | |
359 | return 0; | |
360 | } | |
361 | ||
362 | equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); | |
363 | if (!equiv_cpu_table) { | |
364 | printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n"); | |
365 | return 0; | |
366 | } | |
367 | ||
368 | memset(equiv_cpu_table, 0, size); | |
369 | memcpy(equiv_cpu_table, &buf_pos[3], size); | |
370 | ||
371 | return size + 12; /* add header length */ | |
372 | } | |
373 | ||
374 | /* fake device for request_firmware */ | |
375 | extern struct platform_device *microcode_pdev; | |
376 | ||
377 | static int cpu_request_microcode_amd(int cpu) | |
378 | { | |
379 | char name[30]; | |
380 | const struct firmware *firmware; | |
381 | void *buf; | |
382 | unsigned int *buf_pos; | |
383 | unsigned long size; | |
384 | long offset = 0; | |
385 | int error; | |
386 | void *mc; | |
387 | ||
388 | /* We should bind the task to the CPU */ | |
389 | BUG_ON(cpu != raw_smp_processor_id()); | |
390 | ||
391 | sprintf(name, "amd-ucode/microcode_amd.bin"); | |
392 | error = request_firmware(&firmware, "amd-ucode/microcode_amd.bin", | |
393 | µcode_pdev->dev); | |
394 | if (error) { | |
395 | printk(KERN_ERR "microcode: ucode data file %s load failed\n", | |
396 | name); | |
397 | return error; | |
398 | } | |
399 | ||
400 | buf_pos = buf = firmware->data; | |
401 | size = firmware->size; | |
402 | ||
403 | if (buf_pos[0] != UCODE_MAGIC) { | |
404 | printk(KERN_ERR "microcode: error! Wrong microcode patch file magic\n"); | |
405 | return -EINVAL; | |
406 | } | |
407 | ||
408 | offset = install_equiv_cpu_table(buf, buf_pos[2], offset); | |
409 | ||
410 | if (!offset) { | |
411 | printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); | |
412 | return -EINVAL; | |
413 | } | |
414 | ||
415 | while ((offset = | |
416 | get_next_ucode_from_buffer_amd(&mc, buf, size, offset)) > 0) { | |
417 | error = get_matching_microcode_amd(mc, cpu); | |
418 | if (error < 0) | |
419 | break; | |
420 | /* | |
421 | * It's possible the data file has multiple matching ucode, | |
422 | * lets keep searching till the latest version | |
423 | */ | |
424 | if (error == 1) { | |
425 | apply_microcode_amd(cpu); | |
426 | error = 0; | |
427 | } | |
428 | vfree(mc); | |
429 | } | |
430 | if (offset > 0) { | |
431 | vfree(mc); | |
432 | vfree(equiv_cpu_table); | |
433 | equiv_cpu_table = NULL; | |
434 | } | |
435 | if (offset < 0) | |
436 | error = offset; | |
437 | release_firmware(firmware); | |
438 | ||
439 | return error; | |
440 | } | |
441 | ||
442 | static int apply_microcode_check_cpu_amd(int cpu) | |
443 | { | |
444 | struct cpuinfo_x86 *c = &cpu_data(cpu); | |
445 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
446 | unsigned int rev; | |
447 | cpumask_t old; | |
448 | cpumask_of_cpu_ptr(newmask, cpu); | |
449 | int err = 0; | |
450 | ||
451 | /* Check if the microcode is available */ | |
452 | if (!uci->mc.mc_amd) | |
453 | return 0; | |
454 | ||
455 | old = current->cpus_allowed; | |
456 | set_cpus_allowed(current, newmask); | |
457 | ||
458 | /* Check if the microcode we have in memory matches the CPU */ | |
459 | if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 16) | |
460 | err = -EINVAL; | |
461 | ||
462 | if (!err) { | |
463 | asm volatile("movl %1, %%ecx; rdmsr" | |
464 | : "=a" (rev) | |
465 | : "i" (0x0000008B) : "ecx"); | |
466 | ||
467 | if (uci->rev != rev) | |
468 | err = -EINVAL; | |
469 | } | |
470 | ||
471 | if (!err) | |
472 | apply_microcode_amd(cpu); | |
473 | else | |
474 | printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:" | |
475 | " rev=0x%x\n", | |
476 | cpu, uci->rev); | |
477 | ||
478 | set_cpus_allowed(current, old); | |
479 | return err; | |
480 | } | |
481 | ||
482 | static void microcode_fini_cpu_amd(int cpu) | |
483 | { | |
484 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
485 | ||
486 | mutex_lock(µcode_mutex); | |
487 | uci->valid = 0; | |
488 | vfree(uci->mc.mc_amd); | |
489 | uci->mc.mc_amd = NULL; | |
490 | mutex_unlock(µcode_mutex); | |
491 | } | |
492 | ||
493 | static struct microcode_ops microcode_amd_ops = { | |
494 | .get_next_ucode = get_next_ucode_amd, | |
495 | .get_matching_microcode = get_matching_microcode_amd, | |
496 | .microcode_sanity_check = NULL, | |
497 | .apply_microcode_check_cpu = apply_microcode_check_cpu_amd, | |
498 | .cpu_request_microcode = cpu_request_microcode_amd, | |
499 | .collect_cpu_info = collect_cpu_info_amd, | |
500 | .apply_microcode = apply_microcode_amd, | |
501 | .microcode_fini_cpu = microcode_fini_cpu_amd, | |
502 | }; | |
503 | ||
504 | static int __init microcode_amd_module_init(void) | |
505 | { | |
506 | struct cpuinfo_x86 *c = &cpu_data(get_cpu()); | |
507 | ||
508 | equiv_cpu_table = NULL; | |
509 | if (c->x86_vendor == X86_VENDOR_AMD) | |
510 | return microcode_init(µcode_amd_ops, THIS_MODULE); | |
511 | else | |
512 | return -ENODEV; | |
513 | } | |
514 | ||
515 | static void __exit microcode_amd_module_exit(void) | |
516 | { | |
517 | microcode_exit(); | |
518 | } | |
519 | ||
520 | module_init(microcode_amd_module_init) | |
521 | module_exit(microcode_amd_module_exit) |