aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/microcode.c
diff options
context:
space:
mode:
authorPeter Oruba <peter.oruba@amd.com>2008-07-28 12:44:17 -0400
committerIngo Molnar <mingo@elte.hu>2008-07-28 13:57:55 -0400
commit3e135d887c973b525d43fbb67dfc5972694882f6 (patch)
treef24650c6d57c72f4fe77b96b19d891bee6865349 /arch/x86/kernel/microcode.c
parent1abae31096007cc993f67ae2576fe8f812270ad6 (diff)
x86: code split to two parts
Split off existing code into two seperate files. One file holds general code, the other file vendor specific parts. No functional changes, only refactoring. Temporarily Introduced a new module name 'ucode' for result, due to already taken name 'microcode'. Signed-off-by: Peter Oruba <peter.oruba@amd.com> Cc: Tigran Aivazian <tigran@aivazian.fsnet.co.uk> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/microcode.c')
-rw-r--r--arch/x86/kernel/microcode.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c
new file mode 100644
index 000000000000..c1047d7f7ede
--- /dev/null
+++ b/arch/x86/kernel/microcode.c
@@ -0,0 +1,463 @@
1/*
2 * Intel CPU Microcode Update Driver for Linux
3 *
4 * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
5 * 2006 Shaohua Li <shaohua.li@intel.com>
6 *
7 * This driver allows to upgrade microcode on Intel processors
8 * belonging to IA-32 family - PentiumPro, Pentium II,
9 * Pentium III, Xeon, Pentium 4, etc.
10 *
11 * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
12 * Software Developer's Manual
13 * Order Number 253668 or free download from:
14 *
15 * http://developer.intel.com/design/pentium4/manuals/253668.htm
16 *
17 * For more information, go to http://www.urbanmyth.org/microcode
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version
22 * 2 of the License, or (at your option) any later version.
23 *
24 * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com>
25 * Initial release.
26 * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com>
27 * Added read() support + cleanups.
28 * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com>
29 * Added 'device trimming' support. open(O_WRONLY) zeroes
30 * and frees the saved copy of applied microcode.
31 * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com>
32 * Made to use devfs (/dev/cpu/microcode) + cleanups.
33 * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com>
34 * Added misc device support (now uses both devfs and misc).
35 * Added MICROCODE_IOCFREE ioctl to clear memory.
36 * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com>
37 * Messages for error cases (non Intel & no suitable microcode).
38 * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
39 * Removed ->release(). Removed exclusive open and status bitmap.
40 * Added microcode_rwsem to serialize read()/write()/ioctl().
41 * Removed global kernel lock usage.
42 * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
43 * Write 0 to 0x8B msr and then cpuid before reading revision,
44 * so that it works even if there were no update done by the
45 * BIOS. Otherwise, reading from 0x8B gives junk (which happened
46 * to be 0 on my machine which is why it worked even when I
47 * disabled update by the BIOS)
48 * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
49 * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
50 * Tigran Aivazian <tigran@veritas.com>
51 * Intel Pentium 4 processor support and bugfixes.
52 * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
53 * Bugfix for HT (Hyper-Threading) enabled processors
54 * whereby processor resources are shared by all logical processors
55 * in a single CPU package.
56 * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
57 * Tigran Aivazian <tigran@veritas.com>,
58 * Serialize updates as required on HT processors due to speculative
59 * nature of implementation.
60 * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
61 * Fix the panic when writing zero-length microcode chunk.
62 * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
63 * Jun Nakajima <jun.nakajima@intel.com>
64 * Support for the microcode updates in the new format.
65 * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
66 * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
67 * because we no longer hold a copy of applied microcode
68 * in kernel memory.
69 * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
70 * Fix sigmatch() macro to handle old CPUs with pf == 0.
71 * Thanks to Stuart Swales for pointing out this bug.
72 */
73
74//#define DEBUG /* pr_debug */
75#include <linux/capability.h>
76#include <linux/kernel.h>
77#include <linux/init.h>
78#include <linux/sched.h>
79#include <linux/smp_lock.h>
80#include <linux/cpumask.h>
81#include <linux/module.h>
82#include <linux/slab.h>
83#include <linux/vmalloc.h>
84#include <linux/miscdevice.h>
85#include <linux/spinlock.h>
86#include <linux/mm.h>
87#include <linux/fs.h>
88#include <linux/mutex.h>
89#include <linux/cpu.h>
90#include <linux/firmware.h>
91#include <linux/platform_device.h>
92
93#include <asm/msr.h>
94#include <asm/uaccess.h>
95#include <asm/processor.h>
96#include <asm/microcode.h>
97
98MODULE_DESCRIPTION("Microcode Update Driver");
99MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
100MODULE_LICENSE("GPL");
101
102#define MICROCODE_VERSION "1.14a"
103
104/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
105DEFINE_MUTEX(microcode_mutex);
106
107struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
108
109extern long get_next_ucode(void **mc, long offset);
110extern int microcode_sanity_check(void *mc);
111extern int get_matching_microcode(void *mc, int cpu);
112extern void collect_cpu_info(int cpu_num);
113extern int cpu_request_microcode(int cpu);
114extern void microcode_fini_cpu(int cpu);
115extern void apply_microcode(int cpu);
116extern int apply_microcode_check_cpu(int cpu);
117
118#ifdef CONFIG_MICROCODE_OLD_INTERFACE
119void __user *user_buffer; /* user area microcode data buffer */
120unsigned int user_buffer_size; /* it's size */
121
122static int do_microcode_update (void)
123{
124 long cursor = 0;
125 int error = 0;
126 void *new_mc = NULL;
127 int cpu;
128 cpumask_t old;
129 cpumask_of_cpu_ptr_declare(newmask);
130
131 old = current->cpus_allowed;
132
133 while ((cursor = get_next_ucode(&new_mc, cursor)) > 0) {
134 error = microcode_sanity_check(new_mc);
135 if (error)
136 goto out;
137 /*
138 * It's possible the data file has multiple matching ucode,
139 * lets keep searching till the latest version
140 */
141 for_each_online_cpu(cpu) {
142 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
143
144 if (!uci->valid)
145 continue;
146 cpumask_of_cpu_ptr_next(newmask, cpu);
147 set_cpus_allowed_ptr(current, newmask);
148 error = get_maching_microcode(new_mc, cpu);
149 if (error < 0)
150 goto out;
151 if (error == 1)
152 apply_microcode(cpu);
153 }
154 vfree(new_mc);
155 }
156out:
157 if (cursor > 0)
158 vfree(new_mc);
159 if (cursor < 0)
160 error = cursor;
161 set_cpus_allowed_ptr(current, &old);
162 return error;
163}
164
165static int microcode_open (struct inode *unused1, struct file *unused2)
166{
167 cycle_kernel_lock();
168 return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
169}
170
171static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos)
172{
173 ssize_t ret;
174
175 if ((len >> PAGE_SHIFT) > num_physpages) {
176 printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages);
177 return -EINVAL;
178 }
179
180 get_online_cpus();
181 mutex_lock(&microcode_mutex);
182
183 user_buffer = (void __user *) buf;
184 user_buffer_size = (int) len;
185
186 ret = do_microcode_update();
187 if (!ret)
188 ret = (ssize_t)len;
189
190 mutex_unlock(&microcode_mutex);
191 put_online_cpus();
192
193 return ret;
194}
195
196static const struct file_operations microcode_fops = {
197 .owner = THIS_MODULE,
198 .write = microcode_write,
199 .open = microcode_open,
200};
201
202static struct miscdevice microcode_dev = {
203 .minor = MICROCODE_MINOR,
204 .name = "microcode",
205 .fops = &microcode_fops,
206};
207
208static int __init microcode_dev_init (void)
209{
210 int error;
211
212 error = misc_register(&microcode_dev);
213 if (error) {
214 printk(KERN_ERR
215 "microcode: can't misc_register on minor=%d\n",
216 MICROCODE_MINOR);
217 return error;
218 }
219
220 return 0;
221}
222
223static void microcode_dev_exit (void)
224{
225 misc_deregister(&microcode_dev);
226}
227
228MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
229#else
230#define microcode_dev_init() 0
231#define microcode_dev_exit() do { } while(0)
232#endif
233
234/* fake device for request_firmware */
235struct platform_device *microcode_pdev;
236
237static void microcode_init_cpu(int cpu, int resume)
238{
239 cpumask_t old;
240 cpumask_of_cpu_ptr(newmask, cpu);
241 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
242
243 old = current->cpus_allowed;
244
245 set_cpus_allowed_ptr(current, newmask);
246 mutex_lock(&microcode_mutex);
247 collect_cpu_info(cpu);
248 if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
249 cpu_request_microcode(cpu);
250 mutex_unlock(&microcode_mutex);
251 set_cpus_allowed_ptr(current, &old);
252}
253
254static ssize_t reload_store(struct sys_device *dev,
255 struct sysdev_attribute *attr,
256 const char *buf, size_t sz)
257{
258 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
259 char *end;
260 unsigned long val = simple_strtoul(buf, &end, 0);
261 int err = 0;
262 int cpu = dev->id;
263
264 if (end == buf)
265 return -EINVAL;
266 if (val == 1) {
267 cpumask_t old;
268 cpumask_of_cpu_ptr(newmask, cpu);
269
270 old = current->cpus_allowed;
271
272 get_online_cpus();
273 set_cpus_allowed_ptr(current, newmask);
274
275 mutex_lock(&microcode_mutex);
276 if (uci->valid)
277 err = cpu_request_microcode(cpu);
278 mutex_unlock(&microcode_mutex);
279 put_online_cpus();
280 set_cpus_allowed_ptr(current, &old);
281 }
282 if (err)
283 return err;
284 return sz;
285}
286
287static ssize_t version_show(struct sys_device *dev,
288 struct sysdev_attribute *attr, char *buf)
289{
290 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
291
292 return sprintf(buf, "0x%x\n", uci->rev);
293}
294
295static ssize_t pf_show(struct sys_device *dev,
296 struct sysdev_attribute *attr, char *buf)
297{
298 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
299
300 return sprintf(buf, "0x%x\n", uci->pf);
301}
302
303static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
304static SYSDEV_ATTR(version, 0400, version_show, NULL);
305static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL);
306
307static struct attribute *mc_default_attrs[] = {
308 &attr_reload.attr,
309 &attr_version.attr,
310 &attr_processor_flags.attr,
311 NULL
312};
313
314static struct attribute_group mc_attr_group = {
315 .attrs = mc_default_attrs,
316 .name = "microcode",
317};
318
319static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
320{
321 int err, cpu = sys_dev->id;
322 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
323
324 if (!cpu_online(cpu))
325 return 0;
326
327 pr_debug("microcode: CPU%d added\n", cpu);
328 memset(uci, 0, sizeof(*uci));
329
330 err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group);
331 if (err)
332 return err;
333
334 microcode_init_cpu(cpu, resume);
335
336 return 0;
337}
338
339static int mc_sysdev_add(struct sys_device *sys_dev)
340{
341 return __mc_sysdev_add(sys_dev, 0);
342}
343
344static int mc_sysdev_remove(struct sys_device *sys_dev)
345{
346 int cpu = sys_dev->id;
347
348 if (!cpu_online(cpu))
349 return 0;
350
351 pr_debug("microcode: CPU%d removed\n", cpu);
352 microcode_fini_cpu(cpu);
353 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
354 return 0;
355}
356
357static int mc_sysdev_resume(struct sys_device *dev)
358{
359 int cpu = dev->id;
360
361 if (!cpu_online(cpu))
362 return 0;
363 pr_debug("microcode: CPU%d resumed\n", cpu);
364 /* only CPU 0 will apply ucode here */
365 apply_microcode(0);
366 return 0;
367}
368
369static struct sysdev_driver mc_sysdev_driver = {
370 .add = mc_sysdev_add,
371 .remove = mc_sysdev_remove,
372 .resume = mc_sysdev_resume,
373};
374
375static __cpuinit int
376mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
377{
378 unsigned int cpu = (unsigned long)hcpu;
379 struct sys_device *sys_dev;
380
381 sys_dev = get_cpu_sysdev(cpu);
382 switch (action) {
383 case CPU_UP_CANCELED_FROZEN:
384 /* The CPU refused to come up during a system resume */
385 microcode_fini_cpu(cpu);
386 break;
387 case CPU_ONLINE:
388 case CPU_DOWN_FAILED:
389 mc_sysdev_add(sys_dev);
390 break;
391 case CPU_ONLINE_FROZEN:
392 /* System-wide resume is in progress, try to apply microcode */
393 if (apply_microcode_check_cpu(cpu)) {
394 /* The application of microcode failed */
395 microcode_fini_cpu(cpu);
396 __mc_sysdev_add(sys_dev, 1);
397 break;
398 }
399 case CPU_DOWN_FAILED_FROZEN:
400 if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
401 printk(KERN_ERR "microcode: Failed to create the sysfs "
402 "group for CPU%d\n", cpu);
403 break;
404 case CPU_DOWN_PREPARE:
405 mc_sysdev_remove(sys_dev);
406 break;
407 case CPU_DOWN_PREPARE_FROZEN:
408 /* Suspend is in progress, only remove the interface */
409 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
410 break;
411 }
412 return NOTIFY_OK;
413}
414
415static struct notifier_block __refdata mc_cpu_notifier = {
416 .notifier_call = mc_cpu_callback,
417};
418
419static int __init microcode_init (void)
420{
421 int error;
422
423 printk(KERN_INFO
424 "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n");
425
426 error = microcode_dev_init();
427 if (error)
428 return error;
429 microcode_pdev = platform_device_register_simple("microcode", -1,
430 NULL, 0);
431 if (IS_ERR(microcode_pdev)) {
432 microcode_dev_exit();
433 return PTR_ERR(microcode_pdev);
434 }
435
436 get_online_cpus();
437 error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver);
438 put_online_cpus();
439 if (error) {
440 microcode_dev_exit();
441 platform_device_unregister(microcode_pdev);
442 return error;
443 }
444
445 register_hotcpu_notifier(&mc_cpu_notifier);
446 return 0;
447}
448
449static void __exit microcode_exit (void)
450{
451 microcode_dev_exit();
452
453 unregister_hotcpu_notifier(&mc_cpu_notifier);
454
455 get_online_cpus();
456 sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver);
457 put_online_cpus();
458
459 platform_device_unregister(microcode_pdev);
460}
461
462module_init(microcode_init)
463module_exit(microcode_exit)