x86/mm, kexec: Fix memory corruption with SME on successive kexecs

After issuing successive kexecs it was found that the SHA hash failed
verification when booting the kexec'd kernel. When SME is enabled, the
change from using pages that were marked encrypted to now being marked as
not encrypted (through new identify mapped page tables) results in memory
corruption if there are any cache entries for the previously encrypted
pages. This is because separate cache entries can exist for the same
physical location but tagged both with and without the encryption bit.

To prevent this, issue a wbinvd if SME is active before copying the pages
from the source location to the destination location to clear any possible
cache entry conflicts.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Cc: <kexec@lists.infradead.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/e7fb8610af3a93e8f8ae6f214cd9249adc0df2b4.1501186516.git.thomas.lendacky@amd.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Tom Lendacky and committed by
Ingo Molnar
4e237903 f90e2d9a

+18 -2
+2 -1
arch/x86/include/asm/kexec.h
··· 147 relocate_kernel(unsigned long indirection_page, 148 unsigned long page_list, 149 unsigned long start_address, 150 - unsigned int preserve_context); 151 #endif 152 153 #define ARCH_HAS_KIMAGE_ARCH
··· 147 relocate_kernel(unsigned long indirection_page, 148 unsigned long page_list, 149 unsigned long start_address, 150 + unsigned int preserve_context, 151 + unsigned int sme_active); 152 #endif 153 154 #define ARCH_HAS_KIMAGE_ARCH
+2 -1
arch/x86/kernel/machine_kexec_64.c
··· 335 image->start = relocate_kernel((unsigned long)image->head, 336 (unsigned long)page_list, 337 image->start, 338 - image->preserve_context); 339 340 #ifdef CONFIG_KEXEC_JUMP 341 if (image->preserve_context)
··· 335 image->start = relocate_kernel((unsigned long)image->head, 336 (unsigned long)page_list, 337 image->start, 338 + image->preserve_context, 339 + sme_active()); 340 341 #ifdef CONFIG_KEXEC_JUMP 342 if (image->preserve_context)
+14
arch/x86/kernel/relocate_kernel_64.S
··· 47 * %rsi page_list 48 * %rdx start address 49 * %rcx preserve_context 50 */ 51 52 /* Save the CPU context, used for jumping back */ ··· 71 /* zero out flags, and disable interrupts */ 72 pushq $0 73 popfq 74 75 /* 76 * get physical address of control page now ··· 135 136 /* Flush the TLB (needed?) */ 137 movq %r9, %cr3 138 139 movq %rcx, %r11 140 call swap_pages
··· 47 * %rsi page_list 48 * %rdx start address 49 * %rcx preserve_context 50 + * %r8 sme_active 51 */ 52 53 /* Save the CPU context, used for jumping back */ ··· 70 /* zero out flags, and disable interrupts */ 71 pushq $0 72 popfq 73 + 74 + /* Save SME active flag */ 75 + movq %r8, %r12 76 77 /* 78 * get physical address of control page now ··· 131 132 /* Flush the TLB (needed?) */ 133 movq %r9, %cr3 134 + 135 + /* 136 + * If SME is active, there could be old encrypted cache line 137 + * entries that will conflict with the now unencrypted memory 138 + * used by kexec. Flush the caches before copying the kernel. 139 + */ 140 + testq %r12, %r12 141 + jz 1f 142 + wbinvd 143 + 1: 144 145 movq %rcx, %r11 146 call swap_pages