From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-oi0-x241.google.com (mail-oi0-x241.google.com [IPv6:2607:f8b0:4003:c06::241]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 025DD2193CF63 for ; Tue, 25 Apr 2017 09:36:12 -0700 (PDT) Received: by mail-oi0-x241.google.com with SMTP id y11so35430522oie.1 for ; Tue, 25 Apr 2017 09:36:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nZTw4I+k8I11DOzDi5uBCTYHJ9zX2Y+8ccisfRL2HOk=; b=eucn6NvywkULkcwG0gSrcMUnejZDm7/DGhyMQJLGXjxjde0bb14MnBV0+UWAmK6Lni TVO+V1sXxCwwDMRBDQoVLQRzKJ/LX4pumttCtG38u+wo1s4pyaEuXKHisoDym3Li6Qq0 x+zMLEuiec+kuk0EYi7qXEmPKi/1DIxrLBVbI3EhNE0EHuNpPhk9B0bR9XVQ66B0vsya U8PPFRlMYwIJynzT5SwkMOi5N10DPUGR2RB74BuqTj984UfBsNBd/NaHEP+Hx5fb79M6 7wtXwCZtKyrJFhZAmll50BBTRuCGTjGIRE7vNjwDz6pjq/XAZi7PIYuIPL3ueRRrdL+Q YbPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nZTw4I+k8I11DOzDi5uBCTYHJ9zX2Y+8ccisfRL2HOk=; b=kUQOXgtdGyiQjIDHiB05c6yDgwE6C+tT8ioR+a7ZY/NPX6G3fJeS/TSNIbkuX31Lrx +rSeIb64+LZu3kI1MQiYpOuAB3mgYMCFJcbKIZ9xNBCmJsoh/SnijgVsX0erT54dO9ap heNc16a5hGoJTE5/UATLvSi1s4iJy8E7Nf/rFEggcgEH9e5oH9NqluKAIJEaAPFoMdPk lxJmnxTAquXSmwnoyeDw7G3Z6p+ZP7pqs9mqsK3KoWQPB3rq0sIx0gTsCOUp1I3jsx4j 8vCAadDepJMe6ta7QopTLj+TW8DPcXC4B7S+H7cbn23jzjQv1L6eOLNhJUnHrsrLcYTd 1+fw== X-Gm-Message-State: AN3rC/46Rra9ou9OmlFmpQQ1mCyQ/ge4js9ZCj6G6dUYUKExmbJIahUI nuyqrIxvdBC9WJNOASM= X-Received: by 10.202.171.199 with SMTP id u190mr16518984oie.72.1493138170312; Tue, 25 Apr 2017 09:36:10 -0700 (PDT) Received: from brijesh-build-machine.amd.com ([165.204.77.1]) by smtp.gmail.com with ESMTPSA id j17sm9666356ota.24.2017.04.25.09.36.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 25 Apr 2017 09:36:09 -0700 (PDT) From: Brijesh Singh To: edk2-devel@lists.01.org, lersek@redhat.com, jordan.l.justen@intel.com Cc: jiewen.yao@intel.com, leo.duran@amd.com, star.zeng@intel.com, liming.gao@intel.com, ard.biesheuvel@linaro.org, brijesh.singh@amd.com, William.Tambe@amd.com, thomas.lendacky@amd.com Date: Tue, 25 Apr 2017 12:34:13 -0400 Message-Id: <1493138064-7816-5-git-send-email-brijesh.ksingh@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1493138064-7816-1-git-send-email-brijesh.ksingh@gmail.com> References: <1493138064-7816-1-git-send-email-brijesh.ksingh@gmail.com> Subject: [RFC v3 04/15] OvmfPkg/BaseMemcryptSevLib: Add SEV helper library X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 25 Apr 2017 16:36:12 -0000 From: Brijesh Singh Add Secure Encrypted Virtualization (SEV) helper library. The library provides the routines to: - set or clear memory encryption bit for a given memory region. - query whether SEV is enabled. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh --- OvmfPkg/OvmfPkgIa32.dsc | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 1 + OvmfPkg/OvmfPkgX64.dsc | 1 + OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf | 50 +++ OvmfPkg/Include/Library/MemEncryptSevLib.h | 79 ++++ OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.h | 34 ++ OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h | 182 +++++++++ OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c | 124 ++++++ OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c | 43 ++ OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c | 123 ++++++ OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c | 412 ++++++++++++++++++++ 11 files changed, 1050 insertions(+) diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 7fc52052a5b8..ea45d8f606ee 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -126,6 +126,7 @@ [LibraryClasses] QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf + MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf !if $(SMM_REQUIRE) == FALSE LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf !endif diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 9e4fdc3cf88b..dc38c60a70a7 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -131,6 +131,7 @@ [LibraryClasses] QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf + MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf !if $(SMM_REQUIRE) == FALSE LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf !endif diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 41ab7f84fb98..99df6d80a395 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -131,6 +131,7 @@ [LibraryClasses] QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf + MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf !if $(SMM_REQUIRE) == FALSE LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf !endif diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf new file mode 100644 index 000000000000..949c430af61b --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf @@ -0,0 +1,50 @@ +## @file +# Library provides the helper functions for SEV guest +# +# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD +# License which accompanies this distribution. The full text of the license +# may be found at http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 1.25 + BASE_NAME = MemEncryptSevLib + FILE_GUID = c1594631-3888-4be4-949f-9c630dbc842b + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemEncryptSevLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[Sources.X64] + X64/MemEncryptSevLib.c + X64/VirtualMemory.c + MemEncryptSevLibInternal.c + +[Sources.IA32] + Ia32/MemEncryptSevLib.c + MemEncryptSevLibInternal.c + +[LibraryClasses] + BaseLib + CpuLib + CacheMaintenanceLib + DebugLib + MemoryAllocationLib diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h new file mode 100644 index 000000000000..ce3f5ad723cf --- /dev/null +++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h @@ -0,0 +1,79 @@ +/** @file + + Define Secure Encrypted Virtualization (SEV) base library helper function + + Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _MEM_ENCRYPT_SEV_LIB_H_ +#define _MEM_ENCRYPT_SEV_LIB_H_ + +#include + +/** + Returns a boolean to indicate whether SEV is enabled + + @retval TRUE SEV is active + @retval FALSE SEV is not enabled + **/ +BOOLEAN +EFIAPI +MemEncryptSevIsEnabled ( + VOID + ); + +/** + This function clears memory encryption bit for the memory region specified + by BaseAddress and Number of pages from the current page table context. + + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] NumberOfPages The number of pages from start memory region. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @retval RETURN_SUCCESS The attributes were cleared for the memory region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Clearing memory encryption attribute is not + supported + **/ +RETURN_STATUS +EFIAPI +MemEncryptSevClearPageEncMask ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumberOfPages, + IN BOOLEAN CacheFlush + ); + +/** + This function sets memory encryption bit for the memory region specified by + BaseAddress and Number of pages from the current page table context. + + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] NumberOfPages The number of pages from start memory region. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @retval RETURN_SUCCESS The attributes were set for the memory region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Clearing memory encryption attribute is not + supported + **/ +RETURN_STATUS +EFIAPI +MemEncryptSevSetPageEncMask ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumberOfPages, + IN BOOLEAN CacheFlush + ); +#endif // _MEM_ENCRYPT_SEV_LIB_H_ diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.h b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.h new file mode 100644 index 000000000000..17f67b47dbee --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.h @@ -0,0 +1,34 @@ +/** @file + + Secure Encrypted Virtualization (SEV) library helper function + + Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD + License which accompanies this distribution. The full text of the license may + be found at http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _MEM_ENCRYPT_SEV_LIB_INTERNAL_H_ +#define _MEM_ENCRYPT_SEV_LIB_INTERNAL_H_ + +#include + +/** + Returns a boolean to indicate whether SEV is enabled + + @retval TRUE SEV is active + @retval FALSE SEV is not enabled + **/ +BOOLEAN +EFIAPI +InternalMemEncryptSevIsEnabled ( + VOID + ); + +#endif diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h new file mode 100644 index 000000000000..faf3c6ab01fc --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h @@ -0,0 +1,182 @@ +/** @file + + Virtual Memory Management Services to set or clear the memory encryption bit + +Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h + +**/ + +#ifndef __VIRTUAL_MEMORY__ +#define __VIRTUAL_MEMORY__ + +#include +#include +#include +#include +#include + +#include +#define SYS_CODE64_SEL 0x38 + +#pragma pack(1) + +// +// Page-Map Level-4 Offset (PML4) and +// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB +// + +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Reserved:1; // Reserved + UINT64 MustBeZero:2; // Must Be Zero + UINT64 Available:3; // Available for use by system software + UINT64 PageTableBaseAddress:40; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // No Execute bit + } Bits; + UINT64 Uint64; +} PAGE_MAP_AND_DIRECTORY_POINTER; + +// +// Page Table Entry 4KB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 PAT:1; // + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PageTableBaseAddress:40; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_4K_ENTRY; + +// +// Page Table Entry 2MB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 MustBe1:1; // Must be 1 + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PAT:1; // + UINT64 MustBeZero:8; // Must be zero; + UINT64 PageTableBaseAddress:31; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_ENTRY; + +// +// Page Table Entry 1GB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 MustBe1:1; // Must be 1 + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PAT:1; // + UINT64 MustBeZero:17; // Must be zero; + UINT64 PageTableBaseAddress:22; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_1G_ENTRY; + +#pragma pack() + +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 + +#define PAGETABLE_ENTRY_MASK ((1UL << 9) - 1) +#define PML4_OFFSET(x) ( (x >> 39) & PAGETABLE_ENTRY_MASK) +#define PDP_OFFSET(x) ( (x >> 30) & PAGETABLE_ENTRY_MASK) +#define PDE_OFFSET(x) ( (x >> 21) & PAGETABLE_ENTRY_MASK) +#define PTE_OFFSET(x) ( (x >> 12) & PAGETABLE_ENTRY_MASK) +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +/** + This function clears memory encryption bit for the memory region specified by PhysicalAddress + and length from the current page table context. + + @param[in] PhysicalAddress The physical address that is the start address of a memory region. + @param[in] Length The length of memory region + @param[in] Flush Flush the caches before applying the encryption mask + + @retval RETURN_SUCCESS The attributes were cleared for the memory region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is not supported +**/ +EFI_STATUS +EFIAPI +SetMemoryDecrypted ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINT64 Length, + IN BOOLEAN CacheFlush + ); + +/** + This function sets memory encryption bit for the memory region specified by + PhysicalAddress and length from the current page table context. + + @param[in] PhysicalAddress The physical address that is the start address + of a memory region. + @param[in] Length The length of memory region + @param[in] Flush Flush the caches before applying the + encryption mask + + @retval RETURN_SUCCESS The attributes were cleared for the memory region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is + not supported +**/ +EFI_STATUS +EFIAPI +SetMemoryEncrypted ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINT64 Length, + IN BOOLEAN CacheFlush + ); + +#endif diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c new file mode 100644 index 000000000000..d711538dfb71 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c @@ -0,0 +1,124 @@ +/** @file + + Secure Encrypted Virtualization (SEV) library helper function + + Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD + License which accompanies this distribution. The full text of the license may + be found at http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Uefi.h" +#include +#include +#include +#include +#include +#include + +#include "MemEncryptSevLibInternal.h" + +/** + + Returns a boolean to indicate whether SEV is enabled + + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled + **/ +BOOLEAN +EFIAPI +InternalMemEncryptSevIsEnabled ( + VOID + ) +{ + UINT32 RegEax; + MSR_SEV_STATUS_REGISTER Msr; + CPUID_MEMORY_ENCRYPTION_INFO_EAX Eax; + + // + // Check if memory encryption leaf exist + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_MEMORY_ENCRYPTION_INFO) { + // + // CPUID Fn8000_001F[EAX] Bit 1 (Sev supported) + // + AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, &Eax.Uint32, NULL, NULL, NULL); + + if (Eax.Bits.SevBit) { + // + // Check MSR_0xC0010131 Bit 0 (Sev is Enabled) + // + Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS); + if (Msr.Bits.SevBit) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + This function clears memory encryption bit for the memory region specified + by BaseAddress and Number of pages from the current page table context. + + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] NumberOfPages The number of pages from start memory region. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @retval RETURN_SUCCESS The attributes were cleared for the memory region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Clearing memory encryption attribute is not + supported + **/ +RETURN_STATUS +EFIAPI +MemEncryptSevClearPageEncMask ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumberOfPages, + IN BOOLEAN Flush + ) +{ + // + // Memory encryption bit is not accessible in 32-bit mode + // + return RETURN_UNSUPPORTED; +} + +/** + This function sets memory encryption bit for the memory region specified by + BaseAddress and Number of pages from the current page table context. + + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] NumberOfPages The number of pages from start memory region. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @retval RETURN_SUCCESS The attributes were set for the memory region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Clearing memory encryption attribute is not + supported + **/ +RETURN_STATUS +EFIAPI +MemEncryptSevSetPageEncMask ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumberOfPages, + IN BOOLEAN Flush + ) +{ + // + // Memory encryption bit is not accessible in 32-bit mode + // + return RETURN_UNSUPPORTED; +} diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c new file mode 100644 index 000000000000..43ecba7a28bb --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c @@ -0,0 +1,43 @@ +/** @file + + Secure Encrypted Virtualization (SEV) library helper function + + Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD + License which accompanies this distribution. The full text of the license may + be found at http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "MemEncryptSevLibInternal.h" + +STATIC BOOLEAN mSevStatus = FALSE; +STATIC BOOLEAN mSevStatusChecked = FALSE; + +/** + + Returns a boolean to indicate whether SEV is enabled + + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled + **/ +BOOLEAN +EFIAPI +MemEncryptSevIsEnabled ( + VOID + ) +{ + if (mSevStatusChecked) { + return mSevStatus; + } + + mSevStatus = InternalMemEncryptSevIsEnabled(); + mSevStatusChecked = TRUE; + + return mSevStatus; +} diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c new file mode 100644 index 000000000000..e0935705dc36 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c @@ -0,0 +1,123 @@ +/** @file + + Secure Encrypted Virtualization (SEV) library helper function + + Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD + License which accompanies this distribution. The full text of the license may + be found at http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Uefi.h" +#include +#include +#include +#include +#include +#include + +#include "VirtualMemory.h" +#include "MemEncryptSevLibInternal.h" + +/** + + Returns a boolean to indicate whether SEV is enabled + + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled + **/ +BOOLEAN +EFIAPI +InternalMemEncryptSevIsEnabled ( + VOID + ) +{ + UINT32 RegEax; + MSR_SEV_STATUS_REGISTER Msr; + CPUID_MEMORY_ENCRYPTION_INFO_EAX Eax; + + // + // Check if memory encryption leaf exist + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_MEMORY_ENCRYPTION_INFO) { + // + // CPUID Fn8000_001F[EAX] Bit 1 (Sev supported) + // + AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, &Eax.Uint32, NULL, NULL, NULL); + + if (Eax.Bits.SevBit) { + // + // Check MSR_0xC0010131 Bit 0 (Sev Enabled) + // + Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS); + if (Msr.Bits.SevBit) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + + This function clears memory encryption bit for the memory region specified by + BaseAddress and Number of pages from the current page table context. + + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] NumberOfPages The number of pages from start memory region. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @retval RETURN_SUCCESS The attributes were cleared for the memory + region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute is + not supported + **/ +RETURN_STATUS +EFIAPI +MemEncryptSevClearPageEncMask ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return SetMemoryDecrypted (BaseAddress, EFI_PAGES_TO_SIZE(NumPages), Flush); +} + +/** + + This function clears memory encryption bit for the memory region specified by + BaseAddress and Number of pages from the current page table context. + + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] NumberOfPages The number of pages from start memory region. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @retval RETURN_SUCCESS The attributes were cleared for the memory + region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute is + not supported + **/ +RETURN_STATUS +EFIAPI +MemEncryptSevSetPageEncMask ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return SetMemoryEncrypted (BaseAddress, EFI_PAGES_TO_SIZE(NumPages), Flush); +} diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c new file mode 100644 index 000000000000..23235f4268e2 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c @@ -0,0 +1,412 @@ +/** @file + + Virtual Memory Management Services to set or clear the memory encryption bit + +Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c + +**/ + +#include +#include +#include + +#include "VirtualMemory.h" + +STATIC BOOLEAN mAddressEncMaskChecked = FALSE; +STATIC UINT64 mAddressEncMask; + +typedef enum { + SetCBit, + ClearCBit +} MAP_RANGE_MODE; + +/** + Get the memory encryption mask + + @param[out] EncryptionMask contains the pte mask. + +**/ +STATIC +UINT64 +GetMemEncryptionAddressMask ( + VOID + ) +{ + UINT64 EncryptionMask; + CPUID_MEMORY_ENCRYPTION_INFO_EBX Ebx; + + if (mAddressEncMaskChecked) { + return mAddressEncMask; + } + + // + // CPUID Fn8000_001F[EBX] Bit 0:5 (memory encryption bit position) + // + AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, NULL, &Ebx.Uint32, NULL, NULL); + EncryptionMask = LShiftU64 (1, Ebx.Bits.PtePosBits); + + mAddressEncMask = EncryptionMask & PAGING_1G_ADDRESS_MASK_64; + mAddressEncMaskChecked = TRUE; + + return mAddressEncMask; +} + +/** + Split 2M page to 4K. + + @param[in] PhysicalAddress Start physical address the 2M page covered. + @param[in, out] PageEntry2M Pointer to 2M page entry. + @param[in] StackBase Stack base address. + @param[in] StackSize Stack size. + +**/ +STATIC +VOID +Split2MPageTo4K ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN OUT UINT64 *PageEntry2M, + IN PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + PHYSICAL_ADDRESS PhysicalAddress4K; + UINTN IndexOfPageTableEntries; + PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1; + UINT64 AddressEncMask; + + PageTableEntry = AllocatePages(1); + + PageTableEntry1 = PageTableEntry; + + AddressEncMask = GetMemEncryptionAddressMask (); + + ASSERT (PageTableEntry != NULL); + ASSERT (*PageEntry2M & AddressEncMask); + + PhysicalAddress4K = PhysicalAddress; + for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) { + // + // Fill in the Page Table entries + // + PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask; + PageTableEntry->Bits.ReadWrite = 1; + PageTableEntry->Bits.Present = 1; + if ((PhysicalAddress4K >= StackBase) && (PhysicalAddress4K < StackBase + StackSize)) { + // + // Set Nx bit for stack. + // + PageTableEntry->Bits.Nx = 1; + } + } + + // + // Fill in 2M page entry. + // + *PageEntry2M = (UINT64) (UINTN) PageTableEntry1 | IA32_PG_P | IA32_PG_RW | AddressEncMask; +} + +/** + Split 1G page to 2M. + + @param[in] PhysicalAddress Start physical address the 1G page covered. + @param[in, out] PageEntry1G Pointer to 1G page entry. + @param[in] StackBase Stack base address. + @param[in] StackSize Stack size. + +**/ +STATIC +VOID +Split1GPageTo2M ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN OUT UINT64 *PageEntry1G, + IN PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + PHYSICAL_ADDRESS PhysicalAddress2M; + UINTN IndexOfPageDirectoryEntries; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINT64 AddressEncMask; + + PageDirectoryEntry = AllocatePages(1); + + AddressEncMask = GetMemEncryptionAddressMask (); + ASSERT (PageDirectoryEntry != NULL); + ASSERT (*PageEntry1G & GetMemEncryptionAddressMask ()); + // + // Fill in 1G page entry. + // + *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | IA32_PG_P | IA32_PG_RW | AddressEncMask; + + PhysicalAddress2M = PhysicalAddress; + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) { + if ((PhysicalAddress2M < StackBase + StackSize) && ((PhysicalAddress2M + SIZE_2MB) > StackBase)) { + // + // Need to split this 2M page that covers stack range. + // + Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize); + } else { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | AddressEncMask; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } + } +} + + +/** + Set or Clear the memory encryption bit + + @param[in] PagetablePoint Page table entry pointer (PTE). + @param[in] Mode Set or Clear encryption bit + +**/ +STATIC VOID +SetOrClearCBit( + IN OUT UINT64* PageTablePointer, + IN MAP_RANGE_MODE Mode + ) +{ + UINT64 AddressEncMask; + + AddressEncMask = GetMemEncryptionAddressMask (); + + if (Mode == SetCBit) { + *PageTablePointer |= AddressEncMask; + } else { + *PageTablePointer &= ~AddressEncMask; + } + +} + +/** + This function either sets or clears memory encryption bit for the memory region + specified by PhysicalAddress and length from the current page table context. + + The function iterates through the physicalAddress one page at a time, and set + or clears the memory encryption mask in the page table. If it encounters + that a given physical address range is part of large page then it attempts to + change the attribute at one go (based on size), otherwise it splits the + large pages into smaller (e.g 2M page into 4K pages) and then try to set or + clear the encryption bit on the smallest page size. + + @param[in] PhysicalAddress The physical address that is the start + address of a memory region. + @param[in] Length The length of memory region + @param[in] Mode Set or Clear mode + @param[in] Flush Flush the caches before applying the + encryption mask + + @retval RETURN_SUCCESS The attributes were cleared for the memory + region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is + not supported +**/ + +STATIC +EFI_STATUS +EFIAPI +SetMemoryEncDec ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINTN Length, + IN MAP_RANGE_MODE Mode, + IN BOOLEAN CacheFlush + ) +{ + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; + PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry; + PAGE_TABLE_ENTRY *PageDirectory2MEntry; + PAGE_TABLE_4K_ENTRY *PageTableEntry; + UINT64 PgTableMask; + UINT64 AddressEncMask; + + // + // Check if we have a valid memory encryption mask + // + AddressEncMask = GetMemEncryptionAddressMask (); + if (!AddressEncMask) { + return RETURN_ACCESS_DENIED; + } + + PgTableMask = AddressEncMask | EFI_PAGE_MASK; + + if (Length == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // We are going to change the memory encryption attribute from C=0 -> C=1 or + // vice versa Flush the caches to ensure that data is written into memory with + // correct C-bit + // + if (CacheFlush) { + WriteBackInvalidateDataCacheRange((VOID*) (UINTN)PhysicalAddress, Length); + } + + while (Length) + { + PageMapLevel4Entry = (VOID*) (AsmReadCr3() & ~PgTableMask); + PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress); + if (!PageMapLevel4Entry->Bits.Present) { + DEBUG ((DEBUG_WARN, "ERROR bad PML4 for %lx\n", PhysicalAddress)); + return EFI_NO_MAPPING; + } + + PageDirectory1GEntry = (VOID*) ((PageMapLevel4Entry->Bits.PageTableBaseAddress<<12) & ~PgTableMask); + PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress); + if (!PageDirectory1GEntry->Bits.Present) { + DEBUG ((DEBUG_WARN, "ERROR bad PDPE for %lx\n", PhysicalAddress)); + return EFI_NO_MAPPING; + } + + // + // If the MustBe1 bit is not 1, it's not actually a 1GB entry + // + if (PageDirectory1GEntry->Bits.MustBe1) { + // + // Valid 1GB page + // If we have at least 1GB to go, we can just update this entry + // + if (!(PhysicalAddress & (BIT30 - 1)) && Length >= BIT30) { + SetOrClearCBit(&PageDirectory1GEntry->Uint64, Mode); + DEBUG ((DEBUG_VERBOSE, "Updated 1GB entry for %lx\n", PhysicalAddress)); + PhysicalAddress += BIT30; + Length -= BIT30; + } else { + // + // We must split the page + // + DEBUG ((DEBUG_VERBOSE, "Spliting 1GB page\n")); + Split1GPageTo2M(((UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress)<<30, (UINT64*) PageDirectory1GEntry, 0, 0); + continue; + } + } else { + // + // Actually a PDP + // + PageUpperDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER*) PageDirectory1GEntry; + PageDirectory2MEntry = (VOID*) ((PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress<<12) & ~PgTableMask); + PageDirectory2MEntry += PDE_OFFSET(PhysicalAddress); + if (!PageDirectory2MEntry->Bits.Present) { + DEBUG ((DEBUG_WARN, "ERROR bad PDE for %lx\n", PhysicalAddress)); + return EFI_NO_MAPPING; + } + // + // If the MustBe1 bit is not a 1, it's not a 2MB entry + // + if (PageDirectory2MEntry->Bits.MustBe1) { + // + // Valid 2MB page + // If we have at least 2MB left to go, we can just update this entry + // + if (!(PhysicalAddress & (BIT21-1)) && Length >= BIT21) { + SetOrClearCBit (&PageDirectory2MEntry->Uint64, Mode); + DEBUG ((DEBUG_VERBOSE, "Updated 2MB entry for %lx\n", PhysicalAddress)); + PhysicalAddress += BIT21; + Length -= BIT21; + } else { + // + // We must split up this page into 4K pages + // + DEBUG ((DEBUG_VERBOSE, "Spliting 2MB page at %lx\n", PhysicalAddress)); + Split2MPageTo4K (((UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress) << 21, (UINT64*) PageDirectory2MEntry, 0, 0); + continue; + } + } else { + PageDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER*) PageDirectory2MEntry; + PageTableEntry = (VOID*) (PageDirectoryPointerEntry->Bits.PageTableBaseAddress<<12 & ~PgTableMask); + PageTableEntry += PTE_OFFSET(PhysicalAddress); + if (!PageTableEntry->Bits.Present) { + DEBUG ((DEBUG_WARN, "ERROR bad PTE for %lx\n", PhysicalAddress)); + return EFI_NO_MAPPING; + } + SetOrClearCBit (&PageTableEntry->Uint64, Mode); + DEBUG ((DEBUG_VERBOSE, "Updated 4KB entry for %lx\n", PhysicalAddress)); + PhysicalAddress += EFI_PAGE_SIZE; + Length -= EFI_PAGE_SIZE; + } + } + } + + // + // Flush TLB + // + CpuFlushTlb(); + + return EFI_SUCCESS; +} + +/** + This function clears memory encryption bit for the memory region specified by + PhysicalAddress and length from the current page table context. + + @param[in] PhysicalAddress The physical address that is the start + address of a memory region. + @param[in] Length The length of memory region + @param[in] Flush Flush the caches before applying the + encryption mask + + @retval RETURN_SUCCESS The attributes were cleared for the memory + region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is + not supported +**/ +EFI_STATUS +EFIAPI +SetMemoryDecrypted ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINTN Length, + IN BOOLEAN CacheFlush + ) +{ + + DEBUG ((DEBUG_VERBOSE, "Clear C-bit Base %Lx Length %Lx flush %d\n", PhysicalAddress, Length, CacheFlush)); + return SetMemoryEncDec (PhysicalAddress, Length, ClearCBit, CacheFlush); +} + +/** + This function sets memory encryption bit for the memory region specified by + PhysicalAddress and length from the current page table context. + + @param[in] PhysicalAddress The physical address that is the start address + of a memory region. + @param[in] Length The length of memory region + @param[in] Flush Flush the caches before applying the + encryption mask + + @retval RETURN_SUCCESS The attributes were cleared for the memory + region. + @retval RETURN_INVALID_PARAMETER Number of pages is zero. + @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is + not supported +**/ +EFI_STATUS +EFIAPI +SetMemoryEncrypted ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINTN Length, + IN BOOLEAN CacheFlush + ) +{ + DEBUG ((DEBUG_VERBOSE, "Set C-bit Base %Lx Length %Lx flush %d\n", PhysicalAddress, Length, CacheFlush)); + return SetMemoryEncDec (PhysicalAddress, Length, SetCBit, CacheFlush); +} -- 2.7.4