/* SPDX-License-Identifier: GPL-2.0-only */1/*2* AMD Memory Encryption Support3*4* Copyright (C) 2016 Advanced Micro Devices, Inc.5*6* Author: Tom Lendacky <[email protected]>7*/89#include <linux/linkage.h>10#include <linux/pgtable.h>11#include <asm/page.h>12#include <asm/processor-flags.h>13#include <asm/msr-index.h>14#include <asm/nospec-branch.h>1516.text17.code6418SYM_FUNC_START(sme_encrypt_execute)1920/*21* Entry parameters:22* RDI - virtual address for the encrypted mapping23* RSI - virtual address for the decrypted mapping24* RDX - length to encrypt25* RCX - virtual address of the encryption workarea, including:26* - stack page (PAGE_SIZE)27* - encryption routine page (PAGE_SIZE)28* - intermediate copy buffer (PMD_SIZE)29* R8 - physical address of the pagetables to use for encryption30*/3132push %rbp33movq %rsp, %rbp /* RBP now has original stack pointer */3435/* Set up a one page stack in the non-encrypted memory area */36movq %rcx, %rax /* Workarea stack page */37leaq PAGE_SIZE(%rax), %rsp /* Set new stack pointer */38addq $PAGE_SIZE, %rax /* Workarea encryption routine */3940push %r1241movq %rdi, %r10 /* Encrypted area */42movq %rsi, %r11 /* Decrypted area */43movq %rdx, %r12 /* Area length */4445/* Copy encryption routine into the workarea */46movq %rax, %rdi /* Workarea encryption routine */47leaq __enc_copy(%rip), %rsi /* Encryption routine */48movq $(.L__enc_copy_end - __enc_copy), %rcx /* Encryption routine length */49rep movsb5051/* Setup registers for call */52movq %r10, %rdi /* Encrypted area */53movq %r11, %rsi /* Decrypted area */54movq %r8, %rdx /* Pagetables used for encryption */55movq %r12, %rcx /* Area length */56movq %rax, %r8 /* Workarea encryption routine */57addq $PAGE_SIZE, %r8 /* Workarea intermediate copy buffer */5859ANNOTATE_RETPOLINE_SAFE60call *%rax /* Call the encryption routine */6162pop %r126364movq %rbp, %rsp /* Restore original stack pointer */65pop %rbp6667/* Offset to __x86_return_thunk would be wrong here */68ANNOTATE_UNRET_SAFE69ret70int371SYM_FUNC_END(sme_encrypt_execute)7273SYM_FUNC_START(__enc_copy)74ANNOTATE_NOENDBR75/*76* Routine used to encrypt memory in place.77* This routine must be run outside of the kernel proper since78* the kernel will be encrypted during the process. So this79* routine is defined here and then copied to an area outside80* of the kernel where it will remain and run decrypted81* during execution.82*83* On entry the registers must be:84* RDI - virtual address for the encrypted mapping85* RSI - virtual address for the decrypted mapping86* RDX - address of the pagetables to use for encryption87* RCX - length of area88* R8 - intermediate copy buffer89*90* RAX - points to this routine91*92* The area will be encrypted by copying from the non-encrypted93* memory space to an intermediate buffer and then copying from the94* intermediate buffer back to the encrypted memory space. The physical95* addresses of the two mappings are the same which results in the area96* being encrypted "in place".97*/98/* Enable the new page tables */99mov %rdx, %cr3100101/* Flush any global TLBs */102mov %cr4, %rdx103andq $~X86_CR4_PGE, %rdx104mov %rdx, %cr4105orq $X86_CR4_PGE, %rdx106mov %rdx, %cr4107108push %r15109push %r12110111movq %rcx, %r9 /* Save area length */112movq %rdi, %r10 /* Save encrypted area address */113movq %rsi, %r11 /* Save decrypted area address */114115/* Set the PAT register PA5 entry to write-protect */116movl $MSR_IA32_CR_PAT, %ecx117rdmsr118mov %rdx, %r15 /* Save original PAT value */119andl $0xffff00ff, %edx /* Clear PA5 */120orl $0x00000500, %edx /* Set PA5 to WP */121wrmsr122123wbinvd /* Invalidate any cache entries */124125/* Copy/encrypt up to 2MB at a time */126movq $PMD_SIZE, %r121271:128cmpq %r12, %r9129jnb 2f130movq %r9, %r121311322:133movq %r11, %rsi /* Source - decrypted area */134movq %r8, %rdi /* Dest - intermediate copy buffer */135movq %r12, %rcx136rep movsb137138movq %r8, %rsi /* Source - intermediate copy buffer */139movq %r10, %rdi /* Dest - encrypted area */140movq %r12, %rcx141rep movsb142143addq %r12, %r11144addq %r12, %r10145subq %r12, %r9 /* Kernel length decrement */146jnz 1b /* Kernel length not zero? */147148/* Restore PAT register */149movl $MSR_IA32_CR_PAT, %ecx150rdmsr151mov %r15, %rdx /* Restore original PAT value */152wrmsr153154pop %r12155pop %r15156157/* Offset to __x86_return_thunk would be wrong here */158ANNOTATE_UNRET_SAFE159ret160int3161.L__enc_copy_end:162SYM_FUNC_END(__enc_copy)163164165