aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBorislav Petkov <bp@suse.de>2015-10-12 05:22:42 -0400
committerIngo Molnar <mingo@kernel.org>2015-10-12 10:15:48 -0400
commit0399f73299f1b7e04de329050f7111b362b7eeb5 (patch)
tree4774fc009e34c1a63397d7e16cddaddfd34adc7b
parent2eff73c0a11f19ff082a566e3429fbaaca7b8e7b (diff)
x86/microcode/amd: Do not overwrite final patch levels
A certain number of patch levels of applied microcode should not be overwritten by the microcode loader, otherwise bad things will happen. Check those and abort update if the current core has one of those final patch levels applied by the BIOS. 32-bit needs special handling, of course. See https://bugzilla.suse.com/show_bug.cgi?id=913996 for more info. Tested-by: Peter Kirchgeßner <pkirchgessner@t-online.de> Signed-off-by: Borislav Petkov <bp@suse.de> Cc: Borislav Petkov <bp@alien8.de> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tony Luck <tony.luck@intel.com> Link: http://lkml.kernel.org/r/1444641762-9437-7-git-send-email-bp@alien8.de Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/microcode_amd.h2
-rw-r--r--arch/x86/kernel/cpu/microcode/amd.c38
-rw-r--r--arch/x86/kernel/cpu/microcode/amd_early.c13
3 files changed, 44 insertions, 9 deletions
diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h
index 9b214e10d499..d3e86cfd08fe 100644
--- a/arch/x86/include/asm/microcode_amd.h
+++ b/arch/x86/include/asm/microcode_amd.h
@@ -76,5 +76,5 @@ static inline int __init save_microcode_in_initrd_amd(void) { return -EINVAL; }
76void reload_ucode_amd(void) {} 76void reload_ucode_amd(void) {}
77#endif 77#endif
78 78
79extern bool check_current_patch_level(u32 *rev); 79extern bool check_current_patch_level(u32 *rev, bool early);
80#endif /* _ASM_X86_MICROCODE_AMD_H */ 80#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 2d630138bf3e..da922d1e2f71 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -178,6 +178,16 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
178} 178}
179 179
180/* 180/*
181 * Those patch levels cannot be updated to newer ones and thus should be final.
182 */
183static u32 final_levels[] = {
184 0x01000098,
185 0x0100009f,
186 0x010000af,
187 0, /* T-101 terminator */
188};
189
190/*
181 * Check the current patch level on this CPU. 191 * Check the current patch level on this CPU.
182 * 192 *
183 * @rev: Use it to return the patch level. It is set to 0 in the case of 193 * @rev: Use it to return the patch level. It is set to 0 in the case of
@@ -187,13 +197,31 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
187 * - true: if update should stop 197 * - true: if update should stop
188 * - false: otherwise 198 * - false: otherwise
189 */ 199 */
190bool check_current_patch_level(u32 *rev) 200bool check_current_patch_level(u32 *rev, bool early)
191{ 201{
192 u32 dummy; 202 u32 lvl, dummy, i;
203 bool ret = false;
204 u32 *levels;
205
206 native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy);
207
208 if (IS_ENABLED(CONFIG_X86_32) && early)
209 levels = (u32 *)__pa_nodebug(&final_levels);
210 else
211 levels = final_levels;
212
213 for (i = 0; levels[i]; i++) {
214 if (lvl == levels[i]) {
215 lvl = 0;
216 ret = true;
217 break;
218 }
219 }
193 220
194 native_rdmsr(MSR_AMD64_PATCH_LEVEL, *rev, dummy); 221 if (rev)
222 *rev = lvl;
195 223
196 return false; 224 return ret;
197} 225}
198 226
199int __apply_microcode_amd(struct microcode_amd *mc_amd) 227int __apply_microcode_amd(struct microcode_amd *mc_amd)
@@ -229,7 +257,7 @@ int apply_microcode_amd(int cpu)
229 mc_amd = p->data; 257 mc_amd = p->data;
230 uci->mc = p->data; 258 uci->mc = p->data;
231 259
232 if (check_current_patch_level(&rev)) 260 if (check_current_patch_level(&rev, false))
233 return -1; 261 return -1;
234 262
235 /* need to apply patch? */ 263 /* need to apply patch? */
diff --git a/arch/x86/kernel/cpu/microcode/amd_early.c b/arch/x86/kernel/cpu/microcode/amd_early.c
index abb90097582f..a54a47b9d8ea 100644
--- a/arch/x86/kernel/cpu/microcode/amd_early.c
+++ b/arch/x86/kernel/cpu/microcode/amd_early.c
@@ -196,7 +196,7 @@ static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
196 return; 196 return;
197 } 197 }
198 198
199 if (check_current_patch_level(&rev)) 199 if (check_current_patch_level(&rev, true))
200 return; 200 return;
201 201
202 while (left > 0) { 202 while (left > 0) {
@@ -330,7 +330,10 @@ void load_ucode_amd_ap(void)
330 if (!container) 330 if (!container)
331 return; 331 return;
332 332
333 if (check_current_patch_level(&rev)) 333 /*
334 * 64-bit runs with paging enabled, thus early==false.
335 */
336 if (check_current_patch_level(&rev, false))
334 return; 337 return;
335 338
336 eax = cpuid_eax(0x00000001); 339 eax = cpuid_eax(0x00000001);
@@ -422,7 +425,11 @@ void reload_ucode_amd(void)
422 struct microcode_amd *mc; 425 struct microcode_amd *mc;
423 u32 rev; 426 u32 rev;
424 427
425 if (check_current_patch_level(&rev)) 428 /*
429 * early==false because this is a syscore ->resume path and by
430 * that time paging is long enabled.
431 */
432 if (check_current_patch_level(&rev, false))
426 return; 433 return;
427 434
428 mc = (struct microcode_amd *)amd_ucode_patch; 435 mc = (struct microcode_amd *)amd_ucode_patch;