Merge branch 'for-rmk' of git://git.marvell.com/orion into devel
[linux-2.6] / arch / x86 / kernel / microcode_amd.c
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 under the terms of the GNU General Public
14  *  License version 2. See file COPYING for details.
15  */
16 #include <linux/firmware.h>
17 #include <linux/pci_ids.h>
18 #include <linux/uaccess.h>
19 #include <linux/vmalloc.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/pci.h>
23
24 #include <asm/microcode.h>
25 #include <asm/processor.h>
26 #include <asm/msr.h>
27
28 MODULE_DESCRIPTION("AMD Microcode Update Driver");
29 MODULE_AUTHOR("Peter Oruba");
30 MODULE_LICENSE("GPL v2");
31
32 #define UCODE_MAGIC                0x00414d44
33 #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
34 #define UCODE_UCODE_TYPE           0x00000001
35
36 struct equiv_cpu_entry {
37         u32     installed_cpu;
38         u32     fixed_errata_mask;
39         u32     fixed_errata_compare;
40         u16     equiv_cpu;
41         u16     res;
42 } __attribute__((packed));
43
44 struct microcode_header_amd {
45         u32     data_code;
46         u32     patch_id;
47         u16     mc_patch_data_id;
48         u8      mc_patch_data_len;
49         u8      init_flag;
50         u32     mc_patch_data_checksum;
51         u32     nb_dev_id;
52         u32     sb_dev_id;
53         u16     processor_rev_id;
54         u8      nb_rev_id;
55         u8      sb_rev_id;
56         u8      bios_api_rev;
57         u8      reserved1[3];
58         u32     match_reg[8];
59 } __attribute__((packed));
60
61 struct microcode_amd {
62         struct microcode_header_amd     hdr;
63         unsigned int                    mpb[0];
64 };
65
66 #define UCODE_MAX_SIZE                  2048
67 #define UCODE_CONTAINER_SECTION_HDR     8
68 #define UCODE_CONTAINER_HEADER_SIZE     12
69
70 static struct equiv_cpu_entry *equiv_cpu_table;
71
72 static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
73 {
74         struct cpuinfo_x86 *c = &cpu_data(cpu);
75         u32 dummy;
76
77         memset(csig, 0, sizeof(*csig));
78         if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
79                 printk(KERN_WARNING "microcode: CPU%d: AMD CPU family 0x%x not "
80                        "supported\n", cpu, c->x86);
81                 return -1;
82         }
83         rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy);
84         printk(KERN_INFO "microcode: CPU%d: patch_level=0x%x\n", cpu, csig->rev);
85         return 0;
86 }
87
88 static int get_matching_microcode(int cpu, void *mc, int rev)
89 {
90         struct microcode_header_amd *mc_header = mc;
91         unsigned int current_cpu_id;
92         u16 equiv_cpu_id = 0;
93         unsigned int i = 0;
94
95         BUG_ON(equiv_cpu_table == NULL);
96         current_cpu_id = cpuid_eax(0x00000001);
97
98         while (equiv_cpu_table[i].installed_cpu != 0) {
99                 if (current_cpu_id == equiv_cpu_table[i].installed_cpu) {
100                         equiv_cpu_id = equiv_cpu_table[i].equiv_cpu;
101                         break;
102                 }
103                 i++;
104         }
105
106         if (!equiv_cpu_id) {
107                 printk(KERN_WARNING "microcode: CPU%d: cpu revision "
108                        "not listed in equivalent cpu table\n", cpu);
109                 return 0;
110         }
111
112         if (mc_header->processor_rev_id != equiv_cpu_id) {
113                 printk(KERN_ERR "microcode: CPU%d: patch mismatch "
114                        "(processor_rev_id: %x, equiv_cpu_id: %x)\n",
115                        cpu, mc_header->processor_rev_id, equiv_cpu_id);
116                 return 0;
117         }
118
119         /* ucode might be chipset specific -- currently we don't support this */
120         if (mc_header->nb_dev_id || mc_header->sb_dev_id) {
121                 printk(KERN_ERR "microcode: CPU%d: loading of chipset "
122                        "specific code not yet supported\n", cpu);
123                 return 0;
124         }
125
126         if (mc_header->patch_id <= rev)
127                 return 0;
128
129         return 1;
130 }
131
132 static int apply_microcode_amd(int cpu)
133 {
134         u32 rev, dummy;
135         int cpu_num = raw_smp_processor_id();
136         struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
137         struct microcode_amd *mc_amd = uci->mc;
138
139         /* We should bind the task to the CPU */
140         BUG_ON(cpu_num != cpu);
141
142         if (mc_amd == NULL)
143                 return 0;
144
145         wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code);
146         /* get patch id after patching */
147         rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
148
149         /* check current patch id and patch's id for match */
150         if (rev != mc_amd->hdr.patch_id) {
151                 printk(KERN_ERR "microcode: CPU%d: update failed "
152                        "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id);
153                 return -1;
154         }
155
156         printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n",
157                cpu, rev);
158
159         uci->cpu_sig.rev = rev;
160
161         return 0;
162 }
163
164 static int get_ucode_data(void *to, const u8 *from, size_t n)
165 {
166         memcpy(to, from, n);
167         return 0;
168 }
169
170 static void *
171 get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size)
172 {
173         unsigned int total_size;
174         u8 section_hdr[UCODE_CONTAINER_SECTION_HDR];
175         void *mc;
176
177         if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR))
178                 return NULL;
179
180         if (section_hdr[0] != UCODE_UCODE_TYPE) {
181                 printk(KERN_ERR "microcode: error: invalid type field in "
182                        "container file section header\n");
183                 return NULL;
184         }
185
186         total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8));
187
188         printk(KERN_DEBUG "microcode: size %u, total_size %u\n",
189                size, total_size);
190
191         if (total_size > size || total_size > UCODE_MAX_SIZE) {
192                 printk(KERN_ERR "microcode: error: size mismatch\n");
193                 return NULL;
194         }
195
196         mc = vmalloc(UCODE_MAX_SIZE);
197         if (mc) {
198                 memset(mc, 0, UCODE_MAX_SIZE);
199                 if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR,
200                                    total_size)) {
201                         vfree(mc);
202                         mc = NULL;
203                 } else
204                         *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR;
205         }
206         return mc;
207 }
208
209 static int install_equiv_cpu_table(const u8 *buf)
210 {
211         u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE];
212         unsigned int *buf_pos = (unsigned int *)container_hdr;
213         unsigned long size;
214
215         if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE))
216                 return 0;
217
218         size = buf_pos[2];
219
220         if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) {
221                 printk(KERN_ERR "microcode: error: invalid type field in "
222                        "container file section header\n");
223                 return 0;
224         }
225
226         equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size);
227         if (!equiv_cpu_table) {
228                 printk(KERN_ERR "microcode: failed to allocate "
229                        "equivalent CPU table\n");
230                 return 0;
231         }
232
233         buf += UCODE_CONTAINER_HEADER_SIZE;
234         if (get_ucode_data(equiv_cpu_table, buf, size)) {
235                 vfree(equiv_cpu_table);
236                 return 0;
237         }
238
239         return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */
240 }
241
242 static void free_equiv_cpu_table(void)
243 {
244         vfree(equiv_cpu_table);
245         equiv_cpu_table = NULL;
246 }
247
248 static enum ucode_state
249 generic_load_microcode(int cpu, const u8 *data, size_t size)
250 {
251         struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
252         const u8 *ucode_ptr = data;
253         void *new_mc = NULL;
254         void *mc;
255         int new_rev = uci->cpu_sig.rev;
256         unsigned int leftover;
257         unsigned long offset;
258         enum ucode_state state = UCODE_OK;
259
260         offset = install_equiv_cpu_table(ucode_ptr);
261         if (!offset) {
262                 printk(KERN_ERR "microcode: failed to create "
263                        "equivalent cpu table\n");
264                 return UCODE_ERROR;
265         }
266
267         ucode_ptr += offset;
268         leftover = size - offset;
269
270         while (leftover) {
271                 unsigned int uninitialized_var(mc_size);
272                 struct microcode_header_amd *mc_header;
273
274                 mc = get_next_ucode(ucode_ptr, leftover, &mc_size);
275                 if (!mc)
276                         break;
277
278                 mc_header = (struct microcode_header_amd *)mc;
279                 if (get_matching_microcode(cpu, mc, new_rev)) {
280                         vfree(new_mc);
281                         new_rev = mc_header->patch_id;
282                         new_mc  = mc;
283                 } else
284                         vfree(mc);
285
286                 ucode_ptr += mc_size;
287                 leftover  -= mc_size;
288         }
289
290         if (new_mc) {
291                 if (!leftover) {
292                         vfree(uci->mc);
293                         uci->mc = new_mc;
294                         pr_debug("microcode: CPU%d found a matching microcode "
295                                  "update with version 0x%x (current=0x%x)\n",
296                                  cpu, new_rev, uci->cpu_sig.rev);
297                 } else {
298                         vfree(new_mc);
299                         state = UCODE_ERROR;
300                 }
301         } else
302                 state = UCODE_NFOUND;
303
304         free_equiv_cpu_table();
305
306         return state;
307 }
308
309 static enum ucode_state request_microcode_fw(int cpu, struct device *device)
310 {
311         const char *fw_name = "amd-ucode/microcode_amd.bin";
312         const struct firmware *firmware;
313         enum ucode_state ret;
314
315         if (request_firmware(&firmware, fw_name, device)) {
316                 printk(KERN_ERR "microcode: failed to load file %s\n", fw_name);
317                 return UCODE_NFOUND;
318         }
319
320         ret = generic_load_microcode(cpu, firmware->data, firmware->size);
321
322         release_firmware(firmware);
323
324         return ret;
325 }
326
327 static enum ucode_state
328 request_microcode_user(int cpu, const void __user *buf, size_t size)
329 {
330         printk(KERN_INFO "microcode: AMD microcode update via "
331                "/dev/cpu/microcode not supported\n");
332         return UCODE_ERROR;
333 }
334
335 static void microcode_fini_cpu_amd(int cpu)
336 {
337         struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
338
339         vfree(uci->mc);
340         uci->mc = NULL;
341 }
342
343 static struct microcode_ops microcode_amd_ops = {
344         .request_microcode_user           = request_microcode_user,
345         .request_microcode_fw             = request_microcode_fw,
346         .collect_cpu_info                 = collect_cpu_info_amd,
347         .apply_microcode                  = apply_microcode_amd,
348         .microcode_fini_cpu               = microcode_fini_cpu_amd,
349 };
350
351 struct microcode_ops * __init init_amd_microcode(void)
352 {
353         return &microcode_amd_ops;
354 }