| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *	Intel CPU Microcode Update Driver for Linux | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> | 
					
						
							|  |  |  |  *		      2006	Shaohua Li <shaohua.li@intel.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	This driver allows to upgrade microcode on Intel processors | 
					
						
							|  |  |  |  *	belonging to IA-32 family - PentiumPro, Pentium II, | 
					
						
							|  |  |  |  *	Pentium III, Xeon, Pentium 4, etc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	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
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	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>, | 
					
						
							| 
									
										
										
										
											2008-08-01 12:46:45 +02:00
										 |  |  |  *		Serialize updates as required on HT processors due to | 
					
						
							|  |  |  |  *		speculative nature of implementation. | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  |  *	1.11	22 Mar 2002 Tigran Aivazian <tigran@veritas.com> | 
					
						
							|  |  |  |  *		Fix the panic when writing zero-length microcode chunk. | 
					
						
							|  |  |  |  *	1.12	29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, | 
					
						
							|  |  |  |  *		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 | 
					
						
							|  |  |  |  *		because we no longer hold a copy of applied microcode | 
					
						
							|  |  |  |  *		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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/miscdevice.h>
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | #include <linux/capability.h>
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | #include <linux/mutex.h>
 | 
					
						
							|  |  |  | #include <linux/cpu.h>
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | #include <linux/fs.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <asm/microcode.h>
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | #include <asm/processor.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Microcode Update Driver"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | #define MICROCODE_VERSION	"2.00"
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | static struct microcode_ops	*microcode_ops; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Synchronization. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * All non cpu-hotplug-callback call sites use: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * - microcode_mutex to synchronize with each other; | 
					
						
							|  |  |  |  * - get/put_online_cpus() to synchronize with | 
					
						
							|  |  |  |  *   the cpu-hotplug-callback call sites. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * We guarantee that only a single cpu is being | 
					
						
							|  |  |  |  * updated at any particular moment of time. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | static DEFINE_MUTEX(microcode_mutex); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | struct ucode_cpu_info		ucode_cpu_info[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:21 +02:00
										 |  |  | EXPORT_SYMBOL_GPL(ucode_cpu_info); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Operations that are run on a target cpu: | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct cpu_info_ctx { | 
					
						
							|  |  |  | 	struct cpu_signature	*cpu_sig; | 
					
						
							|  |  |  | 	int			err; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void collect_cpu_info_local(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpu_info_ctx *ctx = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx->err = microcode_ops->collect_cpu_info(smp_processor_id(), | 
					
						
							|  |  |  | 						   ctx->cpu_sig); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int collect_cpu_info_on_target(int cpu, struct cpu_signature *cpu_sig) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpu_info_ctx ctx = { .cpu_sig = cpu_sig, .err = 0 }; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = smp_call_function_single(cpu, collect_cpu_info_local, &ctx, 1); | 
					
						
							|  |  |  | 	if (!ret) | 
					
						
							|  |  |  | 		ret = ctx.err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int collect_cpu_info(int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(uci, 0, sizeof(*uci)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = collect_cpu_info_on_target(cpu, &uci->cpu_sig); | 
					
						
							|  |  |  | 	if (!ret) | 
					
						
							|  |  |  | 		uci->valid = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct apply_microcode_ctx { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void apply_microcode_local(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct apply_microcode_ctx *ctx = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx->err = microcode_ops->apply_microcode(smp_processor_id()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int apply_microcode_on_target(int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct apply_microcode_ctx ctx = { .err = 0 }; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = smp_call_function_single(cpu, apply_microcode_local, &ctx, 1); | 
					
						
							|  |  |  | 	if (!ret) | 
					
						
							|  |  |  | 		ret = ctx.err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | #ifdef CONFIG_MICROCODE_OLD_INTERFACE
 | 
					
						
							| 
									
										
										
										
											2008-09-11 23:27:52 +02:00
										 |  |  | static int do_microcode_update(const void __user *buf, size_t size) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 	int cpu; | 
					
						
							| 
									
										
										
										
											2009-04-14 19:25:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-11 23:27:52 +02:00
										 |  |  | 	for_each_online_cpu(cpu) { | 
					
						
							|  |  |  | 		struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		enum ucode_state ustate; | 
					
						
							| 
									
										
										
										
											2008-09-11 23:27:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (!uci->valid) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2009-04-14 19:25:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		ustate = microcode_ops->request_microcode_user(cpu, buf, size); | 
					
						
							|  |  |  | 		if (ustate == UCODE_ERROR) { | 
					
						
							|  |  |  | 			error = -1; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} else if (ustate == UCODE_OK) | 
					
						
							|  |  |  | 			apply_microcode_on_target(cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-01 12:46:45 +02:00
										 |  |  | static int microcode_open(struct inode *unused1, struct file *unused2) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-01 12:46:45 +02:00
										 |  |  | static ssize_t microcode_write(struct file *file, const char __user *buf, | 
					
						
							|  |  |  | 			       size_t len, loff_t *ppos) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	ssize_t ret = -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-21 17:03:05 -07:00
										 |  |  | 	if ((len >> PAGE_SHIFT) > totalram_pages) { | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		pr_err("too much data (max %ld pages)\n", totalram_pages); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	get_online_cpus(); | 
					
						
							|  |  |  | 	mutex_lock(µcode_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	if (do_microcode_update(buf, len) == 0) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 		ret = (ssize_t)len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(µcode_mutex); | 
					
						
							|  |  |  | 	put_online_cpus(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct file_operations microcode_fops = { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	.owner			= THIS_MODULE, | 
					
						
							|  |  |  | 	.write			= microcode_write, | 
					
						
							|  |  |  | 	.open			= microcode_open, | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct miscdevice microcode_dev = { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	.minor			= MICROCODE_MINOR, | 
					
						
							|  |  |  | 	.name			= "microcode", | 
					
						
							| 
									
										
										
										
											2009-09-18 23:01:12 +02:00
										 |  |  | 	.nodename		= "cpu/microcode", | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	.fops			= µcode_fops, | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-01 12:46:45 +02:00
										 |  |  | static int __init microcode_dev_init(void) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = misc_register(µcode_dev); | 
					
						
							|  |  |  | 	if (error) { | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		pr_err("can't misc_register on minor=%d\n", MICROCODE_MINOR); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 		return error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-01 12:46:45 +02:00
										 |  |  | static void microcode_dev_exit(void) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	misc_deregister(µcode_dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | #define microcode_dev_init()	0
 | 
					
						
							|  |  |  | #define microcode_dev_exit()	do { } while (0)
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* fake device for request_firmware */ | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | static struct platform_device	*microcode_pdev; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | static int reload_for_cpu(int cpu) | 
					
						
							| 
									
										
										
										
											2009-03-11 16:32:36 +10:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							| 
									
										
										
										
											2009-03-11 16:32:36 +10:30
										 |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(µcode_mutex); | 
					
						
							|  |  |  | 	if (uci->valid) { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		enum ucode_state ustate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev); | 
					
						
							|  |  |  | 		if (ustate == UCODE_OK) | 
					
						
							|  |  |  | 			apply_microcode_on_target(cpu); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			if (ustate == UCODE_ERROR) | 
					
						
							|  |  |  | 				err = -EINVAL; | 
					
						
							| 
									
										
										
										
											2009-03-11 16:32:36 +10:30
										 |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(µcode_mutex); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-11 16:32:36 +10:30
										 |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | static ssize_t reload_store(struct sys_device *dev, | 
					
						
							|  |  |  | 			    struct sysdev_attribute *attr, | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 			    const char *buf, size_t size) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	unsigned long val; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	int cpu = dev->id; | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	char *end; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	val = simple_strtoul(buf, &end, 0); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	if (end == buf) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	if (val == 1) { | 
					
						
							|  |  |  | 		get_online_cpus(); | 
					
						
							| 
									
										
										
										
											2009-03-11 16:32:36 +10:30
										 |  |  | 		if (cpu_online(cpu)) | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 			ret = reload_for_cpu(cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 		put_online_cpus(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!ret) | 
					
						
							|  |  |  | 		ret = size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t version_show(struct sys_device *dev, | 
					
						
							|  |  |  | 			struct sysdev_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 	return sprintf(buf, "0x%x\n", uci->cpu_sig.rev); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t pf_show(struct sys_device *dev, | 
					
						
							|  |  |  | 			struct sysdev_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 	return sprintf(buf, "0x%x\n", uci->cpu_sig.pf); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static SYSDEV_ATTR(reload, 0200, NULL, reload_store); | 
					
						
							|  |  |  | static SYSDEV_ATTR(version, 0400, version_show, NULL); | 
					
						
							|  |  |  | static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *mc_default_attrs[] = { | 
					
						
							|  |  |  | 	&attr_reload.attr, | 
					
						
							|  |  |  | 	&attr_version.attr, | 
					
						
							|  |  |  | 	&attr_processor_flags.attr, | 
					
						
							|  |  |  | 	NULL | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute_group mc_attr_group = { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	.attrs			= mc_default_attrs, | 
					
						
							|  |  |  | 	.name			= "microcode", | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | static void microcode_fini_cpu(int cpu) | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	microcode_ops->microcode_fini_cpu(cpu); | 
					
						
							|  |  |  | 	uci->valid = 0; | 
					
						
							| 
									
										
										
										
											2008-12-20 00:15:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | static enum ucode_state microcode_resume_cpu(int cpu) | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	if (!uci->mc) | 
					
						
							|  |  |  | 		return UCODE_NFOUND; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 	pr_debug("CPU%d updated upon resume\n", cpu); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	apply_microcode_on_target(cpu); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return UCODE_OK; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | static enum ucode_state microcode_init_cpu(int cpu) | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	enum ucode_state ustate; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	if (collect_cpu_info(cpu)) | 
					
						
							|  |  |  | 		return UCODE_ERROR; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	/* --dimm. Trigger a delayed update? */ | 
					
						
							|  |  |  | 	if (system_state != SYSTEM_RUNNING) | 
					
						
							|  |  |  | 		return UCODE_NFOUND; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev); | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	if (ustate == UCODE_OK) { | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		pr_debug("CPU%d updated upon init\n", cpu); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		apply_microcode_on_target(cpu); | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	return ustate; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | static enum ucode_state microcode_update_cpu(int cpu) | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							|  |  |  | 	enum ucode_state ustate; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-23 15:04:53 -08:00
										 |  |  | 	if (uci->valid) | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		ustate = microcode_resume_cpu(cpu); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		ustate = microcode_init_cpu(cpu); | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	return ustate; | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int mc_sysdev_add(struct sys_device *sys_dev) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err, cpu = sys_dev->id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cpu_online(cpu)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 	pr_debug("CPU%d added\n", cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	if (microcode_init_cpu(cpu) == UCODE_ERROR) | 
					
						
							|  |  |  | 		err = -EINVAL; | 
					
						
							| 
									
										
										
										
											2009-03-11 16:32:36 +10:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int mc_sysdev_remove(struct sys_device *sys_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int cpu = sys_dev->id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cpu_online(cpu)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 	pr_debug("CPU%d removed\n", cpu); | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 	microcode_fini_cpu(cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int mc_sysdev_resume(struct sys_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int cpu = dev->id; | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!cpu_online(cpu)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2008-09-14 14:50:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * All non-bootup cpus are still disabled, | 
					
						
							|  |  |  | 	 * so only CPU 0 will apply ucode here. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Moreover, there can be no concurrent | 
					
						
							|  |  |  | 	 * updates from any other places at this point. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	WARN_ON(cpu != 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (uci->valid && uci->mc) | 
					
						
							|  |  |  | 		microcode_ops->apply_microcode(cpu); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct sysdev_driver mc_sysdev_driver = { | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	.add			= mc_sysdev_add, | 
					
						
							|  |  |  | 	.remove			= mc_sysdev_remove, | 
					
						
							|  |  |  | 	.resume			= mc_sysdev_resume, | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static __cpuinit int | 
					
						
							|  |  |  | mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int cpu = (unsigned long)hcpu; | 
					
						
							|  |  |  | 	struct sys_device *sys_dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sys_dev = get_cpu_sysdev(cpu); | 
					
						
							|  |  |  | 	switch (action) { | 
					
						
							|  |  |  | 	case CPU_ONLINE: | 
					
						
							|  |  |  | 	case CPU_ONLINE_FROZEN: | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 		microcode_update_cpu(cpu); | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 	case CPU_DOWN_FAILED: | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	case CPU_DOWN_FAILED_FROZEN: | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		pr_debug("CPU%d added\n", cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 		if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 			pr_err("Failed to create group for CPU%d\n", cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case CPU_DOWN_PREPARE: | 
					
						
							|  |  |  | 	case CPU_DOWN_PREPARE_FROZEN: | 
					
						
							|  |  |  | 		/* Suspend is in progress, only remove the interface */ | 
					
						
							|  |  |  | 		sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		pr_debug("CPU%d removed\n", cpu); | 
					
						
							| 
									
										
										
										
											2008-08-20 00:22:26 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case CPU_DEAD: | 
					
						
							|  |  |  | 	case CPU_UP_CANCELED_FROZEN: | 
					
						
							|  |  |  | 		/* The CPU refused to come up during a system resume */ | 
					
						
							|  |  |  | 		microcode_fini_cpu(cpu); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NOTIFY_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct notifier_block __refdata mc_cpu_notifier = { | 
					
						
							| 
									
										
										
										
											2009-03-11 11:19:46 +01:00
										 |  |  | 	.notifier_call	= mc_cpu_callback, | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | static int __init microcode_init(void) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | 	struct cpuinfo_x86 *c = &cpu_data(0); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	int error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | 	if (c->x86_vendor == X86_VENDOR_INTEL) | 
					
						
							|  |  |  | 		microcode_ops = init_intel_microcode(); | 
					
						
							| 
									
										
										
										
											2008-09-24 11:50:35 +02:00
										 |  |  | 	else if (c->x86_vendor == X86_VENDOR_AMD) | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | 		microcode_ops = init_amd_microcode(); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | 	if (!microcode_ops) { | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		pr_err("no support for this CPU vendor\n"); | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	microcode_pdev = platform_device_register_simple("microcode", -1, | 
					
						
							|  |  |  | 							 NULL, 0); | 
					
						
							|  |  |  | 	if (IS_ERR(microcode_pdev)) { | 
					
						
							|  |  |  | 		microcode_dev_exit(); | 
					
						
							|  |  |  | 		return PTR_ERR(microcode_pdev); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	get_online_cpus(); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	mutex_lock(µcode_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(µcode_mutex); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	put_online_cpus(); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	if (error) { | 
					
						
							|  |  |  | 		platform_device_unregister(microcode_pdev); | 
					
						
							|  |  |  | 		return error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	error = microcode_dev_init(); | 
					
						
							|  |  |  | 	if (error) | 
					
						
							|  |  |  | 		return error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	register_hotcpu_notifier(&mc_cpu_notifier); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	pr_info("Microcode Update Driver: v" MICROCODE_VERSION | 
					
						
							| 
									
										
										
										
											2009-12-08 22:30:50 -08:00
										 |  |  | 		" <tigran@aivazian.fsnet.co.uk>, Peter Oruba\n"); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | module_init(microcode_init); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | static void __exit microcode_exit(void) | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	microcode_dev_exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	unregister_hotcpu_notifier(&mc_cpu_notifier); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	get_online_cpus(); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	mutex_lock(µcode_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(µcode_mutex); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:17 +02:00
										 |  |  | 	put_online_cpus(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_device_unregister(microcode_pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:21 +02:00
										 |  |  | 	microcode_ops = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-11 23:48:27 +02:00
										 |  |  | 	pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); | 
					
						
							| 
									
										
										
										
											2008-07-28 18:44:21 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-09-23 12:08:44 +02:00
										 |  |  | module_exit(microcode_exit); |