|  | /* SPDX-License-Identifier: GPL-2.0-only */ | 
|  | /* | 
|  | * AMD Memory Encryption Support | 
|  | * | 
|  | * Copyright (C) 2017 Advanced Micro Devices, Inc. | 
|  | * | 
|  | * Author: Tom Lendacky <thomas.lendacky@amd.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/linkage.h> | 
|  |  | 
|  | #include <asm/processor-flags.h> | 
|  | #include <asm/msr.h> | 
|  | #include <asm/asm-offsets.h> | 
|  |  | 
|  | .text | 
|  | .code32 | 
|  | SYM_FUNC_START(get_sev_encryption_bit) | 
|  | xor	%eax, %eax | 
|  |  | 
|  | #ifdef CONFIG_AMD_MEM_ENCRYPT | 
|  | push	%ebx | 
|  | push	%ecx | 
|  | push	%edx | 
|  |  | 
|  | movl	$0x80000000, %eax	/* CPUID to check the highest leaf */ | 
|  | cpuid | 
|  | cmpl	$0x8000001f, %eax	/* See if 0x8000001f is available */ | 
|  | jb	.Lno_sev | 
|  |  | 
|  | /* | 
|  | * Check for the SEV feature: | 
|  | *   CPUID Fn8000_001F[EAX] - Bit 1 | 
|  | *   CPUID Fn8000_001F[EBX] - Bits 5:0 | 
|  | *     Pagetable bit position used to indicate encryption | 
|  | */ | 
|  | movl	$0x8000001f, %eax | 
|  | cpuid | 
|  | bt	$1, %eax		/* Check if SEV is available */ | 
|  | jnc	.Lno_sev | 
|  |  | 
|  | movl	$MSR_AMD64_SEV, %ecx	/* Read the SEV MSR */ | 
|  | rdmsr | 
|  | bt	$MSR_AMD64_SEV_ENABLED_BIT, %eax	/* Check if SEV is active */ | 
|  | jnc	.Lno_sev | 
|  |  | 
|  | movl	%ebx, %eax | 
|  | andl	$0x3f, %eax		/* Return the encryption bit location */ | 
|  | jmp	.Lsev_exit | 
|  |  | 
|  | .Lno_sev: | 
|  | xor	%eax, %eax | 
|  |  | 
|  | .Lsev_exit: | 
|  | pop	%edx | 
|  | pop	%ecx | 
|  | pop	%ebx | 
|  |  | 
|  | #endif	/* CONFIG_AMD_MEM_ENCRYPT */ | 
|  |  | 
|  | RET | 
|  | SYM_FUNC_END(get_sev_encryption_bit) | 
|  |  | 
|  | /** | 
|  | * sev_es_req_cpuid - Request a CPUID value from the Hypervisor using | 
|  | *		      the GHCB MSR protocol | 
|  | * | 
|  | * @%eax:	Register to request (0=EAX, 1=EBX, 2=ECX, 3=EDX) | 
|  | * @%edx:	CPUID Function | 
|  | * | 
|  | * Returns 0 in %eax on success, non-zero on failure | 
|  | * %edx returns CPUID value on success | 
|  | */ | 
|  | SYM_CODE_START_LOCAL(sev_es_req_cpuid) | 
|  | shll	$30, %eax | 
|  | orl     $0x00000004, %eax | 
|  | movl    $MSR_AMD64_SEV_ES_GHCB, %ecx | 
|  | wrmsr | 
|  | rep; vmmcall		# VMGEXIT | 
|  | rdmsr | 
|  |  | 
|  | /* Check response */ | 
|  | movl	%eax, %ecx | 
|  | andl	$0x3ffff000, %ecx	# Bits [12-29] MBZ | 
|  | jnz	2f | 
|  |  | 
|  | /* Check return code */ | 
|  | andl    $0xfff, %eax | 
|  | cmpl    $5, %eax | 
|  | jne	2f | 
|  |  | 
|  | /* All good - return success */ | 
|  | xorl	%eax, %eax | 
|  | 1: | 
|  | RET | 
|  | 2: | 
|  | movl	$-1, %eax | 
|  | jmp	1b | 
|  | SYM_CODE_END(sev_es_req_cpuid) | 
|  |  | 
|  | SYM_CODE_START(startup32_vc_handler) | 
|  | pushl	%eax | 
|  | pushl	%ebx | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  |  | 
|  | /* Keep CPUID function in %ebx */ | 
|  | movl	%eax, %ebx | 
|  |  | 
|  | /* Check if error-code == SVM_EXIT_CPUID */ | 
|  | cmpl	$0x72, 16(%esp) | 
|  | jne	.Lfail | 
|  |  | 
|  | movl	$0, %eax		# Request CPUID[fn].EAX | 
|  | movl	%ebx, %edx		# CPUID fn | 
|  | call	sev_es_req_cpuid	# Call helper | 
|  | testl	%eax, %eax		# Check return code | 
|  | jnz	.Lfail | 
|  | movl	%edx, 12(%esp)		# Store result | 
|  |  | 
|  | movl	$1, %eax		# Request CPUID[fn].EBX | 
|  | movl	%ebx, %edx		# CPUID fn | 
|  | call	sev_es_req_cpuid	# Call helper | 
|  | testl	%eax, %eax		# Check return code | 
|  | jnz	.Lfail | 
|  | movl	%edx, 8(%esp)		# Store result | 
|  |  | 
|  | movl	$2, %eax		# Request CPUID[fn].ECX | 
|  | movl	%ebx, %edx		# CPUID fn | 
|  | call	sev_es_req_cpuid	# Call helper | 
|  | testl	%eax, %eax		# Check return code | 
|  | jnz	.Lfail | 
|  | movl	%edx, 4(%esp)		# Store result | 
|  |  | 
|  | movl	$3, %eax		# Request CPUID[fn].EDX | 
|  | movl	%ebx, %edx		# CPUID fn | 
|  | call	sev_es_req_cpuid	# Call helper | 
|  | testl	%eax, %eax		# Check return code | 
|  | jnz	.Lfail | 
|  | movl	%edx, 0(%esp)		# Store result | 
|  |  | 
|  | /* | 
|  | * Sanity check CPUID results from the Hypervisor. See comment in | 
|  | * do_vc_no_ghcb() for more details on why this is necessary. | 
|  | */ | 
|  |  | 
|  | /* Fail if SEV leaf not available in CPUID[0x80000000].EAX */ | 
|  | cmpl    $0x80000000, %ebx | 
|  | jne     .Lcheck_sev | 
|  | cmpl    $0x8000001f, 12(%esp) | 
|  | jb      .Lfail | 
|  | jmp     .Ldone | 
|  |  | 
|  | .Lcheck_sev: | 
|  | /* Fail if SEV bit not set in CPUID[0x8000001f].EAX[1] */ | 
|  | cmpl    $0x8000001f, %ebx | 
|  | jne     .Ldone | 
|  | btl     $1, 12(%esp) | 
|  | jnc     .Lfail | 
|  |  | 
|  | .Ldone: | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebx | 
|  | popl	%eax | 
|  |  | 
|  | /* Remove error code */ | 
|  | addl	$4, %esp | 
|  |  | 
|  | /* Jump over CPUID instruction */ | 
|  | addl	$2, (%esp) | 
|  |  | 
|  | iret | 
|  | .Lfail: | 
|  | /* Send terminate request to Hypervisor */ | 
|  | movl    $0x100, %eax | 
|  | xorl    %edx, %edx | 
|  | movl    $MSR_AMD64_SEV_ES_GHCB, %ecx | 
|  | wrmsr | 
|  | rep; vmmcall | 
|  |  | 
|  | /* If request fails, go to hlt loop */ | 
|  | hlt | 
|  | jmp .Lfail | 
|  | SYM_CODE_END(startup32_vc_handler) | 
|  |  | 
|  | .code64 | 
|  |  | 
|  | #include "../../kernel/sev_verify_cbit.S" | 
|  |  | 
|  | .data | 
|  |  | 
|  | #ifdef CONFIG_AMD_MEM_ENCRYPT | 
|  | .balign	8 | 
|  | SYM_DATA(sme_me_mask,		.quad 0) | 
|  | SYM_DATA(sev_status,		.quad 0) | 
|  | SYM_DATA(sev_check_data,	.quad 0) | 
|  | #endif |