]> git.proxmox.com Git - mirror_ubuntu-kernels.git/blame - arch/x86/kernel/cpu/microcode/core.c
x86/microcode: Issue the debug printk on resume only on success
[mirror_ubuntu-kernels.git] / arch / x86 / kernel / cpu / microcode / core.c
CommitLineData
3e135d88 1/*
6b44e72a 2 * CPU Microcode Update Driver for Linux
3e135d88 3 *
6b44e72a
BP
4 * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
5 * 2006 Shaohua Li <shaohua.li@intel.com>
6 * 2013-2015 Borislav Petkov <bp@alien8.de>
3e135d88 7 *
fe055896
BP
8 * X86 CPU microcode early update for Linux:
9 *
10 * Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
11 * H Peter Anvin" <hpa@zytor.com>
12 * (C) 2015 Borislav Petkov <bp@alien8.de>
13 *
6b44e72a 14 * This driver allows to upgrade microcode on x86 processors.
3e135d88 15 *
6b44e72a
BP
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version
19 * 2 of the License, or (at your option) any later version.
3e135d88 20 */
f58e1f53 21
6b26e1bf 22#define pr_fmt(fmt) "microcode: " fmt
f58e1f53 23
4bae1967 24#include <linux/platform_device.h>
fe055896 25#include <linux/syscore_ops.h>
4bae1967 26#include <linux/miscdevice.h>
871b72dd 27#include <linux/capability.h>
fe055896 28#include <linux/firmware.h>
4bae1967 29#include <linux/kernel.h>
3e135d88
PO
30#include <linux/mutex.h>
31#include <linux/cpu.h>
4bae1967
IM
32#include <linux/fs.h>
33#include <linux/mm.h>
3e135d88 34
fe055896 35#include <asm/microcode_intel.h>
78ff123b 36#include <asm/cpu_device_id.h>
fe055896 37#include <asm/microcode_amd.h>
c93dc84c 38#include <asm/perf_event.h>
fe055896
BP
39#include <asm/microcode.h>
40#include <asm/processor.h>
41#include <asm/cmdline.h>
3e135d88 42
6b26e1bf 43#define MICROCODE_VERSION "2.01"
3e135d88 44
4bae1967 45static struct microcode_ops *microcode_ops;
6b26e1bf
BP
46static bool dis_ucode_ldr;
47
058dc498
BP
48LIST_HEAD(microcode_cache);
49
871b72dd
DA
50/*
51 * Synchronization.
52 *
53 * All non cpu-hotplug-callback call sites use:
54 *
55 * - microcode_mutex to synchronize with each other;
56 * - get/put_online_cpus() to synchronize with
57 * the cpu-hotplug-callback call sites.
58 *
59 * We guarantee that only a single cpu is being
60 * updated at any particular moment of time.
61 */
d45de409 62static DEFINE_MUTEX(microcode_mutex);
3e135d88 63
4bae1967 64struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
3e135d88 65
871b72dd
DA
66/*
67 * Operations that are run on a target cpu:
68 */
69
70struct cpu_info_ctx {
71 struct cpu_signature *cpu_sig;
72 int err;
73};
74
fe055896
BP
75static bool __init check_loader_disabled_bsp(void)
76{
e8c8165e
BP
77 static const char *__dis_opt_str = "dis_ucode_ldr";
78
fe055896
BP
79#ifdef CONFIG_X86_32
80 const char *cmdline = (const char *)__pa_nodebug(boot_command_line);
e8c8165e 81 const char *option = (const char *)__pa_nodebug(__dis_opt_str);
fe055896
BP
82 bool *res = (bool *)__pa_nodebug(&dis_ucode_ldr);
83
84#else /* CONFIG_X86_64 */
85 const char *cmdline = boot_command_line;
e8c8165e 86 const char *option = __dis_opt_str;
fe055896
BP
87 bool *res = &dis_ucode_ldr;
88#endif
89
90 if (cmdline_find_option_bool(cmdline, option))
91 *res = true;
92
93 return *res;
94}
95
96extern struct builtin_fw __start_builtin_fw[];
97extern struct builtin_fw __end_builtin_fw[];
98
99bool get_builtin_firmware(struct cpio_data *cd, const char *name)
100{
101#ifdef CONFIG_FW_LOADER
102 struct builtin_fw *b_fw;
103
104 for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
105 if (!strcmp(name, b_fw->name)) {
106 cd->size = b_fw->size;
107 cd->data = b_fw->data;
108 return true;
109 }
110 }
111#endif
112 return false;
113}
114
115void __init load_ucode_bsp(void)
116{
117 int vendor;
118 unsigned int family;
119
120 if (check_loader_disabled_bsp())
121 return;
122
123 if (!have_cpuid_p())
124 return;
125
99f925ce
BP
126 vendor = x86_cpuid_vendor();
127 family = x86_cpuid_family();
fe055896
BP
128
129 switch (vendor) {
130 case X86_VENDOR_INTEL:
131 if (family >= 6)
132 load_ucode_intel_bsp();
133 break;
134 case X86_VENDOR_AMD:
135 if (family >= 0x10)
136 load_ucode_amd_bsp(family);
137 break;
138 default:
139 break;
140 }
141}
142
143static bool check_loader_disabled_ap(void)
144{
145#ifdef CONFIG_X86_32
146 return *((bool *)__pa_nodebug(&dis_ucode_ldr));
147#else
148 return dis_ucode_ldr;
149#endif
150}
151
152void load_ucode_ap(void)
153{
154 int vendor, family;
155
156 if (check_loader_disabled_ap())
157 return;
158
159 if (!have_cpuid_p())
160 return;
161
99f925ce
BP
162 vendor = x86_cpuid_vendor();
163 family = x86_cpuid_family();
fe055896
BP
164
165 switch (vendor) {
166 case X86_VENDOR_INTEL:
167 if (family >= 6)
168 load_ucode_intel_ap();
169 break;
170 case X86_VENDOR_AMD:
171 if (family >= 0x10)
b3763a67 172 load_ucode_amd_ap(family);
fe055896
BP
173 break;
174 default:
175 break;
176 }
177}
178
4b703305 179static int __init save_microcode_in_initrd(void)
fe055896
BP
180{
181 struct cpuinfo_x86 *c = &boot_cpu_data;
182
183 switch (c->x86_vendor) {
184 case X86_VENDOR_INTEL:
185 if (c->x86 >= 6)
fa6788b8 186 return save_microcode_in_initrd_intel();
fe055896
BP
187 break;
188 case X86_VENDOR_AMD:
189 if (c->x86 >= 0x10)
b3763a67 190 return save_microcode_in_initrd_amd(c->x86);
fe055896
BP
191 break;
192 default:
193 break;
194 }
195
fa6788b8 196 return -EINVAL;
fe055896
BP
197}
198
199void reload_early_microcode(void)
200{
201 int vendor, family;
202
99f925ce
BP
203 vendor = x86_cpuid_vendor();
204 family = x86_cpuid_family();
fe055896
BP
205
206 switch (vendor) {
207 case X86_VENDOR_INTEL:
208 if (family >= 6)
209 reload_ucode_intel();
210 break;
211 case X86_VENDOR_AMD:
212 if (family >= 0x10)
213 reload_ucode_amd();
214 break;
215 default:
216 break;
217 }
218}
219
871b72dd
DA
220static void collect_cpu_info_local(void *arg)
221{
222 struct cpu_info_ctx *ctx = arg;
223
224 ctx->err = microcode_ops->collect_cpu_info(smp_processor_id(),
225 ctx->cpu_sig);
226}
227
228static int collect_cpu_info_on_target(int cpu, struct cpu_signature *cpu_sig)
229{
230 struct cpu_info_ctx ctx = { .cpu_sig = cpu_sig, .err = 0 };
231 int ret;
232
233 ret = smp_call_function_single(cpu, collect_cpu_info_local, &ctx, 1);
234 if (!ret)
235 ret = ctx.err;
236
237 return ret;
238}
239
240static int collect_cpu_info(int cpu)
241{
242 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
243 int ret;
244
245 memset(uci, 0, sizeof(*uci));
246
247 ret = collect_cpu_info_on_target(cpu, &uci->cpu_sig);
248 if (!ret)
249 uci->valid = 1;
250
251 return ret;
252}
253
254struct apply_microcode_ctx {
255 int err;
256};
257
258static void apply_microcode_local(void *arg)
259{
260 struct apply_microcode_ctx *ctx = arg;
261
262 ctx->err = microcode_ops->apply_microcode(smp_processor_id());
263}
264
265static int apply_microcode_on_target(int cpu)
266{
267 struct apply_microcode_ctx ctx = { .err = 0 };
268 int ret;
269
270 ret = smp_call_function_single(cpu, apply_microcode_local, &ctx, 1);
271 if (!ret)
272 ret = ctx.err;
273
274 return ret;
275}
276
3e135d88 277#ifdef CONFIG_MICROCODE_OLD_INTERFACE
a0a29b62 278static int do_microcode_update(const void __user *buf, size_t size)
3e135d88 279{
3e135d88 280 int error = 0;
3e135d88 281 int cpu;
6f66cbc6 282
a0a29b62
DA
283 for_each_online_cpu(cpu) {
284 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
871b72dd 285 enum ucode_state ustate;
a0a29b62
DA
286
287 if (!uci->valid)
288 continue;
6f66cbc6 289
871b72dd
DA
290 ustate = microcode_ops->request_microcode_user(cpu, buf, size);
291 if (ustate == UCODE_ERROR) {
292 error = -1;
293 break;
294 } else if (ustate == UCODE_OK)
295 apply_microcode_on_target(cpu);
3e135d88 296 }
871b72dd 297
3e135d88
PO
298 return error;
299}
300
3f10940e 301static int microcode_open(struct inode *inode, struct file *file)
3e135d88 302{
3f10940e 303 return capable(CAP_SYS_RAWIO) ? nonseekable_open(inode, file) : -EPERM;
3e135d88
PO
304}
305
d33dcb9e
PO
306static ssize_t microcode_write(struct file *file, const char __user *buf,
307 size_t len, loff_t *ppos)
3e135d88 308{
871b72dd 309 ssize_t ret = -EINVAL;
3e135d88 310
4481374c 311 if ((len >> PAGE_SHIFT) > totalram_pages) {
f58e1f53 312 pr_err("too much data (max %ld pages)\n", totalram_pages);
871b72dd 313 return ret;
3e135d88
PO
314 }
315
316 get_online_cpus();
317 mutex_lock(&microcode_mutex);
318
871b72dd 319 if (do_microcode_update(buf, len) == 0)
3e135d88
PO
320 ret = (ssize_t)len;
321
e3e45c01
SE
322 if (ret > 0)
323 perf_check_microcode();
324
3e135d88
PO
325 mutex_unlock(&microcode_mutex);
326 put_online_cpus();
327
328 return ret;
329}
330
331static const struct file_operations microcode_fops = {
871b72dd
DA
332 .owner = THIS_MODULE,
333 .write = microcode_write,
334 .open = microcode_open,
6038f373 335 .llseek = no_llseek,
3e135d88
PO
336};
337
338static struct miscdevice microcode_dev = {
871b72dd
DA
339 .minor = MICROCODE_MINOR,
340 .name = "microcode",
e454cea2 341 .nodename = "cpu/microcode",
871b72dd 342 .fops = &microcode_fops,
3e135d88
PO
343};
344
d33dcb9e 345static int __init microcode_dev_init(void)
3e135d88
PO
346{
347 int error;
348
349 error = misc_register(&microcode_dev);
350 if (error) {
f58e1f53 351 pr_err("can't misc_register on minor=%d\n", MICROCODE_MINOR);
3e135d88
PO
352 return error;
353 }
354
355 return 0;
356}
357
bd399063 358static void __exit microcode_dev_exit(void)
3e135d88
PO
359{
360 misc_deregister(&microcode_dev);
361}
3e135d88 362#else
4bae1967
IM
363#define microcode_dev_init() 0
364#define microcode_dev_exit() do { } while (0)
3e135d88
PO
365#endif
366
367/* fake device for request_firmware */
4bae1967 368static struct platform_device *microcode_pdev;
3e135d88 369
871b72dd 370static int reload_for_cpu(int cpu)
af5c820a 371{
871b72dd 372 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
4dbf32c3 373 enum ucode_state ustate;
af5c820a
RR
374 int err = 0;
375
4dbf32c3
BP
376 if (!uci->valid)
377 return err;
871b72dd 378
48e30685 379 ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev, true);
4dbf32c3
BP
380 if (ustate == UCODE_OK)
381 apply_microcode_on_target(cpu);
382 else
383 if (ustate == UCODE_ERROR)
384 err = -EINVAL;
af5c820a
RR
385 return err;
386}
387
8a25a2fd
KS
388static ssize_t reload_store(struct device *dev,
389 struct device_attribute *attr,
871b72dd 390 const char *buf, size_t size)
3e135d88 391{
871b72dd 392 unsigned long val;
c9fc3f77
BP
393 int cpu;
394 ssize_t ret = 0, tmp_ret;
395
e826abd5
SK
396 ret = kstrtoul(buf, 0, &val);
397 if (ret)
398 return ret;
871b72dd 399
c9fc3f77
BP
400 if (val != 1)
401 return size;
402
403 get_online_cpus();
c93dc84c 404 mutex_lock(&microcode_mutex);
c9fc3f77
BP
405 for_each_online_cpu(cpu) {
406 tmp_ret = reload_for_cpu(cpu);
407 if (tmp_ret != 0)
408 pr_warn("Error reloading microcode on CPU %d\n", cpu);
409
410 /* save retval of the first encountered reload error */
411 if (!ret)
412 ret = tmp_ret;
3e135d88 413 }
c93dc84c
PZ
414 if (!ret)
415 perf_check_microcode();
416 mutex_unlock(&microcode_mutex);
c9fc3f77 417 put_online_cpus();
871b72dd
DA
418
419 if (!ret)
420 ret = size;
421
422 return ret;
3e135d88
PO
423}
424
8a25a2fd
KS
425static ssize_t version_show(struct device *dev,
426 struct device_attribute *attr, char *buf)
3e135d88
PO
427{
428 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
429
d45de409 430 return sprintf(buf, "0x%x\n", uci->cpu_sig.rev);
3e135d88
PO
431}
432
8a25a2fd
KS
433static ssize_t pf_show(struct device *dev,
434 struct device_attribute *attr, char *buf)
3e135d88
PO
435{
436 struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
437
d45de409 438 return sprintf(buf, "0x%x\n", uci->cpu_sig.pf);
3e135d88
PO
439}
440
8a25a2fd
KS
441static DEVICE_ATTR(reload, 0200, NULL, reload_store);
442static DEVICE_ATTR(version, 0400, version_show, NULL);
443static DEVICE_ATTR(processor_flags, 0400, pf_show, NULL);
3e135d88
PO
444
445static struct attribute *mc_default_attrs[] = {
8a25a2fd
KS
446 &dev_attr_version.attr,
447 &dev_attr_processor_flags.attr,
3e135d88
PO
448 NULL
449};
450
451static struct attribute_group mc_attr_group = {
871b72dd
DA
452 .attrs = mc_default_attrs,
453 .name = "microcode",
3e135d88
PO
454};
455
871b72dd 456static void microcode_fini_cpu(int cpu)
d45de409 457{
d45de409 458 microcode_ops->microcode_fini_cpu(cpu);
280a9ca5
DA
459}
460
871b72dd 461static enum ucode_state microcode_resume_cpu(int cpu)
d45de409 462{
bb9d3e47
BP
463 if (apply_microcode_on_target(cpu))
464 return UCODE_ERROR;
871b72dd 465
6b14b818
BP
466 pr_debug("CPU%d updated upon resume\n", cpu);
467
871b72dd 468 return UCODE_OK;
d45de409
DA
469}
470
48e30685 471static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw)
d45de409 472{
871b72dd 473 enum ucode_state ustate;
9cd4d78e
FY
474 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
475
43858f57 476 if (uci->valid)
9cd4d78e 477 return UCODE_OK;
d45de409 478
871b72dd
DA
479 if (collect_cpu_info(cpu))
480 return UCODE_ERROR;
d45de409 481
871b72dd
DA
482 /* --dimm. Trigger a delayed update? */
483 if (system_state != SYSTEM_RUNNING)
484 return UCODE_NFOUND;
d45de409 485
48e30685
BP
486 ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev,
487 refresh_fw);
d45de409 488
871b72dd 489 if (ustate == UCODE_OK) {
f58e1f53 490 pr_debug("CPU%d updated upon init\n", cpu);
871b72dd 491 apply_microcode_on_target(cpu);
d45de409
DA
492 }
493
871b72dd 494 return ustate;
d45de409
DA
495}
496
871b72dd 497static enum ucode_state microcode_update_cpu(int cpu)
d45de409 498{
871b72dd 499 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
d45de409 500
2f99f5c8 501 if (uci->valid)
bb9d3e47 502 return microcode_resume_cpu(cpu);
d45de409 503
48e30685 504 return microcode_init_cpu(cpu, false);
d45de409
DA
505}
506
8a25a2fd 507static int mc_device_add(struct device *dev, struct subsys_interface *sif)
3e135d88 508{
8a25a2fd 509 int err, cpu = dev->id;
3e135d88
PO
510
511 if (!cpu_online(cpu))
512 return 0;
513
f58e1f53 514 pr_debug("CPU%d added\n", cpu);
3e135d88 515
8a25a2fd 516 err = sysfs_create_group(&dev->kobj, &mc_attr_group);
3e135d88
PO
517 if (err)
518 return err;
519
48e30685 520 if (microcode_init_cpu(cpu, true) == UCODE_ERROR)
6c53cbfc 521 return -EINVAL;
af5c820a
RR
522
523 return err;
3e135d88
PO
524}
525
71db87ba 526static void mc_device_remove(struct device *dev, struct subsys_interface *sif)
3e135d88 527{
8a25a2fd 528 int cpu = dev->id;
3e135d88
PO
529
530 if (!cpu_online(cpu))
71db87ba 531 return;
3e135d88 532
f58e1f53 533 pr_debug("CPU%d removed\n", cpu);
d45de409 534 microcode_fini_cpu(cpu);
8a25a2fd 535 sysfs_remove_group(&dev->kobj, &mc_attr_group);
3e135d88
PO
536}
537
8a25a2fd
KS
538static struct subsys_interface mc_cpu_interface = {
539 .name = "microcode",
540 .subsys = &cpu_subsys,
541 .add_dev = mc_device_add,
542 .remove_dev = mc_device_remove,
f3c6ea1b
RW
543};
544
545/**
546 * mc_bp_resume - Update boot CPU microcode during resume.
547 */
548static void mc_bp_resume(void)
3e135d88 549{
f3c6ea1b 550 int cpu = smp_processor_id();
871b72dd 551 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
3e135d88 552
871b72dd
DA
553 if (uci->valid && uci->mc)
554 microcode_ops->apply_microcode(cpu);
fb86b973 555 else if (!uci->mc)
fbae4ba8 556 reload_early_microcode();
3e135d88
PO
557}
558
f3c6ea1b
RW
559static struct syscore_ops mc_syscore_ops = {
560 .resume = mc_bp_resume,
3e135d88
PO
561};
562
29bd7fbc 563static int mc_cpu_online(unsigned int cpu)
3e135d88 564{
8a25a2fd 565 struct device *dev;
3e135d88 566
8a25a2fd 567 dev = get_cpu_device(cpu);
29bd7fbc
SAS
568 microcode_update_cpu(cpu);
569 pr_debug("CPU%d added\n", cpu);
09c3f0d8 570
29bd7fbc
SAS
571 if (sysfs_create_group(&dev->kobj, &mc_attr_group))
572 pr_err("Failed to create group for CPU%d\n", cpu);
573 return 0;
574}
09c3f0d8 575
29bd7fbc
SAS
576static int mc_cpu_down_prep(unsigned int cpu)
577{
578 struct device *dev;
70989449 579
29bd7fbc
SAS
580 dev = get_cpu_device(cpu);
581 /* Suspend is in progress, only remove the interface */
582 sysfs_remove_group(&dev->kobj, &mc_attr_group);
583 pr_debug("CPU%d removed\n", cpu);
70989449
SB
584 /*
585 * When a CPU goes offline, don't free up or invalidate the copy of
586 * the microcode in kernel memory, so that we can reuse it when the
587 * CPU comes back online without unnecessarily requesting the userspace
588 * for it again.
589 */
29bd7fbc 590 return 0;
3e135d88
PO
591}
592
3d8986bc
BP
593static struct attribute *cpu_root_microcode_attrs[] = {
594 &dev_attr_reload.attr,
595 NULL
596};
597
598static struct attribute_group cpu_root_microcode_group = {
599 .name = "microcode",
600 .attrs = cpu_root_microcode_attrs,
601};
602
9a2bc335 603int __init microcode_init(void)
3e135d88 604{
9a2bc335 605 struct cpuinfo_x86 *c = &boot_cpu_data;
3e135d88
PO
606 int error;
607
84aba677 608 if (dis_ucode_ldr)
da63865a 609 return -EINVAL;
65cef131 610
18dbc916
DA
611 if (c->x86_vendor == X86_VENDOR_INTEL)
612 microcode_ops = init_intel_microcode();
82b07865 613 else if (c->x86_vendor == X86_VENDOR_AMD)
18dbc916 614 microcode_ops = init_amd_microcode();
283c1f25 615 else
f58e1f53 616 pr_err("no support for this CPU vendor\n");
283c1f25
AH
617
618 if (!microcode_ops)
18dbc916 619 return -ENODEV;
3e135d88 620
3e135d88
PO
621 microcode_pdev = platform_device_register_simple("microcode", -1,
622 NULL, 0);
bd399063 623 if (IS_ERR(microcode_pdev))
3e135d88 624 return PTR_ERR(microcode_pdev);
3e135d88
PO
625
626 get_online_cpus();
871b72dd
DA
627 mutex_lock(&microcode_mutex);
628
8a25a2fd 629 error = subsys_interface_register(&mc_cpu_interface);
c93dc84c
PZ
630 if (!error)
631 perf_check_microcode();
871b72dd 632 mutex_unlock(&microcode_mutex);
3e135d88 633 put_online_cpus();
871b72dd 634
bd399063
SB
635 if (error)
636 goto out_pdev;
3e135d88 637
3d8986bc
BP
638 error = sysfs_create_group(&cpu_subsys.dev_root->kobj,
639 &cpu_root_microcode_group);
640
641 if (error) {
642 pr_err("Error creating microcode group!\n");
643 goto out_driver;
644 }
645
871b72dd
DA
646 error = microcode_dev_init();
647 if (error)
3d8986bc 648 goto out_ucode_group;
871b72dd 649
f3c6ea1b 650 register_syscore_ops(&mc_syscore_ops);
29bd7fbc
SAS
651 cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "x86/microcode:online",
652 mc_cpu_online, mc_cpu_down_prep);
8d86f390 653
5879a267 654 pr_info("Microcode Update Driver: v%s.", MICROCODE_VERSION);
8d86f390 655
3e135d88 656 return 0;
bd399063 657
3d8986bc
BP
658 out_ucode_group:
659 sysfs_remove_group(&cpu_subsys.dev_root->kobj,
660 &cpu_root_microcode_group);
661
662 out_driver:
bd399063
SB
663 get_online_cpus();
664 mutex_lock(&microcode_mutex);
665
ff4b8a57 666 subsys_interface_unregister(&mc_cpu_interface);
bd399063
SB
667
668 mutex_unlock(&microcode_mutex);
669 put_online_cpus();
670
3d8986bc 671 out_pdev:
bd399063
SB
672 platform_device_unregister(microcode_pdev);
673 return error;
674
3e135d88 675}
4b703305 676fs_initcall(save_microcode_in_initrd);
2d5be37d 677late_initcall(microcode_init);