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>"); | |
5d7b6052 | 43 | MODULE_LICENSE("GPL v2"); |
80cc9f10 PO |
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) | |
a0ac87d6 PO |
50 | #define DEFAULT_UCODE_DATASIZE (896) |
51 | #define MC_HEADER_SIZE (sizeof(struct microcode_header_amd)) | |
52 | #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) | |
80cc9f10 PO |
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 | ||
80cc9f10 PO |
59 | /* serialize access to the physical write */ |
60 | static DEFINE_SPINLOCK(microcode_update_lock); | |
61 | ||
a0a29b62 | 62 | static struct equiv_cpu_entry *equiv_cpu_table; |
80cc9f10 | 63 | |
d45de409 | 64 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) |
80cc9f10 PO |
65 | { |
66 | struct cpuinfo_x86 *c = &cpu_data(cpu); | |
80cc9f10 | 67 | |
d45de409 | 68 | memset(csig, 0, sizeof(*csig)); |
80cc9f10 PO |
69 | |
70 | if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { | |
71 | printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n", | |
72 | cpu); | |
d45de409 | 73 | return -1; |
80cc9f10 PO |
74 | } |
75 | ||
76 | asm volatile("movl %1, %%ecx; rdmsr" | |
d45de409 | 77 | : "=a" (csig->rev) |
80cc9f10 PO |
78 | : "i" (0x0000008B) : "ecx"); |
79 | ||
80 | printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n", | |
d45de409 DA |
81 | csig->rev); |
82 | ||
83 | return 0; | |
80cc9f10 PO |
84 | } |
85 | ||
a0a29b62 | 86 | static int get_matching_microcode(int cpu, void *mc, int rev) |
80cc9f10 | 87 | { |
80cc9f10 | 88 | struct microcode_header_amd *mc_header = mc; |
80cc9f10 PO |
89 | struct pci_dev *nb_pci_dev, *sb_pci_dev; |
90 | unsigned int current_cpu_id; | |
91 | unsigned int equiv_cpu_id = 0x00; | |
92 | unsigned int i = 0; | |
93 | ||
a0a29b62 | 94 | /* |
a1c75cc5 | 95 | * FIXME! dimm: do we need this? Why an update via /dev/... is different |
a0a29b62 DA |
96 | * from the one via firmware? |
97 | * | |
98 | * This is a tricky part. We might be called from a write operation | |
99 | * to the device file instead of the usual process of firmware | |
100 | * loading. This routine needs to be able to distinguish both | |
101 | * cases. This is done by checking if there alread is a equivalent | |
102 | * CPU table installed. If not, we're written through | |
103 | * /dev/cpu/microcode. | |
104 | * Since we ignore all checks. The error case in which going through | |
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 | |
80cc9f10 PO |
110 | if (equiv_cpu_table == NULL) { |
111 | printk(KERN_INFO "microcode: CPU%d microcode update with " | |
112 | "version 0x%x (current=0x%x)\n", | |
d45de409 | 113 | cpu, mc_header->patch_id, uci->cpu_sig.rev); |
80cc9f10 PO |
114 | goto out; |
115 | } | |
a0a29b62 | 116 | #endif |
80cc9f10 PO |
117 | current_cpu_id = cpuid_eax(0x00000001); |
118 | ||
119 | while (equiv_cpu_table[i].installed_cpu != 0) { | |
120 | if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { | |
121 | equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; | |
122 | break; | |
123 | } | |
124 | i++; | |
125 | } | |
126 | ||
127 | if (!equiv_cpu_id) { | |
128 | printk(KERN_ERR "microcode: CPU%d cpu_id " | |
129 | "not found in equivalent cpu table \n", cpu); | |
130 | return 0; | |
131 | } | |
132 | ||
133 | if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) { | |
134 | printk(KERN_ERR | |
135 | "microcode: CPU%d patch does not match " | |
136 | "(patch is %x, cpu extended is %x) \n", | |
137 | cpu, mc_header->processor_rev_id[0], | |
138 | (equiv_cpu_id & 0xff)); | |
139 | return 0; | |
140 | } | |
141 | ||
142 | if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) { | |
143 | printk(KERN_ERR "microcode: CPU%d patch does not match " | |
144 | "(patch is %x, cpu base id is %x) \n", | |
145 | cpu, mc_header->processor_rev_id[1], | |
146 | ((equiv_cpu_id >> 16) & 0xff)); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | /* ucode may be northbridge specific */ | |
152 | if (mc_header->nb_dev_id) { | |
153 | nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, | |
154 | (mc_header->nb_dev_id & 0xff), | |
155 | NULL); | |
156 | if ((!nb_pci_dev) || | |
157 | (mc_header->nb_rev_id != nb_pci_dev->revision)) { | |
158 | printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu); | |
159 | pci_dev_put(nb_pci_dev); | |
160 | return 0; | |
161 | } | |
162 | pci_dev_put(nb_pci_dev); | |
163 | } | |
164 | ||
165 | /* ucode may be southbridge specific */ | |
166 | if (mc_header->sb_dev_id) { | |
167 | sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, | |
168 | (mc_header->sb_dev_id & 0xff), | |
169 | NULL); | |
170 | if ((!sb_pci_dev) || | |
171 | (mc_header->sb_rev_id != sb_pci_dev->revision)) { | |
172 | printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu); | |
173 | pci_dev_put(sb_pci_dev); | |
174 | return 0; | |
175 | } | |
176 | pci_dev_put(sb_pci_dev); | |
177 | } | |
178 | ||
a0a29b62 | 179 | if (mc_header->patch_id <= rev) |
80cc9f10 PO |
180 | return 0; |
181 | ||
80cc9f10 PO |
182 | return 1; |
183 | } | |
184 | ||
185 | static void apply_microcode_amd(int cpu) | |
186 | { | |
187 | unsigned long flags; | |
188 | unsigned int eax, edx; | |
189 | unsigned int rev; | |
190 | int cpu_num = raw_smp_processor_id(); | |
191 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
5b792d32 | 192 | unsigned long addr; |
80cc9f10 PO |
193 | |
194 | /* We should bind the task to the CPU */ | |
195 | BUG_ON(cpu_num != cpu); | |
196 | ||
197 | if (uci->mc.mc_amd == NULL) | |
198 | return; | |
199 | ||
200 | spin_lock_irqsave(µcode_update_lock, flags); | |
201 | ||
5b792d32 RD |
202 | addr = (unsigned long)&uci->mc.mc_amd->hdr.data_code; |
203 | edx = (unsigned int)(((unsigned long)upper_32_bits(addr))); | |
204 | eax = (unsigned int)(((unsigned long)lower_32_bits(addr))); | |
80cc9f10 PO |
205 | |
206 | asm volatile("movl %0, %%ecx; wrmsr" : | |
207 | : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx"); | |
208 | ||
209 | /* get patch id after patching */ | |
210 | asm volatile("movl %1, %%ecx; rdmsr" | |
211 | : "=a" (rev) | |
212 | : "i" (0x0000008B) : "ecx"); | |
213 | ||
214 | spin_unlock_irqrestore(µcode_update_lock, flags); | |
215 | ||
216 | /* check current patch id and patch's id for match */ | |
217 | if (rev != uci->mc.mc_amd->hdr.patch_id) { | |
218 | printk(KERN_ERR "microcode: CPU%d update from revision " | |
219 | "0x%x to 0x%x failed\n", cpu_num, | |
220 | uci->mc.mc_amd->hdr.patch_id, rev); | |
221 | return; | |
222 | } | |
223 | ||
224 | printk(KERN_INFO "microcode: CPU%d updated from revision " | |
225 | "0x%x to 0x%x \n", | |
d45de409 | 226 | cpu_num, uci->cpu_sig.rev, uci->mc.mc_amd->hdr.patch_id); |
80cc9f10 | 227 | |
d45de409 | 228 | uci->cpu_sig.rev = rev; |
80cc9f10 PO |
229 | } |
230 | ||
a0a29b62 DA |
231 | static void * get_next_ucode(u8 *buf, unsigned int size, |
232 | int (*get_ucode_data)(void *, const void *, size_t), | |
233 | unsigned int *mc_size) | |
80cc9f10 | 234 | { |
a0a29b62 DA |
235 | unsigned int total_size; |
236 | #define UCODE_UNKNOWN_HDR 8 | |
237 | u8 hdr[UCODE_UNKNOWN_HDR]; | |
238 | void *mc; | |
80cc9f10 | 239 | |
a0a29b62 DA |
240 | if (get_ucode_data(hdr, buf, UCODE_UNKNOWN_HDR)) |
241 | return NULL; | |
80cc9f10 | 242 | |
a0a29b62 | 243 | if (hdr[0] != UCODE_UCODE_TYPE) { |
80cc9f10 PO |
244 | printk(KERN_ERR "microcode: error! " |
245 | "Wrong microcode payload type field\n"); | |
a0a29b62 | 246 | return NULL; |
80cc9f10 PO |
247 | } |
248 | ||
a1c75cc5 | 249 | /* FIXME! dimm: Why not by means of get_totalsize(hdr)? */ |
a0a29b62 | 250 | total_size = (unsigned long) (hdr[4] + (hdr[5] << 8)); |
80cc9f10 | 251 | |
a0a29b62 DA |
252 | printk(KERN_INFO "microcode: size %u, total_size %u\n", |
253 | size, total_size); | |
80cc9f10 | 254 | |
a0a29b62 | 255 | if (total_size > size || total_size > UCODE_MAX_SIZE) { |
80cc9f10 | 256 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); |
a0a29b62 | 257 | return NULL; |
80cc9f10 PO |
258 | } |
259 | ||
a0a29b62 DA |
260 | mc = vmalloc(UCODE_MAX_SIZE); |
261 | if (mc) { | |
262 | memset(mc, 0, UCODE_MAX_SIZE); | |
263 | if (get_ucode_data(mc, buf + UCODE_UNKNOWN_HDR, total_size)) { | |
264 | vfree(mc); | |
265 | mc = NULL; | |
266 | } else | |
267 | *mc_size = total_size + UCODE_UNKNOWN_HDR; | |
80cc9f10 | 268 | } |
a0a29b62 DA |
269 | #undef UCODE_UNKNOWN_HDR |
270 | return mc; | |
80cc9f10 PO |
271 | } |
272 | ||
a0a29b62 DA |
273 | |
274 | static int install_equiv_cpu_table(u8 *buf, | |
275 | int (*get_ucode_data)(void *, const void *, size_t)) | |
80cc9f10 | 276 | { |
a0a29b62 DA |
277 | #define UCODE_HEADER_SIZE 12 |
278 | u8 *hdr[UCODE_HEADER_SIZE]; | |
279 | unsigned int *buf_pos = (unsigned int *)hdr; | |
280 | unsigned long size; | |
80cc9f10 | 281 | |
a0a29b62 | 282 | if (get_ucode_data(&hdr, buf, UCODE_HEADER_SIZE)) |
80cc9f10 PO |
283 | return 0; |
284 | ||
a0a29b62 | 285 | size = buf_pos[2]; |
80cc9f10 | 286 | |
a0a29b62 | 287 | if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { |
80cc9f10 | 288 | printk(KERN_ERR "microcode: error! " |
a0a29b62 | 289 | "Wrong microcode equivalnet cpu table\n"); |
80cc9f10 PO |
290 | return 0; |
291 | } | |
292 | ||
293 | equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); | |
294 | if (!equiv_cpu_table) { | |
295 | printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n"); | |
296 | return 0; | |
297 | } | |
298 | ||
a0a29b62 DA |
299 | buf += UCODE_HEADER_SIZE; |
300 | if (get_ucode_data(equiv_cpu_table, buf, size)) { | |
301 | vfree(equiv_cpu_table); | |
302 | return 0; | |
303 | } | |
80cc9f10 | 304 | |
a0a29b62 DA |
305 | return size + UCODE_HEADER_SIZE; /* add header length */ |
306 | #undef UCODE_HEADER_SIZE | |
80cc9f10 PO |
307 | } |
308 | ||
a0a29b62 | 309 | static void free_equiv_cpu_table(void) |
80cc9f10 | 310 | { |
a0a29b62 DA |
311 | if (equiv_cpu_table) { |
312 | vfree(equiv_cpu_table); | |
313 | equiv_cpu_table = NULL; | |
80cc9f10 | 314 | } |
a0a29b62 | 315 | } |
80cc9f10 | 316 | |
a0a29b62 DA |
317 | static 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; | |
80cc9f10 | 325 | |
a0a29b62 | 326 | offset = install_equiv_cpu_table(ucode_ptr, get_ucode_data); |
80cc9f10 PO |
327 | if (!offset) { |
328 | printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); | |
329 | return -EINVAL; | |
330 | } | |
331 | ||
a0a29b62 DA |
332 | ucode_ptr += offset; |
333 | leftover = size - offset; | |
334 | ||
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) | |
80cc9f10 | 341 | break; |
a0a29b62 DA |
342 | |
343 | mc_header = (struct microcode_header_amd *)mc; | |
344 | if (get_matching_microcode(cpu, mc, new_rev)) { | |
a1c75cc5 IM |
345 | if (new_mc) |
346 | vfree(new_mc); | |
a0a29b62 DA |
347 | new_rev = mc_header->patch_id; |
348 | new_mc = mc; | |
349 | } else | |
350 | vfree(mc); | |
351 | ||
352 | ucode_ptr += mc_size; | |
353 | leftover -= mc_size; | |
80cc9f10 | 354 | } |
a0a29b62 DA |
355 | |
356 | if (new_mc) { | |
357 | if (!leftover) { | |
358 | if (uci->mc.mc_amd) | |
359 | vfree(uci->mc.mc_amd); | |
360 | uci->mc.mc_amd = (struct microcode_amd *)new_mc; | |
361 | pr_debug("microcode: CPU%d found a matching microcode update with" | |
362 | " version 0x%x (current=0x%x)\n", | |
363 | cpu, uci->mc.mc_amd->hdr.patch_id, uci->cpu_sig.rev); | |
364 | } else | |
365 | vfree(new_mc); | |
80cc9f10 | 366 | } |
a0a29b62 DA |
367 | |
368 | free_equiv_cpu_table(); | |
369 | ||
370 | return (int)leftover; | |
371 | } | |
372 | ||
373 | static int get_ucode_fw(void *to, const void *from, size_t n) | |
374 | { | |
375 | memcpy(to, from, n); | |
376 | return 0; | |
377 | } | |
378 | ||
379 | static int request_microcode_fw(int cpu, struct device *device) | |
380 | { | |
381 | const char *fw_name = "amd-ucode/microcode_amd.bin"; | |
382 | const struct firmware *firmware; | |
383 | int ret; | |
384 | ||
385 | /* We should bind the task to the CPU */ | |
386 | BUG_ON(cpu != raw_smp_processor_id()); | |
387 | ||
388 | ret = request_firmware(&firmware, fw_name, device); | |
389 | if (ret) { | |
390 | printk(KERN_ERR "microcode: ucode data file %s load failed\n", fw_name); | |
391 | return ret; | |
392 | } | |
393 | ||
394 | ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size, | |
395 | &get_ucode_fw); | |
396 | ||
80cc9f10 PO |
397 | release_firmware(firmware); |
398 | ||
a0a29b62 DA |
399 | return ret; |
400 | } | |
401 | ||
402 | static int get_ucode_user(void *to, const void *from, size_t n) | |
403 | { | |
404 | return copy_from_user(to, from, n); | |
405 | } | |
406 | ||
407 | static int request_microcode_user(int cpu, const void __user *buf, size_t size) | |
408 | { | |
409 | /* We should bind the task to the CPU */ | |
410 | BUG_ON(cpu != raw_smp_processor_id()); | |
411 | ||
412 | return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user); | |
80cc9f10 PO |
413 | } |
414 | ||
80cc9f10 PO |
415 | static void microcode_fini_cpu_amd(int cpu) |
416 | { | |
417 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
418 | ||
80cc9f10 PO |
419 | vfree(uci->mc.mc_amd); |
420 | uci->mc.mc_amd = NULL; | |
80cc9f10 PO |
421 | } |
422 | ||
423 | static struct microcode_ops microcode_amd_ops = { | |
a0a29b62 DA |
424 | .request_microcode_user = request_microcode_user, |
425 | .request_microcode_fw = request_microcode_fw, | |
80cc9f10 PO |
426 | .collect_cpu_info = collect_cpu_info_amd, |
427 | .apply_microcode = apply_microcode_amd, | |
428 | .microcode_fini_cpu = microcode_fini_cpu_amd, | |
429 | }; | |
430 | ||
431 | static int __init microcode_amd_module_init(void) | |
432 | { | |
8343ef24 | 433 | struct cpuinfo_x86 *c = &cpu_data(0); |
80cc9f10 PO |
434 | |
435 | equiv_cpu_table = NULL; | |
8343ef24 DA |
436 | if (c->x86_vendor != X86_VENDOR_AMD) { |
437 | printk(KERN_ERR "microcode: CPU platform is not AMD-capable\n"); | |
80cc9f10 | 438 | return -ENODEV; |
8343ef24 DA |
439 | } |
440 | ||
441 | return microcode_init(µcode_amd_ops, THIS_MODULE); | |
80cc9f10 PO |
442 | } |
443 | ||
444 | static void __exit microcode_amd_module_exit(void) | |
445 | { | |
446 | microcode_exit(); | |
447 | } | |
448 | ||
449 | module_init(microcode_amd_module_init) | |
450 | module_exit(microcode_amd_module_exit) |