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 | * | |
2a3282a7 | 13 | * Licensed under the terms of the GNU General Public |
80cc9f10 PO |
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> | |
be957763 | 35 | #include <linux/uaccess.h> |
80cc9f10 PO |
36 | |
37 | #include <asm/msr.h> | |
80cc9f10 PO |
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; | |
3c763fd7 | 65 | u16 processor_rev_id; |
18dbc916 DA |
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 | ||
6cc9b6d9 AH |
78 | #define UCODE_MAX_SIZE 2048 |
79 | #define UCODE_CONTAINER_SECTION_HDR 8 | |
80 | #define UCODE_CONTAINER_HEADER_SIZE 12 | |
80cc9f10 | 81 | |
80cc9f10 PO |
82 | /* serialize access to the physical write */ |
83 | static DEFINE_SPINLOCK(microcode_update_lock); | |
84 | ||
a0a29b62 | 85 | static struct equiv_cpu_entry *equiv_cpu_table; |
80cc9f10 | 86 | |
d45de409 | 87 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) |
80cc9f10 PO |
88 | { |
89 | struct cpuinfo_x86 *c = &cpu_data(cpu); | |
29d0887f | 90 | u32 dummy; |
80cc9f10 | 91 | |
d45de409 | 92 | memset(csig, 0, sizeof(*csig)); |
80cc9f10 PO |
93 | |
94 | if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { | |
95 | printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n", | |
96 | cpu); | |
d45de409 | 97 | return -1; |
80cc9f10 PO |
98 | } |
99 | ||
29d0887f | 100 | rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy); |
80cc9f10 PO |
101 | |
102 | printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n", | |
d45de409 DA |
103 | csig->rev); |
104 | ||
105 | return 0; | |
80cc9f10 PO |
106 | } |
107 | ||
a0a29b62 | 108 | static int get_matching_microcode(int cpu, void *mc, int rev) |
80cc9f10 | 109 | { |
80cc9f10 | 110 | struct microcode_header_amd *mc_header = mc; |
80cc9f10 PO |
111 | unsigned int current_cpu_id; |
112 | unsigned int equiv_cpu_id = 0x00; | |
113 | unsigned int i = 0; | |
114 | ||
a0a29b62 | 115 | BUG_ON(equiv_cpu_table == NULL); |
80cc9f10 PO |
116 | current_cpu_id = cpuid_eax(0x00000001); |
117 | ||
118 | while (equiv_cpu_table[i].installed_cpu != 0) { | |
119 | if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { | |
3c763fd7 | 120 | equiv_cpu_id = equiv_cpu_table[i].equiv_cpu & 0xffff; |
80cc9f10 PO |
121 | break; |
122 | } | |
123 | i++; | |
124 | } | |
125 | ||
126 | if (!equiv_cpu_id) { | |
127 | printk(KERN_ERR "microcode: CPU%d cpu_id " | |
2a3282a7 | 128 | "not found in equivalent cpu table\n", cpu); |
80cc9f10 PO |
129 | return 0; |
130 | } | |
131 | ||
3c763fd7 AH |
132 | if (mc_header->processor_rev_id != equiv_cpu_id) { |
133 | printk(KERN_ERR "microcode: CPU%d patch does not match " | |
134 | "(processor_rev_id: %x, eqiv_cpu_id: %x)\n", | |
135 | cpu, mc_header->processor_rev_id, equiv_cpu_id); | |
80cc9f10 PO |
136 | return 0; |
137 | } | |
138 | ||
98415301 AH |
139 | /* ucode might be chipset specific -- currently we don't support this */ |
140 | if (mc_header->nb_dev_id || mc_header->sb_dev_id) { | |
141 | printk(KERN_WARNING "microcode: CPU%d loading of chipset " | |
142 | "specific code not yet supported\n", cpu); | |
143 | return 0; | |
80cc9f10 PO |
144 | } |
145 | ||
a0a29b62 | 146 | if (mc_header->patch_id <= rev) |
80cc9f10 PO |
147 | return 0; |
148 | ||
80cc9f10 PO |
149 | return 1; |
150 | } | |
151 | ||
152 | static void apply_microcode_amd(int cpu) | |
153 | { | |
154 | unsigned long flags; | |
29d0887f | 155 | u32 rev, dummy; |
80cc9f10 PO |
156 | int cpu_num = raw_smp_processor_id(); |
157 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
18dbc916 | 158 | struct microcode_amd *mc_amd = uci->mc; |
80cc9f10 PO |
159 | |
160 | /* We should bind the task to the CPU */ | |
161 | BUG_ON(cpu_num != cpu); | |
162 | ||
18dbc916 | 163 | if (mc_amd == NULL) |
80cc9f10 PO |
164 | return; |
165 | ||
166 | spin_lock_irqsave(µcode_update_lock, flags); | |
29d0887f | 167 | wrmsrl(MSR_AMD64_PATCH_LOADER, &mc_amd->hdr.data_code); |
80cc9f10 | 168 | /* get patch id after patching */ |
29d0887f | 169 | rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); |
80cc9f10 PO |
170 | spin_unlock_irqrestore(µcode_update_lock, flags); |
171 | ||
172 | /* check current patch id and patch's id for match */ | |
18dbc916 | 173 | if (rev != mc_amd->hdr.patch_id) { |
80cc9f10 PO |
174 | printk(KERN_ERR "microcode: CPU%d update from revision " |
175 | "0x%x to 0x%x failed\n", cpu_num, | |
18dbc916 | 176 | mc_amd->hdr.patch_id, rev); |
80cc9f10 PO |
177 | return; |
178 | } | |
179 | ||
180 | printk(KERN_INFO "microcode: CPU%d updated from revision " | |
2a3282a7 | 181 | "0x%x to 0x%x\n", |
18dbc916 | 182 | cpu_num, uci->cpu_sig.rev, mc_amd->hdr.patch_id); |
80cc9f10 | 183 | |
d45de409 | 184 | uci->cpu_sig.rev = rev; |
80cc9f10 PO |
185 | } |
186 | ||
0657d9eb AH |
187 | static int get_ucode_data(void *to, const u8 *from, size_t n) |
188 | { | |
189 | memcpy(to, from, n); | |
190 | return 0; | |
191 | } | |
192 | ||
8c135206 | 193 | static void *get_next_ucode(const u8 *buf, unsigned int size, |
0657d9eb | 194 | unsigned int *mc_size) |
80cc9f10 | 195 | { |
a0a29b62 | 196 | unsigned int total_size; |
d4738792 | 197 | u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; |
a0a29b62 | 198 | void *mc; |
80cc9f10 | 199 | |
d4738792 | 200 | if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR)) |
a0a29b62 | 201 | return NULL; |
80cc9f10 | 202 | |
d4738792 | 203 | if (section_hdr[0] != UCODE_UCODE_TYPE) { |
80cc9f10 PO |
204 | printk(KERN_ERR "microcode: error! " |
205 | "Wrong microcode payload type field\n"); | |
a0a29b62 | 206 | return NULL; |
80cc9f10 PO |
207 | } |
208 | ||
d4738792 | 209 | total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); |
80cc9f10 | 210 | |
a0a29b62 DA |
211 | printk(KERN_INFO "microcode: size %u, total_size %u\n", |
212 | size, total_size); | |
80cc9f10 | 213 | |
a0a29b62 | 214 | if (total_size > size || total_size > UCODE_MAX_SIZE) { |
80cc9f10 | 215 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); |
a0a29b62 | 216 | return NULL; |
80cc9f10 PO |
217 | } |
218 | ||
a0a29b62 DA |
219 | mc = vmalloc(UCODE_MAX_SIZE); |
220 | if (mc) { | |
221 | memset(mc, 0, UCODE_MAX_SIZE); | |
be957763 AH |
222 | if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, |
223 | total_size)) { | |
a0a29b62 DA |
224 | vfree(mc); |
225 | mc = NULL; | |
226 | } else | |
d4738792 | 227 | *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR; |
80cc9f10 | 228 | } |
a0a29b62 | 229 | return mc; |
80cc9f10 PO |
230 | } |
231 | ||
a0a29b62 | 232 | |
0657d9eb | 233 | static int install_equiv_cpu_table(const u8 *buf) |
80cc9f10 | 234 | { |
b6cffde1 PO |
235 | u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; |
236 | unsigned int *buf_pos = (unsigned int *)container_hdr; | |
a0a29b62 | 237 | unsigned long size; |
80cc9f10 | 238 | |
b6cffde1 | 239 | if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE)) |
80cc9f10 PO |
240 | return 0; |
241 | ||
a0a29b62 | 242 | size = buf_pos[2]; |
80cc9f10 | 243 | |
a0a29b62 | 244 | if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { |
80cc9f10 | 245 | printk(KERN_ERR "microcode: error! " |
2a3282a7 | 246 | "Wrong microcode equivalent cpu table\n"); |
80cc9f10 PO |
247 | return 0; |
248 | } | |
249 | ||
250 | equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); | |
251 | if (!equiv_cpu_table) { | |
252 | printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n"); | |
253 | return 0; | |
254 | } | |
255 | ||
b6cffde1 | 256 | buf += UCODE_CONTAINER_HEADER_SIZE; |
a0a29b62 DA |
257 | if (get_ucode_data(equiv_cpu_table, buf, size)) { |
258 | vfree(equiv_cpu_table); | |
259 | return 0; | |
260 | } | |
80cc9f10 | 261 | |
b6cffde1 | 262 | return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */ |
80cc9f10 PO |
263 | } |
264 | ||
a0a29b62 | 265 | static void free_equiv_cpu_table(void) |
80cc9f10 | 266 | { |
a0a29b62 DA |
267 | if (equiv_cpu_table) { |
268 | vfree(equiv_cpu_table); | |
269 | equiv_cpu_table = NULL; | |
80cc9f10 | 270 | } |
a0a29b62 | 271 | } |
80cc9f10 | 272 | |
0657d9eb | 273 | static int generic_load_microcode(int cpu, const u8 *data, size_t size) |
a0a29b62 DA |
274 | { |
275 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
8c135206 AH |
276 | const u8 *ucode_ptr = data; |
277 | void *new_mc = NULL; | |
278 | void *mc; | |
a0a29b62 DA |
279 | int new_rev = uci->cpu_sig.rev; |
280 | unsigned int leftover; | |
281 | unsigned long offset; | |
80cc9f10 | 282 | |
0657d9eb | 283 | offset = install_equiv_cpu_table(ucode_ptr); |
80cc9f10 PO |
284 | if (!offset) { |
285 | printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); | |
286 | return -EINVAL; | |
287 | } | |
288 | ||
a0a29b62 DA |
289 | ucode_ptr += offset; |
290 | leftover = size - offset; | |
291 | ||
292 | while (leftover) { | |
2f9284e4 | 293 | unsigned int uninitialized_var(mc_size); |
a0a29b62 DA |
294 | struct microcode_header_amd *mc_header; |
295 | ||
0657d9eb | 296 | mc = get_next_ucode(ucode_ptr, leftover, &mc_size); |
a0a29b62 | 297 | if (!mc) |
80cc9f10 | 298 | break; |
a0a29b62 DA |
299 | |
300 | mc_header = (struct microcode_header_amd *)mc; | |
301 | if (get_matching_microcode(cpu, mc, new_rev)) { | |
a1c75cc5 IM |
302 | if (new_mc) |
303 | vfree(new_mc); | |
a0a29b62 DA |
304 | new_rev = mc_header->patch_id; |
305 | new_mc = mc; | |
be957763 | 306 | } else |
a0a29b62 DA |
307 | vfree(mc); |
308 | ||
309 | ucode_ptr += mc_size; | |
310 | leftover -= mc_size; | |
80cc9f10 | 311 | } |
a0a29b62 DA |
312 | |
313 | if (new_mc) { | |
314 | if (!leftover) { | |
18dbc916 DA |
315 | if (uci->mc) |
316 | vfree(uci->mc); | |
317 | uci->mc = new_mc; | |
be957763 AH |
318 | pr_debug("microcode: CPU%d found a matching microcode " |
319 | "update with version 0x%x (current=0x%x)\n", | |
320 | cpu, new_rev, uci->cpu_sig.rev); | |
a0a29b62 DA |
321 | } else |
322 | vfree(new_mc); | |
80cc9f10 | 323 | } |
a0a29b62 DA |
324 | |
325 | free_equiv_cpu_table(); | |
326 | ||
327 | return (int)leftover; | |
328 | } | |
329 | ||
a0a29b62 DA |
330 | static int request_microcode_fw(int cpu, struct device *device) |
331 | { | |
332 | const char *fw_name = "amd-ucode/microcode_amd.bin"; | |
333 | const struct firmware *firmware; | |
334 | int ret; | |
335 | ||
336 | /* We should bind the task to the CPU */ | |
337 | BUG_ON(cpu != raw_smp_processor_id()); | |
338 | ||
339 | ret = request_firmware(&firmware, fw_name, device); | |
340 | if (ret) { | |
be957763 AH |
341 | printk(KERN_ERR "microcode: ucode data file %s load failed\n", |
342 | fw_name); | |
a0a29b62 DA |
343 | return ret; |
344 | } | |
345 | ||
0657d9eb | 346 | ret = generic_load_microcode(cpu, firmware->data, firmware->size); |
a0a29b62 | 347 | |
80cc9f10 PO |
348 | release_firmware(firmware); |
349 | ||
a0a29b62 DA |
350 | return ret; |
351 | } | |
352 | ||
a0a29b62 DA |
353 | static int request_microcode_user(int cpu, const void __user *buf, size_t size) |
354 | { | |
be957763 AH |
355 | printk(KERN_WARNING "microcode: AMD microcode update via " |
356 | "/dev/cpu/microcode is not supported\n"); | |
2f9284e4 | 357 | return -1; |
80cc9f10 PO |
358 | } |
359 | ||
80cc9f10 PO |
360 | static void microcode_fini_cpu_amd(int cpu) |
361 | { | |
362 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | |
363 | ||
18dbc916 DA |
364 | vfree(uci->mc); |
365 | uci->mc = NULL; | |
80cc9f10 PO |
366 | } |
367 | ||
368 | static struct microcode_ops microcode_amd_ops = { | |
a0a29b62 DA |
369 | .request_microcode_user = request_microcode_user, |
370 | .request_microcode_fw = request_microcode_fw, | |
80cc9f10 PO |
371 | .collect_cpu_info = collect_cpu_info_amd, |
372 | .apply_microcode = apply_microcode_amd, | |
373 | .microcode_fini_cpu = microcode_fini_cpu_amd, | |
374 | }; | |
375 | ||
18dbc916 | 376 | struct microcode_ops * __init init_amd_microcode(void) |
80cc9f10 | 377 | { |
18dbc916 | 378 | return µcode_amd_ops; |
80cc9f10 | 379 | } |