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