Linux 3.5-rc5
[deliverable/linux.git] / arch / x86 / kernel / microcode_core.c
CommitLineData
3e135d88
PO
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 *
50a23e6e 15 * http://developer.intel.com/Assets/PDF/manual/253668.pdf
3e135d88
PO
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>,
d33dcb9e
PO
58 * Serialize updates as required on HT processors due to
59 * speculative nature of implementation.
3e135d88
PO
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 */
f58e1f53
JP
73
74#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
75
4bae1967 76#include <linux/platform_device.h>
4bae1967 77#include <linux/miscdevice.h>
871b72dd 78#include <linux/capability.h>
4bae1967
IM
79#include <linux/kernel.h>
80#include <linux/module.h>
3e135d88
PO
81#include <linux/mutex.h>
82#include <linux/cpu.h>
4bae1967
IM
83#include <linux/fs.h>
84#include <linux/mm.h>
f3c6ea1b 85#include <linux/syscore_ops.h>
3e135d88 86
3e135d88 87#include <asm/microcode.h>
4bae1967 88#include <asm/processor.h>
78ff123b 89#include <asm/cpu_device_id.h>
3e135d88
PO
90
91MODULE_DESCRIPTION("Microcode Update Driver");
92MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
93MODULE_LICENSE("GPL");
94
4bae1967 95#define MICROCODE_VERSION "2.00"
3e135d88 96
4bae1967 97static struct microcode_ops *microcode_ops;
3e135d88 98
871b72dd
DA
99/*
100 * Synchronization.
101 *
102 * All non cpu-hotplug-callback call sites use:
103 *
104 * - microcode_mutex to synchronize with each other;
105 * - get/put_online_cpus() to synchronize with
106 * the cpu-hotplug-callback call sites.
107 *
108 * We guarantee that only a single cpu is being
109 * updated at any particular moment of time.
110 */
d45de409 111static DEFINE_MUTEX(microcode_mutex);
3e135d88 112
4bae1967 113struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
8d86f390 114EXPORT_SYMBOL_GPL(ucode_cpu_info);
3e135d88 115
871b72dd
DA
116/*
117 * Operations that are run on a target cpu:
118 */
119
120struct cpu_info_ctx {
121 struct cpu_signature *cpu_sig;
122 int err;
123};
124
125static void collect_cpu_info_local(void *arg)
126{
127 struct cpu_info_ctx *ctx = arg;
128
129 ctx->err = microcode_ops->collect_cpu_info(smp_processor_id(),
130 ctx->cpu_sig);
131}
132
133static int collect_cpu_info_on_target(int cpu, struct cpu_signature *cpu_sig)
134{
135 struct cpu_info_ctx ctx = { .cpu_sig = cpu_sig, .err = 0 };
136 int ret;
137
138 ret = smp_call_function_single(cpu, collect_cpu_info_local, &ctx, 1);
139 if (!ret)
140 ret = ctx.err;
141
142 return ret;
143}
144
145static int collect_cpu_info(int cpu)
146{
147 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
148 int ret;
149
150 memset(uci, 0, sizeof(*uci));
151
152 ret = collect_cpu_info_on_target(cpu, &uci->cpu_sig);
153 if (!ret)
154 uci->valid = 1;
155
156 return ret;
157}
158
159struct apply_microcode_ctx {
160 int err;
161};
162
163static void apply_microcode_local(void *arg)
164{
165 struct apply_microcode_ctx *ctx = arg;
166
167 ctx->err = microcode_ops->apply_microcode(smp_processor_id());
168}
169
170static int apply_microcode_on_target(int cpu)
171{
172 struct apply_microcode_ctx ctx = { .err = 0 };
173 int ret;
174
175 ret = smp_call_function_single(cpu, apply_microcode_local, &ctx, 1);
176 if (!ret)
177 ret = ctx.err;
178
179 return ret;
180}
181
3e135d88 182#ifdef CONFIG_MICROCODE_OLD_INTERFACE
a0a29b62 183static int do_microcode_update(const void __user *buf, size_t size)
3e135d88 184{
3e135d88 185 int error = 0;
3e135d88 186 int cpu;
6f66cbc6 187
a0a29b62
DA
188 for_each_online_cpu(cpu) {
189 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
871b72dd 190 enum ucode_state ustate;
a0a29b62
DA
191
192 if (!uci->valid)
193 continue;
6f66cbc6 194
871b72dd
DA
195 ustate = microcode_ops->request_microcode_user(cpu, buf, size);
196 if (ustate == UCODE_ERROR) {
197 error = -1;
198 break;
199 } else if (ustate == UCODE_OK)
200 apply_microcode_on_target(cpu);
3e135d88 201 }
871b72dd 202
3e135d88
PO
203 return error;
204}
205
3f10940e 206static int microcode_open(struct inode *inode, struct file *file)
3e135d88 207{
3f10940e 208 return capable(CAP_SYS_RAWIO) ? nonseekable_open(inode, file) : -EPERM;
3e135d88
PO
209}
210
d33dcb9e
PO
211static ssize_t microcode_write(struct file *file, const char __user *buf,
212 size_t len, loff_t *ppos)
3e135d88 213{
871b72dd 214 ssize_t ret = -EINVAL;
3e135d88 215
4481374c 216 if ((len >> PAGE_SHIFT) > totalram_pages) {
f58e1f53 217 pr_err("too much data (max %ld pages)\n", totalram_pages);
871b72dd 218 return ret;
3e135d88
PO
219 }
220
221 get_online_cpus();
222 mutex_lock(&microcode_mutex);
223
871b72dd 224 if (do_microcode_update(buf, len) == 0)
3e135d88
PO
225 ret = (ssize_t)len;
226
227 mutex_unlock(&microcode_mutex);
228 put_online_cpus();
229
230 return ret;
231}
232
233static const struct file_operations microcode_fops = {
871b72dd
DA
234 .owner = THIS_MODULE,
235 .write = microcode_write,
236 .open = microcode_open,
6038f373 237 .llseek = no_llseek,
3e135d88
PO
238};
239
240static struct miscdevice microcode_dev = {
871b72dd
DA
241 .minor = MICROCODE_MINOR,
242 .name = "microcode",
e454cea2 243 .nodename = "cpu/microcode",
871b72dd 244 .fops = &microcode_fops,
3e135d88
PO
245};
246
d33dcb9e 247static int __init microcode_dev_init(void)
3e135d88
PO
248{
249 int error;
250
251 error = misc_register(&microcode_dev);
252 if (error) {
f58e1f53 253 pr_err("can't misc_register on minor=%d\n", MICROCODE_MINOR);
3e135d88
PO
254 return error;
255 }
256
257 return 0;
258}
259
bd399063 260static void __exit microcode_dev_exit(void)
3e135d88
PO
261{
262 misc_deregister(&microcode_dev);
263}
264
265MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
578454ff 266MODULE_ALIAS("devname:cpu/microcode");
3e135d88 267#else
4bae1967
IM
268#define microcode_dev_init() 0
269#define microcode_dev_exit() do { } while (0)
3e135d88
PO
270#endif
271
272/* fake device for request_firmware */
4bae1967 273static struct platform_device *microcode_pdev;
3e135d88 274
871b72dd 275static int reload_for_cpu(int cpu)
af5c820a 276{
871b72dd 277 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
af5c820a
RR
278 int err = 0;
279
280 mutex_lock(&microcode_mutex);
281 if (uci->valid) {
871b72dd
DA
282 enum ucode_state ustate;
283
284 ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev);
285 if (ustate == UCODE_OK)
286 apply_microcode_on_target(cpu);
287 else
288 if (ustate == UCODE_ERROR)
289 err = -EINVAL;
af5c820a
RR
290 }
291 mutex_unlock(&microcode_mutex);
871b72dd 292
af5c820a
RR
293 return err;
294}
295
8a25a2fd
KS
296static ssize_t reload_store(struct device *dev,
297 struct device_attribute *attr,
871b72dd 298 const char *buf, size_t size)
3e135d88 299{
871b72dd 300 unsigned long val;
3e135d88 301 int cpu = dev->id;
e826abd5 302 ssize_t ret = 0;
3e135d88 303
e826abd5
SK
304 ret = kstrtoul(buf, 0, &val);
305 if (ret)
306 return ret;
871b72dd 307
3e135d88 308 if (val == 1) {
3e135d88 309 get_online_cpus();
af5c820a 310 if (cpu_online(cpu))
871b72dd 311 ret = reload_for_cpu(cpu);
3e135d88 312 put_online_cpus();
3e135d88 313 }
871b72dd
DA
314
315 if (!ret)
316 ret = size;
317
318 return ret;
3e135d88
PO
319}
320
8a25a2fd
KS
321static ssize_t version_show(struct device *dev,
322 struct device_attribute *attr, char *buf)
3e135d88
PO
323{
324 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
325
d45de409 326 return sprintf(buf, "0x%x\n", uci->cpu_sig.rev);
3e135d88
PO
327}
328
8a25a2fd
KS
329static ssize_t pf_show(struct device *dev,
330 struct device_attribute *attr, char *buf)
3e135d88
PO
331{
332 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
333
d45de409 334 return sprintf(buf, "0x%x\n", uci->cpu_sig.pf);
3e135d88
PO
335}
336
8a25a2fd
KS
337static DEVICE_ATTR(reload, 0200, NULL, reload_store);
338static DEVICE_ATTR(version, 0400, version_show, NULL);
339static DEVICE_ATTR(processor_flags, 0400, pf_show, NULL);
3e135d88
PO
340
341static struct attribute *mc_default_attrs[] = {
8a25a2fd
KS
342 &dev_attr_reload.attr,
343 &dev_attr_version.attr,
344 &dev_attr_processor_flags.attr,
3e135d88
PO
345 NULL
346};
347
348static struct attribute_group mc_attr_group = {
871b72dd
DA
349 .attrs = mc_default_attrs,
350 .name = "microcode",
3e135d88
PO
351};
352
871b72dd 353static void microcode_fini_cpu(int cpu)
d45de409
DA
354{
355 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
356
d45de409
DA
357 microcode_ops->microcode_fini_cpu(cpu);
358 uci->valid = 0;
280a9ca5
DA
359}
360
871b72dd 361static enum ucode_state microcode_resume_cpu(int cpu)
d45de409
DA
362{
363 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
364
871b72dd
DA
365 if (!uci->mc)
366 return UCODE_NFOUND;
367
f58e1f53 368 pr_debug("CPU%d updated upon resume\n", cpu);
871b72dd
DA
369 apply_microcode_on_target(cpu);
370
371 return UCODE_OK;
d45de409
DA
372}
373
871b72dd 374static enum ucode_state microcode_init_cpu(int cpu)
d45de409 375{
871b72dd 376 enum ucode_state ustate;
d45de409 377
871b72dd
DA
378 if (collect_cpu_info(cpu))
379 return UCODE_ERROR;
d45de409 380
871b72dd
DA
381 /* --dimm. Trigger a delayed update? */
382 if (system_state != SYSTEM_RUNNING)
383 return UCODE_NFOUND;
d45de409 384
871b72dd 385 ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev);
d45de409 386
871b72dd 387 if (ustate == UCODE_OK) {
f58e1f53 388 pr_debug("CPU%d updated upon init\n", cpu);
871b72dd 389 apply_microcode_on_target(cpu);
d45de409
DA
390 }
391
871b72dd 392 return ustate;
d45de409
DA
393}
394
871b72dd 395static enum ucode_state microcode_update_cpu(int cpu)
d45de409 396{
871b72dd
DA
397 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
398 enum ucode_state ustate;
d45de409 399
2f99f5c8 400 if (uci->valid)
871b72dd
DA
401 ustate = microcode_resume_cpu(cpu);
402 else
403 ustate = microcode_init_cpu(cpu);
d45de409 404
871b72dd 405 return ustate;
d45de409
DA
406}
407
8a25a2fd 408static int mc_device_add(struct device *dev, struct subsys_interface *sif)
3e135d88 409{
8a25a2fd 410 int err, cpu = dev->id;
3e135d88
PO
411
412 if (!cpu_online(cpu))
413 return 0;
414
f58e1f53 415 pr_debug("CPU%d added\n", cpu);
3e135d88 416
8a25a2fd 417 err = sysfs_create_group(&dev->kobj, &mc_attr_group);
3e135d88
PO
418 if (err)
419 return err;
420
a956bd6f 421 if (microcode_init_cpu(cpu) == UCODE_ERROR)
6c53cbfc 422 return -EINVAL;
af5c820a
RR
423
424 return err;
3e135d88
PO
425}
426
8a25a2fd 427static int mc_device_remove(struct device *dev, struct subsys_interface *sif)
3e135d88 428{
8a25a2fd 429 int cpu = dev->id;
3e135d88
PO
430
431 if (!cpu_online(cpu))
432 return 0;
433
f58e1f53 434 pr_debug("CPU%d removed\n", cpu);
d45de409 435 microcode_fini_cpu(cpu);
8a25a2fd 436 sysfs_remove_group(&dev->kobj, &mc_attr_group);
3e135d88
PO
437 return 0;
438}
439
8a25a2fd
KS
440static struct subsys_interface mc_cpu_interface = {
441 .name = "microcode",
442 .subsys = &cpu_subsys,
443 .add_dev = mc_device_add,
444 .remove_dev = mc_device_remove,
f3c6ea1b
RW
445};
446
447/**
448 * mc_bp_resume - Update boot CPU microcode during resume.
449 */
450static void mc_bp_resume(void)
3e135d88 451{
f3c6ea1b 452 int cpu = smp_processor_id();
871b72dd 453 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
3e135d88 454
871b72dd
DA
455 if (uci->valid && uci->mc)
456 microcode_ops->apply_microcode(cpu);
3e135d88
PO
457}
458
f3c6ea1b
RW
459static struct syscore_ops mc_syscore_ops = {
460 .resume = mc_bp_resume,
3e135d88
PO
461};
462
463static __cpuinit int
464mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
465{
466 unsigned int cpu = (unsigned long)hcpu;
8a25a2fd 467 struct device *dev;
3e135d88 468
8a25a2fd 469 dev = get_cpu_device(cpu);
3e135d88 470 switch (action) {
3e135d88 471 case CPU_ONLINE:
3e135d88 472 case CPU_ONLINE_FROZEN:
871b72dd 473 microcode_update_cpu(cpu);
d45de409 474 case CPU_DOWN_FAILED:
3e135d88 475 case CPU_DOWN_FAILED_FROZEN:
f58e1f53 476 pr_debug("CPU%d added\n", cpu);
8a25a2fd 477 if (sysfs_create_group(&dev->kobj, &mc_attr_group))
f58e1f53 478 pr_err("Failed to create group for CPU%d\n", cpu);
3e135d88
PO
479 break;
480 case CPU_DOWN_PREPARE:
3e135d88
PO
481 case CPU_DOWN_PREPARE_FROZEN:
482 /* Suspend is in progress, only remove the interface */
8a25a2fd 483 sysfs_remove_group(&dev->kobj, &mc_attr_group);
f58e1f53 484 pr_debug("CPU%d removed\n", cpu);
d45de409 485 break;
70989449
SB
486
487 /*
488 * When a CPU goes offline, don't free up or invalidate the copy of
489 * the microcode in kernel memory, so that we can reuse it when the
490 * CPU comes back online without unnecessarily requesting the userspace
491 * for it again.
492 */
d45de409
DA
493 case CPU_UP_CANCELED_FROZEN:
494 /* The CPU refused to come up during a system resume */
495 microcode_fini_cpu(cpu);
3e135d88
PO
496 break;
497 }
498 return NOTIFY_OK;
499}
500
501static struct notifier_block __refdata mc_cpu_notifier = {
4bae1967 502 .notifier_call = mc_cpu_callback,
3e135d88
PO
503};
504
78ff123b
AK
505#ifdef MODULE
506/* Autoload on Intel and AMD systems */
507static const struct x86_cpu_id microcode_id[] = {
508#ifdef CONFIG_MICROCODE_INTEL
509 { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, },
510#endif
511#ifdef CONFIG_MICROCODE_AMD
512 { X86_VENDOR_AMD, X86_FAMILY_ANY, X86_MODEL_ANY, },
513#endif
514 {}
515};
516MODULE_DEVICE_TABLE(x86cpu, microcode_id);
517#endif
518
18dbc916 519static int __init microcode_init(void)
3e135d88 520{
18dbc916 521 struct cpuinfo_x86 *c = &cpu_data(0);
3e135d88
PO
522 int error;
523
18dbc916
DA
524 if (c->x86_vendor == X86_VENDOR_INTEL)
525 microcode_ops = init_intel_microcode();
82b07865 526 else if (c->x86_vendor == X86_VENDOR_AMD)
18dbc916 527 microcode_ops = init_amd_microcode();
283c1f25 528 else
f58e1f53 529 pr_err("no support for this CPU vendor\n");
283c1f25
AH
530
531 if (!microcode_ops)
18dbc916 532 return -ENODEV;
3e135d88 533
3e135d88
PO
534 microcode_pdev = platform_device_register_simple("microcode", -1,
535 NULL, 0);
bd399063 536 if (IS_ERR(microcode_pdev))
3e135d88 537 return PTR_ERR(microcode_pdev);
3e135d88
PO
538
539 get_online_cpus();
871b72dd
DA
540 mutex_lock(&microcode_mutex);
541
8a25a2fd 542 error = subsys_interface_register(&mc_cpu_interface);
871b72dd
DA
543
544 mutex_unlock(&microcode_mutex);
3e135d88 545 put_online_cpus();
871b72dd 546
bd399063
SB
547 if (error)
548 goto out_pdev;
3e135d88 549
871b72dd
DA
550 error = microcode_dev_init();
551 if (error)
ff4b8a57 552 goto out_driver;
871b72dd 553
f3c6ea1b 554 register_syscore_ops(&mc_syscore_ops);
3e135d88 555 register_hotcpu_notifier(&mc_cpu_notifier);
8d86f390 556
871b72dd 557 pr_info("Microcode Update Driver: v" MICROCODE_VERSION
f58e1f53 558 " <tigran@aivazian.fsnet.co.uk>, Peter Oruba\n");
8d86f390 559
3e135d88 560 return 0;
bd399063 561
ff4b8a57 562out_driver:
bd399063
SB
563 get_online_cpus();
564 mutex_lock(&microcode_mutex);
565
ff4b8a57 566 subsys_interface_unregister(&mc_cpu_interface);
bd399063
SB
567
568 mutex_unlock(&microcode_mutex);
569 put_online_cpus();
570
571out_pdev:
572 platform_device_unregister(microcode_pdev);
573 return error;
574
3e135d88 575}
871b72dd 576module_init(microcode_init);
3e135d88 577
18dbc916 578static void __exit microcode_exit(void)
3e135d88 579{
f72c1a57
BP
580 struct cpuinfo_x86 *c = &cpu_data(0);
581
3e135d88
PO
582 microcode_dev_exit();
583
584 unregister_hotcpu_notifier(&mc_cpu_notifier);
4ac5fc6a 585 unregister_syscore_ops(&mc_syscore_ops);
3e135d88
PO
586
587 get_online_cpus();
871b72dd
DA
588 mutex_lock(&microcode_mutex);
589
8a25a2fd 590 subsys_interface_unregister(&mc_cpu_interface);
871b72dd
DA
591
592 mutex_unlock(&microcode_mutex);
3e135d88
PO
593 put_online_cpus();
594
595 platform_device_unregister(microcode_pdev);
3e135d88 596
8d86f390
PO
597 microcode_ops = NULL;
598
f72c1a57
BP
599 if (c->x86_vendor == X86_VENDOR_AMD)
600 exit_amd_microcode();
601
871b72dd 602 pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n");
8d86f390 603}
18dbc916 604module_exit(microcode_exit);
This page took 0.279604 seconds and 5 git commands to generate.