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