microcode_intel.c 14.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *	Intel CPU Microcode Update Driver for Linux
 *
4
 *	Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
5
 *		      2006	Shaohua Li <shaohua.li@intel.com>
L
Linus Torvalds 已提交
6 7
 *
 *	This driver allows to upgrade microcode on Intel processors
B
Ben Castricum 已提交
8
 *	belonging to IA-32 family - PentiumPro, Pentium II,
L
Linus Torvalds 已提交
9 10
 *	Pentium III, Xeon, Pentium 4, etc.
 *
B
Ben Castricum 已提交
11 12 13 14 15
 *	Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
 *	Software Developer's Manual
 *	Order Number 253668 or free download from:
 *
 *	http://developer.intel.com/design/pentium4/manuals/253668.htm
L
Linus Torvalds 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
 *
 *	For more information, go to http://www.urbanmyth.org/microcode
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 *
 *	1.0	16 Feb 2000, Tigran Aivazian <tigran@sco.com>
 *		Initial release.
 *	1.01	18 Feb 2000, Tigran Aivazian <tigran@sco.com>
 *		Added read() support + cleanups.
 *	1.02	21 Feb 2000, Tigran Aivazian <tigran@sco.com>
 *		Added 'device trimming' support. open(O_WRONLY) zeroes
 *		and frees the saved copy of applied microcode.
 *	1.03	29 Feb 2000, Tigran Aivazian <tigran@sco.com>
 *		Made to use devfs (/dev/cpu/microcode) + cleanups.
 *	1.04	06 Jun 2000, Simon Trimmer <simon@veritas.com>
 *		Added misc device support (now uses both devfs and misc).
 *		Added MICROCODE_IOCFREE ioctl to clear memory.
 *	1.05	09 Jun 2000, Simon Trimmer <simon@veritas.com>
 *		Messages for error cases (non Intel & no suitable microcode).
 *	1.06	03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
 *		Removed ->release(). Removed exclusive open and status bitmap.
 *		Added microcode_rwsem to serialize read()/write()/ioctl().
 *		Removed global kernel lock usage.
 *	1.07	07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
 *		Write 0 to 0x8B msr and then cpuid before reading revision,
 *		so that it works even if there were no update done by the
 *		BIOS. Otherwise, reading from 0x8B gives junk (which happened
 *		to be 0 on my machine which is why it worked even when I
 *		disabled update by the BIOS)
 *		Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
 *	1.08	11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
 *			     Tigran Aivazian <tigran@veritas.com>
 *		Intel Pentium 4 processor support and bugfixes.
 *	1.09	30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
 *		Bugfix for HT (Hyper-Threading) enabled processors
 *		whereby processor resources are shared by all logical processors
 *		in a single CPU package.
 *	1.10	28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
 *		Tigran Aivazian <tigran@veritas.com>,
58 59
 *		Serialize updates as required on HT processors due to
 *		speculative nature of implementation.
L
Linus Torvalds 已提交
60 61
 *	1.11	22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
 *		Fix the panic when writing zero-length microcode chunk.
B
Ben Castricum 已提交
62
 *	1.12	29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
L
Linus Torvalds 已提交
63 64 65 66
 *		Jun Nakajima <jun.nakajima@intel.com>
 *		Support for the microcode updates in the new format.
 *	1.13	10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
 *		Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
B
Ben Castricum 已提交
67
 *		because we no longer hold a copy of applied microcode
L
Linus Torvalds 已提交
68 69 70 71 72 73
 *		in kernel memory.
 *	1.14	25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
 *		Fix sigmatch() macro to handle old CPUs with pf == 0.
 *		Thanks to Stuart Swales for pointing out this bug.
 */

74
/* #define DEBUG */ /* pr_debug */
75
#include <linux/capability.h>
L
Linus Torvalds 已提交
76 77 78
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
A
Arnd Bergmann 已提交
79
#include <linux/smp_lock.h>
80
#include <linux/cpumask.h>
L
Linus Torvalds 已提交
81 82 83 84 85 86
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
A
Alexey Dobriyan 已提交
87
#include <linux/fs.h>
88
#include <linux/mutex.h>
89 90 91
#include <linux/cpu.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
92 93 94 95

#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
96
#include <asm/microcode.h>
L
Linus Torvalds 已提交
97

P
Peter Oruba 已提交
98
MODULE_DESCRIPTION("Microcode Update Driver");
99
MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
L
Linus Torvalds 已提交
100 101
MODULE_LICENSE("GPL");

102 103 104 105 106
#define DEFAULT_UCODE_DATASIZE 	(2000)
#define MC_HEADER_SIZE		(sizeof(struct microcode_header_intel))
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
#define EXT_HEADER_SIZE		(sizeof(struct extended_sigtable))
#define EXT_SIGNATURE_SIZE	(sizeof(struct extended_signature))
P
Peter Oruba 已提交
107
#define DWSIZE			(sizeof(u32))
L
Linus Torvalds 已提交
108
#define get_totalsize(mc) \
109 110 111 112
	(((struct microcode_intel *)mc)->hdr.totalsize ? \
	 ((struct microcode_intel *)mc)->hdr.totalsize : \
	 DEFAULT_UCODE_TOTALSIZE)

L
Linus Torvalds 已提交
113
#define get_datasize(mc) \
114 115
	(((struct microcode_intel *)mc)->hdr.datasize ? \
	 ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124

#define sigmatch(s1, s2, p1, p2) \
	(((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0))))

#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)

/* serialize access to the physical write to MSR 0x79 */
static DEFINE_SPINLOCK(microcode_update_lock);

125
static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
L
Linus Torvalds 已提交
126
{
127
	struct cpuinfo_x86 *c = &cpu_data(cpu_num);
L
Linus Torvalds 已提交
128 129
	unsigned int val[2];

130
	memset(csig, 0, sizeof(*csig));
L
Linus Torvalds 已提交
131 132

	if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
P
Peter Oruba 已提交
133
	    cpu_has(c, X86_FEATURE_IA64)) {
134 135
		printk(KERN_ERR "microcode: CPU%d not a capable Intel "
			"processor\n", cpu_num);
136
		return -1;
137
	}
L
Linus Torvalds 已提交
138

139
	csig->sig = cpuid_eax(0x00000001);
140 141 142 143

	if ((c->x86_model >= 5) || (c->x86 > 6)) {
		/* get processor flags from MSR 0x17 */
		rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
144
		csig->pf = 1 << ((val[1] >> 18) & 7);
L
Linus Torvalds 已提交
145 146 147
	}

	wrmsr(MSR_IA32_UCODE_REV, 0, 0);
148
	/* see notes above for revision 1.07.  Apparent chip bug */
149
	sync_core();
L
Linus Torvalds 已提交
150
	/* get the current revision from MSR 0x8B */
151
	rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev);
L
Linus Torvalds 已提交
152
	pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n",
153 154 155
			csig->sig, csig->pf, csig->rev);

	return 0;
L
Linus Torvalds 已提交
156 157
}

158
static inline int microcode_update_match(int cpu_num,
159
	struct microcode_header_intel *mc_header, int sig, int pf)
L
Linus Torvalds 已提交
160 161 162
{
	struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;

163 164
	if (!sigmatch(sig, uci->cpu_sig.sig, pf, uci->cpu_sig.pf)
		|| mc_header->rev <= uci->cpu_sig.rev)
165 166
		return 0;
	return 1;
L
Linus Torvalds 已提交
167 168
}

P
Peter Oruba 已提交
169
static int microcode_sanity_check(void *mc)
L
Linus Torvalds 已提交
170
{
171
	struct microcode_header_intel *mc_header = mc;
172 173 174 175 176 177 178
	struct extended_sigtable *ext_header = NULL;
	struct extended_signature *ext_sig;
	unsigned long total_size, data_size, ext_table_size;
	int sum, orig_sum, ext_sigcount = 0, i;

	total_size = get_totalsize(mc_header);
	data_size = get_datasize(mc_header);
179
	if (data_size + MC_HEADER_SIZE > total_size) {
180 181 182 183
		printk(KERN_ERR "microcode: error! "
			"Bad data size in microcode data file\n");
		return -EINVAL;
	}
L
Linus Torvalds 已提交
184

185 186 187 188 189 190 191 192 193 194 195 196
	if (mc_header->ldrver != 1 || mc_header->hdrver != 1) {
		printk(KERN_ERR "microcode: error! "
			"Unknown microcode update format\n");
		return -EINVAL;
	}
	ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
	if (ext_table_size) {
		if ((ext_table_size < EXT_HEADER_SIZE)
		 || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
			printk(KERN_ERR "microcode: error! "
				"Small exttable size in microcode data file\n");
			return -EINVAL;
L
Linus Torvalds 已提交
197
		}
198 199 200 201 202
		ext_header = mc + MC_HEADER_SIZE + data_size;
		if (ext_table_size != exttable_size(ext_header)) {
			printk(KERN_ERR "microcode: error! "
				"Bad exttable size in microcode data file\n");
			return -EFAULT;
L
Linus Torvalds 已提交
203
		}
204 205
		ext_sigcount = ext_header->count;
	}
L
Linus Torvalds 已提交
206

207 208 209
	/* check extended table checksum */
	if (ext_table_size) {
		int ext_table_sum = 0;
210
		int *ext_tablep = (int *)ext_header;
211 212 213 214 215 216 217 218

		i = ext_table_size / DWSIZE;
		while (i--)
			ext_table_sum += ext_tablep[i];
		if (ext_table_sum) {
			printk(KERN_WARNING "microcode: aborting, "
				"bad extended signature table checksum\n");
			return -EINVAL;
L
Linus Torvalds 已提交
219
		}
220
	}
L
Linus Torvalds 已提交
221

222 223 224 225 226 227 228 229 230 231 232 233 234
	/* calculate the checksum */
	orig_sum = 0;
	i = (MC_HEADER_SIZE + data_size) / DWSIZE;
	while (i--)
		orig_sum += ((int *)mc)[i];
	if (orig_sum) {
		printk(KERN_ERR "microcode: aborting, bad checksum\n");
		return -EINVAL;
	}
	if (!ext_table_size)
		return 0;
	/* check extended signature checksum */
	for (i = 0; i < ext_sigcount; i++) {
J
Jan Engelhardt 已提交
235 236
		ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
			  EXT_SIGNATURE_SIZE * i;
237 238 239 240 241 242
		sum = orig_sum
			- (mc_header->sig + mc_header->pf + mc_header->cksum)
			+ (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
		if (sum) {
			printk(KERN_ERR "microcode: aborting, bad checksum\n");
			return -EINVAL;
L
Linus Torvalds 已提交
243
		}
244 245 246
	}
	return 0;
}
247

248 249 250 251 252
/*
 * return 0 - no update found
 * return 1 - found update
 * return < 0 - error
 */
P
Peter Oruba 已提交
253
static int get_matching_microcode(void *mc, int cpu)
254 255
{
	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
256
	struct microcode_header_intel *mc_header = mc;
257 258 259 260 261 262 263 264 265 266 267 268 269
	struct extended_sigtable *ext_header;
	unsigned long total_size = get_totalsize(mc_header);
	int ext_sigcount, i;
	struct extended_signature *ext_sig;
	void *new_mc;

	if (microcode_update_match(cpu, mc_header,
			mc_header->sig, mc_header->pf))
		goto find;

	if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE)
		return 0;

J
Jan Engelhardt 已提交
270
	ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE;
271
	ext_sigcount = ext_header->count;
J
Jan Engelhardt 已提交
272
	ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
273 274 275 276 277 278 279 280
	for (i = 0; i < ext_sigcount; i++) {
		if (microcode_update_match(cpu, mc_header,
				ext_sig->sig, ext_sig->pf))
			goto find;
		ext_sig++;
	}
	return 0;
find:
281
	pr_debug("microcode: CPU%d found a matching microcode update with"
P
Peter Oruba 已提交
282
		 " version 0x%x (current=0x%x)\n",
283
		 cpu, mc_header->rev, uci->cpu_sig.rev);
284 285 286 287 288
	new_mc = vmalloc(total_size);
	if (!new_mc) {
		printk(KERN_ERR "microcode: error! Can not allocate memory\n");
		return -ENOMEM;
	}
L
Linus Torvalds 已提交
289

290
	/* free previous update file */
291
	vfree(uci->mc.mc_intel);
L
Linus Torvalds 已提交
292

293
	memcpy(new_mc, mc, total_size);
294
	uci->mc.mc_intel = new_mc;
295
	return 1;
L
Linus Torvalds 已提交
296 297
}

P
Peter Oruba 已提交
298
static void apply_microcode(int cpu)
L
Linus Torvalds 已提交
299 300 301
{
	unsigned long flags;
	unsigned int val[2];
302
	int cpu_num = raw_smp_processor_id();
L
Linus Torvalds 已提交
303 304
	struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;

305 306 307
	/* We should bind the task to the CPU */
	BUG_ON(cpu_num != cpu);

308
	if (uci->mc.mc_intel == NULL)
L
Linus Torvalds 已提交
309 310 311
		return;

	/* serialize access to the physical write to MSR 0x79 */
B
Ben Castricum 已提交
312
	spin_lock_irqsave(&microcode_update_lock, flags);
L
Linus Torvalds 已提交
313 314 315

	/* write microcode via MSR 0x79 */
	wrmsr(MSR_IA32_UCODE_WRITE,
316 317
	      (unsigned long) uci->mc.mc_intel->bits,
	      (unsigned long) uci->mc.mc_intel->bits >> 16 >> 16);
L
Linus Torvalds 已提交
318 319
	wrmsr(MSR_IA32_UCODE_REV, 0, 0);

320
	/* see notes above for revision 1.07.  Apparent chip bug */
321
	sync_core();
322

L
Linus Torvalds 已提交
323 324 325 326
	/* get the current revision from MSR 0x8B */
	rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);

	spin_unlock_irqrestore(&microcode_update_lock, flags);
327
	if (val[1] != uci->mc.mc_intel->hdr.rev) {
328
		printk(KERN_ERR "microcode: CPU%d update from revision "
329
			"0x%x to 0x%x failed\n", cpu_num, uci->cpu_sig.rev, val[1]);
330 331
		return;
	}
332
	printk(KERN_INFO "microcode: CPU%d updated from revision "
333
	       "0x%x to 0x%x, date = %04x-%02x-%02x \n",
334
		cpu_num, uci->cpu_sig.rev, val[1],
335 336 337
		uci->mc.mc_intel->hdr.date & 0xffff,
		uci->mc.mc_intel->hdr.date >> 24,
		(uci->mc.mc_intel->hdr.date >> 16) & 0xff);
338
	uci->cpu_sig.rev = val[1];
L
Linus Torvalds 已提交
339 340
}

341
#ifdef CONFIG_MICROCODE_OLD_INTERFACE
P
Peter Oruba 已提交
342 343
extern void __user *user_buffer;        /* user area microcode data buffer */
extern unsigned int user_buffer_size;   /* it's size */
L
Linus Torvalds 已提交
344

P
Peter Oruba 已提交
345
static long get_next_ucode(void **mc, long offset)
346
{
347
	struct microcode_header_intel mc_header;
348 349 350 351 352 353 354 355
	unsigned long total_size;

	/* No more data */
	if (offset >= user_buffer_size)
		return 0;
	if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) {
		printk(KERN_ERR "microcode: error! Can not read user data\n");
		return -EFAULT;
L
Linus Torvalds 已提交
356
	}
357
	total_size = get_totalsize(&mc_header);
358
	if (offset + total_size > user_buffer_size) {
359 360 361
		printk(KERN_ERR "microcode: error! Bad total size in microcode "
				"data file\n");
		return -EINVAL;
L
Linus Torvalds 已提交
362
	}
363 364 365 366 367 368 369
	*mc = vmalloc(total_size);
	if (!*mc)
		return -ENOMEM;
	if (copy_from_user(*mc, user_buffer + offset, total_size)) {
		printk(KERN_ERR "microcode: error! Can not read user data\n");
		vfree(*mc);
		return -EFAULT;
L
Linus Torvalds 已提交
370
	}
371 372 373 374
	return offset + total_size;
}
#endif

375
static long get_next_ucode_from_buffer(void **mc, const u8 *buf,
376 377
	unsigned long size, long offset)
{
378
	struct microcode_header_intel *mc_header;
379 380 381 382 383
	unsigned long total_size;

	/* No more data */
	if (offset >= size)
		return 0;
384
	mc_header = (struct microcode_header_intel *)(buf + offset);
385 386
	total_size = get_totalsize(mc_header);

387
	if (offset + total_size > size) {
388 389 390 391 392 393 394 395 396 397 398 399 400 401
		printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
		return -EINVAL;
	}

	*mc = vmalloc(total_size);
	if (!*mc) {
		printk(KERN_ERR "microcode: error! Can not allocate memory\n");
		return -ENOMEM;
	}
	memcpy(*mc, buf + offset, total_size);
	return offset + total_size;
}

/* fake device for request_firmware */
P
Peter Oruba 已提交
402
extern struct platform_device *microcode_pdev;
403

P
Peter Oruba 已提交
404
static int cpu_request_microcode(int cpu)
405 406
{
	char name[30];
407
	struct cpuinfo_x86 *c = &cpu_data(cpu);
408
	const struct firmware *firmware;
409
	const u8 *buf;
410 411 412 413 414 415 416
	unsigned long size;
	long offset = 0;
	int error;
	void *mc;

	/* We should bind the task to the CPU */
	BUG_ON(cpu != raw_smp_processor_id());
P
Peter Oruba 已提交
417
	sprintf(name, "intel-ucode/%02x-%02x-%02x",
418 419 420
		c->x86, c->x86_model, c->x86_mask);
	error = request_firmware(&firmware, name, &microcode_pdev->dev);
	if (error) {
B
Ben Castricum 已提交
421
		pr_debug("microcode: data file %s load failed\n", name);
422 423
		return error;
	}
J
Jan Engelhardt 已提交
424
	buf = firmware->data;
425 426 427 428 429 430
	size = firmware->size;
	while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset))
			> 0) {
		error = microcode_sanity_check(mc);
		if (error)
			break;
P
Peter Oruba 已提交
431
		error = get_matching_microcode(mc, cpu);
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
		if (error < 0)
			break;
		/*
		 * It's possible the data file has multiple matching ucode,
		 * lets keep searching till the latest version
		 */
		if (error == 1) {
			apply_microcode(cpu);
			error = 0;
		}
		vfree(mc);
	}
	if (offset > 0)
		vfree(mc);
	if (offset < 0)
		error = offset;
	release_firmware(firmware);

	return error;
}

P
Peter Oruba 已提交
453
static void microcode_fini_cpu(int cpu)
454 455 456
{
	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

P
Peter Oruba 已提交
457
	vfree(uci->mc.mc_intel);
458
	uci->mc.mc_intel = NULL;
459
}
P
Peter Oruba 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472

static struct microcode_ops microcode_intel_ops = {
	.get_next_ucode                   = get_next_ucode,
	.get_matching_microcode           = get_matching_microcode,
	.microcode_sanity_check           = microcode_sanity_check,
	.cpu_request_microcode            = cpu_request_microcode,
	.collect_cpu_info                 = collect_cpu_info,
	.apply_microcode                  = apply_microcode,
	.microcode_fini_cpu               = microcode_fini_cpu,
};

static int __init microcode_intel_module_init(void)
{
473
	struct cpuinfo_x86 *c = &cpu_data(0);
P
Peter Oruba 已提交
474

475 476
	if (c->x86_vendor != X86_VENDOR_INTEL) {
                printk(KERN_ERR "microcode: CPU platform is not Intel-capable\n");
P
Peter Oruba 已提交
477
		return -ENODEV;
478 479 480
	}

	return microcode_init(&microcode_intel_ops, THIS_MODULE);
P
Peter Oruba 已提交
481 482 483 484 485 486 487 488 489
}

static void __exit microcode_intel_module_exit(void)
{
	microcode_exit();
}

module_init(microcode_intel_module_init)
module_exit(microcode_intel_module_exit)