aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/microcode_amd.c236
1 files changed, 121 insertions, 115 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index cacdc9a5ee49..5511216b4434 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -75,9 +75,6 @@ struct microcode_amd {
75 75
76static struct equiv_cpu_entry *equiv_cpu_table; 76static struct equiv_cpu_entry *equiv_cpu_table;
77 77
78/* page-sized ucode patch buffer */
79void *patch;
80
81struct ucode_patch { 78struct ucode_patch {
82 struct list_head plist; 79 struct list_head plist;
83 void *data; 80 void *data;
@@ -184,7 +181,7 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
184 return 0; 181 return 0;
185} 182}
186 183
187static unsigned int verify_ucode_size(int cpu, u32 patch_size, 184static unsigned int verify_patch_size(int cpu, u32 patch_size,
188 unsigned int size) 185 unsigned int size)
189{ 186{
190 struct cpuinfo_x86 *c = &cpu_data(cpu); 187 struct cpuinfo_x86 *c = &cpu_data(cpu);
@@ -214,73 +211,25 @@ static unsigned int verify_ucode_size(int cpu, u32 patch_size,
214 return patch_size; 211 return patch_size;
215} 212}
216 213
217/*
218 * we signal a good patch is found by returning its size > 0
219 */
220static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
221 unsigned int leftover_size, int rev,
222 unsigned int *current_size)
223{
224 struct microcode_header_amd *mc_hdr;
225 unsigned int actual_size, patch_size;
226 u16 equiv_cpu_id;
227
228 /* size of the current patch we're staring at */
229 patch_size = *(u32 *)(ucode_ptr + 4);
230 *current_size = patch_size + SECTION_HDR_SIZE;
231
232 equiv_cpu_id = find_equiv_id(cpu);
233 if (!equiv_cpu_id)
234 return 0;
235
236 /*
237 * let's look at the patch header itself now
238 */
239 mc_hdr = (struct microcode_header_amd *)(ucode_ptr + SECTION_HDR_SIZE);
240
241 if (mc_hdr->processor_rev_id != equiv_cpu_id)
242 return 0;
243
244 /* ucode might be chipset specific -- currently we don't support this */
245 if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
246 pr_err("CPU%d: chipset specific code not yet supported\n",
247 cpu);
248 return 0;
249 }
250
251 if (mc_hdr->patch_id <= rev)
252 return 0;
253
254 /*
255 * now that the header looks sane, verify its size
256 */
257 actual_size = verify_ucode_size(cpu, patch_size, leftover_size);
258 if (!actual_size)
259 return 0;
260
261 /* clear the patch buffer */
262 memset(patch, 0, PAGE_SIZE);
263
264 /* all looks ok, get the binary patch */
265 memcpy(patch, ucode_ptr + SECTION_HDR_SIZE, actual_size);
266
267 return actual_size;
268}
269
270static int apply_microcode_amd(int cpu) 214static int apply_microcode_amd(int cpu)
271{ 215{
272 u32 rev, dummy;
273 int cpu_num = raw_smp_processor_id();
274 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
275 struct microcode_amd *mc_amd = uci->mc;
276 struct cpuinfo_x86 *c = &cpu_data(cpu); 216 struct cpuinfo_x86 *c = &cpu_data(cpu);
217 struct microcode_amd *mc_amd;
218 struct ucode_cpu_info *uci;
219 struct ucode_patch *p;
220 u32 rev, dummy;
221
222 BUG_ON(raw_smp_processor_id() != cpu);
277 223
278 /* We should bind the task to the CPU */ 224 uci = ucode_cpu_info + cpu;
279 BUG_ON(cpu_num != cpu);
280 225
281 if (mc_amd == NULL) 226 p = find_patch(cpu);
227 if (!p)
282 return 0; 228 return 0;
283 229
230 mc_amd = p->data;
231 uci->mc = p->data;
232
284 rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); 233 rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
285 234
286 /* need to apply patch? */ 235 /* need to apply patch? */
@@ -336,61 +285,113 @@ static void free_equiv_cpu_table(void)
336 equiv_cpu_table = NULL; 285 equiv_cpu_table = NULL;
337} 286}
338 287
339static enum ucode_state 288static void cleanup(void)
340generic_load_microcode(int cpu, const u8 *data, size_t size)
341{ 289{
342 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 290 free_equiv_cpu_table();
343 struct microcode_header_amd *mc_hdr = NULL; 291 free_cache();
344 unsigned int mc_size, leftover, current_size = 0; 292}
293
294/*
295 * We return the current size even if some of the checks failed so that
296 * we can skip over the next patch. If we return a negative value, we
297 * signal a grave error like a memory allocation has failed and the
298 * driver cannot continue functioning normally. In such cases, we tear
299 * down everything we've used up so far and exit.
300 */
301static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
302{
303 struct cpuinfo_x86 *c = &cpu_data(cpu);
304 struct microcode_header_amd *mc_hdr;
305 struct ucode_patch *patch;
306 unsigned int patch_size, crnt_size, ret;
307 u32 proc_fam;
308 u16 proc_id;
309
310 patch_size = *(u32 *)(fw + 4);
311 crnt_size = patch_size + SECTION_HDR_SIZE;
312 mc_hdr = (struct microcode_header_amd *)(fw + SECTION_HDR_SIZE);
313 proc_id = mc_hdr->processor_rev_id;
314
315 proc_fam = find_cpu_family_by_equiv_cpu(proc_id);
316 if (!proc_fam) {
317 pr_err("No patch family for equiv ID: 0x%04x\n", proc_id);
318 return crnt_size;
319 }
320
321 /* check if patch is for the current family */
322 proc_fam = ((proc_fam >> 8) & 0xf) + ((proc_fam >> 20) & 0xff);
323 if (proc_fam != c->x86)
324 return crnt_size;
325
326 if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
327 pr_err("Patch-ID 0x%08x: chipset-specific code unsupported.\n",
328 mc_hdr->patch_id);
329 return crnt_size;
330 }
331
332 ret = verify_patch_size(cpu, patch_size, leftover);
333 if (!ret) {
334 pr_err("Patch-ID 0x%08x: size mismatch.\n", mc_hdr->patch_id);
335 return crnt_size;
336 }
337
338 patch = kzalloc(sizeof(*patch), GFP_KERNEL);
339 if (!patch) {
340 pr_err("Patch allocation failure.\n");
341 return -EINVAL;
342 }
343
344 patch->data = kzalloc(patch_size, GFP_KERNEL);
345 if (!patch->data) {
346 pr_err("Patch data allocation failure.\n");
347 kfree(patch);
348 return -EINVAL;
349 }
350
351 /* All looks ok, copy patch... */
352 memcpy(patch->data, fw + SECTION_HDR_SIZE, patch_size);
353 INIT_LIST_HEAD(&patch->plist);
354 patch->patch_id = mc_hdr->patch_id;
355 patch->equiv_cpu = proc_id;
356
357 /* ... and add to cache. */
358 update_cache(patch);
359
360 return crnt_size;
361}
362
363static enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size)
364{
365 enum ucode_state ret = UCODE_ERROR;
366 unsigned int leftover;
367 u8 *fw = (u8 *)data;
368 int crnt_size = 0;
345 int offset; 369 int offset;
346 const u8 *ucode_ptr = data;
347 void *new_mc = NULL;
348 unsigned int new_rev = uci->cpu_sig.rev;
349 enum ucode_state state = UCODE_ERROR;
350 370
351 offset = install_equiv_cpu_table(ucode_ptr); 371 offset = install_equiv_cpu_table(data);
352 if (offset < 0) { 372 if (offset < 0) {
353 pr_err("failed to create equivalent cpu table\n"); 373 pr_err("failed to create equivalent cpu table\n");
354 goto out; 374 return ret;
355 } 375 }
356 ucode_ptr += offset; 376 fw += offset;
357 leftover = size - offset; 377 leftover = size - offset;
358 378
359 if (*(u32 *)ucode_ptr != UCODE_UCODE_TYPE) { 379 if (*(u32 *)fw != UCODE_UCODE_TYPE) {
360 pr_err("invalid type field in container file section header\n"); 380 pr_err("invalid type field in container file section header\n");
361 goto free_table; 381 free_equiv_cpu_table();
382 return ret;
362 } 383 }
363 384
364 while (leftover) { 385 while (leftover) {
365 mc_size = get_matching_microcode(cpu, ucode_ptr, leftover, 386 crnt_size = verify_and_add_patch(cpu, fw, leftover);
366 new_rev, &current_size); 387 if (crnt_size < 0)
367 if (mc_size) { 388 return ret;
368 mc_hdr = patch;
369 new_mc = patch;
370 new_rev = mc_hdr->patch_id;
371 goto out_ok;
372 }
373 389
374 ucode_ptr += current_size; 390 fw += crnt_size;
375 leftover -= current_size; 391 leftover -= crnt_size;
376 } 392 }
377 393
378 if (!new_mc) { 394 return UCODE_OK;
379 state = UCODE_NFOUND;
380 goto free_table;
381 }
382
383out_ok:
384 uci->mc = new_mc;
385 state = UCODE_OK;
386 pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
387 cpu, uci->cpu_sig.rev, new_rev);
388
389free_table:
390 free_equiv_cpu_table();
391
392out:
393 return state;
394} 395}
395 396
396/* 397/*
@@ -401,7 +402,7 @@ out:
401 * 402 *
402 * This legacy file is always smaller than 2K in size. 403 * This legacy file is always smaller than 2K in size.
403 * 404 *
404 * Starting at family 15h they are in family specific firmware files: 405 * Beginning with family 15h, they are in family-specific firmware files:
405 * 406 *
406 * amd-ucode/microcode_amd_fam15h.bin 407 * amd-ucode/microcode_amd_fam15h.bin
407 * amd-ucode/microcode_amd_fam16h.bin 408 * amd-ucode/microcode_amd_fam16h.bin
@@ -413,9 +414,13 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
413 bool refresh_fw) 414 bool refresh_fw)
414{ 415{
415 char fw_name[36] = "amd-ucode/microcode_amd.bin"; 416 char fw_name[36] = "amd-ucode/microcode_amd.bin";
416 const struct firmware *fw;
417 enum ucode_state ret = UCODE_NFOUND;
418 struct cpuinfo_x86 *c = &cpu_data(cpu); 417 struct cpuinfo_x86 *c = &cpu_data(cpu);
418 enum ucode_state ret = UCODE_NFOUND;
419 const struct firmware *fw;
420
421 /* reload ucode container only on the boot cpu */
422 if (!refresh_fw || c->cpu_index != boot_cpu_data.cpu_index)
423 return UCODE_OK;
419 424
420 if (c->x86 >= 0x15) 425 if (c->x86 >= 0x15)
421 snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86); 426 snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
@@ -431,12 +436,17 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
431 goto fw_release; 436 goto fw_release;
432 } 437 }
433 438
434 ret = generic_load_microcode(cpu, fw->data, fw->size); 439 /* free old equiv table */
440 free_equiv_cpu_table();
441
442 ret = load_microcode_amd(cpu, fw->data, fw->size);
443 if (ret != UCODE_OK)
444 cleanup();
435 445
436fw_release: 446 fw_release:
437 release_firmware(fw); 447 release_firmware(fw);
438 448
439out: 449 out:
440 return ret; 450 return ret;
441} 451}
442 452
@@ -470,14 +480,10 @@ struct microcode_ops * __init init_amd_microcode(void)
470 return NULL; 480 return NULL;
471 } 481 }
472 482
473 patch = (void *)get_zeroed_page(GFP_KERNEL);
474 if (!patch)
475 return NULL;
476
477 return &microcode_amd_ops; 483 return &microcode_amd_ops;
478} 484}
479 485
480void __exit exit_amd_microcode(void) 486void __exit exit_amd_microcode(void)
481{ 487{
482 free_page((unsigned long)patch); 488 cleanup();
483} 489}