Commit 77f9b1fb6244878ab4aab342a814880d17253b75
1 parent
94060ff278
Exists in
v2017.01-smarct4x
and in
37 other branches
x86: ivybridge: Perform Intel microcode update on boot
Microcode updates are stored in the device tree. Work through these and apply any that are needed. Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 6 changed files with 179 additions and 0 deletions Side-by-side Diff
arch/x86/cpu/ivybridge/Makefile
arch/x86/cpu/ivybridge/cpu.c
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | #include <asm/post.h> |
22 | 22 | #include <asm/processor.h> |
23 | 23 | #include <asm/arch/model_206ax.h> |
24 | +#include <asm/arch/microcode.h> | |
24 | 25 | #include <asm/arch/pch.h> |
25 | 26 | |
26 | 27 | DECLARE_GLOBAL_DATA_PTR; |
... | ... | @@ -198,6 +199,10 @@ |
198 | 199 | /* Halt if there was a built in self test failure */ |
199 | 200 | ret = report_bist_failure(); |
200 | 201 | if (ret) |
202 | + return ret; | |
203 | + | |
204 | + ret = microcode_update_intel(); | |
205 | + if (ret && ret != -ENOENT && ret != -EEXIST) | |
201 | 206 | return ret; |
202 | 207 | |
203 | 208 | /* Print processor name */ |
arch/x86/cpu/ivybridge/microcode_intel.c
1 | +/* | |
2 | + * Copyright (c) 2014 Google, Inc | |
3 | + * Copyright (C) 2000 Ronald G. Minnich | |
4 | + * | |
5 | + * Microcode update for Intel PIII and later CPUs | |
6 | + * | |
7 | + * SPDX-License-Identifier: GPL-2.0 | |
8 | + */ | |
9 | + | |
10 | +#include <common.h> | |
11 | +#include <errno.h> | |
12 | +#include <fdtdec.h> | |
13 | +#include <libfdt.h> | |
14 | +#include <asm/cpu.h> | |
15 | +#include <asm/msr.h> | |
16 | +#include <asm/processor.h> | |
17 | + | |
18 | +/** | |
19 | + * struct microcode_update - standard microcode header from Intel | |
20 | + * | |
21 | + * We read this information out of the device tree and use it to determine | |
22 | + * whether the update is applicable or not. We also use the same structure | |
23 | + * to read information from the CPU. | |
24 | + */ | |
25 | +struct microcode_update { | |
26 | + uint header_version; | |
27 | + uint update_revision; | |
28 | + uint date_code; | |
29 | + uint processor_signature; | |
30 | + uint checksum; | |
31 | + uint loader_revision; | |
32 | + uint processor_flags; | |
33 | + const void *data; | |
34 | + int size; | |
35 | +}; | |
36 | + | |
37 | +static int microcode_decode_node(const void *blob, int node, | |
38 | + struct microcode_update *update) | |
39 | +{ | |
40 | + update->data = fdt_getprop(blob, node, "data", &update->size); | |
41 | + if (!update->data) | |
42 | + return -EINVAL; | |
43 | + | |
44 | + update->header_version = fdtdec_get_int(blob, node, | |
45 | + "intel,header-version", 0); | |
46 | + update->update_revision = fdtdec_get_int(blob, node, | |
47 | + "intel,update-revision", 0); | |
48 | + update->date_code = fdtdec_get_int(blob, node, | |
49 | + "intel,date-code", 0); | |
50 | + update->processor_signature = fdtdec_get_int(blob, node, | |
51 | + "intel.processor-signature", 0); | |
52 | + update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0); | |
53 | + update->loader_revision = fdtdec_get_int(blob, node, | |
54 | + "loader-revision", 0); | |
55 | + update->processor_flags = fdtdec_get_int(blob, node, | |
56 | + "processor-flags", 0); | |
57 | + | |
58 | + return 0; | |
59 | +} | |
60 | + | |
61 | +static uint32_t microcode_read_rev(void) | |
62 | +{ | |
63 | + /* | |
64 | + * Some Intel CPUs can be very finicky about the CPUID sequence used. | |
65 | + * So this is implemented in assembly so that it works reliably. | |
66 | + */ | |
67 | + uint32_t low, high; | |
68 | + | |
69 | + asm volatile ( | |
70 | + "xorl %%eax, %%eax\n" | |
71 | + "xorl %%edx, %%edx\n" | |
72 | + "movl $0x8b, %%ecx\n" | |
73 | + "wrmsr\n" | |
74 | + "movl $0x01, %%eax\n" | |
75 | + "cpuid\n" | |
76 | + "movl $0x8b, %%ecx\n" | |
77 | + "rdmsr\n" | |
78 | + : /* outputs */ | |
79 | + "=a" (low), "=d" (high) | |
80 | + : /* inputs */ | |
81 | + : /* clobbers */ | |
82 | + "ebx", "ecx" | |
83 | + ); | |
84 | + | |
85 | + return high; | |
86 | +} | |
87 | + | |
88 | +static void microcode_read_cpu(struct microcode_update *cpu) | |
89 | +{ | |
90 | + /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ | |
91 | + unsigned int x86_model, x86_family; | |
92 | + struct cpuid_result result; | |
93 | + uint32_t low, high; | |
94 | + | |
95 | + wrmsr(0x8b, 0, 0); | |
96 | + result = cpuid(1); | |
97 | + rdmsr(0x8b, low, cpu->update_revision); | |
98 | + x86_model = (result.eax >> 4) & 0x0f; | |
99 | + x86_family = (result.eax >> 8) & 0x0f; | |
100 | + cpu->processor_signature = result.eax; | |
101 | + | |
102 | + cpu->processor_flags = 0; | |
103 | + if ((x86_model >= 5) || (x86_family > 6)) { | |
104 | + rdmsr(0x17, low, high); | |
105 | + cpu->processor_flags = 1 << ((high >> 18) & 7); | |
106 | + } | |
107 | + debug("microcode: sig=%#x pf=%#x revision=%#x\n", | |
108 | + cpu->processor_signature, cpu->processor_flags, | |
109 | + cpu->update_revision); | |
110 | +} | |
111 | + | |
112 | +/* Get a microcode update from the device tree and apply it */ | |
113 | +int microcode_update_intel(void) | |
114 | +{ | |
115 | + struct microcode_update cpu, update; | |
116 | + const void *blob = gd->fdt_blob; | |
117 | + int count; | |
118 | + int node; | |
119 | + int ret; | |
120 | + | |
121 | + microcode_read_cpu(&cpu); | |
122 | + node = 0; | |
123 | + count = 0; | |
124 | + do { | |
125 | + node = fdtdec_next_compatible(blob, node, | |
126 | + COMPAT_INTEL_MICROCODE); | |
127 | + if (node < 0) { | |
128 | + debug("%s: Found %d updates\n", __func__, count); | |
129 | + return count ? 0 : -ENOENT; | |
130 | + } | |
131 | + | |
132 | + ret = microcode_decode_node(blob, node, &update); | |
133 | + if (ret) { | |
134 | + debug("%s: Unable to decode update: %d\n", __func__, | |
135 | + ret); | |
136 | + return ret; | |
137 | + } | |
138 | + if (update.processor_signature == cpu.processor_signature && | |
139 | + (update.processor_flags & cpu.processor_flags)) { | |
140 | + debug("%s: Update already exists\n", __func__); | |
141 | + return -EEXIST; | |
142 | + } | |
143 | + | |
144 | + wrmsr(0x79, (ulong)update.data, 0); | |
145 | + debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n", | |
146 | + microcode_read_rev(), update.date_code & 0xffff, | |
147 | + (update.date_code >> 24) & 0xff, | |
148 | + (update.date_code >> 16) & 0xff); | |
149 | + count++; | |
150 | + } while (1); | |
151 | +} |
arch/x86/include/asm/arch-ivybridge/microcode.h
1 | +/* | |
2 | + * Copyright (c) 2015 Google, Inc | |
3 | + * | |
4 | + * SPDX-License-Identifier: GPL-2.0+ | |
5 | + */ | |
6 | + | |
7 | +#ifndef __ASM_ARCH_MICROCODE_H | |
8 | +#define __ASM_ARCH_MICROCODE_H | |
9 | + | |
10 | +/** | |
11 | + * microcode_update_intel() - Apply microcode updates | |
12 | + * | |
13 | + * Applies any microcode updates in the device tree. | |
14 | + * | |
15 | + * @return 0 if OK, -EEXIST if the updates were already applied, -ENOENT if | |
16 | + * not updates were found, -EINVAL if an update was invalid | |
17 | + */ | |
18 | +int microcode_update_intel(void); | |
19 | + | |
20 | +#endif |
include/fdtdec.h
... | ... | @@ -118,6 +118,7 @@ |
118 | 118 | COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */ |
119 | 119 | COMPAT_PARADE_PS8625, /* Parade PS8622 EDP->LVDS bridge */ |
120 | 120 | COMPAT_INTEL_LPC, /* Intel Low Pin Count I/F */ |
121 | + COMPAT_INTEL_MICROCODE, /* Intel microcode update */ | |
121 | 122 | |
122 | 123 | COMPAT_COUNT, |
123 | 124 | }; |
lib/fdtdec.c
... | ... | @@ -73,6 +73,7 @@ |
73 | 73 | COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"), |
74 | 74 | COMPAT(PARADE_PS8625, "parade,ps8625"), |
75 | 75 | COMPAT(COMPAT_INTEL_LPC, "intel,lpc"), |
76 | + COMPAT(INTEL_MICROCODE, "intel,microcode"), | |
76 | 77 | }; |
77 | 78 | |
78 | 79 | const char *fdtdec_get_compatible(enum fdt_compat_id id) |