162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD Memory Encryption Support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Tom Lendacky <thomas.lendacky@amd.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/linkage.h> 1162306a36Sopenharmony_ci#include <linux/pgtable.h> 1262306a36Sopenharmony_ci#include <asm/page.h> 1362306a36Sopenharmony_ci#include <asm/processor-flags.h> 1462306a36Sopenharmony_ci#include <asm/msr-index.h> 1562306a36Sopenharmony_ci#include <asm/nospec-branch.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci .text 1862306a36Sopenharmony_ci .code64 1962306a36Sopenharmony_ciSYM_FUNC_START(sme_encrypt_execute) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci /* 2262306a36Sopenharmony_ci * Entry parameters: 2362306a36Sopenharmony_ci * RDI - virtual address for the encrypted mapping 2462306a36Sopenharmony_ci * RSI - virtual address for the decrypted mapping 2562306a36Sopenharmony_ci * RDX - length to encrypt 2662306a36Sopenharmony_ci * RCX - virtual address of the encryption workarea, including: 2762306a36Sopenharmony_ci * - stack page (PAGE_SIZE) 2862306a36Sopenharmony_ci * - encryption routine page (PAGE_SIZE) 2962306a36Sopenharmony_ci * - intermediate copy buffer (PMD_SIZE) 3062306a36Sopenharmony_ci * R8 - physical address of the pagetables to use for encryption 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci push %rbp 3462306a36Sopenharmony_ci movq %rsp, %rbp /* RBP now has original stack pointer */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* Set up a one page stack in the non-encrypted memory area */ 3762306a36Sopenharmony_ci movq %rcx, %rax /* Workarea stack page */ 3862306a36Sopenharmony_ci leaq PAGE_SIZE(%rax), %rsp /* Set new stack pointer */ 3962306a36Sopenharmony_ci addq $PAGE_SIZE, %rax /* Workarea encryption routine */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci push %r12 4262306a36Sopenharmony_ci movq %rdi, %r10 /* Encrypted area */ 4362306a36Sopenharmony_ci movq %rsi, %r11 /* Decrypted area */ 4462306a36Sopenharmony_ci movq %rdx, %r12 /* Area length */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Copy encryption routine into the workarea */ 4762306a36Sopenharmony_ci movq %rax, %rdi /* Workarea encryption routine */ 4862306a36Sopenharmony_ci leaq __enc_copy(%rip), %rsi /* Encryption routine */ 4962306a36Sopenharmony_ci movq $(.L__enc_copy_end - __enc_copy), %rcx /* Encryption routine length */ 5062306a36Sopenharmony_ci rep movsb 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* Setup registers for call */ 5362306a36Sopenharmony_ci movq %r10, %rdi /* Encrypted area */ 5462306a36Sopenharmony_ci movq %r11, %rsi /* Decrypted area */ 5562306a36Sopenharmony_ci movq %r8, %rdx /* Pagetables used for encryption */ 5662306a36Sopenharmony_ci movq %r12, %rcx /* Area length */ 5762306a36Sopenharmony_ci movq %rax, %r8 /* Workarea encryption routine */ 5862306a36Sopenharmony_ci addq $PAGE_SIZE, %r8 /* Workarea intermediate copy buffer */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ANNOTATE_RETPOLINE_SAFE 6162306a36Sopenharmony_ci call *%rax /* Call the encryption routine */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci pop %r12 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci movq %rbp, %rsp /* Restore original stack pointer */ 6662306a36Sopenharmony_ci pop %rbp 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Offset to __x86_return_thunk would be wrong here */ 6962306a36Sopenharmony_ci ANNOTATE_UNRET_SAFE 7062306a36Sopenharmony_ci ret 7162306a36Sopenharmony_ci int3 7262306a36Sopenharmony_ciSYM_FUNC_END(sme_encrypt_execute) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciSYM_FUNC_START(__enc_copy) 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * Routine used to encrypt memory in place. 7762306a36Sopenharmony_ci * This routine must be run outside of the kernel proper since 7862306a36Sopenharmony_ci * the kernel will be encrypted during the process. So this 7962306a36Sopenharmony_ci * routine is defined here and then copied to an area outside 8062306a36Sopenharmony_ci * of the kernel where it will remain and run decrypted 8162306a36Sopenharmony_ci * during execution. 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * On entry the registers must be: 8462306a36Sopenharmony_ci * RDI - virtual address for the encrypted mapping 8562306a36Sopenharmony_ci * RSI - virtual address for the decrypted mapping 8662306a36Sopenharmony_ci * RDX - address of the pagetables to use for encryption 8762306a36Sopenharmony_ci * RCX - length of area 8862306a36Sopenharmony_ci * R8 - intermediate copy buffer 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * RAX - points to this routine 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * The area will be encrypted by copying from the non-encrypted 9362306a36Sopenharmony_ci * memory space to an intermediate buffer and then copying from the 9462306a36Sopenharmony_ci * intermediate buffer back to the encrypted memory space. The physical 9562306a36Sopenharmony_ci * addresses of the two mappings are the same which results in the area 9662306a36Sopenharmony_ci * being encrypted "in place". 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci /* Enable the new page tables */ 9962306a36Sopenharmony_ci mov %rdx, %cr3 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Flush any global TLBs */ 10262306a36Sopenharmony_ci mov %cr4, %rdx 10362306a36Sopenharmony_ci andq $~X86_CR4_PGE, %rdx 10462306a36Sopenharmony_ci mov %rdx, %cr4 10562306a36Sopenharmony_ci orq $X86_CR4_PGE, %rdx 10662306a36Sopenharmony_ci mov %rdx, %cr4 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci push %r15 10962306a36Sopenharmony_ci push %r12 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci movq %rcx, %r9 /* Save area length */ 11262306a36Sopenharmony_ci movq %rdi, %r10 /* Save encrypted area address */ 11362306a36Sopenharmony_ci movq %rsi, %r11 /* Save decrypted area address */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Set the PAT register PA5 entry to write-protect */ 11662306a36Sopenharmony_ci movl $MSR_IA32_CR_PAT, %ecx 11762306a36Sopenharmony_ci rdmsr 11862306a36Sopenharmony_ci mov %rdx, %r15 /* Save original PAT value */ 11962306a36Sopenharmony_ci andl $0xffff00ff, %edx /* Clear PA5 */ 12062306a36Sopenharmony_ci orl $0x00000500, %edx /* Set PA5 to WP */ 12162306a36Sopenharmony_ci wrmsr 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci wbinvd /* Invalidate any cache entries */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Copy/encrypt up to 2MB at a time */ 12662306a36Sopenharmony_ci movq $PMD_SIZE, %r12 12762306a36Sopenharmony_ci1: 12862306a36Sopenharmony_ci cmpq %r12, %r9 12962306a36Sopenharmony_ci jnb 2f 13062306a36Sopenharmony_ci movq %r9, %r12 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci2: 13362306a36Sopenharmony_ci movq %r11, %rsi /* Source - decrypted area */ 13462306a36Sopenharmony_ci movq %r8, %rdi /* Dest - intermediate copy buffer */ 13562306a36Sopenharmony_ci movq %r12, %rcx 13662306a36Sopenharmony_ci rep movsb 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci movq %r8, %rsi /* Source - intermediate copy buffer */ 13962306a36Sopenharmony_ci movq %r10, %rdi /* Dest - encrypted area */ 14062306a36Sopenharmony_ci movq %r12, %rcx 14162306a36Sopenharmony_ci rep movsb 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci addq %r12, %r11 14462306a36Sopenharmony_ci addq %r12, %r10 14562306a36Sopenharmony_ci subq %r12, %r9 /* Kernel length decrement */ 14662306a36Sopenharmony_ci jnz 1b /* Kernel length not zero? */ 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Restore PAT register */ 14962306a36Sopenharmony_ci movl $MSR_IA32_CR_PAT, %ecx 15062306a36Sopenharmony_ci rdmsr 15162306a36Sopenharmony_ci mov %r15, %rdx /* Restore original PAT value */ 15262306a36Sopenharmony_ci wrmsr 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci pop %r12 15562306a36Sopenharmony_ci pop %r15 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Offset to __x86_return_thunk would be wrong here */ 15862306a36Sopenharmony_ci ANNOTATE_UNRET_SAFE 15962306a36Sopenharmony_ci ret 16062306a36Sopenharmony_ci int3 16162306a36Sopenharmony_ci.L__enc_copy_end: 16262306a36Sopenharmony_ciSYM_FUNC_END(__enc_copy) 163