18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AMD Memory Encryption Support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Tom Lendacky <thomas.lendacky@amd.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/linkage.h>
118c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
128c2ecf20Sopenharmony_ci#include <asm/page.h>
138c2ecf20Sopenharmony_ci#include <asm/processor-flags.h>
148c2ecf20Sopenharmony_ci#include <asm/msr-index.h>
158c2ecf20Sopenharmony_ci#include <asm/nospec-branch.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	.text
188c2ecf20Sopenharmony_ci	.code64
198c2ecf20Sopenharmony_ciSYM_FUNC_START(sme_encrypt_execute)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	/*
228c2ecf20Sopenharmony_ci	 * Entry parameters:
238c2ecf20Sopenharmony_ci	 *   RDI - virtual address for the encrypted mapping
248c2ecf20Sopenharmony_ci	 *   RSI - virtual address for the decrypted mapping
258c2ecf20Sopenharmony_ci	 *   RDX - length to encrypt
268c2ecf20Sopenharmony_ci	 *   RCX - virtual address of the encryption workarea, including:
278c2ecf20Sopenharmony_ci	 *     - stack page (PAGE_SIZE)
288c2ecf20Sopenharmony_ci	 *     - encryption routine page (PAGE_SIZE)
298c2ecf20Sopenharmony_ci	 *     - intermediate copy buffer (PMD_PAGE_SIZE)
308c2ecf20Sopenharmony_ci	 *    R8 - physcial address of the pagetables to use for encryption
318c2ecf20Sopenharmony_ci	 */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	push	%rbp
348c2ecf20Sopenharmony_ci	movq	%rsp, %rbp		/* RBP now has original stack pointer */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	/* Set up a one page stack in the non-encrypted memory area */
378c2ecf20Sopenharmony_ci	movq	%rcx, %rax		/* Workarea stack page */
388c2ecf20Sopenharmony_ci	leaq	PAGE_SIZE(%rax), %rsp	/* Set new stack pointer */
398c2ecf20Sopenharmony_ci	addq	$PAGE_SIZE, %rax	/* Workarea encryption routine */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	push	%r12
428c2ecf20Sopenharmony_ci	movq	%rdi, %r10		/* Encrypted area */
438c2ecf20Sopenharmony_ci	movq	%rsi, %r11		/* Decrypted area */
448c2ecf20Sopenharmony_ci	movq	%rdx, %r12		/* Area length */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* Copy encryption routine into the workarea */
478c2ecf20Sopenharmony_ci	movq	%rax, %rdi				/* Workarea encryption routine */
488c2ecf20Sopenharmony_ci	leaq	__enc_copy(%rip), %rsi			/* Encryption routine */
498c2ecf20Sopenharmony_ci	movq	$(.L__enc_copy_end - __enc_copy), %rcx	/* Encryption routine length */
508c2ecf20Sopenharmony_ci	rep	movsb
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* Setup registers for call */
538c2ecf20Sopenharmony_ci	movq	%r10, %rdi		/* Encrypted area */
548c2ecf20Sopenharmony_ci	movq	%r11, %rsi		/* Decrypted area */
558c2ecf20Sopenharmony_ci	movq	%r8, %rdx		/* Pagetables used for encryption */
568c2ecf20Sopenharmony_ci	movq	%r12, %rcx		/* Area length */
578c2ecf20Sopenharmony_ci	movq	%rax, %r8		/* Workarea encryption routine */
588c2ecf20Sopenharmony_ci	addq	$PAGE_SIZE, %r8		/* Workarea intermediate copy buffer */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ANNOTATE_RETPOLINE_SAFE
618c2ecf20Sopenharmony_ci	call	*%rax			/* Call the encryption routine */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	pop	%r12
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	movq	%rbp, %rsp		/* Restore original stack pointer */
668c2ecf20Sopenharmony_ci	pop	%rbp
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* Offset to __x86_return_thunk would be wrong here */
698c2ecf20Sopenharmony_ci	ANNOTATE_UNRET_SAFE
708c2ecf20Sopenharmony_ci	ret
718c2ecf20Sopenharmony_ci	int3
728c2ecf20Sopenharmony_ciSYM_FUNC_END(sme_encrypt_execute)
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciSYM_FUNC_START(__enc_copy)
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * Routine used to encrypt memory in place.
778c2ecf20Sopenharmony_ci *   This routine must be run outside of the kernel proper since
788c2ecf20Sopenharmony_ci *   the kernel will be encrypted during the process. So this
798c2ecf20Sopenharmony_ci *   routine is defined here and then copied to an area outside
808c2ecf20Sopenharmony_ci *   of the kernel where it will remain and run decrypted
818c2ecf20Sopenharmony_ci *   during execution.
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci *   On entry the registers must be:
848c2ecf20Sopenharmony_ci *     RDI - virtual address for the encrypted mapping
858c2ecf20Sopenharmony_ci *     RSI - virtual address for the decrypted mapping
868c2ecf20Sopenharmony_ci *     RDX - address of the pagetables to use for encryption
878c2ecf20Sopenharmony_ci *     RCX - length of area
888c2ecf20Sopenharmony_ci *      R8 - intermediate copy buffer
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci *     RAX - points to this routine
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci * The area will be encrypted by copying from the non-encrypted
938c2ecf20Sopenharmony_ci * memory space to an intermediate buffer and then copying from the
948c2ecf20Sopenharmony_ci * intermediate buffer back to the encrypted memory space. The physical
958c2ecf20Sopenharmony_ci * addresses of the two mappings are the same which results in the area
968c2ecf20Sopenharmony_ci * being encrypted "in place".
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_ci	/* Enable the new page tables */
998c2ecf20Sopenharmony_ci	mov	%rdx, %cr3
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Flush any global TLBs */
1028c2ecf20Sopenharmony_ci	mov	%cr4, %rdx
1038c2ecf20Sopenharmony_ci	andq	$~X86_CR4_PGE, %rdx
1048c2ecf20Sopenharmony_ci	mov	%rdx, %cr4
1058c2ecf20Sopenharmony_ci	orq	$X86_CR4_PGE, %rdx
1068c2ecf20Sopenharmony_ci	mov	%rdx, %cr4
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	push	%r15
1098c2ecf20Sopenharmony_ci	push	%r12
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	movq	%rcx, %r9		/* Save area length */
1128c2ecf20Sopenharmony_ci	movq	%rdi, %r10		/* Save encrypted area address */
1138c2ecf20Sopenharmony_ci	movq	%rsi, %r11		/* Save decrypted area address */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Set the PAT register PA5 entry to write-protect */
1168c2ecf20Sopenharmony_ci	movl	$MSR_IA32_CR_PAT, %ecx
1178c2ecf20Sopenharmony_ci	rdmsr
1188c2ecf20Sopenharmony_ci	mov	%rdx, %r15		/* Save original PAT value */
1198c2ecf20Sopenharmony_ci	andl	$0xffff00ff, %edx	/* Clear PA5 */
1208c2ecf20Sopenharmony_ci	orl	$0x00000500, %edx	/* Set PA5 to WP */
1218c2ecf20Sopenharmony_ci	wrmsr
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	wbinvd				/* Invalidate any cache entries */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Copy/encrypt up to 2MB at a time */
1268c2ecf20Sopenharmony_ci	movq	$PMD_PAGE_SIZE, %r12
1278c2ecf20Sopenharmony_ci1:
1288c2ecf20Sopenharmony_ci	cmpq	%r12, %r9
1298c2ecf20Sopenharmony_ci	jnb	2f
1308c2ecf20Sopenharmony_ci	movq	%r9, %r12
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci2:
1338c2ecf20Sopenharmony_ci	movq	%r11, %rsi		/* Source - decrypted area */
1348c2ecf20Sopenharmony_ci	movq	%r8, %rdi		/* Dest   - intermediate copy buffer */
1358c2ecf20Sopenharmony_ci	movq	%r12, %rcx
1368c2ecf20Sopenharmony_ci	rep	movsb
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	movq	%r8, %rsi		/* Source - intermediate copy buffer */
1398c2ecf20Sopenharmony_ci	movq	%r10, %rdi		/* Dest   - encrypted area */
1408c2ecf20Sopenharmony_ci	movq	%r12, %rcx
1418c2ecf20Sopenharmony_ci	rep	movsb
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	addq	%r12, %r11
1448c2ecf20Sopenharmony_ci	addq	%r12, %r10
1458c2ecf20Sopenharmony_ci	subq	%r12, %r9		/* Kernel length decrement */
1468c2ecf20Sopenharmony_ci	jnz	1b			/* Kernel length not zero? */
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Restore PAT register */
1498c2ecf20Sopenharmony_ci	movl	$MSR_IA32_CR_PAT, %ecx
1508c2ecf20Sopenharmony_ci	rdmsr
1518c2ecf20Sopenharmony_ci	mov	%r15, %rdx		/* Restore original PAT value */
1528c2ecf20Sopenharmony_ci	wrmsr
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	pop	%r12
1558c2ecf20Sopenharmony_ci	pop	%r15
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* Offset to __x86_return_thunk would be wrong here */
1588c2ecf20Sopenharmony_ci	ANNOTATE_UNRET_SAFE
1598c2ecf20Sopenharmony_ci	ret
1608c2ecf20Sopenharmony_ci	int3
1618c2ecf20Sopenharmony_ci.L__enc_copy_end:
1628c2ecf20Sopenharmony_ciSYM_FUNC_END(__enc_copy)
163