diff options
| -rw-r--r-- | arch/x86/include/asm/microcode_amd.h | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/microcode/amd.c | 78 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/microcode/core.c | 41 |
3 files changed, 52 insertions, 69 deletions
diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h index 3e3e20be829a..3d57009e168b 100644 --- a/arch/x86/include/asm/microcode_amd.h +++ b/arch/x86/include/asm/microcode_amd.h | |||
| @@ -54,6 +54,4 @@ static inline int __init | |||
| 54 | save_microcode_in_initrd_amd(unsigned int family) { return -EINVAL; } | 54 | save_microcode_in_initrd_amd(unsigned int family) { return -EINVAL; } |
| 55 | void reload_ucode_amd(void) {} | 55 | void reload_ucode_amd(void) {} |
| 56 | #endif | 56 | #endif |
| 57 | |||
| 58 | extern bool check_current_patch_level(u32 *rev, bool early); | ||
| 59 | #endif /* _ASM_X86_MICROCODE_AMD_H */ | 57 | #endif /* _ASM_X86_MICROCODE_AMD_H */ |
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 7727f278de58..61743476c25b 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c | |||
| @@ -207,7 +207,7 @@ apply_microcode_early_amd(u32 cpuid_1_eax, void *ucode, size_t size, | |||
| 207 | struct cont_desc desc = { 0 }; | 207 | struct cont_desc desc = { 0 }; |
| 208 | u8 (*patch)[PATCH_MAX_SIZE]; | 208 | u8 (*patch)[PATCH_MAX_SIZE]; |
| 209 | struct microcode_amd *mc; | 209 | struct microcode_amd *mc; |
| 210 | u32 rev, *new_rev; | 210 | u32 rev, dummy, *new_rev; |
| 211 | bool ret = false; | 211 | bool ret = false; |
| 212 | 212 | ||
| 213 | #ifdef CONFIG_X86_32 | 213 | #ifdef CONFIG_X86_32 |
| @@ -218,9 +218,6 @@ apply_microcode_early_amd(u32 cpuid_1_eax, void *ucode, size_t size, | |||
| 218 | patch = &amd_ucode_patch; | 218 | patch = &amd_ucode_patch; |
| 219 | #endif | 219 | #endif |
| 220 | 220 | ||
| 221 | if (check_current_patch_level(&rev, true)) | ||
| 222 | return false; | ||
| 223 | |||
| 224 | desc.cpuid_1_eax = cpuid_1_eax; | 221 | desc.cpuid_1_eax = cpuid_1_eax; |
| 225 | 222 | ||
| 226 | scan_containers(ucode, size, &desc); | 223 | scan_containers(ucode, size, &desc); |
| @@ -231,6 +228,7 @@ apply_microcode_early_amd(u32 cpuid_1_eax, void *ucode, size_t size, | |||
| 231 | if (!mc) | 228 | if (!mc) |
| 232 | return ret; | 229 | return ret; |
| 233 | 230 | ||
| 231 | native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); | ||
| 234 | if (rev >= mc->hdr.patch_id) | 232 | if (rev >= mc->hdr.patch_id) |
| 235 | return ret; | 233 | return ret; |
| 236 | 234 | ||
| @@ -328,13 +326,8 @@ void load_ucode_amd_ap(unsigned int cpuid_1_eax) | |||
| 328 | { | 326 | { |
| 329 | struct equiv_cpu_entry *eq; | 327 | struct equiv_cpu_entry *eq; |
| 330 | struct microcode_amd *mc; | 328 | struct microcode_amd *mc; |
| 331 | u32 rev; | ||
| 332 | u16 eq_id; | 329 | u16 eq_id; |
| 333 | 330 | ||
| 334 | /* 64-bit runs with paging enabled, thus early==false. */ | ||
| 335 | if (check_current_patch_level(&rev, false)) | ||
| 336 | return; | ||
| 337 | |||
| 338 | /* First AP hasn't cached it yet, go through the blob. */ | 331 | /* First AP hasn't cached it yet, go through the blob. */ |
| 339 | if (!cont.data) { | 332 | if (!cont.data) { |
| 340 | struct cpio_data cp; | 333 | struct cpio_data cp; |
| @@ -371,6 +364,10 @@ reget: | |||
| 371 | return; | 364 | return; |
| 372 | 365 | ||
| 373 | if (eq_id == cont.eq_id) { | 366 | if (eq_id == cont.eq_id) { |
| 367 | u32 rev, dummy; | ||
| 368 | |||
| 369 | native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); | ||
| 370 | |||
| 374 | mc = (struct microcode_amd *)amd_ucode_patch; | 371 | mc = (struct microcode_amd *)amd_ucode_patch; |
| 375 | 372 | ||
| 376 | if (mc && rev < mc->hdr.patch_id) { | 373 | if (mc && rev < mc->hdr.patch_id) { |
| @@ -436,19 +433,14 @@ int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax) | |||
| 436 | void reload_ucode_amd(void) | 433 | void reload_ucode_amd(void) |
| 437 | { | 434 | { |
| 438 | struct microcode_amd *mc; | 435 | struct microcode_amd *mc; |
| 439 | u32 rev; | 436 | u32 rev, dummy; |
| 440 | |||
| 441 | /* | ||
| 442 | * early==false because this is a syscore ->resume path and by | ||
| 443 | * that time paging is long enabled. | ||
| 444 | */ | ||
| 445 | if (check_current_patch_level(&rev, false)) | ||
| 446 | return; | ||
| 447 | 437 | ||
| 448 | mc = (struct microcode_amd *)amd_ucode_patch; | 438 | mc = (struct microcode_amd *)amd_ucode_patch; |
| 449 | if (!mc) | 439 | if (!mc) |
| 450 | return; | 440 | return; |
| 451 | 441 | ||
| 442 | rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); | ||
| 443 | |||
| 452 | if (rev < mc->hdr.patch_id) { | 444 | if (rev < mc->hdr.patch_id) { |
| 453 | if (!__apply_microcode_amd(mc)) { | 445 | if (!__apply_microcode_amd(mc)) { |
| 454 | ucode_new_rev = mc->hdr.patch_id; | 446 | ucode_new_rev = mc->hdr.patch_id; |
| @@ -586,60 +578,13 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size, | |||
| 586 | return patch_size; | 578 | return patch_size; |
| 587 | } | 579 | } |
| 588 | 580 | ||
| 589 | /* | ||
| 590 | * Those patch levels cannot be updated to newer ones and thus should be final. | ||
| 591 | */ | ||
| 592 | static u32 final_levels[] = { | ||
| 593 | 0x01000098, | ||
| 594 | 0x0100009f, | ||
| 595 | 0x010000af, | ||
| 596 | 0, /* T-101 terminator */ | ||
| 597 | }; | ||
| 598 | |||
| 599 | /* | ||
| 600 | * Check the current patch level on this CPU. | ||
| 601 | * | ||
| 602 | * @rev: Use it to return the patch level. It is set to 0 in the case of | ||
| 603 | * error. | ||
| 604 | * | ||
| 605 | * Returns: | ||
| 606 | * - true: if update should stop | ||
| 607 | * - false: otherwise | ||
| 608 | */ | ||
| 609 | bool check_current_patch_level(u32 *rev, bool early) | ||
| 610 | { | ||
| 611 | u32 lvl, dummy, i; | ||
| 612 | bool ret = false; | ||
| 613 | u32 *levels; | ||
| 614 | |||
| 615 | native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy); | ||
| 616 | |||
| 617 | if (IS_ENABLED(CONFIG_X86_32) && early) | ||
| 618 | levels = (u32 *)__pa_nodebug(&final_levels); | ||
| 619 | else | ||
| 620 | levels = final_levels; | ||
| 621 | |||
| 622 | for (i = 0; levels[i]; i++) { | ||
| 623 | if (lvl == levels[i]) { | ||
| 624 | lvl = 0; | ||
| 625 | ret = true; | ||
| 626 | break; | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 | if (rev) | ||
| 631 | *rev = lvl; | ||
| 632 | |||
| 633 | return ret; | ||
| 634 | } | ||
| 635 | |||
| 636 | static int apply_microcode_amd(int cpu) | 581 | static int apply_microcode_amd(int cpu) |
| 637 | { | 582 | { |
| 638 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 583 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
| 639 | struct microcode_amd *mc_amd; | 584 | struct microcode_amd *mc_amd; |
| 640 | struct ucode_cpu_info *uci; | 585 | struct ucode_cpu_info *uci; |
| 641 | struct ucode_patch *p; | 586 | struct ucode_patch *p; |
| 642 | u32 rev; | 587 | u32 rev, dummy; |
| 643 | 588 | ||
| 644 | BUG_ON(raw_smp_processor_id() != cpu); | 589 | BUG_ON(raw_smp_processor_id() != cpu); |
| 645 | 590 | ||
| @@ -652,8 +597,7 @@ static int apply_microcode_amd(int cpu) | |||
| 652 | mc_amd = p->data; | 597 | mc_amd = p->data; |
| 653 | uci->mc = p->data; | 598 | uci->mc = p->data; |
| 654 | 599 | ||
| 655 | if (check_current_patch_level(&rev, false)) | 600 | rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); |
| 656 | return -1; | ||
| 657 | 601 | ||
| 658 | /* need to apply patch? */ | 602 | /* need to apply patch? */ |
| 659 | if (rev >= mc_amd->hdr.patch_id) { | 603 | if (rev >= mc_amd->hdr.patch_id) { |
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index dc54518299c4..3b74d2f315d3 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c | |||
| @@ -69,6 +69,42 @@ struct cpu_info_ctx { | |||
| 69 | int err; | 69 | int err; |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | /* | ||
| 73 | * Those patch levels cannot be updated to newer ones and thus should be final. | ||
| 74 | */ | ||
| 75 | static u32 final_levels[] = { | ||
| 76 | 0x01000098, | ||
| 77 | 0x0100009f, | ||
| 78 | 0x010000af, | ||
| 79 | 0, /* T-101 terminator */ | ||
| 80 | }; | ||
| 81 | |||
| 82 | /* | ||
| 83 | * Check the current patch level on this CPU. | ||
| 84 | * | ||
| 85 | * Returns: | ||
| 86 | * - true: if update should stop | ||
| 87 | * - false: otherwise | ||
| 88 | */ | ||
| 89 | static bool amd_check_current_patch_level(void) | ||
| 90 | { | ||
| 91 | u32 lvl, dummy, i; | ||
| 92 | u32 *levels; | ||
| 93 | |||
| 94 | native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy); | ||
| 95 | |||
| 96 | if (IS_ENABLED(CONFIG_X86_32)) | ||
| 97 | levels = (u32 *)__pa_nodebug(&final_levels); | ||
| 98 | else | ||
| 99 | levels = final_levels; | ||
| 100 | |||
| 101 | for (i = 0; levels[i]; i++) { | ||
| 102 | if (lvl == levels[i]) | ||
| 103 | return true; | ||
| 104 | } | ||
| 105 | return false; | ||
| 106 | } | ||
| 107 | |||
| 72 | static bool __init check_loader_disabled_bsp(void) | 108 | static bool __init check_loader_disabled_bsp(void) |
| 73 | { | 109 | { |
| 74 | static const char *__dis_opt_str = "dis_ucode_ldr"; | 110 | static const char *__dis_opt_str = "dis_ucode_ldr"; |
| @@ -95,6 +131,11 @@ static bool __init check_loader_disabled_bsp(void) | |||
| 95 | if (native_cpuid_ecx(1) & BIT(31)) | 131 | if (native_cpuid_ecx(1) & BIT(31)) |
| 96 | return *res; | 132 | return *res; |
| 97 | 133 | ||
| 134 | if (x86_cpuid_vendor() == X86_VENDOR_AMD) { | ||
| 135 | if (amd_check_current_patch_level()) | ||
| 136 | return *res; | ||
| 137 | } | ||
| 138 | |||
| 98 | if (cmdline_find_option_bool(cmdline, option) <= 0) | 139 | if (cmdline_find_option_bool(cmdline, option) <= 0) |
| 99 | *res = false; | 140 | *res = false; |
| 100 | 141 | ||
