diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-06 18:02:14 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-06 18:02:14 -0500 |
commit | 82406da4a6998a0c98db0c5afb1695f97889bf79 (patch) | |
tree | 0e56593781cfe0a35143bc51525f803988ae28b1 /arch | |
parent | 7a222156bcf010dbf77fe7d803c375bcc4a51160 (diff) | |
parent | 304fb45374918b166233855bcf498b02586afd80 (diff) |
Merge branch 'x86-microcode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'x86-microcode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86, microcode, AMD: Update copyrights
x86, microcode, AMD: Exit early on success
x86, microcode, AMD: Simplify ucode verification
x86, microcode, AMD: Add a reusable buffer
x86, microcode, AMD: Add a vendor-specific exit function
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/microcode.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_amd.c | 209 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_core.c | 5 |
3 files changed, 121 insertions, 95 deletions
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h index 24215072d0e1..4ebe157bf73d 100644 --- a/arch/x86/include/asm/microcode.h +++ b/arch/x86/include/asm/microcode.h | |||
@@ -48,6 +48,7 @@ static inline struct microcode_ops * __init init_intel_microcode(void) | |||
48 | 48 | ||
49 | #ifdef CONFIG_MICROCODE_AMD | 49 | #ifdef CONFIG_MICROCODE_AMD |
50 | extern struct microcode_ops * __init init_amd_microcode(void); | 50 | extern struct microcode_ops * __init init_amd_microcode(void); |
51 | extern void __exit exit_amd_microcode(void); | ||
51 | 52 | ||
52 | static inline void get_ucode_data(void *to, const u8 *from, size_t n) | 53 | static inline void get_ucode_data(void *to, const u8 *from, size_t n) |
53 | { | 54 | { |
@@ -59,6 +60,7 @@ static inline struct microcode_ops * __init init_amd_microcode(void) | |||
59 | { | 60 | { |
60 | return NULL; | 61 | return NULL; |
61 | } | 62 | } |
63 | static inline void __exit exit_amd_microcode(void) {} | ||
62 | #endif | 64 | #endif |
63 | 65 | ||
64 | #endif /* _ASM_X86_MICROCODE_H */ | 66 | #endif /* _ASM_X86_MICROCODE_H */ |
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index d494799aafcd..fe86493f3ed1 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c | |||
@@ -1,14 +1,18 @@ | |||
1 | /* | 1 | /* |
2 | * AMD CPU Microcode Update Driver for Linux | 2 | * AMD CPU Microcode Update Driver for Linux |
3 | * Copyright (C) 2008 Advanced Micro Devices Inc. | 3 | * Copyright (C) 2008-2011 Advanced Micro Devices Inc. |
4 | * | 4 | * |
5 | * Author: Peter Oruba <peter.oruba@amd.com> | 5 | * Author: Peter Oruba <peter.oruba@amd.com> |
6 | * | 6 | * |
7 | * Based on work by: | 7 | * Based on work by: |
8 | * Tigran Aivazian <tigran@aivazian.fsnet.co.uk> | 8 | * Tigran Aivazian <tigran@aivazian.fsnet.co.uk> |
9 | * | 9 | * |
10 | * This driver allows to upgrade microcode on AMD | 10 | * Maintainers: |
11 | * family 0x10 and 0x11 processors. | 11 | * Andreas Herrmann <andreas.herrmann3@amd.com> |
12 | * Borislav Petkov <borislav.petkov@amd.com> | ||
13 | * | ||
14 | * This driver allows to upgrade microcode on F10h AMD | ||
15 | * CPUs and later. | ||
12 | * | 16 | * |
13 | * Licensed under the terms of the GNU General Public | 17 | * Licensed under the terms of the GNU General Public |
14 | * License version 2. See file COPYING for details. | 18 | * License version 2. See file COPYING for details. |
@@ -71,6 +75,9 @@ struct microcode_amd { | |||
71 | 75 | ||
72 | static struct equiv_cpu_entry *equiv_cpu_table; | 76 | static struct equiv_cpu_entry *equiv_cpu_table; |
73 | 77 | ||
78 | /* page-sized ucode patch buffer */ | ||
79 | void *patch; | ||
80 | |||
74 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) | 81 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) |
75 | { | 82 | { |
76 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 83 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
@@ -86,27 +93,76 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) | |||
86 | return 0; | 93 | return 0; |
87 | } | 94 | } |
88 | 95 | ||
89 | static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr, | 96 | static unsigned int verify_ucode_size(int cpu, u32 patch_size, |
90 | int rev) | 97 | unsigned int size) |
91 | { | 98 | { |
92 | unsigned int current_cpu_id; | 99 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
93 | u16 equiv_cpu_id = 0; | 100 | u32 max_size; |
94 | unsigned int i = 0; | 101 | |
102 | #define F1XH_MPB_MAX_SIZE 2048 | ||
103 | #define F14H_MPB_MAX_SIZE 1824 | ||
104 | #define F15H_MPB_MAX_SIZE 4096 | ||
105 | |||
106 | switch (c->x86) { | ||
107 | case 0x14: | ||
108 | max_size = F14H_MPB_MAX_SIZE; | ||
109 | break; | ||
110 | case 0x15: | ||
111 | max_size = F15H_MPB_MAX_SIZE; | ||
112 | break; | ||
113 | default: | ||
114 | max_size = F1XH_MPB_MAX_SIZE; | ||
115 | break; | ||
116 | } | ||
117 | |||
118 | if (patch_size > min_t(u32, size, max_size)) { | ||
119 | pr_err("patch size mismatch\n"); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | return patch_size; | ||
124 | } | ||
125 | |||
126 | static u16 find_equiv_id(void) | ||
127 | { | ||
128 | unsigned int current_cpu_id, i = 0; | ||
95 | 129 | ||
96 | BUG_ON(equiv_cpu_table == NULL); | 130 | BUG_ON(equiv_cpu_table == NULL); |
131 | |||
97 | current_cpu_id = cpuid_eax(0x00000001); | 132 | current_cpu_id = cpuid_eax(0x00000001); |
98 | 133 | ||
99 | while (equiv_cpu_table[i].installed_cpu != 0) { | 134 | while (equiv_cpu_table[i].installed_cpu != 0) { |
100 | if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { | 135 | if (current_cpu_id == equiv_cpu_table[i].installed_cpu) |
101 | equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; | 136 | return equiv_cpu_table[i].equiv_cpu; |
102 | break; | 137 | |
103 | } | ||
104 | i++; | 138 | i++; |
105 | } | 139 | } |
140 | return 0; | ||
141 | } | ||
106 | 142 | ||
143 | /* | ||
144 | * we signal a good patch is found by returning its size > 0 | ||
145 | */ | ||
146 | static int get_matching_microcode(int cpu, const u8 *ucode_ptr, | ||
147 | unsigned int leftover_size, int rev, | ||
148 | unsigned int *current_size) | ||
149 | { | ||
150 | struct microcode_header_amd *mc_hdr; | ||
151 | unsigned int actual_size; | ||
152 | u16 equiv_cpu_id; | ||
153 | |||
154 | /* size of the current patch we're staring at */ | ||
155 | *current_size = *(u32 *)(ucode_ptr + 4) + SECTION_HDR_SIZE; | ||
156 | |||
157 | equiv_cpu_id = find_equiv_id(); | ||
107 | if (!equiv_cpu_id) | 158 | if (!equiv_cpu_id) |
108 | return 0; | 159 | return 0; |
109 | 160 | ||
161 | /* | ||
162 | * let's look at the patch header itself now | ||
163 | */ | ||
164 | mc_hdr = (struct microcode_header_amd *)(ucode_ptr + SECTION_HDR_SIZE); | ||
165 | |||
110 | if (mc_hdr->processor_rev_id != equiv_cpu_id) | 166 | if (mc_hdr->processor_rev_id != equiv_cpu_id) |
111 | return 0; | 167 | return 0; |
112 | 168 | ||
@@ -120,7 +176,20 @@ static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr, | |||
120 | if (mc_hdr->patch_id <= rev) | 176 | if (mc_hdr->patch_id <= rev) |
121 | return 0; | 177 | return 0; |
122 | 178 | ||
123 | return 1; | 179 | /* |
180 | * now that the header looks sane, verify its size | ||
181 | */ | ||
182 | actual_size = verify_ucode_size(cpu, *current_size, leftover_size); | ||
183 | if (!actual_size) | ||
184 | return 0; | ||
185 | |||
186 | /* clear the patch buffer */ | ||
187 | memset(patch, 0, PAGE_SIZE); | ||
188 | |||
189 | /* all looks ok, get the binary patch */ | ||
190 | get_ucode_data(patch, ucode_ptr + SECTION_HDR_SIZE, actual_size); | ||
191 | |||
192 | return actual_size; | ||
124 | } | 193 | } |
125 | 194 | ||
126 | static int apply_microcode_amd(int cpu) | 195 | static int apply_microcode_amd(int cpu) |
@@ -155,63 +224,6 @@ static int apply_microcode_amd(int cpu) | |||
155 | return 0; | 224 | return 0; |
156 | } | 225 | } |
157 | 226 | ||
158 | static unsigned int verify_ucode_size(int cpu, const u8 *buf, unsigned int size) | ||
159 | { | ||
160 | struct cpuinfo_x86 *c = &cpu_data(cpu); | ||
161 | u32 max_size, actual_size; | ||
162 | |||
163 | #define F1XH_MPB_MAX_SIZE 2048 | ||
164 | #define F14H_MPB_MAX_SIZE 1824 | ||
165 | #define F15H_MPB_MAX_SIZE 4096 | ||
166 | |||
167 | switch (c->x86) { | ||
168 | case 0x14: | ||
169 | max_size = F14H_MPB_MAX_SIZE; | ||
170 | break; | ||
171 | case 0x15: | ||
172 | max_size = F15H_MPB_MAX_SIZE; | ||
173 | break; | ||
174 | default: | ||
175 | max_size = F1XH_MPB_MAX_SIZE; | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | actual_size = *(u32 *)(buf + 4); | ||
180 | |||
181 | if (actual_size + SECTION_HDR_SIZE > size || actual_size > max_size) { | ||
182 | pr_err("section size mismatch\n"); | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | return actual_size; | ||
187 | } | ||
188 | |||
189 | static struct microcode_header_amd * | ||
190 | get_next_ucode(int cpu, const u8 *buf, unsigned int size, unsigned int *mc_size) | ||
191 | { | ||
192 | struct microcode_header_amd *mc = NULL; | ||
193 | unsigned int actual_size = 0; | ||
194 | |||
195 | if (*(u32 *)buf != UCODE_UCODE_TYPE) { | ||
196 | pr_err("invalid type field in container file section header\n"); | ||
197 | goto out; | ||
198 | } | ||
199 | |||
200 | actual_size = verify_ucode_size(cpu, buf, size); | ||
201 | if (!actual_size) | ||
202 | goto out; | ||
203 | |||
204 | mc = vzalloc(actual_size); | ||
205 | if (!mc) | ||
206 | goto out; | ||
207 | |||
208 | get_ucode_data(mc, buf + SECTION_HDR_SIZE, actual_size); | ||
209 | *mc_size = actual_size + SECTION_HDR_SIZE; | ||
210 | |||
211 | out: | ||
212 | return mc; | ||
213 | } | ||
214 | |||
215 | static int install_equiv_cpu_table(const u8 *buf) | 227 | static int install_equiv_cpu_table(const u8 *buf) |
216 | { | 228 | { |
217 | unsigned int *ibuf = (unsigned int *)buf; | 229 | unsigned int *ibuf = (unsigned int *)buf; |
@@ -247,36 +259,38 @@ generic_load_microcode(int cpu, const u8 *data, size_t size) | |||
247 | { | 259 | { |
248 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 260 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
249 | struct microcode_header_amd *mc_hdr = NULL; | 261 | struct microcode_header_amd *mc_hdr = NULL; |
250 | unsigned int mc_size, leftover; | 262 | unsigned int mc_size, leftover, current_size = 0; |
251 | int offset; | 263 | int offset; |
252 | const u8 *ucode_ptr = data; | 264 | const u8 *ucode_ptr = data; |
253 | void *new_mc = NULL; | 265 | void *new_mc = NULL; |
254 | unsigned int new_rev = uci->cpu_sig.rev; | 266 | unsigned int new_rev = uci->cpu_sig.rev; |
255 | enum ucode_state state = UCODE_OK; | 267 | enum ucode_state state = UCODE_ERROR; |
256 | 268 | ||
257 | offset = install_equiv_cpu_table(ucode_ptr); | 269 | offset = install_equiv_cpu_table(ucode_ptr); |
258 | if (offset < 0) { | 270 | if (offset < 0) { |
259 | pr_err("failed to create equivalent cpu table\n"); | 271 | pr_err("failed to create equivalent cpu table\n"); |
260 | return UCODE_ERROR; | 272 | goto out; |
261 | } | 273 | } |
262 | |||
263 | ucode_ptr += offset; | 274 | ucode_ptr += offset; |
264 | leftover = size - offset; | 275 | leftover = size - offset; |
265 | 276 | ||
266 | while (leftover) { | 277 | if (*(u32 *)ucode_ptr != UCODE_UCODE_TYPE) { |
267 | mc_hdr = get_next_ucode(cpu, ucode_ptr, leftover, &mc_size); | 278 | pr_err("invalid type field in container file section header\n"); |
268 | if (!mc_hdr) | 279 | goto free_table; |
269 | break; | 280 | } |
270 | 281 | ||
271 | if (get_matching_microcode(cpu, mc_hdr, new_rev)) { | 282 | while (leftover) { |
272 | vfree(new_mc); | 283 | mc_size = get_matching_microcode(cpu, ucode_ptr, leftover, |
284 | new_rev, ¤t_size); | ||
285 | if (mc_size) { | ||
286 | mc_hdr = patch; | ||
287 | new_mc = patch; | ||
273 | new_rev = mc_hdr->patch_id; | 288 | new_rev = mc_hdr->patch_id; |
274 | new_mc = mc_hdr; | 289 | goto out_ok; |
275 | } else | 290 | } |
276 | vfree(mc_hdr); | ||
277 | 291 | ||
278 | ucode_ptr += mc_size; | 292 | ucode_ptr += current_size; |
279 | leftover -= mc_size; | 293 | leftover -= current_size; |
280 | } | 294 | } |
281 | 295 | ||
282 | if (!new_mc) { | 296 | if (!new_mc) { |
@@ -284,19 +298,16 @@ generic_load_microcode(int cpu, const u8 *data, size_t size) | |||
284 | goto free_table; | 298 | goto free_table; |
285 | } | 299 | } |
286 | 300 | ||
287 | if (!leftover) { | 301 | out_ok: |
288 | vfree(uci->mc); | 302 | uci->mc = new_mc; |
289 | uci->mc = new_mc; | 303 | state = UCODE_OK; |
290 | pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n", | 304 | pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n", |
291 | cpu, uci->cpu_sig.rev, new_rev); | 305 | cpu, uci->cpu_sig.rev, new_rev); |
292 | } else { | ||
293 | vfree(new_mc); | ||
294 | state = UCODE_ERROR; | ||
295 | } | ||
296 | 306 | ||
297 | free_table: | 307 | free_table: |
298 | free_equiv_cpu_table(); | 308 | free_equiv_cpu_table(); |
299 | 309 | ||
310 | out: | ||
300 | return state; | 311 | return state; |
301 | } | 312 | } |
302 | 313 | ||
@@ -337,7 +348,6 @@ static void microcode_fini_cpu_amd(int cpu) | |||
337 | { | 348 | { |
338 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 349 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
339 | 350 | ||
340 | vfree(uci->mc); | ||
341 | uci->mc = NULL; | 351 | uci->mc = NULL; |
342 | } | 352 | } |
343 | 353 | ||
@@ -351,5 +361,14 @@ static struct microcode_ops microcode_amd_ops = { | |||
351 | 361 | ||
352 | struct microcode_ops * __init init_amd_microcode(void) | 362 | struct microcode_ops * __init init_amd_microcode(void) |
353 | { | 363 | { |
364 | patch = (void *)get_zeroed_page(GFP_KERNEL); | ||
365 | if (!patch) | ||
366 | return NULL; | ||
367 | |||
354 | return µcode_amd_ops; | 368 | return µcode_amd_ops; |
355 | } | 369 | } |
370 | |||
371 | void __exit exit_amd_microcode(void) | ||
372 | { | ||
373 | free_page((unsigned long)patch); | ||
374 | } | ||
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index 9d46f5e43b51..9302e2d0eb4b 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c | |||
@@ -563,6 +563,8 @@ module_init(microcode_init); | |||
563 | 563 | ||
564 | static void __exit microcode_exit(void) | 564 | static void __exit microcode_exit(void) |
565 | { | 565 | { |
566 | struct cpuinfo_x86 *c = &cpu_data(0); | ||
567 | |||
566 | microcode_dev_exit(); | 568 | microcode_dev_exit(); |
567 | 569 | ||
568 | unregister_hotcpu_notifier(&mc_cpu_notifier); | 570 | unregister_hotcpu_notifier(&mc_cpu_notifier); |
@@ -580,6 +582,9 @@ static void __exit microcode_exit(void) | |||
580 | 582 | ||
581 | microcode_ops = NULL; | 583 | microcode_ops = NULL; |
582 | 584 | ||
585 | if (c->x86_vendor == X86_VENDOR_AMD) | ||
586 | exit_amd_microcode(); | ||
587 | |||
583 | pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); | 588 | pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); |
584 | } | 589 | } |
585 | module_exit(microcode_exit); | 590 | module_exit(microcode_exit); |