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