From c330af0246ac9b1c37d17fc79881fc2dd96ec80c Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Thu, 7 Jan 2021 12:48:22 -0600 Subject: OvmfPkg/MemEncryptSevLib: Address range encryption state interface BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3108 Update the MemEncryptSevLib library to include an interface that can report the encryption state on a range of memory. The values will represent the range as being unencrypted, encrypted, a mix of unencrypted and encrypted, and error (e.g. ranges that aren't mapped). Cc: Jordan Justen Cc: Laszlo Ersek Cc: Ard Biesheuvel Cc: Brijesh Singh Acked-by: Laszlo Ersek Signed-off-by: Tom Lendacky Message-Id: <0d98f4d42a2b67310c29bac7bcdcf1eda6835847.1610045305.git.thomas.lendacky@amd.com> --- .../BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf | 1 + .../BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c | 31 ++- .../BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf | 1 + .../BaseMemEncryptSevLib/SecMemEncryptSevLib.inf | 1 + .../BaseMemEncryptSevLib/X64/MemEncryptSevLib.c | 32 +++- .../BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c | 19 +- .../BaseMemEncryptSevLib/X64/SecVirtualMemory.c | 20 ++ .../BaseMemEncryptSevLib/X64/VirtualMemory.c | 207 +++++++++++++++++++++ .../BaseMemEncryptSevLib/X64/VirtualMemory.h | 35 +++- 9 files changed, 335 insertions(+), 12 deletions(-) create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c (limited to 'OvmfPkg/Library') diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf index 4480e4c..8e3b8dd 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf @@ -36,6 +36,7 @@ [Sources.X64] X64/MemEncryptSevLib.c X64/PeiDxeVirtualMemory.c + X64/VirtualMemory.c X64/VirtualMemory.h [Sources.IA32] diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c index b4f6e57..12a5bf4 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c @@ -2,7 +2,7 @@ Secure Encrypted Virtualization (SEV) library helper function - Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ Copyright (c) 2017 - 2020, AMD Incorporated. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -82,3 +82,32 @@ MemEncryptSevSetPageEncMask ( // return RETURN_UNSUPPORTED; } + +/** + Returns the encryption state of the specified virtual address range. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress Base address to check + @param[in] Length Length of virtual address range + + @retval MemEncryptSevAddressRangeUnencrypted Address range is mapped + unencrypted + @retval MemEncryptSevAddressRangeEncrypted Address range is mapped + encrypted + @retval MemEncryptSevAddressRangeMixed Address range is mapped mixed + @retval MemEncryptSevAddressRangeError Address range is not mapped +**/ +MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE +EFIAPI +MemEncryptSevGetAddressRangeState ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN Length + ) +{ + // + // Memory is always encrypted in 32-bit mode + // + return MemEncryptSevAddressRangeEncrypted; +} diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf index 0697f1d..03a78c3 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf @@ -36,6 +36,7 @@ [Sources.X64] X64/MemEncryptSevLib.c X64/PeiDxeVirtualMemory.c + X64/VirtualMemory.c X64/VirtualMemory.h [Sources.IA32] diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf index 7cd0111..279c38b 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf @@ -35,6 +35,7 @@ [Sources.X64] X64/MemEncryptSevLib.c X64/SecVirtualMemory.c + X64/VirtualMemory.c X64/VirtualMemory.h [Sources.IA32] diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c index cf0921e..4fea6a6 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c @@ -2,7 +2,7 @@ Secure Encrypted Virtualization (SEV) library helper function - Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ Copyright (c) 2017 - 2020, AMD Incorporated. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -88,3 +88,33 @@ MemEncryptSevSetPageEncMask ( Flush ); } + +/** + Returns the encryption state of the specified virtual address range. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress Base address to check + @param[in] Length Length of virtual address range + + @retval MemEncryptSevAddressRangeUnencrypted Address range is mapped + unencrypted + @retval MemEncryptSevAddressRangeEncrypted Address range is mapped + encrypted + @retval MemEncryptSevAddressRangeMixed Address range is mapped mixed + @retval MemEncryptSevAddressRangeError Address range is not mapped +**/ +MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE +EFIAPI +MemEncryptSevGetAddressRangeState ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN Length + ) +{ + return InternalMemEncryptSevGetAddressRangeState ( + Cr3BaseAddress, + BaseAddress, + Length + ); +} diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c index 3a5bab6..d3455e8 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c @@ -28,14 +28,14 @@ typedef enum { } MAP_RANGE_MODE; /** - Get the memory encryption mask + Return the pagetable memory encryption mask. - @param[out] EncryptionMask contains the pte mask. + @return The pagetable memory encryption mask. **/ -STATIC UINT64 -GetMemEncryptionAddressMask ( +EFIAPI +InternalGetMemEncryptionAddressMask ( VOID ) { @@ -200,7 +200,7 @@ Split2MPageTo4K ( PageTableEntry1 = PageTableEntry; - AddressEncMask = GetMemEncryptionAddressMask (); + AddressEncMask = InternalGetMemEncryptionAddressMask (); ASSERT (PageTableEntry != NULL); ASSERT (*PageEntry2M & AddressEncMask); @@ -286,7 +286,7 @@ SetPageTablePoolReadOnly ( LevelSize[3] = SIZE_1GB; LevelSize[4] = SIZE_512GB; - AddressEncMask = GetMemEncryptionAddressMask(); + AddressEncMask = InternalGetMemEncryptionAddressMask(); PageTable = (UINT64 *)(UINTN)PageTableBase; PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE; @@ -431,7 +431,7 @@ Split1GPageTo2M ( PageDirectoryEntry = AllocatePageTableMemory(1); - AddressEncMask = GetMemEncryptionAddressMask (); + AddressEncMask = InternalGetMemEncryptionAddressMask (); ASSERT (PageDirectoryEntry != NULL); ASSERT (*PageEntry1G & AddressEncMask); // @@ -485,7 +485,7 @@ SetOrClearCBit( { UINT64 AddressEncMask; - AddressEncMask = GetMemEncryptionAddressMask (); + AddressEncMask = InternalGetMemEncryptionAddressMask (); if (Mode == SetCBit) { *PageTablePointer |= AddressEncMask; @@ -527,6 +527,7 @@ DisableReadOnlyPageWriteProtect ( /** Enable Write Protect on pages marked as read-only. **/ +STATIC VOID EnableReadOnlyPageWriteProtect ( VOID @@ -605,7 +606,7 @@ SetMemoryEncDec ( // // Check if we have a valid memory encryption mask // - AddressEncMask = GetMemEncryptionAddressMask (); + AddressEncMask = InternalGetMemEncryptionAddressMask (); if (!AddressEncMask) { return RETURN_ACCESS_DENIED; } diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecVirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecVirtualMemory.c index 5c337ea..bca5e3f 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecVirtualMemory.c +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecVirtualMemory.c @@ -14,6 +14,26 @@ #include "VirtualMemory.h" /** + Return the pagetable memory encryption mask. + + @return The pagetable memory encryption mask. + +**/ +UINT64 +EFIAPI +InternalGetMemEncryptionAddressMask ( + VOID + ) +{ + UINT64 EncryptionMask; + + EncryptionMask = MemEncryptSevGetEncryptionMask (); + EncryptionMask &= PAGING_1G_ADDRESS_MASK_64; + + return EncryptionMask; +} + +/** This function clears memory encryption bit for the memory region specified by PhysicalAddress and Length from the current page table context. diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c new file mode 100644 index 0000000..36aabcf --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c @@ -0,0 +1,207 @@ +/** @file + + Virtual Memory Management Services to test an address range encryption state + + Copyright (c) 2020, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#include "VirtualMemory.h" + +/** + Returns the (updated) address range state based upon the page table + entry. + + @param[in] CurrentState The current address range state + @param[in] PageDirectoryEntry The page table entry to check + @param[in] AddressEncMask The encryption mask + + @retval MemEncryptSevAddressRangeUnencrypted Address range is mapped + unencrypted + @retval MemEncryptSevAddressRangeEncrypted Address range is mapped + encrypted + @retval MemEncryptSevAddressRangeMixed Address range is mapped mixed +**/ +STATIC +MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE +UpdateAddressState ( + IN MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE CurrentState, + IN UINT64 PageDirectoryEntry, + IN UINT64 AddressEncMask + ) +{ + if (CurrentState == MemEncryptSevAddressRangeEncrypted) { + if ((PageDirectoryEntry & AddressEncMask) == 0) { + CurrentState = MemEncryptSevAddressRangeMixed; + } + } else if (CurrentState == MemEncryptSevAddressRangeUnencrypted) { + if ((PageDirectoryEntry & AddressEncMask) != 0) { + CurrentState = MemEncryptSevAddressRangeMixed; + } + } else if (CurrentState == MemEncryptSevAddressRangeError) { + // + // First address check, set initial state + // + if ((PageDirectoryEntry & AddressEncMask) == 0) { + CurrentState = MemEncryptSevAddressRangeUnencrypted; + } else { + CurrentState = MemEncryptSevAddressRangeEncrypted; + } + } + + return CurrentState; +} + +/** + Returns the encryption state of the specified virtual address range. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress Base address to check + @param[in] Length Length of virtual address range + + @retval MemEncryptSevAddressRangeUnencrypted Address range is mapped + unencrypted + @retval MemEncryptSevAddressRangeEncrypted Address range is mapped + encrypted + @retval MemEncryptSevAddressRangeMixed Address range is mapped mixed + @retval MemEncryptSevAddressRangeError Address range is not mapped +**/ +MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE +EFIAPI +InternalMemEncryptSevGetAddressRangeState ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN Length + ) +{ + 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 AddressEncMask; + UINT64 PgTableMask; + PHYSICAL_ADDRESS Address; + PHYSICAL_ADDRESS AddressEnd; + MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State; + + // + // If Cr3BaseAddress is not specified then read the current CR3 + // + if (Cr3BaseAddress == 0) { + Cr3BaseAddress = AsmReadCr3(); + } + + AddressEncMask = MemEncryptSevGetEncryptionMask (); + AddressEncMask &= PAGING_1G_ADDRESS_MASK_64; + + PgTableMask = AddressEncMask | EFI_PAGE_MASK; + + State = MemEncryptSevAddressRangeError; + + // + // Encryption is on a page basis, so start at the beginning of the + // virtual address page boundary and walk page-by-page. + // + Address = (PHYSICAL_ADDRESS) (UINTN) BaseAddress & ~EFI_PAGE_MASK; + AddressEnd = (PHYSICAL_ADDRESS) + (UINTN) (BaseAddress + Length); + + while (Address < AddressEnd) { + PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask); + PageMapLevel4Entry += PML4_OFFSET (Address); + if (!PageMapLevel4Entry->Bits.Present) { + return MemEncryptSevAddressRangeError; + } + + PageDirectory1GEntry = (VOID *) ( + (PageMapLevel4Entry->Bits.PageTableBaseAddress << + 12) & ~PgTableMask + ); + PageDirectory1GEntry += PDP_OFFSET (Address); + if (!PageDirectory1GEntry->Bits.Present) { + return MemEncryptSevAddressRangeError; + } + + // + // If the MustBe1 bit is not 1, it's not actually a 1GB entry + // + if (PageDirectory1GEntry->Bits.MustBe1) { + // + // Valid 1GB page + // + State = UpdateAddressState ( + State, + PageDirectory1GEntry->Uint64, + AddressEncMask + ); + + Address += BIT30; + continue; + } + + // + // Actually a PDP + // + PageUpperDirectoryPointerEntry = + (PAGE_MAP_AND_DIRECTORY_POINTER *) PageDirectory1GEntry; + PageDirectory2MEntry = + (VOID *) ( + (PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress << + 12) & ~PgTableMask + ); + PageDirectory2MEntry += PDE_OFFSET (Address); + if (!PageDirectory2MEntry->Bits.Present) { + return MemEncryptSevAddressRangeError; + } + + // + // If the MustBe1 bit is not a 1, it's not a 2MB entry + // + if (PageDirectory2MEntry->Bits.MustBe1) { + // + // Valid 2MB page + // + State = UpdateAddressState ( + State, + PageDirectory2MEntry->Uint64, + AddressEncMask + ); + + Address += BIT21; + continue; + } + + // + // Actually a PMD + // + PageDirectoryPointerEntry = + (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory2MEntry; + PageTableEntry = + (VOID *)( + (PageDirectoryPointerEntry->Bits.PageTableBaseAddress << + 12) & ~PgTableMask + ); + PageTableEntry += PTE_OFFSET (Address); + if (!PageTableEntry->Bits.Present) { + return MemEncryptSevAddressRangeError; + } + + State = UpdateAddressState ( + State, + PageTableEntry->Uint64, + AddressEncMask + ); + + Address += EFI_PAGE_SIZE; + } + + return State; +} diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h index 26d26cd..996f94f 100644 --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h @@ -3,7 +3,7 @@ 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.
+ Copyright (c) 2017 - 2020, AMD Incorporated. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -178,7 +178,17 @@ typedef struct { UINTN FreePages; } PAGE_TABLE_POOL; +/** + Return the pagetable memory encryption mask. + @return The pagetable memory encryption mask. + +**/ +UINT64 +EFIAPI +InternalGetMemEncryptionAddressMask ( + VOID + ); /** This function clears memory encryption bit for the memory region specified by @@ -234,4 +244,27 @@ InternalMemEncryptSevSetMemoryEncrypted ( IN BOOLEAN Flush ); +/** + Returns the encryption state of the specified virtual address range. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress Base address to check + @param[in] Length Length of virtual address range + + @retval MemEncryptSevAddressRangeUnencrypted Address range is mapped + unencrypted + @retval MemEncryptSevAddressRangeEncrypted Address range is mapped + encrypted + @retval MemEncryptSevAddressRangeMixed Address range is mapped mixed + @retval MemEncryptSevAddressRangeError Address range is not mapped +**/ +MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE +EFIAPI +InternalMemEncryptSevGetAddressRangeState ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN Length + ); + #endif -- cgit v1.1