ia64/xen-unstable

view xen/arch/x86/microcode_amd.c @ 18499:3eb7a0cfffc2

x86, microcode: More code cleanups.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Sep 16 13:09:04 2008 +0100 (2008-09-16)
parents 087b8b29b6b2
children 3c42b5ad0a4f
line source
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 */
17 #include <xen/config.h>
18 #include <xen/lib.h>
19 #include <xen/kernel.h>
20 #include <xen/init.h>
21 #include <xen/sched.h>
22 #include <xen/smp.h>
23 #include <xen/spinlock.h>
25 #include <asm/msr.h>
26 #include <asm/uaccess.h>
27 #include <asm/processor.h>
28 #include <asm/microcode.h>
30 #define pr_debug(x...) ((void)0)
32 #define UCODE_MAGIC 0x00414d44
33 #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
34 #define UCODE_UCODE_TYPE 0x00000001
36 #define UCODE_MAX_SIZE (2048)
37 #define DEFAULT_UCODE_DATASIZE (896)
38 #define MC_HEADER_SIZE (sizeof(struct microcode_header_amd))
39 #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
40 #define DWSIZE (sizeof(uint32_t))
41 /* For now we support a fixed ucode total size only */
42 #define get_totalsize(mc) \
43 ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \
44 + MC_HEADER_SIZE)
46 /* serialize access to the physical write */
47 static DEFINE_SPINLOCK(microcode_update_lock);
49 struct equiv_cpu_entry *equiv_cpu_table;
51 static long install_equiv_cpu_table(const void *, uint32_t, long);
53 static int collect_cpu_info(int cpu, struct cpu_signature *csig)
54 {
55 struct cpuinfo_x86 *c = &cpu_data[cpu];
57 memset(csig, 0, sizeof(*csig));
59 if ( (c->x86_vendor != X86_VENDOR_AMD) || (c->x86 < 0x10) )
60 {
61 printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
62 cpu);
63 return -1;
64 }
66 asm volatile (
67 "movl %1, %%ecx; rdmsr"
68 : "=a" (csig->rev)
69 : "i" (MSR_AMD_PATCHLEVEL) : "ecx" );
71 printk(KERN_INFO "microcode: collect_cpu_info: patch_id=0x%x\n",
72 csig->rev);
74 return 0;
75 }
77 static int get_matching_microcode(void *mc, int cpu)
78 {
79 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
80 struct microcode_header_amd *mc_header = mc;
81 unsigned long total_size = get_totalsize(mc_header);
82 void *new_mc;
83 unsigned int current_cpu_id;
84 unsigned int equiv_cpu_id = 0x00;
85 unsigned int i;
87 /* We should bind the task to the CPU */
88 BUG_ON(cpu != raw_smp_processor_id());
90 /* This is a tricky part. We might be called from a write operation
91 * to the device file instead of the usual process of firmware
92 * loading. This routine needs to be able to distinguish both
93 * cases. This is done by checking if there already is a equivalent
94 * CPU table installed. If not, we're written through
95 * /dev/cpu/microcode.
96 * Since we ignore all checks. The error case in which going through
97 * firmware loading and that table is not loaded has already been
98 * checked earlier.
99 */
100 if ( equiv_cpu_table == NULL )
101 {
102 printk(KERN_INFO "microcode: CPU%d microcode update with "
103 "version 0x%x (current=0x%x)\n",
104 cpu, mc_header->patch_id, uci->cpu_sig.rev);
105 goto out;
106 }
108 current_cpu_id = cpuid_eax(0x00000001);
110 for ( i = 0; equiv_cpu_table[i].installed_cpu != 0; i++ )
111 {
112 if ( current_cpu_id == equiv_cpu_table[i].installed_cpu )
113 {
114 equiv_cpu_id = equiv_cpu_table[i].equiv_cpu;
115 break;
116 }
117 }
119 if ( !equiv_cpu_id )
120 {
121 printk(KERN_ERR "microcode: CPU%d cpu_id "
122 "not found in equivalent cpu table \n", cpu);
123 return 0;
124 }
126 if ( (mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff) )
127 {
128 printk(KERN_INFO
129 "microcode: CPU%d patch does not match "
130 "(patch is %x, cpu extended is %x) \n",
131 cpu, mc_header->processor_rev_id[0],
132 (equiv_cpu_id & 0xff));
133 return 0;
134 }
136 if ( (mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff) )
137 {
138 printk(KERN_INFO "microcode: CPU%d patch does not match "
139 "(patch is %x, cpu base id is %x) \n",
140 cpu, mc_header->processor_rev_id[1],
141 ((equiv_cpu_id >> 16) & 0xff));
142 return 0;
143 }
145 if ( mc_header->patch_id <= uci->cpu_sig.rev )
146 return 0;
148 printk(KERN_INFO "microcode: CPU%d found a matching microcode "
149 "update with version 0x%x (current=0x%x)\n",
150 cpu, mc_header->patch_id, uci->cpu_sig.rev);
152 out:
153 new_mc = xmalloc_bytes(UCODE_MAX_SIZE);
154 if ( new_mc == NULL )
155 {
156 printk(KERN_ERR "microcode: error, can't allocate memory\n");
157 return -ENOMEM;
158 }
159 memset(new_mc, 0, UCODE_MAX_SIZE);
161 /* free previous update file */
162 xfree(uci->mc.mc_amd);
164 memcpy(new_mc, mc, total_size);
166 uci->mc.mc_amd = new_mc;
167 return 1;
168 }
170 static int apply_microcode(int cpu)
171 {
172 unsigned long flags;
173 uint32_t eax, edx, rev;
174 int cpu_num = raw_smp_processor_id();
175 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
176 uint64_t addr;
178 /* We should bind the task to the CPU */
179 BUG_ON(cpu_num != cpu);
181 if ( uci->mc.mc_amd == NULL )
182 return -EINVAL;
184 spin_lock_irqsave(&microcode_update_lock, flags);
186 addr = (unsigned long)&uci->mc.mc_amd->hdr.data_code;
187 edx = (uint32_t)(addr >> 32);
188 eax = (uint32_t)addr;
190 asm volatile (
191 "movl %0, %%ecx; wrmsr" :
192 : "i" (MSR_AMD_PATCHLOADER), "a" (eax), "d" (edx) : "ecx" );
194 /* get patch id after patching */
195 asm volatile (
196 "movl %1, %%ecx; rdmsr"
197 : "=a" (rev)
198 : "i" (MSR_AMD_PATCHLEVEL) : "ecx");
200 spin_unlock_irqrestore(&microcode_update_lock, flags);
202 /* check current patch id and patch's id for match */
203 if ( rev != uci->mc.mc_amd->hdr.patch_id )
204 {
205 printk(KERN_ERR "microcode: CPU%d update from revision "
206 "0x%x to 0x%x failed\n", cpu_num,
207 uci->mc.mc_amd->hdr.patch_id, rev);
208 return -EIO;
209 }
211 printk("microcode: CPU%d updated from revision "
212 "0x%x to 0x%x \n",
213 cpu_num, uci->cpu_sig.rev, uci->mc.mc_amd->hdr.patch_id);
215 uci->cpu_sig.rev = rev;
217 return 0;
218 }
220 static long get_next_ucode_from_buffer_amd(void **mc, const void *buf,
221 unsigned long size, long offset)
222 {
223 struct microcode_header_amd *mc_header;
224 unsigned long total_size;
225 const uint8_t *buf_pos = buf;
227 /* No more data */
228 if ( offset >= size )
229 return 0;
231 if ( buf_pos[offset] != UCODE_UCODE_TYPE )
232 {
233 printk(KERN_ERR "microcode: error! "
234 "Wrong microcode payload type field\n");
235 return -EINVAL;
236 }
238 mc_header = (struct microcode_header_amd *)(&buf_pos[offset+8]);
240 total_size = (unsigned long) (buf_pos[offset+4] +
241 (buf_pos[offset+5] << 8));
243 printk(KERN_INFO "microcode: size %lu, total_size %lu, offset %ld\n",
244 size, total_size, offset);
246 if ( (offset + total_size) > size )
247 {
248 printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
249 return -EINVAL;
250 }
252 *mc = xmalloc_bytes(UCODE_MAX_SIZE);
253 if ( *mc == NULL )
254 {
255 printk(KERN_ERR "microcode: error! "
256 "Can not allocate memory for microcode patch\n");
257 return -ENOMEM;
258 }
260 memset(*mc, 0, UCODE_MAX_SIZE);
261 memcpy(*mc, (const void *)(buf + offset + 8), total_size);
263 return offset + total_size + 8;
264 }
266 static long install_equiv_cpu_table(const void *buf,
267 uint32_t size, long offset)
268 {
269 const uint32_t *buf_pos = buf;
271 /* No more data */
272 if ( offset >= size )
273 return 0;
275 if ( buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE )
276 {
277 printk(KERN_ERR "microcode: error! "
278 "Wrong microcode equivalnet cpu table type field\n");
279 return 0;
280 }
282 if ( size == 0 )
283 {
284 printk(KERN_ERR "microcode: error! "
285 "Wrong microcode equivalnet cpu table length\n");
286 return 0;
287 }
289 equiv_cpu_table = xmalloc_bytes(size);
290 if ( equiv_cpu_table == NULL )
291 {
292 printk(KERN_ERR "microcode: error, can't allocate "
293 "memory for equiv CPU table\n");
294 return 0;
295 }
297 memset(equiv_cpu_table, 0, size);
298 memcpy(equiv_cpu_table, (const void *)&buf_pos[3], size);
300 return size + 12; /* add header length */
301 }
303 static int cpu_request_microcode(int cpu, const void *buf, size_t size)
304 {
305 const uint32_t *buf_pos;
306 long offset = 0;
307 int error = 0;
308 void *mc;
310 /* We should bind the task to the CPU */
311 BUG_ON(cpu != raw_smp_processor_id());
313 buf_pos = (const uint32_t *)buf;
315 if ( buf_pos[0] != UCODE_MAGIC )
316 {
317 printk(KERN_ERR "microcode: error! Wrong "
318 "microcode patch file magic\n");
319 return -EINVAL;
320 }
322 offset = install_equiv_cpu_table(buf, (uint32_t)(buf_pos[2]), offset);
323 if ( !offset )
324 {
325 printk(KERN_ERR "microcode: installing equivalent cpu table failed\n");
326 return -EINVAL;
327 }
329 while ( (offset =
330 get_next_ucode_from_buffer_amd(&mc, buf, size, offset)) > 0 )
331 {
332 error = get_matching_microcode(mc, cpu);
333 if ( error < 0 )
334 break;
335 /*
336 * It's possible the data file has multiple matching ucode,
337 * lets keep searching till the latest version
338 */
339 if ( error == 1 )
340 {
341 apply_microcode(cpu);
342 error = 0;
343 }
344 xfree(mc);
345 }
346 if ( offset > 0 )
347 {
348 xfree(mc);
349 xfree(equiv_cpu_table);
350 equiv_cpu_table = NULL;
351 }
352 if ( offset < 0 )
353 error = offset;
355 return error;
356 }
358 static struct microcode_ops microcode_amd_ops = {
359 .get_matching_microcode = get_matching_microcode,
360 .cpu_request_microcode = cpu_request_microcode,
361 .collect_cpu_info = collect_cpu_info,
362 .apply_microcode = apply_microcode,
363 };
365 static __init int microcode_init_amd(void)
366 {
367 if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
368 microcode_ops = &microcode_amd_ops;
369 return 0;
370 }
371 __initcall(microcode_init_amd);