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